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?
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.
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.
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.
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.
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.
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 include
d later, will not get considered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With