Overview
Ruby modules serve as containers for methods, constants, and classes that can be mixed into other classes or used as namespaces. Module definition creates a named constant that references a Module object, establishing a scope for organizing related functionality.
The module
keyword begins a module definition, followed by the module name written in CamelCase. Module definitions end with the end
keyword and create a new constant in the current scope.
module Authentication
TOKEN_EXPIRY = 3600
def generate_token
SecureRandom.hex(32)
end
end
Modules cannot be instantiated directly with new
like classes. They exist primarily for code organization, mixins through include
and extend
, and namespace creation. Ruby automatically creates a Module object when parsing module definitions.
# Module as namespace
module APIClient
class HTTPError < StandardError; end
BASE_URL = "https://api.example.com"
def self.request(endpoint)
# implementation
end
end
# Access namespaced elements
APIClient::BASE_URL
APIClient.request("/users")
Module definitions can contain method definitions, constant assignments, class definitions, nested modules, and executable code. The module acts as a lexical scope, creating a constant lookup path that affects constant resolution within the module.
module DataProcessor
VERSION = "1.0.0"
class ParseError < StandardError; end
module Formatters
def self.json(data)
JSON.generate(data)
end
end
def self.process(input)
# Module methods callable without mixing in
validate_input(input)
end
private
def self.validate_input(input)
# Private module methods
end
end
Basic Usage
Module definition begins with the module
keyword followed by the module name. The name must be a constant (starting with uppercase letter) and follows Ruby constant naming conventions.
module UserHelpers
def full_name(user)
"#{user.first_name} #{user.last_name}"
end
def active_users
User.where(active: true)
end
end
# Include in class to add instance methods
class UserController
include UserHelpers
def show
user = User.find(params[:id])
render json: { name: full_name(user) }
end
end
Modules support constant definition within their scope. Constants defined in modules are accessed using the scope resolution operator ::
.
module Configuration
DEFAULT_TIMEOUT = 30
RETRY_ATTEMPTS = 3
API_VERSION = "v2"
def self.timeout
DEFAULT_TIMEOUT
end
end
# Accessing module constants
timeout = Configuration::DEFAULT_TIMEOUT
Configuration.timeout # => 30
Module methods are defined using self
as the receiver, creating methods callable directly on the module without mixing in.
module MathUtils
def self.factorial(n)
return 1 if n <= 1
n * factorial(n - 1)
end
def self.fibonacci(n)
return n if n <= 1
fibonacci(n - 1) + fibonacci(n - 2)
end
# Alternative syntax for module methods
class << self
def prime?(num)
return false if num < 2
(2..Math.sqrt(num)).none? { |i| num % i == 0 }
end
end
end
MathUtils.factorial(5) # => 120
MathUtils.prime?(17) # => true
Nested modules create hierarchical namespaces. Inner modules are accessed through the outer module's namespace.
module Company
NAME = "Tech Corp"
module Products
class Software
def initialize(name)
@name = name
end
end
module Support
HOURS = "9AM - 5PM"
def self.contact_info
"support@techcorp.com"
end
end
end
end
# Access nested elements
software = Company::Products::Software.new("CRM")
hours = Company::Products::Support::HOURS
contact = Company::Products::Support.contact_info
Modules can include other modules, creating composition chains. When a module includes another module, it gains access to the included module's instance methods.
module Trackable
def created_at
@created_at ||= Time.now
end
end
module Auditable
include Trackable
def audit_trail
"Created: #{created_at}"
end
end
class Document
include Auditable
def initialize(title)
@title = title
end
end
doc = Document.new("Report")
doc.audit_trail # => "Created: 2024-01-15 10:30:00 UTC"
Advanced Usage
Module definition supports metaprogramming through dynamic method creation and module callbacks. The included
hook executes when the module is included in another class or module.
module Configurable
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
@config = {}
end
end
module ClassMethods
def configure
yield(@config)
end
def config
@config
end
end
def configured_value(key)
self.class.config[key]
end
end
class Service
include Configurable
configure do |config|
config[:timeout] = 60
config[:retries] = 3
end
end
service = Service.new
service.configured_value(:timeout) # => 60
Modules support prepend
for method interception and decoration patterns. Prepended modules are inserted before the class in the ancestor chain.
module Loggable
def save
puts "Saving #{self.class.name}"
result = super # Calls the original method
puts "Saved successfully" if result
result
end
end
class User
def save
@saved = true
end
end
# Prepend allows method wrapping
User.prepend(Loggable)
user = User.new
user.save
# Output:
# Saving User
# Saved successfully
Module refinements provide scoped monkey patching without global modifications. Refinements are activated with using
and only affect the current scope.
module StringExtensions
refine String do
def palindrome?
self == self.reverse
end
end
end
# Without using refinement
"racecar".palindrome? # => NoMethodError
class TextAnalyzer
using StringExtensions
def analyze(text)
if text.palindrome?
"#{text} is a palindrome"
else
"#{text} is not a palindrome"
end
end
end
analyzer = TextAnalyzer.new
analyzer.analyze("racecar") # => "racecar is a palindrome"
Anonymous modules created with Module.new
support dynamic module creation and runtime composition.
def create_validator_module(rules)
Module.new do
rules.each do |field, validation|
define_method("validate_#{field}") do |value|
case validation
when :presence
!value.nil? && value != ""
when :numeric
value.is_a?(Numeric)
when Proc
validation.call(value)
end
end
end
define_method(:validate_all) do |data|
rules.keys.all? do |field|
send("validate_#{field}", data[field])
end
end
end
end
# Create dynamic validation module
ValidationRules = create_validator_module(
name: :presence,
age: :numeric,
email: ->(val) { val.include?("@") }
)
class User
include ValidationRules
def initialize(data)
@data = data
end
def valid?
validate_all(@data)
end
end
Module constants support dynamic assignment and introspection. Constants can be assigned conditionally and accessed programmatically.
module EnvironmentConfig
# Conditional constant assignment
if ENV['RAILS_ENV'] == 'production'
API_ENDPOINT = "https://api.production.com"
DEBUG_MODE = false
else
API_ENDPOINT = "https://api.staging.com"
DEBUG_MODE = true
end
# Dynamic constant creation
%w[USER ADMIN MODERATOR].each_with_index do |role, index|
const_set("#{role}_LEVEL", index + 1)
end
def self.constants_hash
constants.each_with_object({}) do |const_name, hash|
hash[const_name] = const_get(const_name)
end
end
end
EnvironmentConfig::USER_LEVEL # => 1
EnvironmentConfig::ADMIN_LEVEL # => 2
EnvironmentConfig.constants_hash # => {:API_ENDPOINT=>"...", :DEBUG_MODE=>true, ...}
Common Pitfalls
Module constant lookup follows lexical scoping rules that can produce unexpected results when constants are referenced from different contexts.
GLOBAL_CONSTANT = "global"
module A
CONSTANT = "module A"
def self.lookup_constant
CONSTANT # Finds A::CONSTANT
end
def lookup_from_instance_method
CONSTANT # Also finds A::CONSTANT due to lexical scope
end
end
module B
CONSTANT = "module B"
def self.call_a_method
# This creates a new context for constant lookup
A.new.extend(A).lookup_from_instance_method # Finds A::CONSTANT
end
def self.dynamic_lookup
# Dynamic contexts may not follow lexical scoping
eval("CONSTANT") # Finds B::CONSTANT in this context
end
end
# Unexpected behavior with include
class Example
CONSTANT = "class constant"
include A
def check_constant
CONSTANT # Finds Example::CONSTANT, not A::CONSTANT
end
end
Method name collisions occur when including multiple modules with identical method names. Ruby uses the last included module's methods.
module A
def helper_method
"from module A"
end
end
module B
def helper_method
"from module B"
end
end
class Service
include A
include B # B's methods override A's methods
end
service = Service.new
service.helper_method # => "from module B"
# Check method lookup chain
Service.ancestors # => [Service, B, A, Object, ...]
# Accessing overridden methods requires explicit module reference
class Service
def call_a_method
A.instance_method(:helper_method).bind(self).call
end
end
Module extension with extend
adds methods as singleton methods, not instance methods, causing confusion about method availability.
module Utilities
def format_date(date)
date.strftime("%Y-%m-%d")
end
end
class Report
extend Utilities # Methods become class methods
end
# This works
Report.format_date(Date.today)
# This fails
report = Report.new
report.format_date(Date.today) # => NoMethodError
# Common mistake: mixing extend and include
class Document
include Utilities # Instance methods
extend Utilities # Class methods - now available both ways
end
# Both work but may indicate design confusion
Document.format_date(Date.today) # Class method
Document.new.format_date(Date.today) # Instance method
Private and protected method visibility in modules does not behave identically to classes when methods are mixed in.
module SecureOperations
def public_method
private_helper # This works within module context
end
private
def private_helper
"secret operation"
end
end
class Processor
include SecureOperations
def process
private_helper # This works - private methods become private in including class
end
def external_call
other_processor = Processor.new
other_processor.private_helper # => NoMethodError: private method
end
end
# Module privacy affects constants differently
module PrivateConstants
PRIVATE_CONST = "hidden"
private_constant :PRIVATE_CONST
end
class User
include PrivateConstants
def access_constant
PRIVATE_CONST # This works within the class
end
end
# External access fails
PrivateConstants::PRIVATE_CONST # => NameError: private constant
Circular dependencies between modules can cause loading issues and infinite recursion in method calls.
# file: module_a.rb
require_relative 'module_b'
module ModuleA
include ModuleB
def method_a
method_b + " -> A"
end
end
# file: module_b.rb
require_relative 'module_a'
module ModuleB
include ModuleA
def method_b
method_a + " -> B" # Infinite recursion
end
end
# Loading either file causes circular require
# Using the modules causes stack overflow
# Solution: break circular dependency
module SharedBehavior
def shared_method
"shared"
end
end
module ModuleA
include SharedBehavior
def method_a
shared_method + " -> A"
end
end
module ModuleB
include SharedBehavior
def method_b
shared_method + " -> B"
end
end
Autoloading modules can cause issues when module names don't match file names or directory structure, leading to LoadError or NameError exceptions.
# Incorrect: file named user_helper.rb
module UserHelpers # Should be UserHelper (singular)
def format_name
# implementation
end
end
# Rails autoloader expects:
# app/models/user_helper.rb -> UserHelper
# app/models/user_helpers.rb -> UserHelpers
# Nested module autoloading issues
module API
module V1
class UsersController # Expected file: app/controllers/api/v1/users_controller.rb
end
end
end
# Incorrect nesting can break autoloading
module API::V1 # This syntax can cause issues with autoloading
class UsersController
end
end
# Prefer explicit nesting for autoloading compatibility
module API
module V1
class UsersController
end
end
end
Reference
Module Definition Syntax
Syntax | Description |
---|---|
module Name |
Basic module definition |
module Name::Nested |
Nested module shorthand (avoid with autoloading) |
module Name; end |
Empty module definition |
Module.new { } |
Anonymous module creation |
Module Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#include(module) |
Module | self | Adds module methods as instance methods |
#extend(module) |
Module | self | Adds module methods as singleton methods |
#prepend(module) |
Module | self | Inserts module before class in ancestor chain |
#included_modules |
None | Array | Returns array of included modules |
#ancestors |
None | Array | Returns complete ancestor chain |
#const_defined?(name) |
String/Symbol | Boolean | Checks if constant is defined |
#const_get(name) |
String/Symbol | Object | Retrieves constant value |
#const_set(name, value) |
String/Symbol, Object | Object | Sets constant value |
#constants |
None | Array | Returns array of constant names |
Module Callbacks
Callback | Parameters | Description |
---|---|---|
included(base) |
Class/Module | Called when module is included |
extended(base) |
Object | Called when module extends an object |
prepended(base) |
Class/Module | Called when module is prepended |
const_missing(name) |
Symbol | Called when undefined constant is referenced |
method_added(name) |
Symbol | Called when method is defined in module |
Visibility Modifiers
Modifier | Scope | Description |
---|---|---|
public |
Methods defined after | Methods accessible everywhere |
protected |
Methods defined after | Methods accessible within class hierarchy |
private |
Methods defined after | Methods accessible only within same object |
private_constant |
Specific constants | Makes constants private |
deprecate_constant |
Specific constants | Marks constants as deprecated |
Module Introspection
Method | Returns | Description |
---|---|---|
#name |
String | Module name |
#to_s |
String | String representation |
#inspect |
String | Detailed string representation |
#instance_methods |
Array | Public and protected instance methods |
#private_instance_methods |
Array | Private instance methods |
#singleton_methods |
Array | Module methods (class methods) |
Refinement Methods
Method | Parameters | Returns | Description |
---|---|---|---|
refine(class) |
Class | Module | Creates refinement for class |
using(module) |
Module | self | Activates refinements in current scope |
import_methods(module) |
Module | self | Imports methods from module |
Common Constants
Constant | Value | Description |
---|---|---|
Module |
Class | The Module class itself |
Object |
Class | Root class for all objects |
BasicObject |
Class | Minimal root class |
Error Classes
Exception | Description |
---|---|
NameError |
Undefined constant or variable |
NoMethodError |
Undefined method |
ArgumentError |
Wrong number or type of arguments |
LoadError |
Cannot load required file |
CircularInclusionError |
Circular inclusion detected |