Overview
Ruby modules serve as namespaces that group related classes, constants, and methods under a common identifier. Modules prevent naming conflicts by creating separate scopes where identically named classes or constants can coexist without interference. Ruby implements namespacing through the Module
class and the ::
constant resolution operator.
Modules function as containers that encapsulate code and provide organizational structure. Unlike classes, modules cannot be instantiated directly but serve as mixins through inclusion or as standalone namespaces. The Ruby constant lookup mechanism searches through the nesting hierarchy, allowing nested modules to access outer module constants while maintaining clear boundaries.
module Authentication
class User
def initialize(name)
@name = name
end
end
TOKEN_EXPIRY = 3600
end
# Accessing namespaced class
user = Authentication::User.new("alice")
puts Authentication::TOKEN_EXPIRY # => 3600
Ruby's module system supports nested namespacing, where modules contain other modules, creating hierarchical organization. The interpreter maintains a constant table for each module, enabling fast constant resolution while preserving namespace isolation.
module API
module V1
class UsersController
def index
"V1 users"
end
end
end
module V2
class UsersController
def index
"V2 users"
end
end
end
end
puts API::V1::UsersController.new.index # => "V1 users"
puts API::V2::UsersController.new.index # => "V2 users"
Basic Usage
Creating namespaced modules requires defining a module with the module
keyword and placing classes, constants, or methods inside. Ruby resolves constants using the scope resolution operator ::
which explicitly specifies the namespace path.
module Database
class Connection
def initialize(host, port)
@host = host
@port = port
end
def connect
"Connecting to #{@host}:#{@port}"
end
end
DEFAULT_TIMEOUT = 30
def self.establish_connection
Connection.new("localhost", 5432)
end
end
# Creating instances through namespace
conn = Database::Connection.new("example.com", 3306)
puts conn.connect # => "Connecting to example.com:3306"
# Accessing module constants
puts Database::DEFAULT_TIMEOUT # => 30
# Calling module methods
db_conn = Database.establish_connection
Module nesting creates hierarchical namespaces where inner modules inherit the namespace context of their containers. Each module maintains its own constant table while having access to constants defined in enclosing modules.
module Company
FOUNDED_YEAR = 2010
module Engineering
TEAM_SIZE = 50
module Backend
class ApiServer
def self.info
"Founded: #{FOUNDED_YEAR}, Team: #{TEAM_SIZE}"
end
end
end
end
end
puts Company::Engineering::Backend::ApiServer.info
# => "Founded: 2010, Team: 50"
Reopening modules allows adding functionality to existing namespaces across multiple files. Ruby merges the definitions, maintaining the same constant table and enabling modular code organization.
# file: user_base.rb
module UserManagement
class User
attr_reader :name
def initialize(name)
@name = name
end
end
end
# file: user_extensions.rb
module UserManagement
class User
def email=(email)
@email = email
end
def email
@email
end
end
class Admin < User
def privileges
[:read, :write, :delete]
end
end
end
user = UserManagement::User.new("bob")
user.email = "bob@example.com"
puts user.email # => "bob@example.com"
Advanced Usage
Nested module definitions create complex namespace hierarchies that support sophisticated organizational patterns. Ruby maintains separate constant tables for each nesting level, enabling fine-grained control over scope and access.
module Enterprise
module Authentication
module Providers
class LDAP
def self.authenticate(username, password)
# LDAP authentication logic
"LDAP: #{username} authenticated"
end
end
class OAuth2
def self.authenticate(token)
"OAuth2: token #{token} validated"
end
end
def self.available_providers
constants.map { |const| const_get(const) }
end
end
def self.authenticate_user(provider, credentials)
provider_class = Providers.const_get(provider)
provider_class.authenticate(*credentials)
end
end
end
# Dynamic provider selection
providers = Enterprise::Authentication::Providers.available_providers
puts providers.map(&:name) # => ["Enterprise::Authentication::Providers::LDAP", "Enterprise::Authentication::Providers::OAuth2"]
result = Enterprise::Authentication.authenticate_user("LDAP", ["admin", "secret"])
puts result # => "LDAP: admin authenticated"
Module constants can be dynamically defined and accessed using metaprogramming techniques. The const_set
and const_get
methods provide runtime manipulation of the constant table.
module Configuration
ENVIRONMENTS = %w[development staging production]
ENVIRONMENTS.each do |env|
const_set("#{env.upcase}_CONFIG", {
database_url: "#{env}_db_url",
api_key: "#{env}_api_key"
})
end
def self.get_config(environment)
const_get("#{environment.upcase}_CONFIG")
end
def self.set_dynamic_config(name, value)
const_set(name.to_s.upcase, value)
end
end
puts Configuration::DEVELOPMENT_CONFIG
# => {:database_url=>"development_db_url", :api_key=>"development_api_key"}
Configuration.set_dynamic_config(:custom_setting, "custom_value")
puts Configuration::CUSTOM_SETTING # => "custom_value"
Namespace aliasing through constant assignment creates shorter references to deeply nested modules, improving code readability while maintaining the original namespace structure.
module VeryLongCompanyName
module InternalSystems
module LegacyDatabaseAdapters
class PostgreSQLAdapter
def self.query(sql)
"Executing: #{sql}"
end
end
class MySQLAdapter
def self.query(sql)
"MySQL: #{sql}"
end
end
end
end
end
# Create aliases for convenience
DB = VeryLongCompanyName::InternalSystems::LegacyDatabaseAdapters
PgAdapter = DB::PostgreSQLAdapter
MyAdapter = DB::MySQLAdapter
puts PgAdapter.query("SELECT * FROM users") # => "Executing: SELECT * FROM users"
puts MyAdapter.query("SELECT * FROM users") # => "MySQL: SELECT * FROM users"
Module functions defined with module_function
become available both as instance methods when the module is included and as module-level methods when called directly on the module.
module MathUtils
def calculate_tax(amount, rate)
amount * rate
end
def format_currency(amount)
"$#{'%.2f' % amount}"
end
module_function :calculate_tax, :format_currency
module Advanced
def compound_interest(principal, rate, time)
principal * ((1 + rate) ** time)
end
module_function :compound_interest
end
end
# Module-level access
tax = MathUtils.calculate_tax(100, 0.08)
formatted = MathUtils.format_currency(tax)
puts formatted # => "$8.00"
# Nested module function
interest = MathUtils::Advanced.compound_interest(1000, 0.05, 10)
puts MathUtils.format_currency(interest) # => "$1628.89"
Common Pitfalls
Constant resolution in nested modules can produce unexpected results when constants are defined in multiple scopes. Ruby searches constants using lexical scoping rules, checking the current nesting before searching outer scopes.
GLOBAL_SETTING = "global"
module Outer
GLOBAL_SETTING = "outer"
module Inner
# This will find Outer::GLOBAL_SETTING, not ::GLOBAL_SETTING
def self.show_setting
GLOBAL_SETTING
end
# Explicit global access required
def self.show_global_setting
::GLOBAL_SETTING
end
# Local constant shadows outer scopes completely
GLOBAL_SETTING = "inner"
def self.show_inner_setting
GLOBAL_SETTING # Now returns "inner"
end
end
end
puts Outer::Inner.show_inner_setting # => "inner"
puts Outer::Inner.show_global_setting # => "global"
# Removing the local constant changes resolution
Outer::Inner.send(:remove_const, :GLOBAL_SETTING)
puts Outer::Inner.show_setting # => "outer"
Module reopening can accidentally overwrite existing constants or methods if the same names are used across different files. Ruby issues warnings for constant redefinition but allows the operation.
module UserService
VERSION = "1.0"
def self.process_user(user)
"Processing #{user} with version #{VERSION}"
end
end
puts UserService.process_user("alice") # => "Processing alice with version 1.0"
# Later in another file - accidentally redefines VERSION
module UserService
VERSION = "2.0" # Warning: already initialized constant UserService::VERSION
def self.enhanced_process(user)
"Enhanced processing #{user} with version #{VERSION}"
end
end
puts UserService.process_user("bob") # => "Processing bob with version 2.0" (unexpected!)
puts UserService::VERSION # => "2.0"
Circular dependencies between namespaced modules can cause loading issues when modules reference each other during class definition. Ruby's autoloading mechanism may fail to resolve dependencies correctly.
# problematic_circular.rb
module Services
class UserService
def create_user
# References PaymentService during class loading
PaymentService.setup_account
end
end
class PaymentService
def self.setup_account
# References UserService during class loading
UserService.new.validate_user
end
end
end
Better approach using delayed resolution:
module Services
class UserService
def create_user
# Resolve at runtime, not class definition time
Services::PaymentService.setup_account
end
end
class PaymentService
def self.setup_account
# Use dependency injection or delayed lookup
user_service_class = Services.const_get(:UserService)
user_service_class.new.validate_user
end
end
end
Namespace pollution occurs when including modules that define constants or methods conflicting with the including class's namespace. Module inclusion merges the module's constants into the class's constant table.
module Utilities
VERSION = "util-1.0"
def helper_method
"Utility helper"
end
end
class Application
VERSION = "app-2.0"
include Utilities # This creates a conflict
end
# The class constant shadows the included module constant
puts Application::VERSION # => "app-2.0"
# But the included method is available
app = Application.new
puts app.helper_method # => "Utility helper"
# Access to module's VERSION requires explicit path
puts Application.const_get("Utilities")::VERSION # Complex and error-prone
Reference
Module Definition Syntax
Pattern | Description | Example |
---|---|---|
module Name |
Define top-level module | module Utils |
module A::B |
Define nested module directly | module Company::Auth |
module A; module B |
Define through nesting | module Company; module Auth |
Module.new |
Create anonymous module | mod = Module.new |
Constant Access Methods
Method | Parameters | Returns | Description |
---|---|---|---|
:: |
Module::CONSTANT |
Object |
Scope resolution operator |
const_get(name) |
name (String/Symbol) |
Object |
Dynamic constant lookup |
const_set(name, value) |
name , value |
Object |
Dynamic constant definition |
const_defined?(name) |
name (String/Symbol) |
Boolean |
Check constant existence |
const_missing(name) |
name (Symbol) |
Object |
Called when constant not found |
constants |
None | Array |
List all constants in module |
Module Introspection Methods
Method | Parameters | Returns | Description |
---|---|---|---|
nesting |
None | Array |
Current module nesting stack |
name |
None | String/nil |
Module name or nil for anonymous |
to_s |
None | String |
String representation of module |
ancestors |
None | Array |
Module hierarchy including self |
included_modules |
None | Array |
Modules included in this module |
Constant Resolution Rules
Context | Search Order | Description |
---|---|---|
Lexical scope | Current → Outer → Global | Based on nesting at definition |
Instance method | Class → Included → Super → Global | Method context determines search |
Class method | Eigenclass → Class → Global | Singleton class context |
const_get |
Module only | Searches only specified module |
::CONST |
Global only | Forces global constant lookup |
Module Callbacks
Callback | Triggered When | Parameters | Usage |
---|---|---|---|
included(mod) |
Module included in class | mod (Module) |
Setup code for inclusion |
extended(obj) |
Module extends object | obj (Object) |
Setup code for extension |
prepended(mod) |
Module prepended to class | mod (Module) |
Setup code for prepending |
const_missing(name) |
Undefined constant accessed | name (Symbol) |
Autoloading or dynamic constants |
Common Module Patterns
# Namespace container
module MyApp
class User; end
class Admin; end
end
# Configuration module
module Config
DATABASE_URL = ENV.fetch('DATABASE_URL')
API_VERSION = 'v1'
end
# Factory module
module UserFactory
def self.create(type)
case type
when :admin then AdminUser.new
when :guest then GuestUser.new
end
end
end
# Utility module with module functions
module StringUtils
def titleize(str)
str.split.map(&:capitalize).join(' ')
end
module_function :titleize
end
# Nested namespace with delegation
module API
module V1
def self.router
@router ||= Router.new
end
end
def self.current_version
V1
end
end
Error Classes
Error | Cause | Resolution |
---|---|---|
NameError |
Undefined constant | Check constant name and nesting |
TypeError |
Invalid constant assignment | Ensure proper constant naming |
ArgumentError |
Invalid module operation | Verify method parameters |
NoMethodError |
Missing module method | Check method availability in context |
Performance Considerations
Operation | Complexity | Notes |
---|---|---|
Constant lookup | O(1) to O(n) | Depends on nesting depth |
Module inclusion | O(n) | Linear with number of methods |
const_get |
O(1) | Direct hash lookup |
constants enumeration |
O(n) | Scans entire constant table |
Deep nesting access | O(n) | Each :: adds lookup cost |