2010-10-12 12 views
14

Ich habe ein Mixin, für das ich eine Liste aller Klassen erhalten möchte, die es enthalten haben. Im mixin Modul, das habe ich folgende:Eine Liste der Klassen erhalten, die ein Modul enthalten

module MyModule 
    def self.included(base) 
    @classes ||= [] 
    @classes << base.name 
    end 

    def self.classes 
    @classes 
    end 
end 

class MyClass 
    include MyModule 
end 

Das funktioniert ziemlich gut:

> MyModule.classes #=> nil 
> MyClass.new #=> #<MyClass ...> 
> MyModule.classes #=> ["MyClass"] 

Nun würde Ich mag diesen Teil extrahieren in ein separates Modul, das in meinen andere enthält sein kann, Mixins. So kam ich mit dem Follow-up:

module ListIncludedClasses 
    def self.included(base) 
    p "...adding #{base.name} to #{self.name}.classes" 

    @classes ||= [] 
    @classes << base.name 

    base.extend(ClassMethods) 
    end 

    def self.classes 
    @classes 
    end 

    module ClassMethods 
    def included(module_base) 
     p "...adding #{module_base.name} to #{self.name}.classes" 

     @module_classes ||= [] 
     @module_classes << module_base.name 
     super(module_base) 
    end 
    def classes 
     @module_classes 
    end 
    end 

end 

module MyModule 
    include ListIncludedClasses 
end 

Dies gilt allerdings nicht funktioniert, weil die #include (module_base) -Methode MyModule von ListIncludedClasses hinzugefügt wird nie laufen zu bekommen. Interessanterweise fügt es erfolgreich #classes zu MyModule hinzu.

> MyModule.classes #=> 
    "...adding Rateable to ListIncludedClasses.classes" 
    => nil 
> ListIncludedClasses #=> ["MyModule"] 
> MyClass.new #=> #<MyClass ...> 
# ^^ THIS SHOULD BE ADDING MyClass TO MyModule.classes ^^ 
> MyModule.classes #=> nil 

Was fehlt mir?

+0

Vergessen Sie nicht, anzugeben, dass ich Ihre Frage beantwortet habe! – Tom

+0

haben Sie das versucht: http://stackoverflow.com/questions/3527445/when-are-modules-included-in-a-ruby-class-running-in-rails – rickypai

Antwort

14
module MyMod; end 

class A; include MyMod; end 
class B < A; end 
class C; end 

ObjectSpace.each_object(Class).select { |c| c.included_modules.include? MyMod } 
    #=> [B, A] 
1

Wahrscheinlich sollten Sie extend anstelle von include verwenden, da Former Methoden auf Klassenebene hinzufügt, während letztere - Methoden auf Instanzebene (warum Sie Zugriff auf @classes haben).

Versuchen Sie folgendes:

module MyModule 
    extend ListIncludedClasses::ClassMethods 
end 
+2

Ich weiß nicht, wenn Sie es verpasst haben, aber extend (ClassMethods) befindet sich in der self.included (base) -Methode, also erweitert es die Klassenmethoden. Es gibt andere Instanzmethoden, die hinzugefügt werden müssen. Dies ist die allgemein akzeptierte Technik, um sowohl die Instanzmethoden als auch die Klassenmethoden zu erweitern. Auch das direkte Erweitern der Klassenmethoden, wie Sie es vorgeschlagen haben, macht keinen Unterschied. – jangosteve

2

Eigentlich Ihr Modul Erweiterungsmodul funktioniert. Das Problem ist in Ihrem Test: Wenn Sie eine zufällige unbenannte Klasse mit Class.new erstellt haben, haben Sie vergessen, MyModule einzuschließen. Als Nebenbemerkung können Sie Ihren schreibgeschützten Accessor für Klassen verwenden, die das Modul enthalten, und die hilfreiche Methode Module#attr_reader verwenden.

+0

Danke für die Antwort. Ja, es funktioniert, wenn ich es jetzt versuche, ich bin mir nicht sicher, was ich gerade gemacht habe. Ich denke, ich hatte es in einem Rails-Projekt, anstatt es zu isolieren, also wer weiß. Es ist nicht so, dass ich die 'MyClass' Definition mit' include' vergaß, ich habe sie im obigen Beispiel nicht neu eingegeben. Sonst hätte MyClass.new 'uninitialized constant MyClass' geworfen. Auch 'Module # attr_reader' erzeugt Instanzmethoden, also hätte ich sie in' ClassMethods' und dann auch in einem 'class << self' Block in' ListIncludedClasses' verwenden können. Es ist effizienter, also wäre es in der Produktion der richtige Weg. – jangosteve

Verwandte Themen