2013-02-27 135 views
32

这是一个延续这种原始的SO问题:Using "::" instead of "module ..." for Ruby namespacing红宝石 - 词法范围VS继承

在原来的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?

+1

威尔森,你觉得是值得的赏金其回答以下?谢谢 – rainkinz 2013-03-07 21:37:40

+0

提示:在你的代码中添加两个“puts Module.nesting”。 另请参阅:http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ – 2013-03-08 15:22:14

回答

33

你能想到的module Somethingclass Somethingdef something每次出现的“网关”到一个新的范围。当Ruby搜索被引用的名称的定义时,它首先在当前作用域(方法,类或模块)中查找,如果没有找到,它将通过包含“网关”和搜索的每个那里的范围。

在您的例子方法baz被定义为

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

所以,当试图确定FOO值,第一类Bar检查,由于Bar不包含FOO搜索向上移动通过“class Bar网关”进入Foo模块,该模块是包含范围。 Foo确实包含常量FOO(555),所以这是您看到的结果。

方法glorf被定义为:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

这里的“网关”是class Foo::Bar,所以当FOO没有内部Bar发现“网关”穿过Foo模块和直入顶层,那里有另外一个FOO(123)这就是显示的内容。

说明了如何使用class Foo::Bar创建一个单一的“网关”,跳过的Foo范围,但module Foo; class Bar ...打开两个单独的“网关”

+3

顺便说一句。网关术语。在Ruby源代码中,似乎有人可以称之为“范围堆栈”。所以每次你输入'class'或'module',一个新的作用域被压入这个栈。当Ruby搜索变量或常量时,它会自下而上地查询这个堆栈,如果在上面找不到变量,则结束于顶层'main'。在'class Foo :: Bar'的情况下,它确实应该将两个范围推到堆栈上('Foo'和'Bar'),但它只推动一个,因此我们得到了“问题”。 – Casper 2013-03-04 11:32:01

+0

这与原始答案有何不同? – rainkinz 2013-03-04 15:30:15

+1

@Casper有道理。我在某处(某处不记得在哪里)读到了关于“网关”的想法,以此作为思考发生了什么的一种方式,但我没有看到实现。我想这个行为的一个解释是它允许你打开一个嵌套类(以猴子修补它),而不需要担心封闭的范围干扰。 – matt 2013-03-08 15:41:04

5

哇,很好的问题。我可以想出最好的答案就是在这种情况下,您正在使用模块来定义一个名称空间。

检查了这一点:

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如图所示)。

+0

感谢您的例子!除了一点之外,这是有道理的:当你定义Foo :: Bar类时,你是不是将它命名为Foo? “Foo :: Bar”中的“Foo ::”部分是否暗示你正在为该类命名空间? – wmock 2013-02-27 19:34:51

+0

Foo :: Bar.new.glorf返回123对我来说很难理解,当Foo :: Bar.new.baz返回555. – wmock 2013-02-27 19:36:10

+1

我也会这么想,但它看起来像是什么,你正在命名空间Bar (在Foo下)显式地由Foo :: Bar和该上下文中的其他内容仍然来自默认命名空间。 – rainkinz 2013-02-27 19:37:31

0

第一个电话:

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

打印调用类的实例的方法巴兹的结果美孚::酒吧

通知,富::酒吧#巴兹定义实际上是FOO上的封闭。下面的Ruby的范围规则:

  1. FOO中搜索在美孚::酒吧(类,而不是实例)范围,但没有找到,
  2. FOO中搜索在封闭范围美孚(因为我们是模块定义范围内),并且在那里发现(555)

第二呼叫:

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

打印调用类的实例的方法glorf的结果美孚::酒吧

通知,富::酒吧#glorf定义这个时候也对FOO封闭,但如果我们按照红宝石的范围规则,你会发现,当值这个时候关闭是::通过以下方式FOO(顶层范围FOO):

  1. FOO中搜索在美孚::酒吧(类,而不是实例)命名空间,它没有发现
  2. FOO搜索在封闭的范围(“顶层”),它是发现那里(123)
0

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

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

模块的Foo被定义为

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

在其中方法巴兹属于Bar类模块美孚=> [Bar, Foo, Object, Kernel, BasicObject]

,并在该范围FOO被分配了555

+0

上面提到的'main'是irb的神器,实际上'FOO = 123'是进入Object类的顶层方法。 – aug2uag 2013-03-09 01:06:10