I
Intransition
I bet a few people have come across it without fully knowing what was
going on and just fixed their issue by adding `::` to the "X". That's
what I did a few times before I realized what the hell was going on.
The change needed to cause the error is this:
=A0 =A0 module X
=A0 =A0 =A0 class Foo < BasicObject
=A0 =A0 =A0 =A0 def call
=A0 =A0 =A0 =A0 =A0 X
=A0 =A0 =A0 =A0 end
=A0 =A0 =A0 end
=A0 =A0 end
Right off you can see this only effect Ruby 1.9+ (b/c 1.8 has no
BasicObject).
Next up... why it happens and how to fix.
Ok, so the fix to this problem is... #const_missing:
=A0 =A0 module X
=A0 =A0 =A0 class Foo < BasicObject
=A0 =A0 =A0 =A0 def call
=A0 =A0 =A0 =A0 =A0 X
=A0 =A0 =A0 =A0 end
def self.const_missing(const)
Object.const_get(const)
end
=A0 =A0 =A0 end
=A0 =A0 end
Which tells us why we get the error in the first place. Toplevel
constants, like toplevel instance methods, are defined on Object
itself -- the toplevel (aka `main`) is (mostly) just a proxy for
Object. And since BasicObject doesn't inherit from Object like every
other object in Ruby's universe, it can't find, in this case, module
X.
So that's the deal. If you ever run into this, you now know what to
do.
SIDE NOTE: I'm pretty sure that toplevel should not be a proxy of
object. And that constant look up should terminate with the toplevel
(just after Object) rather than with the Object class. A big benefit
from this would be the ability eval scripts at the toplevel instead of
in a protected module that emulated the toplevel (yet another proxy)
in order to prevent method definitions from polluting every object. I
mean think about that --every object. Talk about your monkey patching!
I've mentioned this to matz before, and while he's never said as much,
i'm hopeful that Ruby 2.0 (whenever that might arrive) will take the
idea to heart.
P.S. In my attempt to "quizify" this Ruby quirk, I have to say, James
Edward Gray II, I am humbled!