Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't `block_given?` work in this dynamically-defined method?

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

block.call if block_given?

However, in method defined dynamically like the one below, block_given? doesn't 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?

like image 737
Nathan Long Avatar asked Aug 22 '12 09:08

Nathan Long


1 Answers

Blocks are closures, so they remember local variables (eg method_name). They also remember blocks: yield and block_given? are looking for the block that was active at the time that define_method was called, not the block passed to bar. There wasn't one, so block given returns false.

A better illustration of this is

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'}

which outputs

Was given a block? true
block passed to create_method
block passed to the created method
like image 81
Frederick Cheung Avatar answered Sep 30 '22 14:09

Frederick Cheung