Include and prepend are extremely useful tools for adding and modifying logic in multiple places at once, or even just
splitting a file into components. Let's have a look at what they're doing behind the scenes.
In order to explain the differences, advantages, and disadvantages of include and prepend, there are a couple of
concepts that we'll need to cover first.
In Ruby, every class has an ancestry chain. The most basic way to construct that chain is through subclassing:
class B < A
super + 'baz'
# => [B, A, Object, Kernel, BasicObject]
In the example above, the chain starts with
B, then moves up to
A and then through the superclasses of
The ancestry chain is instrumental in finding the correct method block to execute when a method is called. Calling a
method traverses the object's ancestry chain searching each object along the way for a method with the same signature as
the one called.
In the example above, when we call
B is searched for an instance method named
bar, which is not found.
So we look along the chain one position, at
A, which does contain our instance method so we stop looking.
is executed on the
B object we created, returning
When we call
B is searched for
foo, which is found, so the searching stops. However, when executing
super is called.
super starts searching again for the
foo method, but this time starting at
foo, so we can stop our search.
foo is run (returning
Pretty easy right? So what are include and prepend useful for?
Including a module into a class is like giving the class another superclass. The methods in the module act like methods
from a superclass, except included modules will be inserted before any actual superclasses in the ancestry chain.
Prepending a module to a class is like redefining the class to be a subclass of itself, where the only methods in the
subclass are the module's methods, and the original class's methods are like superclass methods. These examples should
make it a little more clear:
# => [Example, Ext, Object, Kernel, BasicObject]
# => [Ext, Example, Object, Kernel, BasicObject]
In the first example, the
Ext module was included in
Example and was inserted after
Example in the ancestry
chain (much like a superclass). While in the second example, the
Ext module was prepended to
Example as was inserted
Example in the ancestry chain (much like a subclass, only the bottom of the chain isn't the name of the
Both include and prepend have their uses in the real world, so let's create a scenario for each and explore both of
A common use case for include, is when we have a method and associated logic that we want to define in a single file but
use in multiple classes. However, in one of those classes, we want to override one of the methods we defined in our
super + ' else'
# => 'do something'
# => 'do something else'
By using include,
Ext is inserted after the each class in their respective ancestry chains. This allows us to call
foo and get access to
Prepend is much less commonly used, but it's useful if we are using someone else's library and need to override the
library's default behaviour.
class ExternalClass # pls no touch
'I am me'
super + ', or am I?'
# => 'I am me, or am I?'
By using prepend,
Ext is inserted before the external class. This allows us to call
super in our custom module
and get access to
Both of these use cases have, and will continue to, come up in the real world time and time again. Now that you know a
prepend do behind the scenes, go out into the wild and
prepend a little
If you think I've made a mistake/am completely and utterly wrong/just want to provide some feedback, please reach out.
prepend when overwriting/extending an existing class. Use
include when creating a small snippet of
logic that classes can include and override if need be.