0

Please consider the following different ways to define method m:

  • Method 1:

    class C
      def m; yield; end
    end
    
  • Method 2:

    class C
      def initialize
        (class << self; self; end).class_eval do            
          define_method(:m){|&b| b.call }
        end
      end
    end
    
  • Method 3:

    class C
      def initialize
        (class << self; self; end).class_eval do            
          define_method(:m){puts block_given?; yield}            
        end
      end
    end
    

Then I can invoke m using Object#send.

o = C.new
o.send(:m) {puts 'test'}

While calling m using Method 1 or Method 2 works fine, Method 3 gives this error:

no block given (yield) (LocalJumpError)

I understand that a block is not an object, but instead just a part of method calling syntax and you cannot pass an implicit block from one function to another without writing something obscure like this:

def printer
  yield
end
def proxy
  printer &Proc.new
end

proxy { puts "&Proc.new probably creates Proc object from block"}

But in that case, why does Method 1 work? It would be awesome to get an answer which would explain what's happening under the hood.

sawa
  • 165,429
  • 45
  • 277
  • 381
yuyoyuppe
  • 1,582
  • 2
  • 20
  • 33
  • It is not clear what you mean by "define ... statically", "with an explicit block", or "the first case". I can guess you are probably mentioning either your Method 1, Method 2, or Method 3, but it is not clear which. – sawa Oct 12 '14 at 15:43
  • @sawa I thought it would be clear with the code fragment. You quotted Method 1, Method 2 and Method 1 respectively:) – yuyoyuppe Oct 12 '14 at 15:48
  • Why did you use `send` to call `m`? It doesn't look like it has any meaning. – sawa Oct 12 '14 at 15:59
  • @sawa this code is derived from a context where it should be called like that, but in this particular case it doesn't seem to matter. – yuyoyuppe Oct 12 '14 at 16:14
  • 1
    In case you are not aware, in Method 2 you could call `class_eval` on the class, rather than on the singleton class: `self.class.class_eval do`. – Cary Swoveland Oct 12 '14 at 17:02

1 Answers1

3

The problem with Method 3 is the scope. yield refers to the block passed to initialize, if any. It does not refer to the block passed to m. And since you created o by C.new without a block, the expected yield is absent, and that causes the error.

In Method 1, yield refers to the block passed to m. In Method 2, b refers to a proc converted from the block passed to m.

sawa
  • 165,429
  • 45
  • 277
  • 381