我正在尝试清理我的代码并摆脱许多丑陋的哈希。在我的观点中,我定义了几个这样的动作:
@actions = {
:interest => {'Show interest', link_to(..), :disabled => true},
:follow => {'Follow this case', link_to(..)}
...
}
随着这些哈希的增长,可维护性降低。我想将上述格式转换为:
actions do
item :interest, 'Show interest', link_to(..), :disabled => true
item :follow, 'Follow', link_to(..)
...
end
如何构建我的帮助方法以允许此操作?优选地,“项目”方法应仅在“行动”块中可用,而不在全局范围内。
谢谢!
答案 0 :(得分:2)
我认为这种技术被称为“洁净室”,你有一个匿名对象,其中包含你想要调用的方法,以便该方法只能在你的块中使用:
def actions(&block)
cleanroom = Class.new{
def item(*args)
puts "these args were passed in: #{args.inspect}"
end
}
cr = cleanroom.new
cr.instance_eval &block
end
当然这个“项目”方法只是放了一些文字,但你可以随心所欲。
actions do
item "foo", "bar", "baz"
end #=> these args were passed in: ["foo", "bar", "baz"]
答案 1 :(得分:1)
这是一个类似的解决方案,实际上是在每次调用操作时创建数据结构并避免创建新类:
def action
class << @actions ||= {}
def item(name, *args) self[name] = args end
end
@actions.instance_eval(&Proc.new) if block_given?
@actions
end
您现在可以使用dsl构建该结构:
actions do
item :interest, 'Show interest', link_to(..), :disabled => true
end
actions # => { :interest => [ 'Show interest', link_to(..), :disabled => true ] }
actions.item :follow, 'Follow', link_to(..)
答案 2 :(得分:1)
我想做类似的事情,最后得到一个复杂但非常有用的类,我命名为DslProxy。它是我的铁扩展宝石的一部分,但欢迎你把它拉出来使用它,或者看看它并看看它是如何工作的。
DslProxy的文档位于:http://rubydoc.info/gems/iron-extensions/1.1.2/DslProxy
github repo在这里:https://github.com/irongaze/iron-extensions
基本上,做这件事很难。正如其他人所说,instance_eval(通常非常适合元编程)会丢失调用上下文/绑定,因此会丢失实例变量。如果你想嵌套这些构建器调用,事情会变得更加毛茸茸。
以下是我的DslProxy可以做的事情的样本:
class ItemBuilder
def actions(&block)
@actions = []
DslProxy.exec(self, &block)
@actions
end
def item(*args)
@actions << Item.new(*args)
end
end
# ... in your view ...
<%
@times = 5
builder = ItemBuilder.new
builder.actions do
item :foo, link_to(...)
@times.times do
item :bob, link_to(...)
end
end
%>
保留调用上下文(例如,link_to调用工作),传播实例变量(例如@times可用),并且ItemBuilder实例定义的方法在没有显式接收器的情况下可用(例如调用项目按预期工作)。
与所有元编程一样,这很复杂。您可能会发现在此处查看此类的规范很有帮助:https://github.com/irongaze/iron-extensions/blob/master/spec/extensions/dsl_proxy_spec.rb
如有问题请随时与我联系,或将问题发布到我的github跟踪器。 : - )
答案 3 :(得分:0)
我进行了一些实验,最终找到了迄今为止有效的解决方案:
def actions(&block)
@actions ||= []
def item(id, content = '', options = {})
@actions << [id, {
:content => content || ''
}.merge(options)]
end
block.call
end
在我的观点中允许我这样做:
actions do
item :primary, link_to('Write letter', ...), :disabled => true
end
@ actions-variable填充了这些值。