2010-08-08 158 views
2

有时你可以看到:如何在Ruby中的块中提供类/对象方法?

do_this do 
    available_method1 "arg1" 
    available_method2 "arg1" 
end 

当我使用块从do_this方法然后我得到了一些方法,我可以说块内使用。

我想知道这是如何完成的?代码如何看起来像在幕后?

我希望能够通过块提供一些方法。

回答

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 
+0

这种例子有什么资源吗?用这样的ruby代码来描述各种设计模式是很好的。 – 2010-08-29 03:56:50

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

相关问题