是否可以为子模块提供与顶级类相同的名称?

Joh*_*hir 27 ruby namespaces module class

背景:

这是问题所在,简化为最小的例子:

# bar.rb
class Bar
end

# foo/bar.rb
module Foo::Bar
end

# foo.rb
class Foo
  include Foo::Bar
end

# runner.rb
require 'bar'
require 'foo'
Run Code Online (Sandbox Code Playgroud)
? ruby runner.rb
./foo.rb:2: warning: toplevel constant Bar referenced by Foo::Bar
./foo.rb:2:in `include': wrong argument type Class (expected Module) (TypeError)
    from ./foo.rb:2
    from runner.rb:2:in `require'
    from runner.rb:2

Dav*_*ler 20

优秀; 您的代码示例非常清晰.你所拥有的是一个花园式的循环依赖,被Ruby的范围分辨算子的特殊性所掩盖.

当您运行Ruby代码时require 'foo',ruby会找到foo.rb并执行它,然后查找foo/bar.rb并执行它.因此,当Ruby遇到你的Foo类并执行时include Foo::Bar,它会查找Bar类中命名的常量Foo,因为这就是Foo::Bar表示的.当找不到它时,它会在其他封闭的范围内搜索命名的常量Bar,并最终在顶层找到它.但是, Bar是一类,因此不能被include天.

即使你能说服require运行foo/bar.rb之前foo.rb,它不会帮助; module Foo::Bar表示"找到常量Foo,如果是类或模块,则开始在其中定义一个名为Bar" 的模块. Foo还没有创建,所以需求仍然会失败.

重命名Foo::Bar,以Foo::UserBar将没有帮助,因为名称冲突不是最终过错.

那么,什么可以做什么?在高层次上,你必须以某种方式打破这个循环.最简单的是Foo分为两部分,如下所示:

# bar.rb
class Bar
  A = 4
end

# foo.rb
class Foo
  # Stuff that doesn't depend on Foo::Bar goes here.
end

# foo/bar.rb
module Foo::Bar
  A = 5
end

class Foo # Yep, we re-open class Foo inside foo/bar.rb
  include Bar # Note that you don't need Foo:: as we automatically search Foo first.
end

Bar::A      # => 4
Foo::Bar::A # => 5
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 嗯...有一种方法可以做到这一点,而不用foo/bar.rb文件中的`class Foo include Bar end`.你可以在foo.rb做`class Foo require'foo/bar'包括Foo :: Bar end` (2认同)