34

这是这个原始 SO 问题的延续:Using "::" instead of "module ..." for Ruby namespacing

在最初的 SO 问题中,这是我仍然无法理解的场景:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

class Foo::Bar
  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123

有人可以解释为什么第一个电话返回 555 以及为什么第二个电话返回 123 吗?

4

4 回答 4

36

您可以将 的每次出现module Somethingclass Somethingdef something视为进入新范围的“门户”。当 Ruby 搜索已被引用的名称的定义时,它首先在当前范围(方法、类或模块)中查找,如果没有找到,它将返回每个包含“网关”并搜索那里的范围。

在您的示例中,该方法baz定义为

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

因此,当尝试确定 的值时FOO,首先Bar检查类,并且由于Bar不包含 a FOO,因此搜索通过“<code>class Bar gateway”向上移动到Foo作为包含范围的模块中。Foo确实包含一个常数FOO(555) 所以这是你看到的结果。

该方法glorf定义为:

class Foo::Bar
  def glorf
    puts FOO
  end
end

这里的“网关”是class Foo::Bar,所以当在“网关”FOO内部找不到时,它Bar会通过Foo模块并直接进入顶层,在那里还有另一个FOO(123)是显示的内容。

请注意 usingclass Foo::Bar创建单个“网关”,跳过 的范围Foo,但会module Foo; class Bar ...打开两个单独的“网关”</p>

于 2013-03-03T17:02:01.057 回答
6

哇,好问题。我能想到的最佳答案是在这种情况下,您使用模块来定义命名空间。

看一下这个:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end

    def glorf3
      puts ::FOO
    end
  end
end

class Foo::Bar
  def glorf2
    puts Foo::FOO
  end

  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
puts Foo::Bar.new.glorf2  # -> 555
puts Foo::Bar.new.glorf3  # -> 123

所以我的想法是,当你定义:

module Foo
  FOO = 555
end

您正在FOO. Foo所以当你在这里使用它时:

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

你在Foo命名空间中。但是,当您在以下位置引用它时:

class Foo::Bar
  def glorf
    puts FOO
  end
end

FOO来自默认命名空间(如 所示::FOO)。

于 2013-02-27T19:14:45.443 回答
0

第一个电话:

puts Foo::Bar.new.baz    # -> 555

打印调用类Foo::Bar实例的方法baz的结果

注意Foo::Bar#baz定义实际上是 FOO 的一个闭包。遵循 ruby​​ 的作用域规则:

  1. FOOFoo::Bar(类,而不是实例)范围内搜索,没有找到,
  2. 在封闭范围Foo中搜索FOO(因为我们在模块定义中)并在那里找到它(555)

第二个电话:

puts Foo::Bar.new.glorf  # -> 123

打印调用类Foo::Bar实例的方法glorf的结果

请注意,这次Fo​​o::Bar#glorf定义也是 FOO 的一个闭包,但是如果我们遵循 ruby​​ 的范围规则,您会注意到这次关闭的值是::FOO(顶级范围 FOO),方式如下:

  1. 在Foo::Bar(类,而不是实例)命名空间中搜索FOO ,但未找到
  2. 在封闭范围('顶级')中搜索FOO并在那里找到它(123)
于 2013-03-01T22:16:52.143 回答
0

glorf 是类 Foo 的一个方法,在=> [Foo, Module, Object, Kernel, BasicObject]

在该范围内(即在默认/主模块中),FOO 被分配 123

模块 Foo 定义为

module Foo
  FOO = 555
  class Bar
    def baz
      puts FOO
    end
  end
end

其中方法 baz 属于模块 Foo 中的类 Bar=> [Bar, Foo, Object, Kernel, BasicObject]

在那个范围内,FOO 被分配了 555

于 2013-03-04T08:06:44.827 回答