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.