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

¿Cómo incluir el módulo B dentro de A, para que las clases que ya incluyen el módulo A también obtengan métodos del módulo B?Incluyendo un módulo en otro módulo

+0

En última instancia, ¿qué es lo que quiere lograr aquí? ¿Puedes ilustrar tu caso de uso con un ejemplo concreto? Tal vez su problema se puede abordar de manera diferente. – Wei

+0

que querían incluir mi módulo en módulo 'ActionDispatch :: :: enrutamiento rieles UrlFor', por lo que todas las clases Carriles que lo incluyen, tendrían automáticamente mis nuevos métodos también. Lo resolví de manera diferente, pero me sorprendió bastante que no funcionara de esta manera. – szimek

Respuesta

2

No se puede.

Cuando include mixin M en una clase C, Ruby crea una nueva clase ⟦M′⟧ cuyo método de tabla de puntos de la tabla de métodos de la mixin M y cuya superclase es la superclase de C, a continuación, hace que esta clase de la nueva superclase de C. Esto se repite para cada mixin que se mezcló en M.

Tenga en cuenta que este algoritmo se ejecuta solo una vez, cuando se combina M en . Los módulos que se interponen include D más adelante, no conseguirá considerado.

1

debe incluir B en A antes clase C; Incluir un; final.

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] 

Editar

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

No puedo cambiar el orden en que las clases incluyen estos módulos. Estoy tratando de extender Rieles 'ActionDispatch :: :: enrutamiento UrlFor' módulo que ya está incluido en algunas clases Rieles – szimek

+0

No es esto lo que' ActiveSupport :: Concern' fue construido para? –

+0

Gracias, pero la idea es que me gustaría incluir mi módulo dentro de 'ActionDispatch :: Routing :: UrlFor', de modo que todas las clases de Rails que lo incluyen obtendrían _automáticamente_ estos nuevos métodos. No quiero iterar sobre todas las clases de Rails que incluyen este módulo. Si tuviera una lista de todas estas clases, simplemente podría incluir mi módulo directamente en ellas. – szimek

3

Como ya se ha dicho, Ruby no funciona como esto - cuando una clase incluye un módulo, que no mantiene ninguna referencia a la instancia de ese módulo por lo que si el módulo incluye otros módulos, las clases que tienen ya incluido, no sabrá sobre el cambio.

Se puede evitar esto mediante el almacenamiento de las clases que incluyen el módulo en el propio módulo - de esa manera usted puede actualizarlos cuando el módulo incluye otro módulo, es decir algo como esto:

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 

No estoy seguro que es la dirección que quiere tomar, pero muestra que se puede hacer en principio.

Cuestiones relacionadas