Ruby - Lexical scope vs Inheritance
You can think of each appearance of module Something
, class Something
or def 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 as
module Foo class Bar def baz puts FOO end endend
So when trying to determine the value of FOO
, first the class Bar
is checked, and since Bar
doesn’t contain a FOO
the search moves up through the “class Bar
gateway” into the Foo
module which is the containing scope. Foo
does contain a constant FOO
(555) so this is the result you see.
The method glorf
is defined as:
class Foo::Bar def glorf puts FOO endend
Here the “gateway” is class Foo::Bar
, so when FOO
isn’t found inside Bar
the “gateway” passes through the Foo
module and straight into the top level, where there is another FOO
(123) which is what is displayed.
Note how using class Foo::Bar
creates a single “gateway”, skipping over the scope of Foo
, but module Foo; class Bar ...
opens two separate “gateways”
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:
FOO = 123module Foo FOO = 555endmodule Foo class Bar def baz puts FOO end def glorf3 puts ::FOO end endendclass Foo::Bar def glorf2 puts Foo::FOO end def glorf puts FOO endendputs Foo::Bar.new.baz # -> 555puts Foo::Bar.new.glorf # -> 123puts Foo::Bar.new.glorf2 # -> 555puts Foo::Bar.new.glorf3 # -> 123
So my thought is that when you define:
module Foo FOO = 555end
you are creating FOO
in the namespace of Foo
. So when you use it here:
module Foo class Bar def baz puts FOO end endend
you are in the Foo
namespace. However, when you refer to it in:
class Foo::Bar def glorf puts FOO endend
FOO
is coming from the default namespace (as illustrated by ::FOO
).
the first call:
puts Foo::Bar.new.baz # -> 555
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:
- FOO is searched for in Foo::Bar (the class, not the instance) scope, it is not found,
- FOO is searched for in the enclosing scope Foo (because we are within the module definition) and it is found there (555)
the second call:
puts Foo::Bar.new.glorf # -> 123
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:
- FOO is searched for in Foo::Bar (the class, not the instance) namespace, it is not found
- FOO is searched in the enclosing scope ('top level') and it is found there (123)