About define_method

I want to define a method in kernel using Flattern Scope.

Kernel.send :define_method, :each_event do
events.each_pair do |message, condition|
yield message, condition
end
end

so, I think this is equal to:

def Kernel.each_event

events.each_pair do |message, condition|
  yield message, condition
end

end

Then I can pass a block to this method, for example:
Kernel.each_event do |message, condition|

end

However, if I use send define_method method to define each_event, I
would met a exception:
in `each_event’: no block given (LocalJumpError)

I do not know why? if I do not use “send”, the “yield” work well. Also,
I can define method as follows according to “meta programming ruby”
says:

Kernel.send :define_method, :each_event do |&block|
events.each_pair do |message, condition|
block.call message, condition
end
end

please help me to figure out why I can not use the first solution.

On Mon, Oct 15, 2012 at 8:37 AM, li shoubo [email protected] wrote:

please help me to figure out why I can not use the first solution.

You simply cannot define methods which use block passed to them via
define_method as you have found out. Minimalistic example:

$ ruby -e ‘module Kernel;def foo; yield 1 end end; foo {|x| p x}’
1
$ ruby -e ‘module Kernel;define_method(foo) { yield 1 };end;foo {|x| p
x}’
-e:1:in <module:Kernel>': undefined local variable or method foo’
for Kernel:Module (NameError)
from -e:1:in `’

Another variant is to use class_eval:

$ ruby -e ‘Kernel.class_eval “def foo; yield 1 end”;foo {|x| p x}’
1

Kind regards

robert

thanks, I use your example in irb:

module Kernel

define_method(:foo) { yield 1}

end

no error, but if I invoke it:

Kernel.foo { 2 }

the error jumps,

LocalJumpError: no block given

however, this is OK

module Kernel

define_method(:foo) { 1 }

end

It seems I can not use yield in define_method, can you tell me more
details?

Robert K. wrote in post #1079864:

On Mon, Oct 15, 2012 at 8:37 AM, li shoubo [email protected] wrote:

please help me to figure out why I can not use the first solution.

You simply cannot define methods which use block passed to them via
define_method as you have found out. Minimalistic example:

$ ruby -e ‘module Kernel;def foo; yield 1 end end; foo {|x| p x}’
1
$ ruby -e ‘module Kernel;define_method(foo) { yield 1 };end;foo {|x| p
x}’
-e:1:in <module:Kernel>': undefined local variable or method foo’
for Kernel:Module (NameError)
from -e:1:in `’

Another variant is to use class_eval:

$ ruby -e ‘Kernel.class_eval “def foo; yield 1 end”;foo {|x| p x}’
1

Kind regards

robert

On Oct 15, 2012, at 00:15 , Robert K. [email protected]
wrote:

$ ruby -e ‘module Kernel;define_method(foo) { yield 1 };end;foo {|x| p x}’
-e:1:in <module:Kernel>': undefined local variable or method foo’

you meant :foo

can you give me more details? Or it is a rule that I can not use yield
in define_method method to define a new one?

Robert K. wrote in post #1079874:

On Mon, Oct 15, 2012 at 9:49 AM, Ryan D. [email protected]
wrote:

On Oct 15, 2012, at 00:15 , Robert K. [email protected] wrote:

$ ruby -e ‘module Kernel;define_method(foo) { yield 1 };end;foo {|x| p x}’
-e:1:in <module:Kernel>': undefined local variable or method foo’

you meant :foo

Yes, of course. Thanks for catching that. Then we get

$ ruby -e ‘module Kernel;define_method(:foo) { yield 1 };end;foo {|x| p
x}’
-e:1:in block in <module:Kernel>': no block given (yield) (LocalJumpError) from -e:1:in

Kind regards

robert

On Mon, Oct 15, 2012 at 9:49 AM, Ryan D. [email protected]
wrote:

On Oct 15, 2012, at 00:15 , Robert K. [email protected] wrote:

$ ruby -e ‘module Kernel;define_method(foo) { yield 1 };end;foo {|x| p x}’
-e:1:in <module:Kernel>': undefined local variable or method foo’

you meant :foo

Yes, of course. Thanks for catching that. Then we get

$ ruby -e ‘module Kernel;define_method(:foo) { yield 1 };end;foo {|x| p
x}’
-e:1:in block in <module:Kernel>': no block given (yield) (LocalJumpError) from -e:1:in

Kind regards

robert

On Mon, Oct 15, 2012 at 1:27 PM, li shoubo [email protected] wrote:

can you give me more details? Or it is a rule that I can not use yield
in define_method method to define a new one?

You got it. But you can use Proc#call instead:

irb(main):013:0> String.send(:define_method, :foo) {|a, &b| b.call(a +
length())}
=> #<Proc:0x802b6c4c@(irb):12 (lambda)>
irb(main):014:0> s=“bar”
=> “bar”
irb(main):015:0> s.foo(10) {|x| p x}
13
=> 13

Kind regards

robert

I looked at all the posts again, and realized that both, you li, and
Robert, have already presented the proper solution to your original
post. In fact you yourself, li, offered the solution at the end of your
first post above. Namely, contrary to the quote in ‘The Ruby P.ming
Language’ book, the following code with ‘define_method’ works just fine:

Kernel.send :define_method, :each_event do |events, &block|
events.each_pair do |message, condition|
block.call message, condition
end
end
events = { one: 1, two: 2, three: 3 }
each_event(events) { |m, c| puts “message:#{m}, condition:#{c}” }

li shoubo wrote in post #1079862:

I want to define a method in kernel using Flattern Scope.

Kernel.send :define_method, :each_event do
events.each_pair do |message, condition|
yield message, condition
end
end
. . .
However, if I use send define_method method to define each_event, I
would met a exception:
in `each_event’: no block given (LocalJumpError)

I do not know why? if I do not use “send”, the “yield” work well. Also,
I can define method as follows according to “meta programming ruby”
says:

Kernel.send :define_method, :each_event do |&block|
events.each_pair do |message, condition|
block.call message, condition
end
end

The problem is not the ‘send’, but rather the ‘define_method’. Namely,
‘define_method’ does not allow you to specify a method body that expects
a blok. If you need to dynamically create a method that accepts a block,
you need to use the ‘def method_name; …; end’ and the ‘class_eval’
(see: D. Flanagan’s & Y. Matsumoto’s book ‘The Ruby P.ming
Language’ page:275).

So, your example should look something like the following:

Kernel.class_eval do
def each_event(events)
events.each_pair do |message, condition|
yield message, condition
end
end
end
events = { one: 1, two: 2, three: 3 }
each_event(events) { |x, y| puts “#{x}, #{y}” }

Regards, igor