2011-09-26 183 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