2010-10-12 15 views
14

Tengo un mixin del cual me gustaría obtener una lista de todas las clases que lo han incluido. En el módulo de mixin, hice lo siguiente:Obteniendo una lista de clases que incluyen un módulo

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

    def self.classes 
    @classes 
    end 
end 

class MyClass 
    include MyModule 
end 

Esto funciona bastante bien:

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

Ahora, me gustaría extraer esta parte a cabo en un módulo independiente que puede ser incluido en mi otra Mixins. Por lo tanto, se me ocurrió lo siguiente:

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 

esto no funciona, sin embargo, porque el método #include (module_base) que se añade a MyModule de ListIncludedClasses nunca es ser atropellados. Curiosamente, agrega exitosamente # classes a MyModule.

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

¿Qué me falta?

+0

¡No olvide indicar que he respondido su pregunta! – Tom

+0

has probado esto: http://stackoverflow.com/questions/3527445/when-are-modules-included-in-a-ruby-class-running-in-rails – rickypai

Respuesta

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

Probablemente debería usar extender en lugar de incluir ya que el anterior agrega métodos de nivel de clase, mientras que los métodos de último nivel de instancia (por qué tiene acceso a @classes).

Prueba esto:

module MyModule 
    extend ListIncludedClasses::ClassMethods 
end 
+2

No sé si te lo perdiste, pero extender (ClassMethods) está en el método self.included (base), por lo que extiende los métodos de clase. Hay otros métodos de instancia que deben agregarse; esta es la técnica popularmente aceptada tanto para incluir los métodos de instancia como para extender los métodos de clase. Además, la extensión directa de los métodos de clase, como sugirió, no hace diferencia. – jangosteve

2

En realidad, sus obras módulo de extensión de módulos. El problema está en tu prueba: cuando creaste una clase aleatoria sin nombre con Class.new, olvidaste incluir MyModule. Como nota al margen, puede tomar su acceso de solo lectura para las clases que incluyen el módulo y utilizar el útil método Module#attr_reader.

+0

Gracias por la respuesta. Sí, funciona cuando lo intento ahora, no estoy seguro de lo que estaba haciendo en ese momento. Creo que lo tenía en un proyecto de Rails en lugar de aislarlo, así que quién sabe. No es que olvidé la definición de 'MyClass' con' include', simplemente no lo volví a escribir en el ejemplo anterior. De lo contrario, MyClass.new habría arrojado 'MyClass constante no inicializada'. Además 'Module # attr_reader' crea métodos de instancia, entonces sí podría haberlo usado dentro de' ClassMethods', y luego también dentro de un bloque 'class << self' en' ListIncludedClasses'. Es más eficiente, por lo que en producción ese sería el camino a seguir. – jangosteve

Cuestiones relacionadas