Ruby provides a great platform for modular coding. You’re not limited to just class hierarchies that other languages provide. Through the power of mixins, code can be reused in many robust ways. The two primary ways to utilize the power of mixins are through Ruby’s extend and include.
I know this one has been done many times before, but I felt some further elaboration would help. There are many use cases where extend and include are relevant. I’m also writing this down so I won’t forget it later :-). Let’s dive in.
high level
As a general rule of thumb, you use extend for class level methods, and include for instance level methods. This isn’t always the case, as we’ll see in a later example. For now, let’s just assume this to be the true. Here’s an example that demonstrates this:
This demonstrates the simple way to handle the difference between instance and class level methods. More often than not, you may want a module to be able to have instance AND class level methods, so you can just mix that module in, and have the class gain both. This is possible with this slightly more advanced example:
common mixin usage
This acheives the same effect as the first example, but with a more simple interface. Instead of having to extend and include 2 seperate modules when you include SayHello, it automatically extends the sub-module SayBye. This is accomplished using the self.included method, which rails calls whenever a module is included. From inside this method, we capture the base object and tell it to extend our class level methods in the SayBye module. In our example the base object is the Greeter class.
extending existing objects
If you have an existing object, whether it be a class or an instance of a class you can use the extend method to mixin a module’s methods at the class or instance levels. This sounds confusing and contradictory to what I just told you, but I think an example will help clarify:
For the sake of this example, I have a module with some methods, in this case the ‘hello’ method. I also have a class, Greeter which I want to gain those methods. Let’s say I do the following.
In this case, I used extend to add the hello method to an already instantiated Greeter object. In this case my g object gained some instance methods unique to that object. If I instantiated a different copy of my Greeter object, it would not have the ‘hello’ method on it. Similarly, extend can be used on the class itself for it to gain class level methods:
The important thing to take away from these examples is that extend works at the level of the type of object you’ve passed it.
include before class is instantiated
Given the same code as above:
Let’s say we want to mixin the hello method to any new instances of Greeter. We can do it like this:
Remember, use include to have a class gain instance methods before it’s instantiated. Use extend if you want the object to gain instance methods on an already instantiated/existing object. This can be confusing at first, but once you understand what’s going on under the hood, it’s not so bad.
conclusion
I’ve covered all the basic areas where you’d want to use mixins. Hopefully going forward using modules in Ruby will be easier for you and you’ll be able to enjoy coding more with the ability to make your code more modular.
Happy coding!