带有模块的 Ruby 类命名空间:为什么我会收到带有双冒号的 NameError 而不是模块块?

2024-03-24

我正在处理许多预先存在的文件、类和模块,并尝试为框架的不同组件提供更好的命名空间。我一直使用模块作为命名空间的方式,主要是因为这似乎是标准约定(并且能够“包含”框架的不同部分可能很有用)。

问题在于,全局命名空间下有大量本应存在于模块下的类。例如,假设有一个类简单定义为:

class FirstClass
  def meth
    puts "HELLO"
  end
end

但现在我想在模块中包含此类:

使用双冒号:

module Foo; end

class Foo::FirstClass
  def meth
    puts 'HELLO'
  end
end

使用模块块:

module Foo
  class FirstClass
    def meth
      puts 'HELLO'
    end
  end

使用双冒号更简洁,也更容易实现,因为我正在更改许多类定义。这两种方式都有效,我thought它们实际上是同一件事,但显然它们不是。与模块块相比,双冒号方法似乎会在每个类中产生不同的命名空间。例如,“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

使用双冒号:

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

该代码在使用模块块时有效,但不适用于双冒号。使用双冒号时,会引发 NameError,因为它解决了FirstClass as Foo::SecondClass::FirstClass(代替Foo::FirstClass),这是不存在的。

这可以通过包含来轻松解决Foo in SecondClass,但是为什么默认情况下不这样做呢?

注意:我使用的是 Ruby 2.1.5,我知道它已经过时了,但我在 repl.it 上使用 ruby​​ 2.5.5p157 得到了相同的结果:https://repl.it/@joep2/Colon-vs-Block-Namespacing https://repl.it/@joep2/Colon-vs-Block-Namespacing


这可能看起来违反直觉,但 Ruby 中的持续查找是使用 current 完成的词汇范围,即当前的词法嵌套级别(源代码中的位置),而不是语义嵌套级别。

这可以通过检查来测试Module.nesting,它打印当前的词法范围:

class Foo::SecondClass
  pp Module.nesting       # -> [Foo::SecondClass]
end

module Foo
  class SecondClass
    pp Module.nesting     # -> [Foo::SecondClass, Foo]
  end
end

由于 Ruby 使用此嵌套级别进行符号查找,这意味着在您尝试查找的情况下FirstClass嵌套内[Foo::SecondClass],Ruby不会找到它。

但是,当您尝试在嵌套中查找它时[Foo::SecondClass, Foo],它会发现FirstClass under Foo,正如您所期望的那样。

为了解决这个问题,你可以这样做:

class Foo::SecondClass
  def meth
    Foo::FirstClass.new.meth
  end
end

现在它将按您的预期工作,因为您提供了必要的查找提示FirstClass,并告诉 Ruby 它在里面Foo.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带有模块的 Ruby 类命名空间:为什么我会收到带有双冒号的 NameError 而不是模块块? 的相关文章

随机推荐