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)
希望这可以帮助.