8

我知道我可以使用 动态定义类上的方法define_method,并且我可以使用块的数量来指定此方法采用的参数。

我想动态定义一个同时接受可选参数和块的方法。在 Ruby 1.9 中,这很容易,因为现在允许将块传递给块。

不幸的是,Ruby 1.8 不允许这样做,因此以下内容将不起作用:

#Ruby 1.8
class X
  define_method :foo do |bar, &baz|
    puts bar
    baz.call if block_given?
  end
end

x = X.new
x.foo("foo") { puts "called!"} #=> LocalJumpError: no block given

将显式替换为block.callyield不能解决问题。
不幸的是,升级到 Ruby 1.9 对我来说不是一个选择。这是一个棘手的问题,还是有解决办法?

4

2 回答 2

7

这适用于 Ruby 1.8.7,但不适用于 1.8.6:

class X
  define_method(:foo) do |bar, &baz|
    puts bar
    baz.call if baz
  end
end

测试:

X.new.foo("No block")
X.new.foo("With block") { puts "  In the block!"}
p = proc {puts "  In the proc!"}
X.new.foo("With proc", &p)

给出:

No block
With block
  In the block!
With proc
  In the proc!

(它给出了 1.8.6 syntax error, unexpected tAMPER, expecting '|'。)

如果你想要可选参数和块,你可以尝试这样的事情:

class X
  define_method(:foo) do |*args, &baz|
    if args[0]
      bar = args[0]
    else
      bar = "default"
    end
    puts bar
    baz.call if baz
  end
end

测试:

X.new.foo
X.new.foo { puts "  No arg but block"}

给出:

default
default
  No arg but block
于 2011-09-27T00:40:05.583 回答
4

您可以做的是使用class_eval字符串而不是define_method. 这样做的缺点(除了不那么优雅)是你失去了词法作用域。但这通常是不需要的。

于 2011-09-27T00:25:20.440 回答