Overview
Ruby constants provide immutable naming for values that remain fixed throughout program execution. Unlike variables, constants begin with uppercase letters and generate warnings when reassigned. Ruby implements namespacing through modules and classes, creating hierarchical constant scopes that organize code and prevent naming conflicts.
Constants exist within lexical scopes defined by modules, classes, and the main object. Ruby resolves constants through a specific lookup chain that examines the current scope, ancestor modules, and outer lexical scopes. This resolution mechanism enables modular code organization while maintaining predictable constant access patterns.
# Top-level constant
VERSION = "1.0.0"
module Authentication
# Module-scoped constant
TOKEN_EXPIRY = 3600
class User
# Class-scoped constant
DEFAULT_ROLE = "guest"
end
end
Ruby's constant system supports nested namespaces through the ::
operator, which provides explicit constant resolution. The constant resolution follows Ruby's method lookup chain, checking included modules and inheritance hierarchies. Constants can reference other constants within their scope or use absolute paths for cross-namespace access.
module API
VERSION = "2.0"
module V1
VERSION = "1.5"
BASE_URL = "https://api.example.com/v1"
end
end
API::VERSION # => "2.0"
API::V1::VERSION # => "1.5"
Basic Usage
Defining constants requires uppercase initial letters, with Ruby convention favoring SCREAMING_SNAKE_CASE
for multi-word constants. Constants can hold any Ruby object, including classes, modules, strings, numbers, and complex data structures.
# Basic constant definitions
MAX_CONNECTIONS = 100
DATABASE_URL = "postgresql://localhost/myapp"
SUPPORTED_FORMATS = %w[json xml csv]
# Constants holding classes
String = String
CustomError = Class.new(StandardError)
Module namespacing organizes related constants under common prefixes, preventing naming collisions across different libraries or application components. Modules serve as constant containers without requiring instantiation.
module Configuration
DATABASE_HOST = "localhost"
DATABASE_PORT = 5432
CACHE_ENABLED = true
module Redis
HOST = "redis.example.com"
PORT = 6379
TIMEOUT = 5
end
end
# Accessing namespaced constants
Configuration::DATABASE_HOST # => "localhost"
Configuration::Redis::HOST # => "redis.example.com"
Constant assignment within methods creates constants in the surrounding lexical scope, not the method's local scope. This behavior differs from variable assignment and can create constants in unexpected locations.
class DatabaseConfig
def self.setup
HOST = "db.example.com" # Creates DatabaseConfig::HOST
PORT = 5432 # Creates DatabaseConfig::PORT
end
end
DatabaseConfig.setup
DatabaseConfig::HOST # => "db.example.com"
The const_set
and const_get
methods provide dynamic constant manipulation, accepting string or symbol names. These methods operate on the receiving module or class, creating or retrieving constants programmatically.
module DynamicConfig
# Dynamic constant creation
const_set(:API_KEY, "secret123")
const_set("TIMEOUT", 30)
end
# Dynamic constant access
DynamicConfig.const_get(:API_KEY) # => "secret123"
DynamicConfig.const_get("TIMEOUT") # => 30
Advanced Usage
Constant autoloading provides lazy loading mechanisms for large codebases, defining constants only when first accessed. Ruby's autoload
method associates constant names with file paths, loading the file when the constant is referenced.
module Library
autoload :Parser, 'library/parser'
autoload :Validator, 'library/validator'
autoload :Formatter, 'library/formatter'
end
# Constants load automatically on first access
Library::Parser # Loads 'library/parser.rb'
Library::Validator # Loads 'library/validator.rb'
Constant inheritance follows Ruby's class hierarchy, with subclasses inheriting parent constants unless explicitly overridden. This inheritance enables shared configuration across class hierarchies while allowing specific customizations.
class BaseService
TIMEOUT = 30
RETRIES = 3
def self.config
{ timeout: self::TIMEOUT, retries: self::RETRIES }
end
end
class FastService < BaseService
TIMEOUT = 10 # Override parent constant
# RETRIES inherited from BaseService
end
BaseService.config # => {timeout: 30, retries: 3}
FastService.config # => {timeout: 10, retries: 3}
Module inclusion and prepending affect constant resolution through the ancestor chain. Included modules insert their constants into the lookup path, while prepended modules take precedence over the including class.
module Configurable
CACHE_TTL = 3600
LOG_LEVEL = "info"
end
module Override
LOG_LEVEL = "debug"
end
class Service
include Configurable
prepend Override
LOG_LEVEL = "warn"
end
# Resolution follows: Service -> Override -> Service -> Configurable
Service::LOG_LEVEL # => "warn" (Service's own constant wins)
Constant removal requires remove_const
, which deletes constants from their defining scope. This method returns the constant's value and prevents further access through normal resolution.
module TemporaryConfig
DEBUG_MODE = true
VERBOSE = false
end
# Remove specific constants
old_value = TemporaryConfig.send(:remove_const, :DEBUG_MODE)
# TemporaryConfig::DEBUG_MODE no longer exists
# Check constant existence
TemporaryConfig.const_defined?(:VERBOSE) # => true
TemporaryConfig.const_defined?(:DEBUG_MODE) # => false
Common Pitfalls
Constant reassignment generates warnings but allows the assignment to proceed, creating subtle bugs when constants change unexpectedly. Ruby prints warnings to $stderr
but continues program execution with the new value.
API_VERSION = "1.0"
API_VERSION = "2.0" # warning: already initialized constant API_VERSION
# warning: previous definition was here
puts API_VERSION # => "2.0"
Constant resolution within class_eval
and module_eval
uses the lexical scope where the string or block was defined, not the receiver's scope. This behavior differs from method definitions and can access unexpected constants.
GLOBAL_CONST = "global"
module Outer
CONST = "outer"
class Inner
CONST = "inner"
# Different resolution contexts
def self.lexical_access
eval("CONST") # Resolves in current lexical scope
end
def self.string_eval_access
eval("CONST", binding) # Uses current binding's scope
end
end
end
Outer::Inner.lexical_access # => "inner"
Outer::Inner.string_eval_access # => "inner"
Constant caching can cause unexpected behavior when constants are redefined in development environments. Ruby caches constant lookups for performance, requiring explicit cache invalidation or process restarts.
module Cache
SETTINGS = { timeout: 30 }
end
# Code that caches the constant reference
cached_settings = Cache::SETTINGS
# Later redefinition
Cache.send(:remove_const, :SETTINGS)
Cache::SETTINGS = { timeout: 60 }
cached_settings # Still points to original hash: {timeout: 30}
Cache::SETTINGS # New hash: {timeout: 60}
Circular constant dependencies create loading issues when constants reference each other across module boundaries. Ruby may not fully initialize one constant before the other attempts to reference it.
# file: user.rb
module Models
class User
ADMIN_ROLE = Roles::ADMIN # Depends on Roles module
end
end
# file: role.rb
module Roles
ADMIN = "administrator"
USER_CLASS = Models::User # Depends on User class
end
# Loading either file first may cause NameError
Constant scoping with string interpolation and dynamic method calls can access constants from unexpected scopes when the receiving object differs from the lexical context.
module A
CONST = "A's constant"
module B
CONST = "B's constant"
def self.access_const(name)
const_get(name)
end
end
end
# Different resolution contexts
A::B.access_const("CONST") # => "B's constant"
A::B.const_get("CONST") # => "B's constant"
A.const_get("B").const_get("CONST") # => "B's constant"
Reference
Constant Definition Methods
Method | Parameters | Returns | Description |
---|---|---|---|
const_set(name, value) |
name (String/Symbol), value (Object) |
Object |
Defines constant with given name and value |
const_get(name, inherit=true) |
name (String/Symbol), inherit (Boolean) |
Object |
Retrieves constant by name, optionally checking ancestors |
const_defined?(name, inherit=true) |
name (String/Symbol), inherit (Boolean) |
Boolean |
Checks if constant exists in scope |
remove_const(name) |
name (String/Symbol) |
Object |
Removes constant and returns its value |
constants(inherit=true) |
inherit (Boolean) |
Array<Symbol> |
Lists all constants in scope |
Autoloading Methods
Method | Parameters | Returns | Description |
---|---|---|---|
autoload(const, filename) |
const (String/Symbol), filename (String) |
nil |
Sets up autoloading for constant |
autoload?(const) |
const (String/Symbol) |
String/nil |
Returns filename if constant has autoload registered |
Constant Resolution Operators
Operator | Usage | Description |
---|---|---|
:: |
Module::CONST |
Absolute constant reference from root |
:: |
::CONST |
Top-level constant reference |
Common Constants
Constant | Type | Description |
---|---|---|
RUBY_VERSION |
String | Current Ruby version |
RUBY_PLATFORM |
String | Platform identifier |
RUBY_ENGINE |
String | Ruby implementation name |
ARGV |
Array | Command line arguments |
ENV |
Hash-like | Environment variables |
STDIN |
IO | Standard input stream |
STDOUT |
IO | Standard output stream |
STDERR |
IO | Standard error stream |
Resolution Hierarchy
Ruby resolves constants through this lookup chain:
- Current lexical scope
- Included modules (in reverse inclusion order)
- Parent class/module constants
- Ancestor chain (superclasses and their included modules)
- Top-level constants
- Object constants (if not already checked)
Constant Naming Conventions
Pattern | Usage | Example |
---|---|---|
SCREAMING_SNAKE_CASE |
Configuration values | MAX_RETRY_ATTEMPTS |
PascalCase |
Classes and modules | UserAuthentication |
ACRONYM_CASE |
Acronyms and abbreviations | HTTP_TIMEOUT , JSON_PARSER |
Error Types
Exception | Trigger | Description |
---|---|---|
NameError |
Undefined constant access | Constant not found in resolution chain |
TypeError |
Invalid constant name | Non-string/symbol passed to const methods |
ArgumentError |
Invalid constant format | Lowercase or invalid character in name |