有时您可以看到:
do_this do
available_method1 "arg1"
available_method2 "arg1"
end
当我使用do_this方法中的块时,我得到了一些我可以在该块中使用的方法。
我想知道这是如何实现的?代码如何在幕后看起来像?
我希望能够通过块提供一些方法。
答案 0 :(得分:4)
它被称为域特定语言(DSL)。 Here's (Last archived version)关于各种形式的Ruby DSL块的一些很好的信息。
实际上有两种方法可以使用不同的语法:
do_thing do |thing| # with a block parameter
thing.foo :bar
thing.baz :wibble
end
# versus
do_thing do # with block-specific methods
foo :bar
baz :wibble
end
第一个更明确,更不可能失败,而第二个更简洁。
第一个可以像这样实现,只需将新实例作为块参数传递给yield
:
class MyThing
def self.create
yield new
end
def foo(stuff)
puts "doing foo with #{stuff}"
end
end
MyThing.create do |thing|
thing.foo :bar
end
第二个,它在新对象的上下文中运行块,使其可以访问self
,实例变量和方法:
class MyThing
def self.create(&block)
new.instance_eval &block
end
def foo(stuff)
puts "doing foo with #{stuff}"
end
end
MyThing.create do
foo :bar
end
如果您真的想在不致电MyThing.create
的情况下这样做,只需:
def create_thing(&block)
MyThing.create &block
end
答案 1 :(得分:2)
通常使用instance_eval
将块中的self
的值更改为某个不同的对象,然后处理这些方法调用。
作为一个简单的例子:
class ExampleReceiver
def available_method1 arg ; p [:available_method1, arg] ; end
def available_method2 arg ; p [:available_method2, arg] ; end
end
def do_this(&blk) ; ExampleReceiver.new.instance_eval(&blk) ; end
do_this do
available_method1 "arg1" #=> [:available_method1, "arg1"]
available_method2 "arg1" #=> [:available_method2, "arg1"]
end
虽然这是一个强大的语言功能,并且之前已经使用过很好的效果,但是对于它是否是一个好主意还存在一些争论。如果您不知道发生了什么,您可能会惊讶@some_instance_variable
的值在块内外发生变化,因为它相对于self
的当前值。
有关更多讨论和详细信息,请参阅Daniel Azuma's excellent article。