cult3

Working with Mixins in Ruby

Jul 08, 2015

Table of contents:

  1. What is a Mixin?
  2. Using Modules methods as Instance Methods
  3. Using Modules methods as Class Methods
  4. Using Instance Methods and Class Methods
  5. Why would you use a Mixin?
  6. When would you use a Mixin over Inheritance?
  7. Conclusion

Over the last couple of weeks we’ve looked at a couple of important concepts when working with Classes in Ruby such as Inheritance and using Modules.

Ruby is a programming language that only allows single inheritance. This means a class can only inherit from one parent class.

However, there are a lot of situations where it would be advantageous to “inherit” functionality from multiple places.

Fortunately Ruby provides this functionality as Mixins. In today’s tutorial we are going to be looking at using Mixins, why you would use them and when you should use them over classical inheritance.

What is a Mixin?

A Mixin is basically just a Module that is included into a Class. When you “mixin” a Module into a Class, the Class will have access to the methods of the Module.

If you are familiar with PHP, a Mixin is essentially the same as a Trait.

A couple of weeks ago we looked at the difference between Class Methods and Instance Methods.

The methods of a Module that are mixed into a class can either be Class Methods or Instance methods depending on how you add the Mixin to the Class.

Using Modules methods as Instance Methods

To add Module methods as instance methods on a Class you should include the Mixin as part of the Class.

For example, imagine you had this incredibly useful Module:

module Greetings
  def hello
    puts 'Hello!'
  end

  def bonjour
    puts 'Bonjour!'
  end

  def hola
    puts 'Hola!'
  end
end

To add these methods as instance methods on a class, you would simply do this:

class User
  include Greetings
end

Now you will have access to the methods on any instance of that Class:

philip = User.new
philip.hola
# => Hola!

But if you try to call the methods as Class Methods, you will get an error:

User.hola
# => undefined method 'hola' for User:Class (NoMethodError)

Using Modules methods as Class Methods

To add Module methods as Class Methods, instead of using include you would use extend:

class User
  extend Greetings
end

Now you can call the methods on the Class:

User.hola!
# => Hola!

But not on an instance of the Class:

philip = User.new
philip.hola
# => undefined method 'hola' for #<User:0x007fbd5b9ae438> (NoMethodError)

Using Instance Methods and Class Methods

When you create a new instance of a Class, the initialize method will automatically be invoked.

When you include a Module in a Class, the included method will be invoked on the Module.

This makes it very easy to add both Instance Methods and Class Methods using a pattern you will see a lot in Ruby code.

For example, imagine we have the following Utilities module:

module Utilities
  def method_one
    puts 'Hello from an instance method'
  end

  module ClassMethods
    def method_two
      puts 'Hello from a class method'
    end
  end
end

In this example I’ve separated the Class Methods into their own nested module. You don’t have to name the module ClassMethods, but this is the convention you will see being used.

Next we can implement the self.included hook that will be automatically called whenever this module is included in a Class:

def self.included(base)
  base.extend(ClassMethods)
end

This method will receive the instance of the Class that is being instantiated. Inside of the included method we use the extend method to add the ClassMethods module.

Now when we include this module in a Class we have access to both the Instance Methods and the Class Methods:

class User
  include Utilities
end

User.new.method_one
# => Hello from an instance method

User.method_two
# => Hello from a class method

Why would you use a Mixin?

So hopefully it’s clear as to how to use a Mixin, but an important question is why you would want to use a Mixin?

Mixins are perfect when you want to share functionality between different classes. Instead of repeating the same code over and over again, you can simple group the common functionality into a Module and then include it into each Class that requires it.

When would you use a Mixin over Inheritance?

Another important thing to understand is, when do you use a Mixin over normal Inheritance?

As I mentioned in Understanding Inheritance in Ruby, Inheritance has a lot of semantic meaning within the hierarchy of an application. There is a good reason why you can only inherit from a single parent.

Inheritance means that a class is a “type of something” and suggests specialisation. For example, a Pikachu object is a type of Pokemon and so it makes sense to inherit from the Pokemon class.

When a class should be capable of something, you should use a Mixin. For example, DVD, MP3, and Bluray classes all have the play method, but just because they are all capable of the same action, does not mean they all should inherit from the same parent.

The difference is subtle, but extremely important. If it’s not immediately clear, don’t worry! With experience you will begin to understand the difference and why it is so important.

Conclusion

Modules are an important part of the Ruby programming language and they are something that you will see being used extensively in almost every non-trivial example of Ruby code you will see.

The concept and implementation of Modules is really very easy once you understand their purpose and what benefits they introduce.

The difference between using a Mixin and using Inheritance is slightly more tricky, but it’s one of those Computer Science concepts that is universally applicable.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.