2012-07-16 100 views
7
module A; def a; end; end 
module B; def b; end; end 

class C; include A; end 

module A; include B; end 
class D; include A; end 

C.new.b # undefined method error 
D.new.b # nil 

C.ancestors # [C, A, Object...] 
D.ancestors # [D, A, B, Object...] 

如何在A中包含模塊B,以便已包含模塊A的類也可以從模塊B獲取方法?將模塊包含在其他模塊中

+0

最終,你想在這裏實現什麼?你能舉一個具體的例子來說明你的用例嗎?也許你的問題可能會有所不同。 – Wei 2012-07-16 12:12:32

+0

我想將我的模塊加入到ActionDispatch :: Routing :: UrlFor' Rails模塊中,這樣所有包含它的Rails類都會自動擁有我的新方法。我以不同的方式解決了這個問題,但對於這種方式不起作用感到非常驚訝。 – szimek 2012-07-16 16:48:10

回答

2

你不能。

當你include一個mixin M成類C,紅寶石創建一個新的類⟦M′⟧,其方法表指向混入M的方法表,其超類是C超類,然後讓這個類的新超C。對於混入M的每個mixin都會重複此操作。

請注意,當您將M混合到C中時,此算法僅運行一次。以後得到include的模塊將不會被考慮。

1

你應該在B包括A前C類;包括A;結束

module A; def a; end; end 
module B; def b; end; end 


module A; include B; end 

class C; include A; end 
class D; include A; end 

p C.new.b # nil 
p D.new.b # nil 

p C.ancestors # [C, A, B, Object, Kernel, BasicObject] 
p D.ancestors # [D, A, B, Object, Kernel, BasicObject] 

編輯

module A; def a; end; end 
module B; def b; end; end 

class C; include A; end 

module A; include B; end 
class D; include A; end 

C.send(:include, A) 

p C.new.b # nil 
p D.new.b # nil 

p C.ancestors # [C, A, Object...] 
p D.ancestors # [D, A, B, Object...] 
+1

我無法更改類包含這些模塊的順序。 我試圖擴展Rails的'ActionDispatch :: Routing :: UrlFor'模塊,它已經包含在一些Rails類中 – szimek 2012-07-16 11:43:54

+0

這不就是ActiveSupport :: Concern的構建目的嗎? – 2012-07-16 11:56:49

+0

謝謝,但想法是我想將模塊包含在ActionDispatch :: Routing :: UrlFor'中,以便包含它的所有Rails類將自動獲取這些新方法。我不想迭代每個包含這個模塊的Rails類。如果我有所有這些類的列表,我可以簡單地將我的模塊直接包含到它們中。 – szimek 2012-07-16 12:00:22

3

如前所述,Ruby不會像這樣工作 - 當一個類包含一個模塊時,它不會保留對該模塊實例的任何引用,所以如果該模塊包含其他模塊,已經包括它不會知道變化。

你可以解決這個問題通過存儲包括模塊本身在模塊的類 - 這樣你可以更新他們時,該模塊包括另一個模塊,即是這樣的:

module A 

    class<<self 
    attr_accessor :observers 
    end 

    self.observers = [] 

    def a 
    "#{self} successfully called a" 
    end 

    def self.included(base) 
    self.observers << base unless self.observers.include?(base) 
    end 

    def self.include(mod) 
    observers.each {|o| o.send(:include,mod) } 
    super 
    end 

end 

module B 
    def b 
    "#{self} successfully called b" 
    end 
end 

class C 
    include A 
end 

module A 
    include B 
end 

class D 
    include A 
end 

module E 
    def e 
    "#{self} successfully called e" 
    end 
end 

module A 
    include E 
end 

puts C.new.b 
puts D.new.b 
puts C.new.e 
puts D.new.e 

不知道這是你想要採取的方向,但表明它可以在原則上完成。