A little challenge - reproduce this error

On Thu, Jun 09, 2011 at 01:19:25AM +0900, Chad P. wrote:

It works the same both 1.8.7, 1.9.1, and 1.9.2 for me.

Wow. Um. Sorry. I mangled the grammar of that sentence. It should
have read as follows:

It works in 1.8.7, 1.9.1, and 1.9.2 for me.

On Jun 8, 8:39pm, Christopher D. [email protected] wrote:

I could swear that I have run into that error in some things in the
past (and resolved it by fully qualifying the affected references),
but now I can’t figure out how to reproduce it.

I realize now maybe how I should have phrased this as a challenge

module X
  class Foo
    def call
      X
    end
  end
end

Change one line of this code to cause this error:

uninitialized constant X::Foo::X (NameError)

and without directly calling #raise.

Something like that anyway. Hindsight being 20/20 and all. Of course,
I made it a challenge simply b/c I thought it would be a bit more
interesting that way.

I let this sit for a bit longer then I’ll give the lowdown and a
useful tip on how to deal with it when it arises.

Change one line of this code to cause this error:

uninitialized constant X::Foo::X (NameError)

and without directly calling #raise.

Just to be clear, do you mean “without invoking #call” (as in, pasting
this into irb should raise the error), or is the intention that #call
will be invoked?

John F.
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: User John Feminella - Stack Overflow

On Jun 9, 1:22pm, John F. [email protected] wrote:

Change one line of this code to cause this error:

uninitialized constant X::Foo::X (NameError)

and without directly calling #raise.

Just to be clear, do you mean “without invoking #call” (as in, pasting
this into irb should raise the error), or is the intention that #call
will be invoked?

When #call is invoked, the error is raised. So yea, guess I should
have added:

X::Foo.new.call

Thomas S. wrote in post #1004199:

module X
  class Foo
    def call
      X
    end
  end
end

Ah, that’s important, since module and class behave differently for
constant lookups:

module X
class Foo
self::X # warning: toplevel constant X referenced by X::Foo::X
end
module Bar
self::X # uninitialized constant X::Bar::X (NameError)
end
end

But I second John’s request. Is the error triggered by X::Foo.new.call,
or just by running the module/class definition code?

On Fri, Jun 10, 2011 at 12:08 PM, Intransition [email protected]
wrote:

When #call is invoked, the error is raised. So yea, guess I should
have added:

X::Foo.new.call

Well, I give up :slight_smile:

On Jun 10, 1:19pm, Josh C. [email protected] wrote:

Well, I give up :slight_smile:

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:

module X
  class Foo < BasicObject
    def call
      X
    end
  end
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.

On Jun 10, 2:52pm, Intransition [email protected] wrote:

module X
Next up… why it happens and how to fix.
Ok, so the fix to this problem is… #const_missing:

module X
class Foo < BasicObject
def call
X
end
def self.const_missing(const)
Object.const_get(const)
end
end
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 G. II, I am humbled! :slight_smile:

On Sat, Jun 11, 2011 at 8:36 AM, Intransition [email protected]
wrote:

Right off you can see this only effect Ruby 1.9+ (b/c 1.8 has no
end
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.

Fascinating stuff!

I could see that. I already avoid defining toplevel methods, but when I
do
make them, I usually make them on main’s singleton class (unless, of
course,
it really does need to be globally callable).

P.S. In my attempt to “quizify” this Ruby quirk, I have to say, James
Edward G. II, I am humbled! :slight_smile:

lol.