复制控制器类实例变量 [英] Copy a controller class instance variables
问题描述
我想编写一个简单的erb模板生成器,以使用 Generator
模块从视图中解析存储的erb模板.我从rails控制器调用 Generator
,以生成它的单例实例,并通过 self
指针将其传递给 WallController
.
I want to write a simple erb template generator to parse stored erb templates from the views using Generator
module. I call the Generator
from the rails controller to generate it's singleton instances and pass it the WallController
by self
pointer.
require 'generator'
class WallController < ApplicationController
def index
header = File.read 'app/views/application-header.html'.freeze
@instances = {header: header}
# Load view generators
Generator.generate_instances self
end
end
Generator.generate_instances 实际上试图做的第一件事是复制 WallController
实例变量(因此具有自指针)以对erb模板执行正确的解析.然后生成返回erb结果文本的方法.
The first thing Generator.generate_instances
actually attempts to do is to copy the WallController
instance variables (hence the self pointer) to perform correct parsing of the erb templates. Then it generates methods returning erb resulted text.
require 'erb'
module Generator
def self.generate_instances environment
# Mimic class environment
if environment.superclass == ApplicationController
environment.instance_variables.each do |v|
puts "Copy instance variable '#{v}' from #{environment.name} to #{self.name}"
value = environment.instance_variable_get(v)
self.send :instance_variable_set, v, value
end
end
# Parse the ERB templates
templates = @instances
return 0 if !templates.is_a?(Hash) or templates.empty?
templates.keys.each.with_index do |key, index|
define_singleton_method key do
ERB.new(templates.values[index]).result
end
end
end
end
Generator
界面的用法如下:
<%=== Generator.header %>
我是Rails的新手,但是我发现Rails控制器的包含文件仅限于一个静态结构.我没有覆盖可能有用的 class Object
或 class Class
单例方法.
I am new to rails but I have found out that rails controller's included files are limited to a single static structure. I didn't manage either to overwrite class Object
or class Class
singleton methods which could be helpful.
但是,在运行上述示例后, WallController
的实例变量将返回 WallController
类地址,而不是由 WallController.index
定义的值.
However, after running the above example the instance variables of WallController
return the WallController
class address in stead of values defined by WallController.index
.
undefined method `empty?' for #<WallController:0x000000000a1f90>
是否存在在其他控制器之间分配Rails控制器实例变量的正确方法?如果没有,为什么常规实例副本不起作用?
Is there a correct way to distribute a rails controller instance variables among other controllers? If not, why are regular instance copy not working?
如果我不得不用红宝石来编写,那将很容易:
If I had to write it in ruby, that would be easy:
module Y
def self.generate_environment environment
environment.instance_variables.each do |v|
puts "Copy #{v} from #{environment.name} to #{self.name}"
value = environment.instance_variable_get v
self.instance_variable_set(v, value)
end if environment.class == Class
puts "Retrived string: #{@hello}"
end
end
class X
def self.index
@hello = 'Hello, World!'
Y.generate_environment self
end
end
X.index
推荐答案
可以使用 viewcomponent 解决此问题,它允许视图控制器使用标准的ruby代码.还解决了以合理的速度将视图代码划分为较小的可重用组件的问题.
This problem may be solved with viewcomponent, which allows for standard ruby code for the view controller. Also solves the problem of dividing the view code to smaller reusable components in reasonable speed.
要使用viewcomponent宝石,请先将其包含在您的Gemfile中.
To use the viewcomponent gem first include it to your Gemfile.
gem 'view_component', require: 'view_component/engine'
在使用 bundle install
更新了gem之后,如果服务器正在运行,则还需要重新启动服务器以应用新的gem.
After updating your gems with bundle install
, you will also need to restart your server if it's running, to apply the new gem.
然后生成组件的用法与其他rails生成器相似.第一个参数是组件名称,第二个参数是组件参数.
Then generating the component is similar in usage to other rails generators. The first argument is a component name and the second is a component argument.
rails generate component Header site_id
现在,我专注于在 app/component
目录,视图和控制器代码中生成的文件.这将只是创建视图标题片段的控制器.
Now I focus on files generated in app/component
directory, view and controller code. This will simply be the controller to create the header snippet of the view.
可以在 app/component/header_component.rb
内部封装来自 WallController
与标头视图相关的所有代码.
Inside of app/component/header_component.rb
can be encapsulated all the code from WallController
related to the header view.
class HeaderComponent < ViewComponent::Base
def initialize(site_id:)
puts "Rendering header component for site: #{site_id}"
# Load site elements
@site = Site.find site_id
@menu_items = []
Site.all.each.with_index do |site, index|
@menu_items.push site.title => site.link
end
end
end
类似地,将所有标题视图erb代码放入 app/component/header.html.erb
.
Similarly, put all the header view erb code to the app/component/header.html.erb
.
可以使用rails render从视图中生成完成的组件:
The finished component can be generated from the view using rails render:
<%= render HeaderComponent.new(site_id: 1) %>
这篇关于复制控制器类实例变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!