是否可以给一个子模块与顶级类相同的名称? [英] Is it possible to give a sub-module the same name as a top-level class?

查看:189
本文介绍了是否可以给一个子模块与顶级类相同的名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:





这里有一个问题,简化为一个最小的例子:

 #bar.rb 
class Bar
end

#foo / bar.rb
模块Foo :: Bar
end

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

.rb
要求'bar'
要求'foo'



 
➔ruby runner.rb
./foo.rb:2:warning:toplevel常量由Foo :: Bar引用的bar
./foo.rb:2:in`include':wrong参数类型Class(expected module)(TypeError)
from ./foo.rb:2
from runner.rb:2:in`require'
from runner.rb:2


解决方案

你的代码示例非常清楚。



当你运行Ruby代码当你运行Ruby代码时, require'foo',ruby发现 foo.rb 并执行它,然后找到 foo / bar.rb 并执行。所以当Ruby遇到你的 Foo 类并执行 include Foo :: Bar 时,它会寻找一个名为 Bar 在类 Foo 中,因为这是 Foo :: Bar 。当它找不到一个时,它会在其他包围范围中搜索名为 Bar 的常量,并最终在顶级找到它。但是 Bar 是一个类,因此不能 include d。 / p>

即使您可以说服 require 运行 foo / bar.rb 之前 foo.rb ,它不会帮助; 模块Foo :: Bar 表示找到常量 Foo ,如果它是一个类或模块,模块中调用 Bar Foo 尚未创建,因此require仍会失败。



重命名 Foo :: Bar Foo :: UserBar 也不会有帮助,因为名称clash不会最终出错。



那么可以做什么呢?在高水平,你必须打破周期不知何故。最简单的是在两个部分中定义 Foo

  bar.rb 
class Bar
A = 4
end

#foo.rb
class Foo
#不依赖的东西在Foo :: Bar去这里。
end

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

类Foo #是的,我们重新打开foo里面的类Foo / bar.rb
include Bar#注意,你不需要Foo ::因为我们首先自动搜索Foo。
end

Bar :: A#=> 4
Foo :: Bar :: A#=> 5

希望这有助。


Background:

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 included.

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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆