This is a continuation this original SO question: Using "::" instead of "module ..." for Ruby namespacing
In the original SO question, here is the scenario presented which I'm still having trouble understanding:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
end
end
class Foo::Bar
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
Can someone provide some explanation behind why the first call is returning 555 and why the second call is returning 123?
wow, great question. The best answer I can come up with is in this case you're using the module to define a namespace.
Check this out:
So my thought is that when you define:
you are creating
FOO
in the namespace ofFoo
. So when you use it here:you are in the
Foo
namespace. However, when you refer to it in:FOO
is coming from the default namespace (as illustrated by::FOO
).the first call:
prints the result of invoking method baz of an instance of class Foo::Bar
notice that Foo::Bar#baz definition is actually a closure on FOO. Following ruby's scope rules:
the second call:
prints the result of invoking method glorf of an instance of class Foo::Bar
notice that Foo::Bar#glorf definition this time is also a closure on FOO, but if we follow ruby's scope rules you'll notice that the value closed upon this time is ::FOO (top level scope FOO) in the following way:
glorf is a method of class Foo, in
=> [Foo, Module, Object, Kernel, BasicObject]
within that scope (i.e. in default/main module), FOO is assigned 123
module Foo is defined as
where method baz belongs to class Bar in module Foo
=> [Bar, Foo, Object, Kernel, BasicObject]
and in that scope FOO was assigned 555
You can think of each appearance of
module Something
,class Something
ordef something
as a “gateway” into a new scope. When Ruby is searching for the definition of a name that has been referenced it first looks in the current scope (the method, class or module), and if it isn’t found there it will go back through each containing “gateway” and search the scope there.In your example the method
baz
is defined asSo when trying to determine the value of
FOO
, first the classBar
is checked, and sinceBar
doesn’t contain aFOO
the search moves up through the “class Bar
gateway” into theFoo
module which is the containing scope.Foo
does contain a constantFOO
(555) so this is the result you see.The method
glorf
is defined as:Here the “gateway” is
class Foo::Bar
, so whenFOO
isn’t found insideBar
the “gateway” passes through theFoo
module and straight into the top level, where there is anotherFOO
(123) which is what is displayed.Note how using
class Foo::Bar
creates a single “gateway”, skipping over the scope ofFoo
, butmodule Foo; class Bar ...
opens two separate “gateways”