2011-09-26 111 views
1

包含模块如何影响范围?具体地,在这个例子中:Ruby类,包含和范围

module ModuleA 
    class ClassA 
    def initialize 
     puts "test passed" 
    end 
    end 
end 

module ModuleB 
    include ModuleA 

    # test 1 
    C = ClassA.new 

    class ClassB 
    def initialize 
     c = ClassA.new 
    end 
    end 
end 

# test 2 and 3 fail without this 
#include ModuleB 

module ModuleC 
    # this doesn't help 
    include ModuleB 

    # test 2 
    ClassB.new 

    # test 3 
    ModuleB::ClassB.new 
end 

试验1工作正常,但试验2测试3失败而不注释的import ModuleB

  • 为什么范围ClassAModuleB(测试1)内而不是在ClassB
  • 为什么import ModuleBClassA纳入ClassB的范围?
+0

对于** test2 **和** test3 **来说,您需要在'ClassB'内部包含'ModuleA',或者让'ClassB derp

回答

7

关键字模块高清是什么被称为“范围门”。他们创造新的范围。

#!/usr/bin/env ruby 

module ModuleA 
    class ClassA 
    def initialize 
     puts "test passed" 
    end 
    end 
end 

module ModuleB 
    include ModuleA 

    # test 1 
    c = ClassA.new # this works as ModuleA has been included into this module 

    class ClassB # class is a scope gate, creates new scope 
    def initialize # def is a scope gate, creates new scope 
     c = ModuleA::ClassA.new # must fully qualify ClassA 
    end 
    end 

    ClassB2 = Class.new do # no scope gate 
    define_method :initialize do # no scope gate 
     c = ClassA.new # this works, no need to fully qualify 
    end 
    end 
end 

b = ModuleB::ClassB.new 
b2 = ModuleB::ClassB2.new 

我在阅读book "Metaprogramming Ruby"后开始理解Ruby中的示波器。这真的很有启发性。

编辑:也回应下面的评论。

一个类本质上是一个Ruby常量(注意它是一个带大写字母的对象)。常量在范围内具有定义的查找算法。 O'Reilly书在第7.9节中对其进行了很好的解释。在这个blog post中也简要描述了它。

在任何类或模块外部定义的顶级常量都像顶级方法:它们在Object中被隐式定义。当从一个类中引用一个顶级常量时,它在搜索继承层次结构时被解析。如果在模块定义中引用该常量,那么在搜索模块的祖先之后,会对Object进行显式检查。

这就是为什么在顶层包含ModuleB使ModuleB中的类在所有模块,类和方法中都可见的原因。

+2

我向每个想成为热门红宝石开发者的人推荐使用元编程Ruby。 –

+1

那么,为什么在顶层包含ModuleB?有助于任何事情?范围门不应该阻止它在'ModuleB'和'ClassB'中产生效果吗? – also

+0

在任何类或模块外部定义的顶级常量都像顶级方法:它们在Object中被隐式定义。因此,当从一个类中引用顶级常量时,在搜索继承层次结构时会解析顶级常量。但是,如果在模块定义中引用该常量,则在搜索 –

0

原因是(我认为)与绑定有关。对我来说,线索是,以下也将无法正常工作:

module ModuleB 
include ModuleA 

class ClassB 
    def initialize 
    c = ClassA.new 
    end 
end 

ClassB.new 
end 

ClassA因为它不是在ClassB的恒定并不意味着在ClassB定义什么 - 该模块只被列入其父模块中。此更改应该使一切工作:

module ModuleB 
    include ModuleA 
    class ClassB 
    def initialize 
     c = ModuleA::ClassA.new 
    end 
    end 
end