#with(object)& block trick

时间:2011-08-19 01:33:25

标签: ruby idioms

有一种常见的习惯用法,比如:

def with clazz, &block
  yield clazz
  clazz
end

with Hash.new |hash|
  hash.merge!{:a => 1}
end

有没有办法进一步定义#with以便有可能做到:

with Hash.new |hash|
  merge!{:a => 1} 
end

甚至:

with Hash.new do
  merge!{:a => 1}
end


更新

后来我偶然发现了我正在寻找的东西(解决方案类似于已接受的解决方案): http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/19153

更新2

它被添加到https://github.com/kristianmandrup/sugar-high

中的sugar-high / dsl中

更新3

docille project on Github很好地利用了这个想法。

3 个答案:

答案 0 :(得分:8)

如果您指的是Rails路由的方式,那么我认为你需要做这样的事情

def with(instance, &block)
  instance.instance_eval(&block)
  instance
end

with(Hash.new) do
  merge!({:a => 1})
  merge!({:b => 1})
end

这就是我在Rails源代码中看到它的方式,无论如何从action_pack/lib/action_dispatch/routing/route_set

中查看draw方法开始

答案 1 :(得分:3)

不是你的伪Ruby:

with Hash.new do |hash|
  merge!{:a => 1} 
end

与使用1.9的tap相同?例如:

>> x = Hash[:a, :b].tap { |h| h.merge!({:c => :d}) }
=> {:a=>:b, :c=>:d}

当然,您仍需要命名块参数。

答案 2 :(得分:1)

您可以使用ruby builtin tap

Hash.new.tap do |hash|
  hash.merge! a: 1
end

对于多个对象,这甚至可能被“滥用”:

[one_long_name, another_long_name].tap do |(a,b)|
  a.prop = b.prop
end

当然,根据您的示例,两者都没有准确地给出with所做的事情:不会在对象的实例中评估该块。但我更喜欢将tap用于多个对象,加上tap返回self,因此可以将其链接起来:

[one_long_name, another_long_name].tap {|(a,b)| a.prop = b.prop }.inspect