Sometimes you can see:
有时你可以看到:
do_this do
available_method1 "arg1"
available_method2 "arg1"
end
When I use the block from do_this method then I get some methods I could use inside that block.
当我使用do_this方法的block时,我得到了一些可以在这个block中使用的方法。
I wonder how this is accomplished? How does the code look like behind the scenes?
我想知道这是如何实现的?代码在幕后是什么样子的?
I want to be able to provide some methods through a block.
我希望能够通过一个块提供一些方法。
2 个解决方案
#1
4
It's called a Domain-Specific Language (DSL). Here's (Last archived version) some great info on various forms of Ruby DSL blocks.
它被称为领域特定语言(DSL)。下面是(最后一个存档版本)关于各种形式的Ruby DSL块的一些重要信息。
There are really two ways to go about doing this, with different syntaxes:
有两种方法可以做到这一点,不同的语法:
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
The first is more explicit and less likely to fail, while the second is more concise.
第一个更明确,更不容易失败,第二个更简洁。
The first can be implemented like so, by simply passing a new instance as the block parameter with yield
:
第一个可以这样实现,只需将一个新实例作为具有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
And the second, which runs the block in the context of the new object, giving it access to self
, instance variables, and methods:
第二,在新对象的上下文中运行block,使它能够访问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
And if you really want to do it without calling MyThing.create
, just:
如果你真的想这样做而不调用MyThing。创建,只是:
def create_thing(&block)
MyThing.create &block
end
#2
2
This is usually done using instance_eval
to change the value of self
inside the block to be some different object, which then handles those method calls.
这通常使用instance_eval将块中的self值更改为某个不同的对象,然后该对象处理这些方法调用。
As a quick example:
作为一个快速的例子:
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
Though this is a powerful language feature, and has been used before to great effect, there is still some debate on whether it's a good idea or not. If you don't know what's going on, you might be surprised that the value of @some_instance_variable
changes inside and outside the block, since it's relative to the current value of self
.
尽管这是一种强大的语言特性,而且以前也曾被用于产生巨大的影响,但关于它是否是一个好主意仍有一些争论。如果您不知道发生了什么,您可能会对@some_instance_variable的值在块内外发生变化感到惊讶,因为它相对于self的当前值。
See Daniel Azuma's excellent article for more discussion and details.
有关更多的讨论和细节,请参阅Daniel Azuma的优秀文章。
#1
4
It's called a Domain-Specific Language (DSL). Here's (Last archived version) some great info on various forms of Ruby DSL blocks.
它被称为领域特定语言(DSL)。下面是(最后一个存档版本)关于各种形式的Ruby DSL块的一些重要信息。
There are really two ways to go about doing this, with different syntaxes:
有两种方法可以做到这一点,不同的语法:
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
The first is more explicit and less likely to fail, while the second is more concise.
第一个更明确,更不容易失败,第二个更简洁。
The first can be implemented like so, by simply passing a new instance as the block parameter with yield
:
第一个可以这样实现,只需将一个新实例作为具有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
And the second, which runs the block in the context of the new object, giving it access to self
, instance variables, and methods:
第二,在新对象的上下文中运行block,使它能够访问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
And if you really want to do it without calling MyThing.create
, just:
如果你真的想这样做而不调用MyThing。创建,只是:
def create_thing(&block)
MyThing.create &block
end
#2
2
This is usually done using instance_eval
to change the value of self
inside the block to be some different object, which then handles those method calls.
这通常使用instance_eval将块中的self值更改为某个不同的对象,然后该对象处理这些方法调用。
As a quick example:
作为一个快速的例子:
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
Though this is a powerful language feature, and has been used before to great effect, there is still some debate on whether it's a good idea or not. If you don't know what's going on, you might be surprised that the value of @some_instance_variable
changes inside and outside the block, since it's relative to the current value of self
.
尽管这是一种强大的语言特性,而且以前也曾被用于产生巨大的影响,但关于它是否是一个好主意仍有一些争论。如果您不知道发生了什么,您可能会对@some_instance_variable的值在块内外发生变化感到惊讶,因为它相对于self的当前值。
See Daniel Azuma's excellent article for more discussion and details.
有关更多的讨论和细节,请参阅Daniel Azuma的优秀文章。