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

I know that I can dynamically define methods in a class using define_method , and that I specify the parameters that this method accepts using the block arity.

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

Unfortunately, Ruby 1.8 does not allow this, so the following will not 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 also does not block.call problem.
The transition to Ruby 1.9, unfortunately, is not an option for me. Is this an insoluble problem or is there a way to overcome it?

+8
ruby metaprogramming
source share
2 answers

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! 

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

If you need additional arguments, as well as a block, you can 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 
+6
source share

What you can do is use class_eval with a string instead of define_method . The disadvantage of this (besides the fact that you are not so elegant) is that you lose vocabulary. But this is often not required.

+4
source share

All Articles