Why is "block_given?" Doesn't work in this dynamically defined method?

When I write methods that take an optional block, I usually use something like

block.call if block_given?

However, in a method defined dynamically, as shown below block_given?, it does not seem to work.

class Foo
  %w[bar baz].each do |method_name|
    define_singleton_method(method_name) do |&block|
      puts "Was #{method_name} given a block? #{block_given?}"
      puts block.call
    end
  end
end

Foo.bar { puts 'I am a block' }

The block is called as expected, but block_given?returns false.

Why is this?

+5
source share
1 answer

Blocks are closures, so they remember local variables (for example, method_name). They also memorize blocks: yieldand block_given?look for the block that was active at the time of the call define_method, and not the block passed to bar. There were none, so this block returns false.

def create_method
  define_singleton_method('foo') do |&block|
    puts "Was given a block? #{block_given?}"
    puts yield
    puts block.call
  end
end

create_method {'block passed to create_method'}
foo {'block passed to the created method'}

Was given a block? true
block passed to create_method
block passed to the created method
+9

All Articles