Polyglot
Languages Ruby mixins
Ruby § mixins

Modules and mixins

A module in Ruby is a namespace and a mixin. As a namespace, it groups related constants, classes, and module-level methods. As a mixin, it provides instance methods that may be included into classes — admitting multiple-inheritance-style code reuse without the substantive complexity. Three principal mechanisms attach a module to a class: include (mixes in instance methods), extend (mixes in singleton methods — methods on the receiver), and prepend (Ruby 2.0+; inserts the module before the class in the lookup chain). The conventional standard-library mixins — Comparable, Enumerable, Forwardable — admit substantial functionality from a single method definition. The combination — modules as namespaces and mixins, the three attachment forms, the substantial standard-library mixins — is the substance of Ruby’s mixin system.

Module declarations

The module keyword introduces a module:

module Geometry
  PI = 3.14159

  def self.degrees_to_radians(d)                  # module-level method
    d * PI / 180
  end

  class Circle                                    # nested class
    def initialize(radius)
      @radius = radius
    end
  end
end

# Access:
Geometry::PI
Geometry.degrees_to_radians(90)
Geometry::Circle.new(5)

A module:

  • May contain constants, methods, and nested classes/modules.
  • May not be instantiated directly.
  • May be a namespace, a mixin, or both.

Module as namespace

For grouping related constants and classes:

module Network
  DEFAULT_PORT = 8080

  class Connection
    def initialize(host, port: DEFAULT_PORT)
      @host = host
      @port = port
    end
  end

  class Server
    def listen(port: DEFAULT_PORT)
      # ...
    end
  end

  module Protocols
    HTTP = "http"
    HTTPS = "https"
  end
end

Network::Connection.new("example.com")
Network::Protocols::HTTP

The conventional uses are library packages, gem internal organisation, and avoiding name collisions.

Module-level methods

Two principal forms:

module Math
  def self.square(n)                              # def self.foo
    n * n
  end

  def self.cube(n)
    n ** 3
  end
end

Math.square(5)                                    # 25

# Or via module_function:
module Helper
  module_function

  def greet(name)
    "Hello, #{name}"
  end
end

Helper.greet("Alice")
Helper.greet("Bob")

The module_function admits using methods both as module-level methods and as instance methods (when the module is included).

include — mixin instance methods

The include mixes in instance methods:

module Greeting
  def hello
    "Hello, I am #{name}"
  end
end

class Person
  include Greeting

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

Person.new("Alice").hello                         # "Hello, I am Alice"

The mechanism admits substantial code reuse:

module Logging
  def log(message)
    puts "[#{Time.now}] #{message}"
  end
end

class Service
  include Logging
  # log is now an instance method on Service
end

Service.new.log("started")

include adds the module to the ancestor chain — between the class and its superclass:

class A
  def foo; "from A"; end
end

module M
  def foo; "from M"; end
end

class B < A
  include M
end

B.new.foo                                         # "from M"
B.ancestors                                       # [B, M, A, Object, Kernel, BasicObject]

The lookup order: B → M → A → Object → .... The mixin shadows the parent class’s method.

extend — mixin singleton methods

The extend mixes in methods on the receiver (typically a class):

module Helpers
  def helper
    "helper called"
  end
end

class Foo
  extend Helpers
end

Foo.helper                                        # "helper called" (class method)

# extend can also add to a single instance:
foo = "hello"
foo.extend(Helpers)
foo.helper                                        # "helper called"

The principal use cases:

  • Adding class methods — modules’ methods become class methods of the receiver.
  • Singleton-method extension — adding methods to a single object.

A common pattern: include and extend simultaneously:

module Greetable
  module ClassMethods
    def all_greetings
      ["hello", "hi", "hey"]
    end
  end

  module InstanceMethods
    def greet
      "Hello"
    end
  end

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

class Person
  include Greetable
end

Person.all_greetings                              # class method
Person.new.greet                                   # instance method

The self.included hook admits running code when the module is included; the pattern is conventional in Rails-style “concerns”.

prepend — mixin before the class

The prepend (Ruby 2.0+) inserts the module before the class in the lookup chain:

module Logging
  def hello
    puts "logging: about to greet"
    super                                         # calls the prepended class's hello
  end
end

class Person
  prepend Logging

  def hello
    puts "Hello!"
  end
end

Person.new.hello
# logging: about to greet
# Hello!

The prepend admits wrapping methods — particularly useful for cross-cutting concerns like logging, caching, or instrumentation.

The ancestor chain with prepend:

class B
  prepend M
end

B.ancestors                                       # [M, B, Object, ...]

M is before BB.new.method looks up M first.

Mixing modules into modules

A module can include other modules:

module Logging
  def log(msg); puts msg; end
end

module Tracing
  include Logging                                 # Tracing now has log

  def trace(msg); log("trace: #{msg}"); end
end

class Service
  include Tracing                                 # gets log AND trace
end

The mechanism admits substantial composition.

Comparable

The standard Comparable mixin derives <, <=, >, >=, ==, between?, clamp from <=>:

class Distance
  include Comparable

  attr_reader :meters

  def initialize(meters)
    @meters = meters
  end

  def <=>(other)
    meters <=> other.meters
  end
end

a = Distance.new(100)
b = Distance.new(200)

a < b                                             # true
a.between?(Distance.new(50), Distance.new(150))   # true
[a, b].min                                        # a

The <=> (spaceship) returns -1, 0, 1, or nil. Defining it admits the substantial comparison surface.

Enumerable

The Enumerable mixin provides substantial iteration methods (map, select, reject, reduce, sort, find, count, to_a, etc.) — derived from a single each method:

class TodoList
  include Enumerable

  def initialize
    @items = []
  end

  def add(item)
    @items << item
    self
  end

  def each(&block)
    @items.each(&block)
  end
end

list = TodoList.new
list.add("buy milk")
list.add("write code")

list.map { |i| i.upcase }                         # via Enumerable
list.select { |i| i.include?("milk") }
list.first
list.count
list.sort
list.to_a

Treated in Enumerable.

Forwardable

The Forwardable admits delegation:

require "forwardable"

class TodoList
  extend Forwardable

  def_delegators :@items, :each, :size, :empty?

  def initialize
    @items = []
  end

  def add(item)
    @items << item
  end
end

list = TodoList.new
list.add("buy milk")
list.size                                         # 1 (delegated to @items)
list.each { |i| puts i }                          # delegated

The pattern admits substantial conciseness for “this object delegates these methods to a contained object”.

Singleton

The Singleton mixin admits the singleton pattern:

require "singleton"

class Logger
  include Singleton

  def log(message)
    puts message
  end
end

Logger.instance.log("hello")
# Logger.new                                      # NoMethodError (private)

The conventional discipline avoids singletons for testability; explicit dependency injection is conventionally preferred.

Custom mixins

The pattern is conventional for shared behaviour:

module Persistable
  def save
    File.write(filename, serialize)
  end

  def filename
    "#{self.class.name.downcase}_#{id}.json"
  end

  def serialize
    raise NotImplementedError, "subclass must implement"
  end
end

class User
  include Persistable

  attr_reader :id

  def initialize(id)
    @id = id
  end

  def serialize
    JSON.dump(id: @id)
  end
end

The pattern admits substantial reuse with hook methods that subclasses override.

Constants in modules

Constants in modules admit lexical scoping:

module API
  TIMEOUT = 30

  class Client
    def fetch(url)
      with_timeout(TIMEOUT) { http_get(url) }    # TIMEOUT visible
    end
  end
end

The constant is found via lexical lookup; the Client class doesn’t need to qualify TIMEOUT as API::TIMEOUT.

Conditional inclusion

class Service
  include Logging if Rails.env.development?       # admitted but rare
end

The pattern is admitted but conventionally avoided; consistent class definitions are preferred.

Common patterns

Concern-style mixin

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

  def track(action)
    self.class.tracker.track(self, action)
  end

  module ClassMethods
    def tracker
      @tracker ||= Tracker.new
    end
  end
end

class User
  include Trackable
end

User.tracker                                      # class method
User.new.track("login")                           # instance method

The pattern is the conventional Rails “concern”.

Comparable for sortable values

class Priority
  include Comparable

  ORDER = { low: 0, medium: 1, high: 2 }

  attr_reader :level

  def initialize(level)
    raise ArgumentError unless ORDER.key?(level)
    @level = level
  end

  def <=>(other)
    ORDER[level] <=> ORDER[other.level]
  end
end

[:high, :low, :medium].map { |l| Priority.new(l) }.sort

Enumerable for collections

class Inventory
  include Enumerable

  def initialize
    @items = {}
  end

  def add(item, quantity)
    @items[item] = quantity
  end

  def each(&block)
    @items.each(&block)
  end
end

inv = Inventory.new
inv.add(:apple, 10)
inv.add(:banana, 5)

inv.count                                         # 2
inv.select { |item, qty| qty > 7 }
inv.find { |item, _| item == :banana }
inv.to_a

Forwardable delegation

require "forwardable"

class TodoList
  extend Forwardable

  def_delegators :@items, :each, :size, :empty?, :first, :last
  def_delegator :@items, :length, :item_count    # alias

  def initialize
    @items = []
  end

  def add(item)
    @items << item
    self
  end
end

list = TodoList.new
list.add("a").add("b")
list.size                                         # 2
list.item_count                                   # 2 (aliased)

Module as namespace and mixin

module HTTP
  TIMEOUT = 30                                    # constant

  class Client                                    # nested class
    def get(url)
      # ...
    end
  end

  module Helpers                                  # nested module (mixin)
    def http_get(url)                             # instance method when included
      Client.new.get(url)
    end
  end

  module_function

  def url_encode(s)                               # module-level method
    # ...
  end
end

# Usage:
client = HTTP::Client.new                         # nested class
encoded = HTTP.url_encode("hello world")          # module method

class Service
  include HTTP::Helpers                           # mixin
end

Refinements (lexical mixins)

module StringEnhancements
  refine String do
    def shout
      upcase + "!"
    end
  end
end

class MyClass
  using StringEnhancements

  def example
    "hello".shout                                 # "HELLO!"
  end
end

# "hello".shout                                   # NoMethodError outside MyClass

Refinements admit lexically-scoped extensions; conventional for library-internal tweaks without polluting global classes.

Hook methods

module Trackable
  def self.included(base)
    puts "#{base} included Trackable"
    base.extend(ClassMethods)
  end

  def self.extended(base)
    puts "#{base} extended Trackable"
  end

  module ClassMethods
    def track(action)
      # ...
    end
  end
end

The hooks (included, extended, prepended, inherited) admit running code when the module is attached.

A note on multiple inheritance

Single inheritance plus mixins covers the substance of multiple-inheritance use cases:

class Animal; end

module Walking
  def walk; end
end

module Swimming
  def swim; end
end

class Duck < Animal
  include Walking
  include Swimming
end

The mechanism admits substantial code reuse without the diamond inheritance problem.

The lookup order is well-defined: included modules are inserted into the ancestor chain in reverse-include order (the last-included module wins for conflicts):

module A; def foo; "A"; end; end
module B; def foo; "B"; end; end

class C
  include A
  include B
end

C.new.foo                                         # "B" (last include wins)
C.ancestors                                       # [C, B, A, Object, ...]

A note on the conventional discipline

The contemporary Ruby modules-and-mixins advice:

  • Use modules for namespacing — group related code.
  • Use modules for mixins — share behaviour.
  • Use include for shared instance methods.
  • Use extend for shared class methods.
  • Use prepend for wrapping/instrumentation.
  • Use Comparable — derive comparisons from <=>.
  • Use Enumerable — derive iteration from each.
  • Use Forwardable — delegate methods to contained objects.
  • Use the included hook for “concern”-style class extensions.
  • Use refinements for lexically-scoped patches.
  • Avoid Singleton — explicit dependency injection is preferred.
  • Avoid global mixin pollution — mix into the smallest scope that works.

The combination — modules as namespaces and mixins, include/extend/prepend for attachment, the Comparable/Enumerable/Forwardable standard mixins, hook methods for substantial introspection — is the substance of Ruby’s mixin system. The discipline produces flexible, composable code without the substantive complexity of true multiple inheritance.