在其他模块中包含一个模块

时间:2012-07-16 11:16:41

标签: ruby

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获取方法?

3 个答案:

答案 0 :(得分: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

不确定这是您想要采取的方向,但表明它原则上可以完成。

答案 1 :(得分:2)

你不能。

当你include混合M进入类C时,Ruby会创建一个新类⟦M′⟧,其方法表指向mixin {{1}的方法表并且其超类是M的超类,然后使该类成为C的新超类。对于混合到C中的每个mixin重复此操作。

请注意,当您将M混合到M时,此算法只运行一次。稍后获得C d的模块将不予考虑。

答案 2 :(得分:1)

你应该在 C类之前在A中加入B;包括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...]