如何在Ruby中的块中提供类/对象方法?

时间:2022-10-30 07:31:46

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的优秀文章。