开发者

Can I dynamically define a Ruby method that takes a block?

I know that I can dynamically define methods on a class using define_method, and that I specify the parameters this method take开发者_高级运维s using the arity of the block.

I want to dynamically define a method that accepts both optional parameters and a block. In Ruby 1.9, this is easy because passing a block to a block is now allowed.

Unfortunately, Ruby 1.8 doesn't allow this, so the following won't work:

#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

Replacing the explicit block.call with yield doesn't fix the problem either.

Upgrading to Ruby 1.9 is unfortunately not an option for me. Is this an intractable problem, or is there a way around it?


This works with Ruby 1.8.7, but not 1.8.6:

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

Testing with:

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)

gives:

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

(with 1.8.6 it gives syntax error, unexpected tAMPER, expecting '|'.)

If you want optional arguments as well as block, you could try something like this:

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

testing with:

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

gives:

default
default
  No arg but block


What you could do is use class_eval with a string instead of define_method. The downside to this (apart from not being as elegant) is that you lose lexical scoping. But this is often not needed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜