问题
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?
回答1:
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
end
end
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
end
end
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”
回答2:
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 = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
def glorf3
puts ::FOO
end
end
end
class Foo::Bar
def glorf2
puts Foo::FOO
end
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
puts Foo::Bar.new.glorf2 # -> 555
puts Foo::Bar.new.glorf3 # -> 123
So my thought is that when you define:
module Foo
FOO = 555
end
you are creating FOO
in the namespace of Foo
. So when you use it here:
module Foo
class Bar
def baz
puts FOO
end
end
end
you are in the Foo
namespace. However, when you refer to it in:
class Foo::Bar
def glorf
puts FOO
end
end
FOO
is coming from the default namespace (as illustrated by ::FOO
).
回答3:
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)
回答4:
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
module Foo
FOO = 555
class Bar
def baz
puts FOO
end
end
end
where method baz belongs to class Bar in module Foo => [Bar, Foo, Object, Kernel, BasicObject]
and in that scope FOO was assigned 555
来源:https://stackoverflow.com/questions/15119724/ruby-lexical-scope-vs-inheritance