At first blush it seems possible to pass blocks implicitly to methods created with define_method by simply using a yield inside the define_method block.

Take a look at the following code:

def make_method define_singleton_method(:hello) { yield } end make_method hello { puts "hi" } #=> LocalJumpError: no block given

So, notwithstanding that we’re actually passing a block to hello the yield is failing to invoke it.

Now let’s try something else:

make_method { puts "make_method block" } hello { puts "hi" } #=> "make_method block"

That’s right, the yield in the define_method block is actually causing the hello method to close over the block passed to make_method . The block passed to hello itself is simply discarded.

Note that Proc.new inside a define_method also uses the block passed to make_method :

def make_method define_singleton_method(:hello) { block = Proc.new; block.call } end make_method { puts "make_method block" } hello #=> "make_method block"

Conclusion

Everyone knows that Ruby’s blocks close over local variables and constants but it appears they close over blocks too. The behavior of yield in define_method is not therefore (in my opinion) an inconsistency as has been argued elsewhere, but it still may be confusing.

As a result of the fact blocks are captured by closures they cannot be passed implicitly to methods created with define_method ; they must instead be passed explicitly using the define_method(meth) { |args, &block| ... } syntax.

Share this: Twitter

Facebook

Reddit

Like this: Like Loading... Related