-
-
Save stevenharman/1211552 to your computer and use it in GitHub Desktop.
module Foo | |
end | |
module Foo::Bar | |
end | |
class Baz | |
end | |
class Quux | |
end | |
class Foo::Bar::Base | |
end | |
# a class that inherits from a base | |
class Foo::Bar::Baz < Foo::Bar::Base | |
def a_method | |
puts Baz | |
end | |
end | |
# a class that mixes the "base stuff" in | |
class Foo::Bar::Quux | |
include Foo::Bar | |
def a_method | |
puts Quux | |
end | |
end | |
puts "a new Foo::Bar::Baz, Baz => " | |
f = Foo::Bar::Baz.new | |
f.a_method #=> Baz | |
puts "a new Foo::Bar::Quux, Quux => " | |
q = Foo::Bar::Quux.new | |
q.a_method #=> Foo::Bar::Quux |
@derickbailey Actually, there are two Baz
classes, one at the main scope and one within the Foo::Bar
module.
I understand using the scope operator to say, "give me the one from the main scope" via ::Bar
or ::Quux
. What I don't understand is why Foo::Bar::Baz#a_method
resolves Baz
to be ::Baz
but Foo::Bar::Quux#a_method
(which mixed Foo::Bar
in) resolves Quux
to be Foo::Bar::Quux
WTF? :)
@derickbailey I think its the mixing in of the Foo::Bar
module that is causing the change in how Quux
is resolved inside Foo::Bar::Quux#a_method
.
Including a module inserts that module in the class hierarchy, and so lookups for constants in that module (i.e. Foo::Bar::Quux
) are found in the included module, and Ruby stops moving up the chain, thus never making it to the main Object::Quux
constant that we really wanted.
Does that make any sense?
ah - i didn't notice that second Baz class... your thinking on the lines of the module sounds correct. since a module is injected into the inheritance hierarchy, it makes sense that the code would move up that hierarchy to see if it can find a reference to the Quux / Baz class in the hierarchy, first. if not, then it looks back to the root namespace...
of course, i've seen situations where this kind of gets tossed upside down, but i think those are edge cases caused by duplicating module and class names in different namespaces. :P
ruby basically uses a "nearest" search when looking for namespaced vs non-namespaced objects. in the case of Baz, there is only one Baz class in the code, so there's only one place to look for it. in the case of Quux, there are two places where it's defined - one with a namespace and one without. when you call code outside of any namespace and look for Quux, you'll find the non-namespaced version. when you call code from within the Foo::Bar namespace, as you're doing in the second q.a_method call, you'll find the Foo::Bar::Quux object because ruby searches the current namespace for the object you asked for, first.
if you want to always find the non-namespaced version, prefix your Quux call with ::
puts ::Quux
this tells ruby to look at the root namespace to find Quux