I made a class with a constant & a method:
class A
FOO = 'hello'
def bar
puts FOO
end
end
A.new.bar
=> 'hello'
And everything works as expected. However when I do this:
A.class_eval do
def bar
puts FOO
end
end
A.new.bar
NameError: uninitialized constant FOO
Weirdness... To get around this I'm doing:
A.class_eval do
def bar
puts self.class::FOO
end
end
Any good explanation on why this is so?
Constants are looked up
So, let's just do what Ruby does:
class Object
– but Object
doesn't have a constant named FOO
and neither do its ancestors Kernel
and BasicObject
.Ergo: Ruby is right. There is no constant named FOO
within the constant lookup path. It didn't remove FOO
from the scope, it was never in scope to begin with.
[Ruby's reflection API actually gives you access to anything you need: #1 is exactly the same as Module.nesting
and #2 is (almost) the same as Module.nesting.first.ancestors
.]
You might think: wait, isn't module_eval
a module declaration? No, it isn't! It's just a method like any other method taking a block that's just like any other block. It doesn't alter the constant lookup rules in any way.
Note that that's not quite true: after all, e.g. instance_eval
does change method lookup rules, for example, so it would not be inconceivable that module_eval
changes constant lookup rules. And even more confusingly, when called with a String
instead of a block, it actually does change Module.nesting
and thus the constant lookup rules!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With