What are modules, concerns and mixins in Ruby on Rails?

khan_mansoor

Mansoor Khan

about 3 minutes Feb 26, 2024

Ruby on Rails (RoR) is a popular web application framework that leverages the Ruby programming language. It introduces several mechanisms to organize and reuse code, among which concerns, mixins, and modules are prominent. Understanding these concepts is key to writing modular, maintainable, and efficient Ruby on Rails applications.

Modules

Modules in Ruby serve two primary purposes: they act as a namespace, preventing name clashes between different parts of a program, and they allow the creation of mixins.

Example of a Module as a Namespace:


module Admin
  class User
    def initialize(name)
      @name = name
    end

    def display_name
      "Admin: #{@name}"
    end
  end
end

admin_user = Admin::User.new("Alice")
puts admin_user.display_name


Example of a Module as a Mixin:

module Greeter
  def greet
    "Hello, #{@name}!"
  end
end

class User
  include Greeter

  def initialize(name)
    @name = name
  end
end

user = User.new("Bob")
puts user.greet


Mixins

Mixins in Ruby are a way to share reusable code across multiple classes. Ruby does not support multiple inheritance directly, but mixins can be used to include functionality from multiple modules, simulating multiple inheritance.

Example of Mixin Usage:


module Drivable
  def drive
    "Driving"
  end
end

module Flyable
  def fly
    "Flying"
  end
end

class Car
  include Drivable
end

class Plane
  include Flyable
end

car = Car.new
plane = Plane.new

puts car.drive
puts plane.fly


Concerns

Concerns in Rails are a way to further organize related model or controller code into modules that can be included as needed. They use Ruby's module functionality but are designed specifically for working with Rails' components. Rails concerns are typically used for sharing code that doesn't fit into a single model or controller, such as validations, associations, or methods used in multiple models.

Example of a Concern:

In app/models/concerns/taggable.rb:

module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :tags
  end

  def add_tag(tag)
    self.tags << tag
  end
end


This concern can then be included in any model that should be taggable:

class Article < ApplicationRecord
  include Taggable
end

class Photo < ApplicationRecord
  include Taggable
end


Differences and Similarities

- Modules provide a namespace and a way to implement mixins. They are a fundamental Ruby feature for organizing code and preventing conflicts.
- Mixins are a way to share code among classes using modules. They simulate multiple inheritance by mixing in methods, constants, etc., from modules.
- Concerns are a Rails-specific feature for organizing model and controller code. They are built on top of Ruby's modules but are designed to work seamlessly within the Rails framework, providing a structure for sharing code (like validations, associations) across models or controllers.

While modules and mixins are Ruby language features, concerns are a pattern used within Rails to utilize modules and mixins in a way that fits the Rails application structure and conventions. All three are about code reuse and organization but are applied at different levels and for slightly different purposes within Ruby and Rails applications.


Example to Illustrate the Difference between Module and Mixin

Module as a Namespace:

module Animals
  class Dog
    def bark
      "Woof!"
    end
  end
end

dog = Animals::Dog.new
puts dog.bark

In this example, Animals is a module used as a namespace to contain the Dog class.


Module as a Mixin:

module Jumpable
  def jump
    "Jumping!"
  end
end

class Dog
  include Jumpable
end

dog = Dog.new
puts dog.jump


Here, Jumpable is a module used as a mixin. By including Jumpable in the Dog class, all instances of Dog gain the jump method.


So, while a module is a structural element in Ruby that can serve multiple purposes, including acting as a namespace and a container for reusable code, a mixin specifically refers to the practice of including a module within a class to add new functionalities to that class.