Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Created September 12, 2011 15:29
Show Gist options
  • Save stevenharman/1211552 to your computer and use it in GitHub Desktop.
Save stevenharman/1211552 to your computer and use it in GitHub Desktop.
Constant scoping weirdness in Ruby...
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
@mxriverlynn
Copy link

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

@stevenharman
Copy link
Author

@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? :)

@stevenharman
Copy link
Author

@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?

@mxriverlynn
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment