A little challenge - reproduce this error

Well, I give up :slight_smile:

ยทยทยท

On Fri, Jun 10, 2011 at 12:08 PM, Intransition <transfire@gmail.com> wrote:

On Jun 9, 1:22 pm, John Feminella <jo...@bitsbuilder.com> wrote:
> > 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.
>
> 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

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, 1:19 pm, Josh Cheek <josh.ch...@gmail.com> wrote:

Well, I give up :slight_smile:

Ok, so the fix to this problem is... #const_missing:

 module X
   class Foo &lt; 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 Gray II, I am humbled! :slight_smile:

ยทยทยท

On Jun 10, 2:52 pm, Intransition <transf...@gmail.com> wrote:

On Jun 10, 1:19 pm, Josh Cheek <josh.ch...@gmail.com> 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 &lt; 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.

>
> > 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.

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.

Fascinating stuff!

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.

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 Gray II, I am humbled! :slight_smile:

lol.

ยทยทยท

On Sat, Jun 11, 2011 at 8:36 AM, Intransition <transfire@gmail.com> wrote:

On Jun 10, 2:52 pm, Intransition <transf...@gmail.com> wrote:
> On Jun 10, 1:19 pm, Josh Cheek <josh.ch...@gmail.com> wrote: