我正在处理许多预先存在的文件、类和模块,并试图为框架的不同组件提供更好的命名空间。我一直使用模块作为命名空间的一种方式,主要是因为这似乎是标准约定(并且能够“包含”框架的不同部分可能很有用)。
问题是在全局命名空间下有大量应该存在于模块下的类。例如,假设有一个简单定义为的类:
class FirstClass
def meth
puts "HELLO"
end
end
Run Code Online (Sandbox Code Playgroud)
但现在我想在一个模块中拥有这个类:
使用双冒号:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
Run Code Online (Sandbox Code Playgroud)
使用模块块:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
Run Code Online (Sandbox Code Playgroud)
使用双冒号更简洁,也更容易实现,因为我正在更改许多类定义。这两种方式都有效,我认为它们实际上是一回事,但显然它们不是。与模块块相比,双冒号方法似乎导致每个类中的命名空间不同。例如,在“Foo”下面有两个类:
使用模块块:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
class SecondClass
def meth
FirstClass.new.meth
end
end
end
Foo::SecondClass.new.meth
Run Code Online (Sandbox Code Playgroud)
使用双冒号:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
class Foo::SecondClass
def meth
FirstClass.new.meth
end
end
Foo::SecondClass.new.meth
Run Code Online (Sandbox Code Playgroud)
该代码在使用模块块时有效,但不适用于双冒号。使用双冒号,会引发 NameError,因为它解析FirstClass为Foo::SecondClass::FirstClass(而不是Foo::FirstClass),而后者不存在。
这可以通过包含Fooin轻松解决SecondClass,但为什么默认情况下不这样做?
注意:我使用的是 Ruby 2.1.5,我知道它已经过时了,但是我在 repl.it 上使用 ruby 2.5.5p157 得到了相同的结果:https ://repl.it/@joep2/Colon-vs-Block-命名空间
这可能看起来违反直觉,但 Ruby 中的持续查找是使用当前词法范围完成的,即当前词法嵌套级别(源代码中的位置),而不是语义嵌套级别。
这可以通过检查来测试Module.nesting,它打印当前词法范围:
class Foo::SecondClass
pp Module.nesting # -> [Foo::SecondClass]
end
module Foo
class SecondClass
pp Module.nesting # -> [Foo::SecondClass, Foo]
end
end
Run Code Online (Sandbox Code Playgroud)
由于 Ruby 使用此嵌套级别进行符号查找,这意味着在您尝试FirstClass在嵌套中查找的情况下[Foo::SecondClass],Ruby 将找不到它。
但是,当您尝试在嵌套中查找它时[Foo::SecondClass, Foo],它会FirstClass在Foo,就像您期望的那样。
为了解决这个问题,你可以这样做:
class Foo::SecondClass
def meth
Foo::FirstClass.new.meth
end
end
Run Code Online (Sandbox Code Playgroud)
现在将按您的预期工作,因为您为 提供了必要的查找提示FirstClass,并告诉 Ruby 它在Foo.
| 归档时间: |
|
| 查看次数: |
194 次 |
| 最近记录: |