Ruby - Лексический объем против наследования
Это продолжение этого исходного вопроса SO: Использование "::" вместо "модуля..." для пространства имен Ruby
В исходном SO-вопросе вот представленный сценарий, который у меня все еще не понятен:
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
Может ли кто-нибудь объяснить, почему первый вызов возвращает 555 и почему второй вызов возвращает 123?
Ответы
Ответ 1
Вы можете представить каждый вид module Something
, class Something
или def something
в качестве "шлюза" в новую область. Когда Ruby ищет определение имени, на которое он ссылается, он сначала просматривает текущую область (метод, класс или модуль), и если он не найден там, он будет возвращаться через каждый из них, содержащий "шлюз", и искать область есть.
В вашем примере метод baz
определяется как
module Foo
class Bar
def baz
puts FOO
end
end
end
Поэтому, когда вы пытаетесь определить значение FOO
, сначала проверяется класс Bar
, и поскольку Bar
не содержит FOO
, поиск перемещается по "class Bar
шлюзу" в FOO
, который является областью хранения. FOO
содержит константу FOO
(555), поэтому это результат, который вы видите.
Метод glorf
определяется как:
class Foo::Bar
def glorf
puts FOO
end
end
Здесь "шлюз" class Foo::Bar
, поэтому, когда FOO
не находится внутри Bar
, "шлюз" проходит через модуль FOO
и прямо на верхний уровень, где есть еще один FOO
( 123), что и отображается.
Обратите внимание, что использование class Foo::Bar
создает один "шлюз", пропускающий область FOO
, но module Foo; class Bar ...
открывает два отдельных "шлюза"
Ответ 2
ничего себе, большой вопрос. Лучший ответ, который я могу придумать, заключается в том, что вы используете модуль для определения пространства имен.
Проверьте это:
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
Итак, я думаю, что когда вы определяете:
module Foo
FOO = 555
end
вы создаете FOO
в пространстве имен FOO
. Поэтому, когда вы используете его здесь:
module Foo
class Bar
def baz
puts FOO
end
end
end
вы находитесь в пространстве имен FOO
. Однако, когда вы ссылаетесь на него в:
class Foo::Bar
def glorf
puts FOO
end
end
FOO
поступает из пространства имен по умолчанию (как показано на рисунке ::FOO
).
Ответ 3
первый вызов:
puts Foo::Bar.new.baz # -> 555
выводит результат вызова метода baz экземпляра класса Foo:: Bar
обратите внимание, что определение Foo:: Bar # baz на самом деле является закрытием на FOO. Следуя правилам рубиновой области:
- FOO выполняется в Foo:: Bar (класс, а не экземпляр), он не найден,
- FOO выполняется в охватывающей области Foo (потому что мы находимся в пределах определения модуля) и находится там (555)
второй вызов:
puts Foo::Bar.new.glorf # -> 123
выводит результат вызова метода glorf экземпляра класса Foo:: Bar
обратите внимание, что определение Foo:: Bar # glorf также является закрытием на FOO, но если мы будем следовать правилам области рубинов, вы заметите, что значение закрыто на этот раз :: FOO (область верхнего уровня FOO) следующим образом:
- FOO выполняется в Foo:: Bar (пространстве имен класса, а не экземпляре), он не найден
- FOO выполняется в охватывающей области ( "верхний уровень" ) и находится там (123)
Ответ 4
glorf - это метод класса Foo, в => [Foo, Module, Object, Kernel, BasicObject]
внутри этой области (то есть в стандартном/основном модуле), FOO назначается 123
модуль Foo определяется как
module Foo
FOO = 555
class Bar
def baz
puts FOO
end
end
end
где метод baz принадлежит классу Bar в модуле Foo => [Bar, Foo, Object, Kernel, BasicObject]
и в этой области FOO было назначено 555