Here's a question to bamboozle your noodle.
It seems there's a difference between method declaration and constant declaration in a block that is evaluated using class_eval
with a class as the receiver. Here's some code that demonstrates the discrepancy:
module X
def save_block(&block)
@block = block
end
end
module Y
extend X
save_block do
SOME_CONSTANT = 1
def meth
"a method"
end
end
def self.included(m)
m.class_eval &@block
end
end
class Z
include Y
end
class W
include Y
end
Executing this code, at least in Ruby 1.9.3, results in the following error:
warning: already initialized constant SOME_CONSTANT
Which I didn't expect. Consider first where meth
is located:
Z.instance_methods(false)
=> [:meth]
W.instance_methods(false)
=> [:meth]
And Y
doesn't have any instance methods:
Y.instance_methods(false)
=> []
This makes sense because the block is executed with Z
and W
as the receivers of class_eval
. However, the constants are different, hence the error message, as shown here:
Y.constants(false)
=> [:SOME_CONSTANT]
Z.constants(false)
=> []
W.constants(false)
=> []
Here, the constant ends up getting defined in Y
(for some bizarre reason) and thus when the block is executed a second time, the constant, SOME_CONSTANT
is already defined.
I very much look forward to some enlightenment.
Update (2012/11/19):
As @phoet pointed out below (the the response to my comment to his answer), constants are defined in the lexical scope of the block, that is to say, in the scope where the block is originally defined. In my example, this would be Y.