在Ruby on Rails中阻止调用

时间:2010-06-16 10:35:42

标签: ruby-on-rails ruby

我正在尝试清理我的代码并摆脱许多丑陋的哈希。在我的观点中,我定义了几个这样的动作:

@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

如何构建我的帮助方法以允许此操作?优选地,“项目”方法应仅在“行动”块中可用,而不在全局范围内。

谢谢!

4 个答案:

答案 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填充了这些值。