Unespected result of define_method with "for" and "each"

Hi everyone,

I was trying to define instance methods on a class based on an given
array of strings. I run into different results using two different types
of iteration. I can’t understand the resulting behaviour just in a case,
the following piece of code will be more meaningful than words:

class Toy

end

%w{e1 e2}.each { |m|
Toy.class_eval do
define_method m do
puts m
end
end
}

for m in %w{u1 u2}
Toy.class_eval do
define_method m do
puts m
end
end
end

p=Toy.new

puts p.e1 # ok
puts p.e2 # ok
puts p.u1 # WTF
puts p.u2 # ok

I tested it with ruby 1.8.7 and with 1.9.2 and I get the same results .
Obviously, the third result is the only I don’t understand :). It seems
that define_method defers the evaluation of “m” object when used with a
for.

I’m relatively new to Ruby and maybe this is the expected behaviour. I
would like to understand the difference between the to version, thank
you.

P.S. I added this example as a gist, you can read it here
https://gist.github.com/868310

On Mon, Mar 14, 2011 at 11:20 AM, lucapette [email protected] wrote:

define_method m do
end

I’m relatively new to Ruby and maybe this is the expected behaviour. I would
like to understand the difference between the to version,
thank you.

It is expected, and the reason is the way closures work and the fact
that the block passed to “each” is a new scope, while the body of the
“for” is not a new scope. In the first case, the closures created by
class_eval and define_method close over a local variable m, which is
different for each iteration
, because it is local to the block passed
to each, and the block is a new scope, so every iteration creates a
new variable m. In the second case, though, the m is a local variable
but to the whole script, it’s not local to the body of the for, since
the body of the for does not create a new scope. And so, in this case
the closures created by class_eval and define_method close over a
single variable m, which after all is said and done refers to the
string “u2”, so when you are calling the method u1, it returns this
string.

Jesus.

Thank you for the reply. It’s a perfect explanation for me. There is
always something new to learn with Ruby. That’s why I love it so much.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs