[英]Block call in Ruby on Rails
我正在嘗試清理我的代碼並擺脫許多丑陋的哈希值。 在我看來,我定義了以下幾種操作:
@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
我如何構造我的助手方法以允許這樣做? 優選地,“項目”方法應該僅在“動作”塊中可用,而不能在全局范圍內使用。
謝謝!
我認為這種技術被稱為“無塵室”,您在其中有一個匿名對象,其中包含您要調用的方法,因此該方法僅在您的塊中可用:
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
當然,此“ item”方法只是放置一些文本,但是您可以執行所需的任何操作。
actions do
item "foo", "bar", "baz"
end #=> these args were passed in: ["foo", "bar", "baz"]
我想做類似的事情,最后得到了一個復雜但非常有用的類,我將其命名為DslProxy。 它是我的延伸鐵寶石的一部分,但是歡迎您將其拉出並使用它,或者看看它是如何工作的。
DslProxy的文檔在這里: http ://rubydoc.info/gems/iron-extensions/1.1.2/DslProxy
github倉庫在這里: 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跟蹤器。 :-)
這是一個類似的解決方案,實際上是在創建數據結構並避免在每次操作調用時都創建一個新類:
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(..)
我進行了一些試驗,最終得出了一個目前可以使用的解決方案:
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填充了這些值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.