patrick
patrick

Reputation: 838

Re-defined method with a yield throws no block given

Can someone explain why the last yielder throws a no block given?

class Foo
  def yielder
    yield "hello"
  end
end

class Mod

  def initialize
    @@foo = Foo.new
  end

  def self.foo
    @@foo
  end

end

worker = Mod.new

Mod.foo.yielder do |hello|
  puts hello
end

Mod.foo.class.send(:define_method,:yielder) do
   yield "new hello"
end

Mod.foo.yielder do |hello|
  puts hello
end

Gives:

hello
test.rb:27:in `block in ': no block given (yield) (LocalJumpError)
from test.rb:30:in `'

Upvotes: 1

Views: 1255

Answers (1)

knut
knut

Reputation: 27875

A short introduction:

You don't need the Mod-instance, if you define @@foo outside initialize.

You don't need the Mod class to get the problem:

class Foo
  def yielder
    p 2
    yield "hello"
  end
end
foo = Foo.new

foo.yielder do |hello|
  puts hello
end

foo.class.send(:define_method,:yielder) do
  p 1
   yield "new hello"
end

foo.yielder do |hello|
  puts hello
end

You may shorten your example again:

class Foo
end
foo = Foo.new

foo.class.send(:define_method,:yielder) do
   yield "new hello"
end

foo.yielder do |hello|
  puts hello
end

This is the same as:

class Foo
  define_method(:yielder) do
    yield "new hello"
  end
end

foo = Foo.new
foo.yielder do |hello|
  puts hello
end

End of Introduction.

And now, I'm not sure if I understood correct what you want (and if I understand ruby correct ;) ) define_method accepts a block and use it as method body. If the new method should receive a block on its own, you must define it in the interface of the definition and call it:

class Foo
  define_method(:yielder) do | &prc |
    prc.call("new hello")
  end
end

foo = Foo.new
foo.yielder do |hello|
  puts hello
end

Or the same logic in your example:

class Foo
  def yielder
    yield "hello"
  end
end

class Mod

  def initialize
    @@foo = Foo.new
  end

  def self.foo
    @@foo
  end

end

worker = Mod.new

Mod.foo.yielder do |hello|
  puts hello
end

Mod.foo.class.send(:define_method,:yielder) do | &prc |
   prc.call "new hello"
end

Mod.foo.yielder do |hello|
  puts hello
end

To make the code more robust, I would recommend some checks with block_given?.

Upvotes: 4

Related Questions