Unexpected behavior when referencing nested classes

J

John Feminella

It was my understanding that consts are first searched for in their
own module scope before Ruby starts looking elsewhere. So, if you have
a class called X in a module M1::M2 and another one at top level, and
you're inside M1::M2, then referencing X gets you M1::M2::X.

This doesn't appear to be the case though:

ruby-1.9.2-p0 :001 > module Foo; end
=> nil
ruby-1.9.2-p0 :002 > module Foo::Bar; class Baz; end; end
=> nil
ruby-1.9.2-p0 :003 > class Baz; def say; "::Baz"; end; end
=> nil
ruby-1.9.2-p0 :004 > class Foo::Bar::Baz; def say; "::Foo::Bar::Baz";
end; def x; Baz.new.say; end; end
=> nil

# expected "Foo::Bar::Baz"
ruby-1.9.2-p0 :005 > Foo::Bar::Baz.new.x
=> "::Baz"

Have I misunderstood how constants work?

~ jf
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

It was my understanding that consts are first searched for in their
own module scope before Ruby starts looking elsewhere. So, if you have
a class called X in a module M1::M2 and another one at top level, and
you're inside M1::M2, then referencing X gets you M1::M2::X.

This doesn't appear to be the case though:

ruby-1.9.2-p0 :001 > module Foo; end
=> nil
ruby-1.9.2-p0 :002 > module Foo::Bar; class Baz; end; end
=> nil
ruby-1.9.2-p0 :003 > class Baz; def say; "::Baz"; end; end
=> nil
ruby-1.9.2-p0 :004 > class Foo::Bar::Baz; def say; "::Foo::Bar::Baz";
end; def x; Baz.new.say; end; end
=> nil

# expected "Foo::Bar::Baz"
ruby-1.9.2-p0 :005 > Foo::Bar::Baz.new.x
=> "::Baz"

Have I misunderstood how constants work?

~ jf
--
John Feminella
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/

I have some vague hypotheses about why it does this, but don't have any idea
how to test them.



module Foo
end

module Foo::Bar
class Baz
end
end

class Baz
def say
"::Baz"
end
end


# gives the unexpected result
class Foo::Bar::Baz
def say
"::Foo::Bar::Baz"
end
def x # !> previous definition of x was here
Baz.new.say
end
end

Foo::Bar::Baz.new.x # => "::Baz"


# gives the expected result
module Foo::Bar
class Baz
def x # !> method redefined; discarding old x
Baz.new.say
end
end
end

Foo::Bar::Baz.new.x # => "::Foo::Bar::Baz"
 
A

Anurag Priyam

Have I misunderstood how constants work?

Yeah. Ruby first tries to find the constant in the "lexical scope" of
the reference - immediately enclosing module/class, then the next
enclosing module/class, and so on.

You can find out the modules/classes that are searched by calling
Module.nesting method at that point.
class Foo::Bar::Baz; def say; "::Foo::Bar::Baz";
end; def x; Baz.new.say; end; end
Foo::Bar::Baz.new.x

Here, Baz is first looked up in Foo::Bar::Baz, and then the global
namespace; confirm with Module.nesting. Ruby finds Baz in the later,
so '::Baz is the expected output.

module Foo::Bar
class Baz
def say; "::Foo::Bar::Baz"; end
def x; Baz.new.say; end
end
end

Foo::Bar::Baz.new.x #=> "::Foo::Bar::Baz"

Here, Ruby first looks up Baz in Foo::Bar::Baz, followed by Foo::Bar
finds, and then the global namespace. Again, you can confirm this with
Module.nesting.

Sometime back, I had answered a similar question. You might want to
have a look[1].

[1]: http://groups.google.com/group/ruby-talk-google/browse_thread/thread/5f3066bf9366e44f#
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top