In the past month or so I’ve been learning Ruby, this is my first blog post about the language and my experiences learning it. I’d love to hear your feedback.
Ruby allows you to work with modules and mixins, giving characteristics to classes and instances that otherwise could only happen through inheritance. One can argue that there are many similarities between Ruby’s mixins and PHP’s traits.
But first things first: what is a mixin? A mixin is a module that you include in a class, because you are “mixing in” the module methods with the instance methods of a class.
When learning about Ruby’s modules behavior, specifically the
include functionality, I couldn’t help but think how similar it was to PHP traits. It reminded me how a class can override an inherited method from a trait, or just plain inheritance of new features that class didn’t have before.
What I learned was the following:
- classes that
extenda module have methods from it, however they are only accessible through a class call
extenddoesn’t change the inheritance chain
- classes that
includea module have methods from it, however they are only accessible through an instance call
includedoes change the inheritance chain
- and, finally;
prepend. Which is a funny case in my opinion, a bit trickier to grasp the concept at first sight. But I got a bit more understanding after playing with the following. I think…
I learned about the existence of
prepend after doing this small exercise. Before this I thought you either
extend a module or
Try out the following code:
What should we get when calling
car = Car.new car.color
The naive me, thought in the beginning the result would be:
# Red # => nil
And my reasoning was, except for monkey patching, isn’t
Car overriding everything because when instantiated we overload the method
#color? To my surprise, it printed (come on, go check it!):
# Blue # => nil
Apparently I was wrong. Playing with
pry I added
super (LOC4) to
Vehicle#color, convinced that the return would be
Vehicle, right? Wrong again!
It looks like,
Paintable is overriding
Car, which is a bit confusing because the keyword is
prepend, for all I know (and the dictionary too, trust me, I checked) it means to attach something in the beginning of something else.
With that in mind, shouldn’t
Paintable come before
Car, and because I have overridden
Car (LOC17), shouldn’t
Car have the last say in what color should be?
Apparently, no. The correct way to read it is:
Paintableis prepending the inheritance chain of
And this is why:
Which means that when calling
#color in the instance we get
Blue and why
Printable now has
Car as a superclass.
Conclusion and Use Cases
Modules add functionality to your class, either by being a
mixin or through class methods when you
I found this blog post with a good example of inheritance vs mixins:
Inheritance means that a class is a “type of something” and suggests specialisation. For example, a
Pikachuobject is a type of
Pokemonand so it makes sense to inherit from the
When a class should be capable of something, you should use a Mixin. For example,
Blurayclasses all have the
#playmethod, but just because they are all capable of the same action, does not mean they all should inherit from the same parent
Car example, the
Paintable module makes more sense to be used with an
extend, since you can change colors not only of cars, vehicles, multiple types of objects that won’t necessarily inherit
Also, beyond including, and extending a module you can prepend it.
prepend changes the inheritance chain as does
include, but with inverse order.
extend does not change the inheritance chain.
You will be want to use
prepend whenever you want your code to be executed before the class. After working with so many legacy applications, I can see this being really useful when you want to extend the functionality of some class, let’s say
Login, you want to run before
Login a check to see if the requester address is accessing only through your closed network,
prepend would allow you to do that. And a good side effect of using
prepend is that you remove the necessity of directly monkey patch a method.
I think only experience will make clear the best way to implement a certain module in an application, but understanding how each of these tools behave is important before you can make that decision.