Modules are one of the most interesting features of Ruby. You can use them to attach specific behavior on your classes, and to organize your code using composition rather than inheritance. Here is a simple example:
Also, a lot of gems make use of modules to organize their code and ease up integration into your application. For example the Sidekiq gem provides the
Sidekiq::Worker module to attach behavior to custom classes and use them as asynchronous workers components.
include is the most common way of importing external code into a class, Ruby provides also two other ways to achieve that:
prepend. However, they don’t have the same behavior at all, and these differences are often misunderstood by Ruby developers.
To understand how to use them, we must first have a deeper look into how Ruby is resolving methods to execute at runtime, using something called the ancestors chain.
The Ancestors chain
When a Ruby class is created, it holds a list of constant names which are its ancestors. They are all the classes that the class inherits from, and the modules they include. For example, by calling ancestors on the
String class, we get the list of its ancestors:
=> [String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
We can see at the top of the chain
BasicObject, which is the root of the Ruby object hierarchy, and also
Object, the superclass of all classes, that also includes the
When we call the method
object_id on a
String instance (or any other class), Ruby will look up through the class ancestors to find the
object_id method, and will eventually find it defined on the
When calling a method that is not defined anywhere, Ruby won’t find the method in any of the classes or modules in the ancestors chain, and will end up calling
BasicObject, which gives a last chance for the developer to execute fallback code.
Knowing the basics about the ancestors chain of Ruby classes, we can now look at the different ways of importing modules.
include is the most used and the simplest way of importing module code. When calling it in a class definition, Ruby will insert the module into the ancestors chain of the class, just after its superclass. Going back at our first example:
If we look at the ancestors of the
Service class, we can see that the
Logging module is present just between the class itself and its direct superclass, which is
=> [Service, Logging, Object, ...]
That’s why we can call methods defined in the module on the class instances. Ruby, not finding these methods on the class, will go one step up on the chain to find them on the module.
Also, it is worth noting that, when including two modules or more, the last included one will always be inserted again right between the class and the rest of the chain:
So, in case of a method conflict like on this example, the first module to respond in the ancestors chain would be the last included module,
On the other end, using
extend on a class will actually import the module methods as class methods. If we have used
extend rather than
include in our example, the
Logging module would not have been inserted into the
Service class ancestors chain. So, we couldn’t have called the
log method on any
Instead, Ruby would have inserted the module in the ancestors chain of the singleton class of the
Service class. This singleton class (named
#Service) is actually where the class methods of
Service are defined. The methods of the module
Logging would then have been available as class methods of
If you want to know a bit more about singleton classes, I wrote another post diving a bit deeper in the topic here.
Then, we could have called the method like this:
Service.log :info, "Something happened"
Often, you will want to use a module to import instance methods on a class, but at the same time to define class methods. Normally, you would have to use two different modules, one with
include to import instance methods, and another one with
extend to define class methods.
A common idiom to achieve that using a single module is to use the
included hook method of
Module, to also import class methods at runtime:
Now, when we include the module into the
Service class, the module methods will be imported as instance methods of the class. The
included method is also called, with the including class as an argument. We can then call
extend on it to import the methods of the
ClassMethods submodule as class methods. The circle is complete.
Available since Ruby 2,
prepend is a bit less known to Rubyists than its two other friends. It actually works like
include, except that instead of inserting the module between the class and its superclass in the chain, it will insert it at the bottom of the chain, even before the class itself.
What it means is that when calling a method on a class instance, Ruby will look into the module methods before looking into the class. This difference of behavior allows you to decorate existing classes with modules and implement “around” logic:
prepend, the module
ServiceDebugger is now inserted at the very bottom of the ancestors chain.
You can verify it yourself again by calling
=> [ServiceDebugger, Service, Object, ...]
run on a
Service instance will execute first the method defined in the
ServiceDebugger module. We can use
super to call the same method on the direct ancestor up the chain, which is the
Service class itself. We took advantage of this behavior to decorate the
Service implementation in a very simple and elegant way.
Thank you for reading, and happy Ruby coding!
This post has been published on my blog as well.
If you liked this post, don’t forget to ♥ :)