In my previous post we saw how to add functionality to already existing objects thanks to the decorator pattern. Particularly, we discussed how to add more equipment (shield and armor) to different King objects which by default only had a sword. We also saw how to do it the Ruby way, mixing modules. But what if we want to add functionality not only to a single object, but to all objects from a class? There are three different ways to do this in Ruby: include, prepend and extend, each of them has its own purpose. But before we can understand the differences between these three methods, we need to get an idea on how the Ancestors chain works.

The Ancestors chain

The Ancestors chain depends directly on the concept of the Ruby’s object model. For our needs today, we need to understand two key concepts of Ruby’s object model:

The implications of this are that the order in which a message is delegated is determined by the Ancestors chain because:

The Ancestors chain is the ordered collection of parent classes our object class inherits from plus all the modules that were mixed into it.

To understand this better, let’s create a class without any explicit parent class nor mixed module and see how its ancestor chain looks like:

In our example, whenever we send a message to MyClass, it will try to handle the message itself, and if it is not capable of doing that, it will delegate the message to the next element in the Ancestors chain. In our example the Object class, which will later do the same if it is not also able to handle the message, until BasicObject, which is the root of Ruby Object’s model hierarchy, is reached.

Ancestors chain

The order in which the modules appear in our class’ Ancestors chain will be determined by the method we use to mix that module into our class. Which means, it will be different if we use include, extend or prepend.

Let’s see how each of this alternatives affect the ancestor chain.

Include

When you use include within your class definition, it inserts the module in your class’ ancestor chain at the point in between your class and its parent class. To make an analogy, it would be as if your class’ new parent class was module you just included .

Include

To see it more clearly, let’s see it with an example. Imagine we have our King class from our previous post, and we want all Kings to be able to greet. For that, we can include a Greeting module with a greet method to make the Kings able to greet.

As a side note, bear in mind that if you include several modules, the module which was included last will be the first to appear in the ancestors chain:

Prepend

Prepend, on the other side, inserts the module before the class itself. That means, completely at the bottom of the ancestors chain. Like this:

Prepend

That gives us the ability to add business logic around already existing methods of the class to which the module is prepended. Let’s see it within an example.

Do you remember the decorator example from the our post? Imagine that we want to decorate all King objects instead of individual ones. We can achieve that with prepend.

Note the use of super as if we were referencing a parent class. We are in fact sending the same message to the next member of the ancestor chain that is able to handle it.

Extend

Extend is a little bit different to the previous methods we saw for including modules since it adds class methods, instead of adding instance methods.

In this example we extend our King class with a method to find Kings by name. For that, I have faked a King repository using a class variable. When a new King object is initialized, it gets added to this repo. To provide the King class with the find_by_name finder method, we just need to extend the class King with the module implementing the finder.

That was it for today! In an upcoming post I will explain you how you can both add instance and class methods with a single strike. Until then, enjoy coding!

AdiĆ³s!