Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Including a module in other module

Tags:

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

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

like image 632
szimek Avatar asked Jul 16 '12 11:07

szimek


People also ask

Can modules include other modules Ruby?

Indeed, a module can be included in another module or class by using the include , prepend and extend keywords. Before to start this section, feel free to read my article about the Ruby Object Model if you're unfamiliar with the ancestor chain in Ruby.

What's the difference between extend prepend and include?

In simple words, the difference between include and extend is that 'include' is for adding methods only to an instance of a class and 'extend' is for adding methods to the class but not to its instance.

How do you call a method from a module?

As with class methods, you call a module method by preceding its name with the module's name and a period, and you reference a constant using the module name and two colons.

What is the relationship between classes and modules?

Classes may generate instances (objects), and have per-instance state (instance variables). Modules may be mixed in to classes and other modules. The mixed in module's constants and methods blend into that class's own, augmenting the class's functionality. Classes, however, cannot be mixed in to anything.


2 Answers

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.

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.

like image 191
griswoldbar Avatar answered Sep 28 '22 07:09

griswoldbar


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.

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

like image 45
Jörg W Mittag Avatar answered Sep 28 '22 07:09

Jörg W Mittag