Overview
Ruby namespace design structures applications through modules that act as containers for constants, methods, and classes. The module system creates hierarchical namespaces that isolate code, prevent naming conflicts, and establish logical boundaries within applications.
Ruby resolves constants through lexical scoping, searching from the current nesting level outward to the top-level namespace. This resolution mechanism enables nested module definitions where inner constants shadow outer ones, and qualified constant access using the ::
operator.
module Authentication
SECRET_KEY = "auth_secret"
module OAuth
SECRET_KEY = "oauth_secret"
def self.authenticate(token)
# Uses OAuth::SECRET_KEY ("oauth_secret")
validate_token(token, SECRET_KEY)
end
end
def self.basic_auth(username, password)
# Uses Authentication::SECRET_KEY ("auth_secret")
encrypt_password(password, SECRET_KEY)
end
end
The include
and extend
methods modify namespace behavior by adding module methods to classes or instances. Include adds instance methods, while extend adds class methods, both affecting method lookup and constant resolution.
module Loggable
LOG_LEVEL = "INFO"
def log_message(msg)
puts "[#{LOG_LEVEL}] #{msg}"
end
end
class ApiClient
include Loggable # Adds log_message as instance method
end
class DatabaseConnection
extend Loggable # Adds log_message as class method
end
Module nesting creates namespace hierarchies where constants defined in outer modules become accessible to inner modules without qualification. This scoping enables organized code structures that mirror domain boundaries.
Basic Usage
Creating namespaced modules requires defining module containers that group related functionality. Constants, classes, and methods defined within modules exist in that namespace scope.
module PaymentProcessing
TIMEOUT_SECONDS = 30
class CreditCardProcessor
def initialize(gateway)
@gateway = gateway
@timeout = TIMEOUT_SECONDS # Accesses PaymentProcessing::TIMEOUT_SECONDS
end
def charge(amount, card)
# Implementation
end
end
class PayPalProcessor
def charge(amount, account)
# Implementation
end
end
end
# Usage
processor = PaymentProcessing::CreditCardProcessor.new("stripe")
Accessing namespaced elements requires the fully qualified constant path or working within the module's scope. The ::
operator provides explicit constant access from any context.
# Explicit access
payment_processor = PaymentProcessing::CreditCardProcessor.new(gateway)
timeout_value = PaymentProcessing::TIMEOUT_SECONDS
# Working within namespace scope
module PaymentProcessing
def self.create_processor(type)
case type
when :credit_card
CreditCardProcessor.new("default") # No qualification needed
when :paypal
PayPalProcessor.new
end
end
end
Nested modules create deeper namespace hierarchies for complex domain modeling. Each level adds specificity while maintaining access to parent namespace constants.
module ECommerce
SITE_NAME = "MyStore"
module Catalog
DEFAULT_CURRENCY = "USD"
module Pricing
TAX_RATE = 0.08
class Calculator
def calculate_total(subtotal)
# Accesses all parent constants
tax = subtotal * TAX_RATE
puts "Calculating for #{SITE_NAME} in #{DEFAULT_CURRENCY}"
subtotal + tax
end
end
end
end
end
Including modules adds their constants to the includer's namespace, creating shortcuts for accessing nested functionality while maintaining namespace boundaries.
module DatabaseOperations
CONNECTION_POOL_SIZE = 10
def execute_query(sql)
# Database logic
end
end
class UserRepository
include DatabaseOperations
def find_users
puts "Pool size: #{CONNECTION_POOL_SIZE}" # Direct access
execute_query("SELECT * FROM users") # Direct method access
end
end
Advanced Usage
Metaprogramming with namespaces enables dynamic module creation and modification. The const_set
and const_get
methods manipulate constants programmatically, while define_method
creates methods within namespace contexts.
module DynamicServices
def self.create_service(name, endpoints)
service_module = Module.new do
endpoints.each do |endpoint, url|
define_method(endpoint) do |params = {}|
make_request(url, params)
end
end
def make_request(url, params)
# HTTP request implementation
"Response from #{url} with #{params}"
end
end
const_set("#{name.capitalize}Service", service_module)
end
end
# Creates DynamicServices::UserService with get_profile and update_profile methods
DynamicServices.create_service(:user, {
get_profile: "/api/users/profile",
update_profile: "/api/users/profile/update"
})
client = Object.new.extend(DynamicServices::UserService)
client.get_profile(id: 123)
Anonymous modules provide namespacing without polluting the constant space. These modules exist at runtime without permanent constant assignments, useful for temporary scoping or dynamic behavior injection.
module FeatureFactory
def self.create_feature_module(features)
Module.new do
features.each do |feature_name, implementation|
define_method(feature_name, &implementation)
end
def enabled_features
self.class.instance_methods(false)
end
end
end
end
# Create anonymous module with specific features
reporting_features = FeatureFactory.create_feature_module({
generate_pdf: -> { "Generating PDF report" },
send_email: -> { "Sending email notification" },
log_activity: -> { "Logging activity" }
})
class ReportGenerator
include reporting_features # No constant pollution
end
generator = ReportGenerator.new
generator.generate_pdf
generator.enabled_features # => [:generate_pdf, :send_email, :log_activity]
Module composition creates complex namespace hierarchies through strategic include and extend operations. This pattern combines multiple namespace concerns into cohesive interfaces.
module Cacheable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def cache_key_prefix
@cache_key_prefix ||= name.downcase.gsub('::', '_')
end
end
def cache_key(id)
"#{self.class.cache_key_prefix}_#{id}"
end
end
module Serializable
def to_hash
instance_variables.each_with_object({}) do |var, hash|
key = var.to_s.delete('@')
hash[key] = instance_variable_get(var)
end
end
end
module Trackable
def track_changes
@changes ||= {}
end
end
module DataAccess
module ActiveRecord
include Cacheable
include Serializable
include Trackable
def self.included(base)
super
base.extend(Cacheable::ClassMethods)
end
end
end
class User
include DataAccess::ActiveRecord
def initialize(name, email)
@name, @email = name, email
end
end
user = User.new("John", "john@example.com")
user.cache_key(123) # => "user_123"
user.to_hash # => {"name"=>"John", "email"=>"john@example.com"}
Prepend operations modify method lookup order within namespaces, enabling method decoration and aspect-oriented programming patterns within namespace boundaries.
module Instrumentation
def self.prepended(base)
base.extend(ClassMethods)
end
module ClassMethods
def instrument_methods(*method_names)
@instrumented_methods = method_names
end
def instrumented_methods
@instrumented_methods || []
end
end
def method_missing(method_name, *args, &block)
if self.class.instrumented_methods.include?(method_name)
start_time = Time.now
result = super
duration = Time.now - start_time
puts "#{self.class.name}##{method_name} executed in #{duration}s"
result
else
super
end
end
end
module BusinessLogic
module OrderProcessing
prepend Instrumentation
instrument_methods :calculate_total, :apply_discounts
def calculate_total(items)
items.sum { |item| item[:price] * item[:quantity] }
end
def apply_discounts(total, discount_rate)
total * (1 - discount_rate)
end
end
end
Common Pitfalls
Constant resolution confusion occurs when Ruby's lexical scoping rules produce unexpected results. Constants resolve based on nesting context, not inheritance or include relationships.
GLOBAL_CONFIG = "global"
module OuterModule
GLOBAL_CONFIG = "outer"
class BaseService
def get_config
GLOBAL_CONFIG # Returns "outer" due to lexical scope
end
end
end
class MyService < OuterModule::BaseService
GLOBAL_CONFIG = "my_service"
def get_specific_config
GLOBAL_CONFIG # Returns "my_service"
end
def get_inherited_config
super # Still returns "outer", not "my_service"
end
end
# Unexpected behavior
service = MyService.new
service.get_config # => "outer" (not "my_service")
service.get_specific_config # => "my_service"
service.get_inherited_config # => "outer"
This pitfall requires explicit constant qualification to access intended values:
class MyService < OuterModule::BaseService
def get_intended_config
self.class::GLOBAL_CONFIG # Explicit class constant access
end
def get_top_level_config
::GLOBAL_CONFIG # Explicit top-level constant access
end
end
Autoloading conflicts arise when namespace structure doesn't match file organization. Rails autoloading expects specific file paths that correspond to constant nesting.
# File: app/services/payment_processing.rb
# INCORRECT: Doesn't match expected namespace structure
class PaymentProcessor
module CreditCard
def self.process(amount)
# Implementation
end
end
end
# File: app/services/payment_processing/credit_card.rb
# CORRECT: Matches autoloading expectations
module PaymentProcessing
module CreditCard
def self.process(amount)
# Implementation
end
end
end
Module inclusion order affects method lookup and can cause surprising behavior when multiple modules define the same method names.
module A
def shared_method
"From A"
end
end
module B
def shared_method
"From B"
end
end
class TestClass
include A
include B # B's methods take precedence
end
TestClass.new.shared_method # => "From B"
# Ancestry shows inclusion order
TestClass.ancestors # => [TestClass, B, A, Object, BasicObject]
Namespace pollution occurs when modules expose unnecessary constants or methods to including classes. This problem grows severe in large applications with many mixed-in modules.
# PROBLEMATIC: Pollutes includer's namespace
module DatabaseHelpers
CONNECTION_TIMEOUT = 30 # Becomes available to all includers
DEBUG_MODE = true # Unintended exposure
def execute_query(sql)
# Implementation
end
def log_query(sql) # Private helper exposed publicly
puts sql if DEBUG_MODE
end
end
# BETTER: Encapsulate private constants and methods
module DatabaseHelpers
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def connection_timeout
30 # Encapsulated configuration
end
end
def execute_query(sql)
log_query(sql) if debug_enabled?
# Implementation
end
private
def debug_enabled?
ENV['DB_DEBUG'] == 'true' # External configuration
end
def log_query(sql)
puts sql
end
end
Circular dependency issues emerge in complex namespace hierarchies where modules attempt to reference each other during load time.
# user.rb
module Models
class User
has_many :orders, class_name: 'Models::Order' # Forward reference
end
end
# order.rb
module Models
class Order
belongs_to :user, class_name: 'Models::User' # Circular reference
end
end
# Solution: Use string class names and lazy loading
module Models
class User
def orders
@orders ||= Order.where(user_id: id) # Runtime resolution
end
end
class Order
def user
@user ||= User.find(user_id) # Runtime resolution
end
end
end
Production Patterns
Large application namespace organization follows hierarchical domain boundaries that reflect business logic and team ownership. This structure prevents naming conflicts while maintaining clear code boundaries.
# Domain-based organization
module UserManagement
module Authentication
class TokenValidator
def validate(token)
# Authentication logic
end
end
class SessionManager
def create_session(user_id)
# Session creation
end
end
end
module Authorization
class RoleChecker
def authorized?(user, resource)
# Authorization logic
end
end
end
module Profiles
class ProfileUpdater
def update(user_id, attributes)
# Profile update logic
end
end
end
end
module PaymentProcessing
module Gateways
class StripeGateway
def charge(amount, card)
# Stripe integration
end
end
class PayPalGateway
def charge(amount, account)
# PayPal integration
end
end
end
module Reconciliation
class TransactionMatcher
def match_transactions(bank_data, system_data)
# Reconciliation logic
end
end
end
end
Configuration namespaces centralize application settings while maintaining environment-specific overrides. This pattern supports different deployment environments without code changes.
module Configuration
module Database
DEFAULT_POOL_SIZE = 5
DEFAULT_TIMEOUT = 30
def self.pool_size
ENV['DB_POOL_SIZE']&.to_i || DEFAULT_POOL_SIZE
end
def self.timeout
ENV['DB_TIMEOUT']&.to_i || DEFAULT_TIMEOUT
end
def self.url
ENV['DATABASE_URL'] || "postgresql://localhost/myapp_#{environment}"
end
def self.environment
ENV['RAILS_ENV'] || 'development'
end
end
module Redis
DEFAULT_URL = "redis://localhost:6379"
def self.url
ENV['REDIS_URL'] || DEFAULT_URL
end
def self.namespace
"myapp:#{Database.environment}"
end
end
module Features
def self.enabled?(feature_name)
ENV["FEATURE_#{feature_name.to_s.upcase}"] == 'true'
end
end
end
# Usage in application
if Configuration::Features.enabled?(:new_payment_flow)
processor = PaymentProcessing::Gateways::StripeGateway.new
else
processor = PaymentProcessing::LegacyProcessor.new
end
Service layer namespaces isolate business logic from framework dependencies, enabling testing and framework-agnostic code organization.
module Services
module Users
class RegistrationService
def initialize(email_service: EmailServices::WelcomeMailer,
validator: Validators::UserValidator.new)
@email_service = email_service
@validator = validator
end
def register(user_attributes)
validation_result = @validator.validate(user_attributes)
return validation_result unless validation_result.success?
user = create_user(user_attributes)
@email_service.send_welcome_email(user.email)
Result.success(user)
end
private
def create_user(attributes)
# User creation logic
end
end
class ProfileUpdateService
def update_profile(user_id, attributes)
user = find_user(user_id)
return Result.error("User not found") unless user
updated_user = user.update(sanitize_attributes(attributes))
Result.success(updated_user)
end
private
def sanitize_attributes(attrs)
attrs.slice(:name, :email, :phone) # Whitelist approach
end
end
end
module Orders
class CheckoutService
include PaymentProcessing # Namespace inclusion
def process_order(cart, payment_info)
total = calculate_total(cart.items)
payment_result = charge_payment(total, payment_info)
return payment_result unless payment_result.success?
order = create_order(cart, payment_result.transaction_id)
Result.success(order)
end
end
end
end
Framework integration namespaces provide clean interfaces between application code and external frameworks like Rails or Sinatra.
module WebInterface
module Controllers
module Api
class BaseController
include AuthenticationHelpers
include ErrorHandling
private
def authenticate_request
token = request.headers['Authorization']
@current_user = UserManagement::Authentication::TokenValidator
.new
.validate(token)
end
end
class UsersController < BaseController
def create
result = Services::Users::RegistrationService
.new
.register(user_params)
if result.success?
render json: result.data, status: :created
else
render json: { errors: result.errors }, status: :unprocessable_entity
end
end
def update
result = Services::Users::ProfileUpdateService
.new
.update_profile(params[:id], user_params)
render json: result.data
end
end
end
end
module Serializers
class UserSerializer
def initialize(user)
@user = user
end
def to_json
{
id: @user.id,
name: @user.name,
email: @user.email,
created_at: @user.created_at
}
end
end
end
end
Reference
Core Namespace Methods
Method | Parameters | Returns | Description |
---|---|---|---|
Module.new(&block) |
Optional block | Module |
Creates anonymous module with optional block evaluation |
#include(module) |
Module | self |
Adds module methods as instance methods |
#extend(module) |
Module | self |
Adds module methods as singleton methods |
#prepend(module) |
Module | self |
Adds module methods with higher precedence in lookup |
#const_get(name, inherit=true) |
String/Symbol, Boolean | Object |
Retrieves constant by name with optional inheritance |
#const_set(name, value) |
String/Symbol, Object | Object |
Sets constant in current namespace |
#const_defined?(name, inherit=true) |
String/Symbol, Boolean | Boolean |
Checks constant existence with optional inheritance |
#constants(inherit=true) |
Boolean | Array<Symbol> |
Lists constants in namespace with optional inheritance |
#ancestors |
None | Array<Module> |
Returns method lookup chain including modules |
#included_modules |
None | Array<Module> |
Lists directly included modules |
Constant Resolution Rules
Context | Resolution Order | Example |
---|---|---|
Lexical nesting | Inside-out scope traversal | Module::InnerModule::CONSTANT |
Class hierarchy | Superclass chain lookup | Parent class constants accessible |
Include/Prepend | Module ancestry chain | Included module constants accessible |
Top-level access | ::CONSTANT syntax |
Explicit top-level namespace access |
Dynamic access | const_get with qualification |
"Namespace::Class".constantize |
Module Hook Methods
Hook Method | Timing | Parameters | Use Case |
---|---|---|---|
included(base) |
After include | Including class/module | Setup class methods, configuration |
extended(base) |
After extend | Extended object | Initialize extended object state |
prepended(base) |
After prepend | Prepended class/module | Method decoration, aspect-oriented setup |
const_missing(name) |
Constant lookup failure | Constant name symbol | Autoloading, dynamic constant creation |
method_missing(name, *args, &block) |
Method call failure | Method name, arguments, block | Dynamic method handling |
Namespace Organization Patterns
Pattern | Structure | Benefits | Use Cases |
---|---|---|---|
Domain-based | Domain::Subdomain::Class |
Business logic alignment | Large applications, team boundaries |
Layer-based | Controllers::Models::Services |
Architectural separation | MVC frameworks, clear responsibilities |
Feature-based | Authentication::UserManagement |
Feature isolation | Microservice preparation, modular design |
Environment-based | Production::Staging::Development |
Environment-specific code | Configuration management, deployment |
Common Constants and Namespaces
Constant | Type | Description | Example Usage |
---|---|---|---|
::Object |
Class | Top-level object class | Default superclass, constant resolution |
::Module |
Class | Module class for metaprogramming | Module.new , dynamic module creation |
::Class |
Class | Class class for metaprogramming | Class.new , dynamic class creation |
::Kernel |
Module | Core Ruby methods module | Method availability across objects |
::BasicObject |
Class | Minimal object implementation | Proxy objects, method delegation |
Error Types
Exception | Trigger Condition | Example | Resolution |
---|---|---|---|
NameError |
Undefined constant/variable access | UndefinedConstant |
Define constant or fix typo |
NoMethodError |
Method call on object without method | obj.undefined_method |
Define method or fix call |
ArgumentError |
Wrong number of arguments | method(arg1, arg2, arg3) |
Match expected parameters |
TypeError |
Wrong object type for operation | "string".length(5) |
Use correct object type |
LoadError |
Failed require/load operation | require 'nonexistent' |
Install gem or fix path |