ruby

How to Use Mixins and Modules in Your Ruby on Rails Application

Cleiviane Costa

Cleiviane Costa on

How to Use Mixins and Modules in Your Ruby on Rails Application

Modules and mixins are, without doubt, great resources that make Ruby so attractive. They give the application the ability to share the code that can be used with ease in other places. It also helps us organize our code by grouping functionalities and concerns, which improves the readability and maintainability of our code.

In this article, we will go through the concepts behind modules and mixins. We'll learn how to create and mix modules into other classes and discuss the benefits of using them in a Ruby on Rails application.

I hope you'll enjoy the journey!

What are Modules

Modules are one of the shiniest resources of Ruby because they provide two great benefits: we can create namespaces to prevent name clashes and we can use them as mixins to share code across the application.

In structural terms, a module is pretty similar to any Ruby class. In fact, for Ruby, a Class is a Module, as we can see below in an irb console:

1> Class.is_a?(Module)
2 => true

Similar to classes, with modules we also group methods and constants and share code. However, there are a few differences between a module and a plain Ruby class:

  • We start the definition with the module keyword instead of class;
  • We can't instantiate modules, so no objects can be created from it;
  • We can't inherit from modules, so we use them as mixins instead;
  • Modules are standalone code, so there's no inheritance hierarchy of modules;

Modules are great places to have services, concerns, constants and any other code that, by having the same responsibility they should stay together.

This is how a module should look like:

1# lib/modules/invoice_creator.rb
2module InvoiceCreator
3  TAX_FEE = 0.5
4
5  def self.generate
6    puts "Don't worry! I'll generate the invoice for you at #{TAX_FEE}%"
7  end
8
9  def invoice_total
10    puts "I'll return the invoice total"
11    1000
12  end
13end

In this example, we can observe that a module can provide two kinds of methods: module methods and instance methods.

The self.generate is a module method, which means that we can use it without having to include (or extend) the module in any other object. This is very common when we are creating service objects, for example. We can call our module method like this:

12.5.3 :006 > InvoiceCreator.generate
2Don't worry! I'll generate the invoice for you
3=> nil

The invoice_total is an instance method and to be able to use it we need to include the module to a class, like this:

1# app/models/invoice.rb
2class Invoice < ApplicationRecord
3  include InvoiceCreator # This includes our module in the Invoice class
4
5  def calculate_tax
6    total = invoice_total # included method from our module
7    tax = total * InvoiceCreator::TAX_FEE
8    puts "This is the invoice tax: #{tax}"
9  end
10end

All instance methods from InvoiceCreator becomes available to the Invoice instances, so we can call the calculate_tax method pretty easily:

12.5.3 :008 > Invoice.new.calculate_tax
2"I'll return the invoice total"
3"This is the invoice tax: 500.0"
4 => nil
52.5.3 :009 >

In addition, inside the calculate_tax you notice that we are using a constant that was defined inside our module. As I mentioned before, modules are great constant keepers!

Imagine now a scenario where we need to have two kinds of InvoiceCreator to generate totally different invoices for suppliers and customers. We would end up having a name classing, and to avoid this, we make use of the other great benefit of modules: namespaces. Let's take a look in the next section.

Namespaces Everywhere

Namespaces can be defined as a way to organize our code when we want to create a local context for a given functionality, which is what we need for the scenario I just described: a different context for the customer's invoice creator and another one for the supplier's invoice creator.

Let's get back to our code. We'll need to create two different modules:

1# lib/modules/customer/invoice_creator.rb
2module Customer
3  module InvoiceCreator
4    def self.generate
5      puts "Don't worry! I'll generate the customer invoice for you"
6    end
7  end
8end
9
10# lib/modules/supplier/invoice_creator.rb
11module Supplier
12  module InvoiceCreator
13    def self.generate
14      puts "Don't worry! I'll generate the supplier invoice for you"
15    end
16  end
17end

Then we can use Customer::InvoiceCreator or Supplier::InvoiceCreator where we need it:

12.5.3 :014 > Customer::InvoiceCreator.generate
2Don't worry! I'll generate the customer invoice for you
3 => nil
42.5.3 :015 > Supplier::InvoiceCreator.generate
5Don't worry! I'll generate the supplier invoice for you
6 => nil
72.5.3 :016 >

That way each specific code will be wrapped up inside its own module, respecting the separation of concerns principle. Besides that, namespacing everywhere is also a great way to keep our code well organized.

Let's see now how we can make use of the other benefits of Ruby modules: mixins.

The Magic of Using Mixins

As you might already know, one characteristic of Ruby is that it implements the single inheritance mechanism, which means that a class can only inherit from one other class. We may often need to inherit from more classes. In Ruby, we can cover that need by using the composition over inheritance pattern.

This is doable by using the mixins. When we mix in a piece of code in another Ruby class we are adding to this class more behavior without using inheritance and this is amazing. Therefore, in Ruby, mixins are modules that we include in classes where they are needed. With that we gain by keeping our code clean and the responsibilities separated, as they should be.

For example, let's say that our InvoiceCreator module is a service that needs to use functionalities provided by several other modules, such as InvoiceCalculator, InvoiceRenderer and InvoiceSender, they will be necessary to fulfill the invoice creation process.

We can achieve this by including a chain of modules as mixins in our code, so we can use the methods directly, like the example below:

1# lib/modules/invoice_calculator.rb
2module InvoiceCalculator
3  def calculate_items_total
4    puts "imagine some math here"
5  end
6end
7
8# lib/modules/invoice_renderer.rb
9module InvoiceRenderer
10  def generate_invoice_pdf
11    puts "imagine that we are using some PDF generation magic here"
12  end
13end
14
15# lib/modules/invoice_sender.rb
16module InvoiceSender
17  def send_invoice
18    puts "imagine your favorite mail service being used here"
19  end
20end

Since we can't make the InvoiceCreator inherit from all the other three modules, we are including them instead. This way the InvoiceCreator includes all the methods from the other Invoice modules, and we can call these in any class/module the InvoiceCreator module gets included in. Be careful though! If any of the modules have methods with the same name, they will overwrite each other.

1# lib/modules/customer/invoice_creator.rb
2module Customer
3  module InvoiceCreator
4    include InvoiceCalculator
5    include InvoiceRenderer
6    include InvoiceSender
7
8    def generate_invoice
9      calculate_items_total # from InvoiceCalculator
10      generate_invoice_pdf # from InvoiceRenderer
11      send_invoice # from InvoiceSender
12    end
13  end
14end

Now we can call our service methods anywhere we include it by doing this:

1# app/models/invoice.rb
2class Invoice < ApplicationRecord
3  include Customer::InvoiceCreator
4
5  def send_invoice_to_customer
6    puts "Don't worry! I'll generate the customer invoice for you"
7    generate_invoice
8  end
9end

This will be the result, calling the methods from the included Invoice modules through the InvoiceCreator module:

12.5.3 :051 > Invoice.new.generate_invoice
2Don't worry! I'll generate the supplier invoice for you
3imagine some math here
4imagine that we are using some PDF generation magic here
5imagine your favorite mail service being used here
6 => nil

Notice that this is how we use composition over inheritance principle. This principle stands that you should prefer to use composition whenever you can. In composition, we create classes that are responsible to provide specific functionalities to others, which is exactly what we are doing.

That's all Folks!

As Ruby developers, we love to use its facilities and syntax sugar when planning, writing, maintaining and refactoring our code. I hope that through this article you can appreciate why modules are such a great resource to help improve the readability of our code, to keep things with only one responsibility and to keep our codebase clean and easy to maintain. And also that we can use the mixins magic to include modules in our code in cases where we would need to inherit from multiple sources.

We'll keep talking about the greatest resources of Ruby and Rails here, so stay tuned!

P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Share this article

RSS
Cleiviane Costa

Cleiviane Costa

Our guest author Cleiviane Costa is a software engineer with 10+ years of experience. She's passionate about Ruby on Rails and loves to write about things that she discovers in her day-to-day activities.

All articles by Cleiviane Costa

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps