在Ruby中使用yield_method中的yield

时间:2010-02-21 17:38:44

标签: ruby metaprogramming

是否可以在给定于define_method的块中生成 yield 关键字?简单的例子:

class Test
  define_method :test do |&b|
    puts b    # => #<Proc:...>
    yield
  end
end

Test.new.test {
  puts "Hi!"
}

此代码在Ruby 1.8.7和1.9.0中都会产生以下错误:

  

test.rb:4:在`test'中:没有给出块(LocalJumpError)       来自test.rb:8

奇怪的是 b 块变量!= nil,但block_given?返回false。有意的Ruby行为是不是通过 Proc 对象识别块?

修改:关于 Beerlington 的回答:b.call()不是我想要的。块变量仅用于指示实际给出块,并且在define_method中未检测到。

我需要使用yield代替block.call

的原因

我愿意为Ruby中定义新类的方式编写一些扩展,因此任何代码当我使用扩展时,你可以接受纯Ruby编写。

因此不能考虑类似的语​​义,因为这会强制我的库的用户只使用一种正确的方法来传递一个块。这会破坏TIMTOWTDI规则,并且不会使我的库透明。

现实生活中的例子

以下代码可以简化为上面的代码,因为my_def使用define_method

require 'my_library'

class Test
  # client can write 'my_def' instead of 'def' since
  # my_library extends Class class
  my_def :test, "some parameter" do
    yield        # oh no, error :(
  end
end

Test.new.test {
  puts "Hi!"
}

2 个答案:

答案 0 :(得分:21)

我认为这就是你要找的东西:

class Test
  define_method :test do |&b|
    b.call
  end
end

Test.new.test {
  puts "Hi!"
}

更多http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/

答案 1 :(得分:18)

您无法在yield块中使用define_method。这是因为闭包捕获了块,观察:

def hello
  define_singleton_method(:bye) { yield }
end

hello { puts "hello!" }

bye {  puts "bye!" } #=> "hello!"

我认为您的用户不会介意无法以您所声明的方式使用'yield'----语法 nothing 就像普通的Ruby方法定义语法一样,所以不太可能任何困惑。

有关无法将块隐式传递给此处的方法的更多信息:http://banisterfiend.wordpress.com/2010/11/06/behavior-of-yield-in-define_method/