2012-07-16 6 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...] 

Jak dołączyć moduł B do wnętrza A, aby klasy, które zawierają już moduł A, otrzymają również metody z modułu B?W tym moduł w innym module

+0

Ostatecznie, co chcesz osiągnąć? Czy możesz zilustrować swój przypadek użycia konkretnym przykładem? Być może twój problem może zostać potraktowany inaczej. – Wei

+0

Chciałem włączyć mój moduł do modułu 'ActionDispatch :: Routing :: UrlFor', aby wszystkie klasy Railsów, które go zawierają, automatycznie otrzymywałyby moje nowe metody. Rozwiązałem to inaczej, ale byłem zaskoczony, że to nie działa w ten sposób. – szimek

Odpowiedz

2

Nie możesz.

Kiedy include wstawką M do klasy C, Ruby tworzy nową klasę ⟦M′⟧ którego metoda tabela wskazuje na stole Sposób wstawek M i którego nadklasą jest nadklasą C, to sprawia, że ​​tej klasy nowy nadklasą C. Powtarza się to dla każdego miksu, który został zmieszany z M.

Należy zauważyć, że ten algorytm jest uruchamiany tylko raz, po połączeniu M z C. Moduły, które później uzyskają include, nie zostaną uwzględnione.

1

Powinieneś zawrzeć B w A przed klasa C; włącz A; koniec.

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] 

Edit

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

Nie mogę zmienić kolejności, w której klasy zawierają te moduły. Próbuję rozszerzyć moduł Rails 'ActionDispatch :: Routing :: UrlFor', który jest już uwzględniony w niektórych klasach Railsów – szimek

+0

Czy to nie jest to, na czym zbudowano' ActiveSupport :: Concern'? –

+0

Dzięki, ale pomysł jest taki, że chciałbym umieścić mój moduł wewnątrz 'ActionDispatch :: Routing :: UrlFor', aby wszystkie klasy Railsów, które go zawierają, automatycznie uzyskałyby te nowe metody. Nie chcę iterować nad każdą klasą Railsów, która zawiera ten moduł. Gdybym miał listę wszystkich tych klas, mógłbym po prostu włączyć mój moduł bezpośrednio do nich. – szimek

3

Jak już powiedziano, Ruby nie działa w ten sposób - gdy klasa zawiera moduł, nie ma żadnego odniesienia do instancji tego modułu, więc jeśli moduł zawiera inne moduły, klasy, które mają już uwzględnione nie będzie wiedzieć o zmianie.

Można obejść ten problem poprzez przechowywanie klas, które zawierają moduł w samym module - w ten sposób można aktualizować je, gdy moduł zawiera inny moduł, czyli mniej więcej tak:

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 

Nie wiesz to jest kierunkiem, który chcesz wybrać, ale pokazuje, że można to zrobić w zasadzie.

Powiązane problemy