zwiebl
zwiebl

Reputation: 725

inheritance from multiple modules?

I understand that when a module is included into a class, it behaves like a direct superclass of the class it is included into.

However, what if there are multiple modules included in the same class? Which one is now a direct superclass if there is no relationship between modules?

And what if module A includes module B (so there is a relationship in a way), overrides some of its methods, and now both A and B are included in the same class. Does that class now define all methods from A and B, and from the methods that are common to A and B, it uses only the methods that A overrides?

Upvotes: 0

Views: 852

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369428

I understand that when a module is included into a class, it behaves like a direct superclass of the class it is included into.

It does not "behave like" a direct superclass. It becomes the direct superclass (and whatever was the direct superclass before, becomes the direct superclass of the module).

However, what if there are multiple modules included in the same class? Which one is now a direct superclass if there is no relationship between modules?

There is no difference. When you include a module, an include class for that module is created, and that include class becomes the direct superclass of the class the module is being included into, whereas whatever was the direct superclass before, now becomes the direct superclass of the include class. You can repeat that as often as you like.

And what if module A includes module B (so there is a relationship in a way),

I glossed over that detail, but it is also rather simple: the process outlined above is applied recursively to modules included in modules. So, if module A includes B, and class C includes A, then

  1. an include class A′ is created
  2. C's direct superclass (presumably Object, but could be anything) becomes A′'s superclass
  3. A′ becomes C's superclass
  4. the process is applied recursively to modules included in A and modules included in modules included in A and so on …:
    1. an include class B′ is created
    2. A′'s direct superclass becomes B′'s superclass
    3. B′ becomes A′'s superclass
    4. the process is applied recursively to modules included in B and modules included in modules included in B and so on …

overrides some of its methods, and now both A and B are included in the same class.

You cannot include both A and B. Ruby will check whether a module is already in the ancestor chain, and only include it once. Since including A will already result in including B, you cannot include B again.

Does that class now define all methods from A and B, and from the methods that are common to A and B, it uses only the methods that A overrides?

The class doesn't define any methods. Inheriting from a module does not magically define any methods. It just makes the module the superclass, that's it.

Please, don't let the return value of Class#superclass confuse you: it does not return the superclass pointer of the class, instead it returns the closest superclass that is not an include class.

Upvotes: 1

ndnenkov
ndnenkov

Reputation: 36101

To help you understand that, ruby provides you with Module#ancestors:

Object.ancestors # => [Object, Kernel, BasicObject]

This is the order, in which an instance method of Object will be searched for. So lets test your examples.


First, two modules included:

module M; end
module N; end
class C
  include M
  include N
end

C.ancestors # => [C, N, M, Object, Kernel, BasicObject]

So the methods will first be searched for in C. If a method with the given name is not found, it is searched for first in N and then in M. In other words - the reverse order of which you included the modules.


Second, module, including a module, included in a class:

module X; end
module Y
  include X
end
class K
  include Y
end

K.ancestors # => [K, Y, X, Object, Kernel, BasicObject]

So we can see that the same rule applies for including in modules. Just as in the previous example a method will first be searched for in C and only then in the modules included in C, here a method will first will be searched for in a module, and only then in the included modules in that module.


The reason for that, other than consistency, is that classes are actually modules in Ruby:

Class.superclass # => Module

There are a lot of rules. For example including the same module twice in the hierarchy, singleton classes, #prepend, #method_missing and so on. But you can deduce all of it knowing how to use #ancestors.

Upvotes: 3

Related Questions