Modules and Mixins
Modules in Ruby serve two important purposes: they act as namespaces to organize your code, and they provide a way to share behavior between classes through mixins. In this tutorial, you’ll learn both patterns with practical examples.
Why Use Modules?
Before we dive into the syntax, let’s understand why modules matter. In Ruby, a class can only inherit from one parent. But what if you want to share functionality across unrelated classes? That’s where modules come in.
Modules let you:
- Group related classes and methods together
- Create namespaces to avoid name collisions
- Share behavior between classes without inheritance
Modules as Namespaces
A module can contain constants, methods, and even other classes. Grouping related code together keeps your codebase organized:
module MathUtils
class Calculator
def add(a, b)
a + b
end
def multiply(a, b)
a * b
end
end
PI = 3.14159
def self.circle_area(radius)
PI * radius * radius
end
end
calc = MathUtils::Calculator.new
puts calc.add(2, 3) # => 5
puts MathUtils.circle_area(5) # => 78.53975
The :: operator accesses constants and classes inside a module. This prevents naming conflicts—your Calculator class won’t clash with Calculator from another library.
Module Constants
Modules are great for storing constants that belong together logically:
module HTTPStatus
OK = 200
NOT_FOUND = 404
SERVER_ERROR = 500
def self.message(code)
messages = { OK => "OK", NOT_FOUND => "Not Found" }
messages[code] || "Unknown"
end
end
puts HTTPStatus::OK # => 200
puts HTTPStatus.message(404) # => Not Found
Constants in modules follow the convention of being UPPERCASE with underscores.
Mixins: Sharing Behavior Across Classes
Ruby doesn’t support multiple inheritance, but mixins let you achieve similar results. Use include to add instance methods:
module Debuggable
def debug_info
"#{self.class} - #{instance_variables.map { |v| "#{v}=#{instance_variable_get(v)}" }.join(', ')}"
end
end
class User
include Debuggable
def initialize(name, email)
@name = name
@email = email
end
end
user = User.new("Alice", "alice@example.com")
puts user.debug_info
# => User - @name=Alice, @email=alice@example.com
Now every User instance has the debug_info method. You can include multiple modules in a single class:
module Timestampable
def created_at
@created_at ||= Time.now
end
def updated_at
@updated_at ||= Time.now
end
end
class Order
include Debuggable
include Timestampable
def initialize(item)
@item = item
end
end
order = Order.new("Widget")
puts order.debug_info
# => Order - @item=Widget
Extending with Class Methods
Use extend to add module methods as class methods:
module Configurable
def configure(&block)
@config ||= {}
block.call(@config) if block_given?
@config
end
def config
@config || {}
end
end
class API
extend Configurable
end
API.configure do |c|
c[:base_url] = "https://api.example.com"
c[:timeout] = 30
end
puts API.config # => {:base_url=>"https://api.example.com", :timeout=>30}
The extend keyword makes the module’s methods available as class methods on the extended class.
Combining Include and Extend
You can use both in the same module to provide both instance and class methods:
module Factory
def create(*args)
new(*args)
end
module ClassMethods
def brand_name
@brand_name ||= "Generic"
end
def brand_name=(name)
@brand_name = name
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
class Widget
include Factory
end
widget = Widget.create("Button")
puts Widget.brand_name # => Generic
The self.included callback runs when the module is included. This is a common pattern in Ruby frameworks like Rails—ActiveRecord uses this to add both instance methods and class methods through modules.
The Prepend Method
Ruby also provides prepend, which works like include but places the module’s methods earlier in the method lookup chain:
module UppercaseName
def name
">> #{super} <<"
end
end
class Person
prepend UppercaseName
def name
"Alice"
end
end
puts Person.new.name # => >> Alice <<
With prepend, the module’s method overrides the class’s method, but you can still call super to access the original.
Real-World Example: Serialization
Here’s how you might use modules in a real application:
module Serializable
def to_json
hash = {}
instance_variables.each do |var|
hash[var.to_s.delete('@')] = instance_variable_get(var)
end
hash.to_json
end
end
class User
include Serializable
def initialize(name, role)
@name = name
@role = role
end
end
user = User.new("Bob", "admin")
puts user.to_json
# => {"name":"Bob","role":"admin"}
When to Use Mixins
Mixins work well for:
- Shared behavior across unrelated classes (like
Debuggable) - Adding utility methods (like
Timestampable) - Implementing interfaces (Ruby uses duck typing, so no formal interfaces)
- Adding cross-cutting concerns (like logging, caching)
Avoid overusing mixins—if you find yourself including many modules with unrelated functionality, consider inheritance or composition instead.
When Not to Use Mixins
Don’t use mixins when:
- You need shared state (modules can’t store instance state)
- You need multiple inheritance (Ruby classes still inherit from one superclass)
- The behavior is specific to one class (keep it private)
- You find yourself overriding methods with conditionals
Module Methods: The Three Types
Ruby modules can define three kinds of methods:
- Instance methods: Added via
include, available on class instances - Class methods: Added via
extendor defined asdef self.method_name - Module methods: Called directly on the module with
ModuleName.method_name
module Helper
# Instance method - available when included
def helper_method
"instance"
end
# Class method - available when extended
module ClassMethods
def class_method
"class"
end
end
# Module method - called as Helper.module_method
def self.module_method
"module"
end
def self.included(base)
base.extend(ClassMethods)
end
end
Summary
Modules in Ruby provide two powerful features:
- Namespaces organize related classes and constants
- Mixins share behavior through
include(instance methods) andextend(class methods)
Use modules to keep your code modular and DRY. But remember: composition (objects containing other objects) is sometimes clearer than inheritance or mixins. When used appropriately, modules are one of Ruby’s most powerful features for code organization and reuse.