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