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

时间:2021-12-12 23:17:51
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...]

How to include module B inside of A, so that classes that already include module A will also get methods from module B?

如何在A中包含模块B,以便已经包含模块A的类也将从模块B获取方法?

3 个解决方案

#1


2  

You can't.

When you include a mixin M into a class C, Ruby creates a new class ⟦M′⟧ whose method table points to the method table of the mixin M and whose superclass is the superclass of C, then makes this class the new superclass of C. This is repeated for every mixin that was mixed into M.

当你将mixin M包含在C类中时,Ruby会创建一个新类⟦M'⟧,其方法表指向mixin M的方法表,其超类是C的超类,然后使该类成为C的新超类。对于混入M的每种混合物,重复这一过程。

Note that this algorithm is run only once, when you mix M into C. Modules that get included later, will not get considered.

请注意,当您将M混合到C中时,此算法仅运行一次。稍后将包含的模块将不会被考虑。

#2


3  

As has already been stated, Ruby doesn't work like this - when a class includes a module, it doesn't maintain any reference to the instance of that module so if the module includes other modules, the classes that have already included it won't know about the change.

正如已经说过的那样,Ruby不会像这样工作 - 当一个类包含一个模块时,它不维护对该模块实例的任何引用,所以如果该模块包含其他模块,那么已经包含它的类就赢了不知道变化。

You could get around this by storing the classes that include the module in the module itself - that way you can update them whenever the module includes another module, i.e. something like this:

您可以通过将包含模块的类存储在模块本身中来解决这个问题 - 这样,只要模块包含另一个模块,您就可以更新它们,例如:

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

Not sure this is the direction you want to take, but shows that it can be done in principle.

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

#3


1  

You should Include B in A before class C; include A; end.

你应该在C级之前将A包含在A中;包括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]

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


2  

You can't.

When you include a mixin M into a class C, Ruby creates a new class ⟦M′⟧ whose method table points to the method table of the mixin M and whose superclass is the superclass of C, then makes this class the new superclass of C. This is repeated for every mixin that was mixed into M.

当你将mixin M包含在C类中时,Ruby会创建一个新类⟦M'⟧,其方法表指向mixin M的方法表,其超类是C的超类,然后使该类成为C的新超类。对于混入M的每种混合物,重复这一过程。

Note that this algorithm is run only once, when you mix M into C. Modules that get included later, will not get considered.

请注意,当您将M混合到C中时,此算法仅运行一次。稍后将包含的模块将不会被考虑。

#2


3  

As has already been stated, Ruby doesn't work like this - when a class includes a module, it doesn't maintain any reference to the instance of that module so if the module includes other modules, the classes that have already included it won't know about the change.

正如已经说过的那样,Ruby不会像这样工作 - 当一个类包含一个模块时,它不维护对该模块实例的任何引用,所以如果该模块包含其他模块,那么已经包含它的类就赢了不知道变化。

You could get around this by storing the classes that include the module in the module itself - that way you can update them whenever the module includes another module, i.e. something like this:

您可以通过将包含模块的类存储在模块本身中来解决这个问题 - 这样,只要模块包含另一个模块,您就可以更新它们,例如:

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

Not sure this is the direction you want to take, but shows that it can be done in principle.

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

#3


1  

You should Include B in A before class C; include A; end.

你应该在C级之前将A包含在A中;包括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]

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...]