是否可以为子模块提供与顶级类相同的名称? [英] Is it possible to give a sub-module the same name as a top-level class?
问题描述
背景:
这就是问题所在,简化为一个最小的例子:
# bar.rb班级酒吧结尾# foo/bar.rb模块 Foo::Bar结尾# foo.rbFoo类包括 Foo::Bar结尾# runner.rb需要酒吧"需要'foo'
<前>➔ 红宝石跑步者.rb./foo.rb:2: 警告: Foo::Bar 引用的顶级常量 Bar./foo.rb:2:in `include': 错误的参数类型类(预期模块)(类型错误)来自 ./foo.rb:2来自 runner.rb:2:in `require'来自 runner.rb:2
优秀;您的代码示例非常清晰.你所拥有的是一个花园式的循环依赖,被 Ruby 的范围解析运算符的特性所掩盖.
当你运行 Ruby 代码 require 'foo'
时,ruby 会找到 foo.rb
并执行它,然后找到 foo/bar.rb
代码> 并执行它.因此,当 Ruby 遇到您的 Foo
类并执行 include Foo::Bar
时,它会在 Foo 类中查找名为
,因为这就是 Bar
的常量Foo::Bar
表示的.当它找不到一个时,它会在其他封闭范围内搜索名为 Bar
的常量,并最终在顶层找到它.但是that Bar
是一个类,所以不能被include
d.
即使你可以说服require
在foo.rb
之前运行foo/bar.rb
,也无济于事;module Foo::Bar
的意思是找到常量Foo
,如果它是一个类或一个模块,开始在其中定义一个名为Bar
的模块".Foo
还没有被创建,所以 require 仍然会失败.
将 Foo::Bar
重命名为 Foo::UserBar
也无济于事,因为名称冲突最终不是问题.
那么你能做什么?在高层次上,您必须以某种方式打破循环.最简单的是将 Foo
定义为两部分,如下所示:
# bar.rb班级酒吧A = 4结尾# foo.rbFoo类# 不依赖于 Foo::Bar 的东西放在这里.结尾# foo/bar.rb模块 Foo::BarA = 5结尾class Foo # 是的,我们在 foo/bar.rb 中重新打开类 Fooinclude Bar # 请注意,您不需要 Foo:: 因为我们会首先自动搜索 Foo.结尾酒吧::A # =>4Foo::Bar::A # =>5
希望这会有所帮助.
Background:
- ruby thinks I'm referencing a top-level constant even when I specify the full namespace
- How do I refer to a submodule's "full path" in ruby?
Here's the problem, distilled down to a minimal example:
# 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'
➔ 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
Excellent; your code sample is very clarifying. What you have there is a garden-variety circular dependency, obscured by the peculiarities of Ruby's scope-resolution operator.
When you run the Ruby code require 'foo'
, ruby finds foo.rb
and executes it, and then finds foo/bar.rb
and executes that. So when Ruby encounters your Foo
class and executes include Foo::Bar
, it looks for a constant named Bar
in the class Foo
, because that's what Foo::Bar
denotes. When it fails to find one, it searches other enclosing scopes for constants named Bar
, and eventually finds it at the top level. But that Bar
is a class, and so can't be include
d.
Even if you could persuade require
to run foo/bar.rb
before foo.rb
, it wouldn't help; module Foo::Bar
means "find the constant Foo
, and if it's a class or a module, start defining a module within it called Bar
". Foo
won't have been created yet, so the require will still fail.
Renaming Foo::Bar
to Foo::UserBar
won't help either, since the name clash isn't ultimately at fault.
So what can you do? At a high level, you have to break the cycle somehow. Simplest is to define Foo
in two parts, like so:
# 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
Hope this helps.
这篇关于是否可以为子模块提供与顶级类相同的名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!