Modules are one of my favorite 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:

module Logging def log(level, message) File.open("log.txt", "a") do |f| f.write "#{level}: #{message}" end end end class Service include Logging def do_something begin # do something rescue StandardError => e log :error, e.message end end end

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.

class MyWorker include Sidekiq::Worker def perform(args) # do some work end end MyWorker.perform_async {something: "useful"}

Though include is the most common way of importing external code into a class, Ruby provides also two other ways to achieve that: extend and 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.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 Kernel module.

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 Object class.

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 method_missing of 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

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:

module Logging # ... end class Service include Logging # ... end

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

> Service.ancestors => [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:

module Logging def log(message) # log in a file end end module Debug def log(message) # debug output end end class Service include Logging include Debug end Service.ancestors # [Service, Debug, Logging, Object, ...]

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, Debug .

Extend

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 Service instance.

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

Then, we could have called the method like this:

Service.log :info, "Something happened"

Often, you 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 a Module , to also import class methods at runtime:

module Logging module ClassMethods def logging_enabled? true end end def self.included(base) base.extend(ClassMethods) end def log(level, message) # ... end end

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.

Prepend

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:

module ServiceDebugger def run(args) puts "Service run start: #{args.inspect}" result = super puts "Service run finished: #{result}" end end class Service prepend ServiceDebugger # perform some real work def run(args) args.each do |arg| sleep 1 end {result: "ok"} end end

Using prepend , the module ServiceDebugger is now inserted at the very bottom of the ancestors chain.

You can verify it yourself again by calling Service.ancestors :

> Service.ancestors => [ServiceDebugger, Service, Object, ...]