Ruby's constant query methods provide introspection and dynamic access to constants within modules and classes.
Overview
Ruby's constant query system enables runtime introspection of constants defined within modules and classes. The core functionality revolves around methods like const_defined?
, const_get
, const_set
, and constants
, which operate on Module instances to examine, retrieve, and manipulate constant definitions.
Constants in Ruby exist within lexical scopes defined by modules and classes. Each Module maintains its own constant table, and Ruby provides mechanisms to query these tables programmatically. The constant lookup follows Ruby's standard resolution rules, searching through the inheritance hierarchy and lexical nesting when resolving constant references.
module Database
HOST = "localhost"
PORT = 5432
class Connection
TIMEOUT = 30
end
end
# Check if constants exist
Database.const_defined?(:HOST) # => true
Database.const_defined?(:MISSING) # => false
Database::Connection.const_defined?(:TIMEOUT) # => true
The constant query methods operate at the Module level, meaning any class or module can examine its own constants or those of other modules. This capability forms the foundation for many metaprogramming techniques, dynamic configuration systems, and reflection-based tools.
class Configuration
def self.load_from_constants
constants.each do |const_name|
value = const_get(const_name)
puts "#{const_name}: #{value}"
end
end
end
Configuration::DATABASE_URL = "postgresql://localhost/app"
Configuration::REDIS_URL = "redis://localhost:6379"
Configuration.load_from_constants
# DATABASE_URL: postgresql://localhost/app
# REDIS_URL: redis://localhost:6379
Ruby's constant queries support inheritance-aware lookups by default. When querying a constant on a class, Ruby searches the class itself, then its superclass chain, and finally any included modules. This behavior can be controlled through optional parameters that restrict searches to specific scopes.
Basic Usage
The primary interface for constant queries consists of four core methods available on all Module instances: const_defined?
, const_get
, constants
, and const_set
. Each method serves a specific purpose in constant introspection and manipulation.
const_defined?
determines whether a constant exists within a module's scope. The method accepts a constant name as a symbol or string and returns a boolean value. By default, it searches through the inheritance hierarchy, but this behavior can be restricted to the receiver module only.
class Animal
KINGDOM = "Animalia"
end
class Mammal < Animal
CLASS_NAME = "Mammalia"
end
# Inheritance-aware queries
Mammal.const_defined?(:CLASS_NAME) # => true
Mammal.const_defined?(:KINGDOM) # => true (inherited)
# Restrict to current module only
Mammal.const_defined?(:KINGDOM, false) # => false
Mammal.const_defined?(:CLASS_NAME, false) # => true
const_get
retrieves the value of a named constant. Like const_defined?
, it follows inheritance rules by default and accepts an optional parameter to restrict the search scope. The method raises a NameError if the constant does not exist.
module Config
DATABASE_HOST = "db.example.com"
DATABASE_PORT = 5432
end
# Retrieve constant values
host = Config.const_get(:DATABASE_HOST) # => "db.example.com"
port = Config.const_get("DATABASE_PORT") # => 5432
# Dynamic access with computed names
setting = "DATABASE_HOST"
value = Config.const_get(setting) # => "db.example.com"
The constants
method returns an array of symbols representing all constants defined in a module. This method supports the same inheritance behavior as the other query methods, allowing examination of constants from the entire hierarchy or just the receiver module.
module Logger
DEBUG = 1
INFO = 2
WARN = 3
ERROR = 4
class FileLogger
DEFAULT_PATH = "/var/log/app.log"
MAX_SIZE = 1024 * 1024
end
end
# Get all constants in module
Logger.constants # => [:DEBUG, :INFO, :WARN, :ERROR, :FileLogger]
# Get constants from class including inherited
Logger::FileLogger.constants # => [:DEFAULT_PATH, :MAX_SIZE]
const_set
defines or reassigns constants dynamically. While Ruby constants are intended to remain unchanged after initial assignment, const_set
allows runtime modification, though Ruby issues warnings when reassigning existing constants.
class DynamicConfig
# Set constants at runtime
const_set(:API_VERSION, "v2")
const_set(:MAX_RETRIES, 3)
end
# Verify constants exist
DynamicConfig::API_VERSION # => "v2"
DynamicConfig::MAX_RETRIES # => 3
# Reassignment generates warnings but succeeds
DynamicConfig.const_set(:API_VERSION, "v3") # warning: already initialized constant
Nested constant names can be resolved using string paths with const_get
. Ruby interprets double colons in constant names as namespace separators, enabling deep constant lookup from any starting module.
module Application
module Database
module Adapters
POSTGRESQL = "postgresql"
MYSQL = "mysql2"
end
end
end
# Nested constant access
Application.const_get("Database::Adapters::POSTGRESQL") # => "postgresql"
# Equivalent to direct access
Application::Database::Adapters::POSTGRESQL # => "postgresql"
Advanced Usage
Advanced constant query patterns involve metaprogramming techniques that dynamically examine and manipulate constant structures. These patterns commonly appear in framework code, configuration systems, and domain-specific languages where constant definitions drive program behavior.
Constant introspection enables automatic discovery and processing of defined constants. This technique works particularly well for registry patterns where constants represent available options, plugins, or configuration values that should be processed uniformly.
module PaymentProcessor
STRIPE = "stripe_processor"
PAYPAL = "paypal_processor"
SQUARE = "square_processor"
def self.available_processors
constants.select do |const_name|
const_name.to_s.end_with?('_PROCESSOR') ||
const_get(const_name).to_s.end_with?('_processor')
end.map do |const_name|
{
name: const_name.to_s.downcase,
value: const_get(const_name),
processor: const_name
}
end
end
def self.processor_for(name)
processor_constant = name.to_s.upcase.to_sym
if const_defined?(processor_constant)
const_get(processor_constant)
else
raise ArgumentError, "Unknown processor: #{name}"
end
end
end
PaymentProcessor.available_processors
# => [{:name=>"stripe", :value=>"stripe_processor", :processor=>:STRIPE}, ...]
PaymentProcessor.processor_for(:stripe) # => "stripe_processor"
Hierarchical constant queries combine inheritance with conditional logic to build sophisticated constant resolution systems. These systems can implement fallback mechanisms, environment-specific overrides, and contextual constant selection.
class Configuration
DEFAULT_TIMEOUT = 30
DEFAULT_RETRIES = 3
DEFAULT_HOST = "localhost"
class Development < Configuration
HOST = "dev.internal"
DEBUG = true
end
class Production < Configuration
TIMEOUT = 60
RETRIES = 5
HOST = "prod.example.com"
SSL_ENABLED = true
end
def self.setting_for(key, environment_class = self)
setting_constant = key.to_s.upcase.to_sym
# Try environment-specific constant first
if environment_class != self &&
environment_class.const_defined?(setting_constant, false)
return environment_class.const_get(setting_constant)
end
# Fall back to base configuration
if const_defined?(setting_constant, false)
const_get(setting_constant)
else
raise ArgumentError, "Unknown setting: #{key}"
end
end
def self.all_settings_for(environment_class)
base_settings = constants(false).each_with_object({}) do |const_name, hash|
hash[const_name.to_s.downcase.to_sym] = const_get(const_name)
end
if environment_class != self
environment_settings = environment_class.constants(false).each_with_object({}) do |const_name, hash|
hash[const_name.to_s.downcase.to_sym] = environment_class.const_get(const_name)
end
base_settings.merge(environment_settings)
else
base_settings
end
end
end
# Environment-specific resolution
Configuration.setting_for(:host, Configuration::Development) # => "dev.internal"
Configuration.setting_for(:timeout, Configuration::Development) # => 30 (fallback)
# Comprehensive environment settings
Configuration.all_settings_for(Configuration::Production)
# => {:default_timeout=>30, :default_retries=>3, :default_host=>"localhost",
# :timeout=>60, :retries=>5, :host=>"prod.example.com", :ssl_enabled=>true}
Dynamic constant namespacing uses string manipulation and const_set
to create organized constant hierarchies at runtime. This approach works well for plugin systems, auto-generated constants, and modular architectures where constant organization reflects runtime structure.
module FeatureFlags
def self.define_feature(name, config = {})
feature_module_name = name.to_s.split('_').map(&:capitalize).join
if const_defined?(feature_module_name)
feature_module = const_get(feature_module_name)
else
feature_module = Module.new
const_set(feature_module_name, feature_module)
end
config.each do |key, value|
constant_name = key.to_s.upcase
feature_module.const_set(constant_name, value)
end
# Define query method
define_singleton_method("#{name}_enabled?") do
feature_module.const_get(:ENABLED) rescue false
end
feature_module
end
def self.feature_status(feature_name)
feature_module_name = feature_name.to_s.split('_').map(&:capitalize).join
if const_defined?(feature_module_name)
feature_module = const_get(feature_module_name)
feature_module.constants.each_with_object({}) do |const_name, status|
status[const_name.to_s.downcase.to_sym] = feature_module.const_get(const_name)
end
else
{}
end
end
end
# Define features with configuration
FeatureFlags.define_feature(:user_profiles, enabled: true, max_size: 1024)
FeatureFlags.define_feature(:advanced_search, enabled: false, timeout: 30)
# Access generated constants
FeatureFlags::UserProfiles::ENABLED # => true
FeatureFlags::UserProfiles::MAX_SIZE # => 1024
# Query feature status
FeatureFlags.user_profiles_enabled? # => true
FeatureFlags.feature_status(:advanced_search) # => {:enabled=>false, :timeout=>30}
Constant proxies implement lazy evaluation and computed constants using constant query methods combined with method_missing. This technique creates constants that behave like regular constants but compute their values dynamically.
module DatabaseConfig
class << self
def const_missing(const_name)
case const_name
when :DATABASE_URL
"#{ADAPTER}://#{USERNAME}:#{PASSWORD}@#{HOST}:#{PORT}/#{DATABASE}"
when :POOL_SIZE
ENV['RAILS_MAX_THREADS'] || 5
when :TIMEOUT
production? ? 30 : 10
else
super
end
end
private
def production?
ENV['RAILS_ENV'] == 'production'
end
end
# Base configuration constants
ADAPTER = ENV['DB_ADAPTER'] || 'postgresql'
HOST = ENV['DB_HOST'] || 'localhost'
PORT = ENV['DB_PORT'] || 5432
DATABASE = ENV['DB_NAME'] || 'app_development'
USERNAME = ENV['DB_USERNAME'] || 'app'
PASSWORD = ENV['DB_PASSWORD'] || 'secret'
end
# Computed constants work like regular constants
ENV['RAILS_ENV'] = 'development'
DatabaseConfig::DATABASE_URL # => "postgresql://app:secret@localhost:5432/app_development"
DatabaseConfig::TIMEOUT # => 10
ENV['RAILS_ENV'] = 'production'
DatabaseConfig::TIMEOUT # => 30
Error Handling & Debugging
Constant query operations generate specific exception types that require targeted handling strategies. The primary exception type, NameError, occurs when accessing undefined constants, while ArgumentError exceptions arise from invalid constant names or namespace violations.
const_get
raises NameError when the requested constant does not exist within the searched scope. This exception includes detailed information about the missing constant and the context where the lookup failed.
module ApiClient
VERSION = "2.1.0"
end
begin
ApiClient.const_get(:MISSING_CONSTANT)
rescue NameError => e
puts "Constant lookup failed: #{e.message}"
puts "Missing constant name: #{e.name}"
puts "Receiver: #{e.receiver}"
# Constant lookup failed: uninitialized constant ApiClient::MISSING_CONSTANT
# Missing constant name: MISSING_CONSTANT
# Receiver: ApiClient
end
Safe constant access patterns use const_defined?
to check existence before retrieval, preventing NameError exceptions in uncertain scenarios. This approach works well when constant presence varies based on configuration, environment, or conditional loading.
module ConfigLoader
def self.safe_get_constant(module_name, const_name, default = nil)
if Object.const_defined?(module_name)
mod = Object.const_get(module_name)
if mod.const_defined?(const_name)
mod.const_get(const_name)
else
default
end
else
default
end
end
def self.require_constant(module_name, const_name)
constant_value = safe_get_constant(module_name, const_name)
if constant_value.nil?
raise ArgumentError,
"Required constant #{module_name}::#{const_name} is not defined"
end
constant_value
end
end
# Safe access with fallbacks
database_url = ConfigLoader.safe_get_constant('Rails', 'DATABASE_URL', 'sqlite3:memory:')
# Required constants with clear error messages
begin
api_key = ConfigLoader.require_constant('Secrets', 'API_KEY')
rescue ArgumentError => e
puts "Configuration error: #{e.message}"
exit 1
end
Constant validation ensures that retrieved constants meet expected criteria before use. This validation becomes critical when constants drive application behavior or when external code defines constants that must conform to specific contracts.
module PluginLoader
ValidationError = Class.new(StandardError)
def self.load_plugin_constant(plugin_module, constant_name, validator = nil)
unless plugin_module.const_defined?(constant_name)
raise ValidationError,
"Plugin #{plugin_module} missing required constant #{constant_name}"
end
constant_value = plugin_module.const_get(constant_name)
if validator && !validator.call(constant_value)
raise ValidationError,
"Plugin constant #{constant_name} failed validation: #{constant_value.inspect}"
end
constant_value
rescue NameError => e
raise ValidationError, "Plugin constant access failed: #{e.message}"
end
def self.validate_plugin_interface(plugin_module)
required_constants = {
NAME: ->(v) { v.is_a?(String) && !v.empty? },
VERSION: ->(v) { v.match?(/\A\d+\.\d+\.\d+\z/) },
HANDLER: ->(v) { v.respond_to?(:call) }
}
errors = []
required_constants.each do |const_name, validator|
begin
load_plugin_constant(plugin_module, const_name, validator)
rescue ValidationError => e
errors << e.message
end
end
unless errors.empty?
raise ValidationError, "Plugin validation failed:\n#{errors.join("\n")}"
end
true
end
end
# Plugin validation example
module SamplePlugin
NAME = "Sample Plugin"
VERSION = "1.0.0"
HANDLER = ->(data) { process_data(data) }
def self.process_data(data)
data.transform_values(&:upcase)
end
end
PluginLoader.validate_plugin_interface(SamplePlugin) # => true
Debugging constant resolution issues requires understanding Ruby's lookup algorithm and the scope hierarchy. Diagnostic methods can trace constant resolution paths and identify where lookups fail in complex inheritance hierarchies.
module ConstantDebugger
def self.trace_constant_lookup(receiver, const_name)
lookup_path = []
# Check receiver module directly
if receiver.const_defined?(const_name, false)
lookup_path << { module: receiver, found: true, source: :direct }
return lookup_path
else
lookup_path << { module: receiver, found: false, source: :direct }
end
# Check inheritance chain for classes
if receiver.is_a?(Class)
receiver.ancestors.each do |ancestor|
next if ancestor == receiver
if ancestor.const_defined?(const_name, false)
lookup_path << { module: ancestor, found: true, source: :inheritance }
return lookup_path
else
lookup_path << { module: ancestor, found: false, source: :inheritance }
end
end
end
# Check lexical nesting
nesting = receiver.name ? receiver.name.split('::') : []
while nesting.any?
nesting.pop
parent_name = nesting.join('::')
begin
parent = parent_name.empty? ? Object : Object.const_get(parent_name)
if parent.const_defined?(const_name, false)
lookup_path << { module: parent, found: true, source: :lexical }
return lookup_path
else
lookup_path << { module: parent, found: false, source: :lexical }
end
rescue NameError
lookup_path << { module: parent_name, found: false, source: :lexical_failed }
end
end
lookup_path
end
def self.diagnose_constant_error(receiver, const_name)
puts "Constant lookup diagnosis for #{receiver}::#{const_name}"
puts "-" * 50
trace = trace_constant_lookup(receiver, const_name)
trace.each_with_index do |step, index|
status = step[:found] ? "✓ FOUND" : "✗ not found"
puts "#{index + 1}. #{step[:module]} (#{step[:source]}): #{status}"
end
if trace.none? { |step| step[:found] }
puts "\nConstant #{const_name} not found in lookup path"
puts "Available constants in #{receiver}:"
puts receiver.constants.sort.join(', ')
end
end
end
# Usage example
class Parent
SHARED_CONFIG = "parent_config"
end
class Child < Parent
CHILD_CONFIG = "child_config"
end
# Debug missing constant
ConstantDebugger.diagnose_constant_error(Child, :MISSING_CONSTANT)
Common Pitfalls
Constant query operations contain subtle behaviors that frequently cause confusion, particularly around inheritance semantics, scoping rules, and the distinction between lexical and dynamic constant access. Understanding these pitfalls prevents common debugging scenarios and unexpected runtime behavior.
The inheritance parameter in constant query methods creates a major source of confusion. By default, const_defined?
and const_get
search through the inheritance hierarchy, but the inherit
parameter can restrict searches to the receiver module only. This distinction becomes critical when dealing with module hierarchies where parent and child modules define constants with the same name.
module BaseConfig
TIMEOUT = 30
RETRIES = 3
end
module DatabaseConfig
include BaseConfig
TIMEOUT = 60 # Override parent constant
HOST = "localhost"
end
# Default behavior includes inheritance
DatabaseConfig.const_defined?(:TIMEOUT) # => true (finds local)
DatabaseConfig.const_defined?(:RETRIES) # => true (finds inherited)
# Restrict to local module only
DatabaseConfig.const_defined?(:TIMEOUT, false) # => true (local override)
DatabaseConfig.const_defined?(:RETRIES, false) # => false (not local)
# This creates problems when checking for local definitions
unless DatabaseConfig.const_defined?(:RETRIES, false)
DatabaseConfig.const_set(:RETRIES, 5) # Sets local value
end
DatabaseConfig::RETRIES # => 5 (local), BaseConfig::RETRIES still 3
Lexical versus dynamic constant resolution represents another common pitfall. Direct constant access follows lexical scoping rules, while const_get
performs dynamic lookup that ignores lexical context. This difference causes issues when constants exist in multiple scopes.
module Outer
SETTING = "outer"
module Inner
SETTING = "inner"
def self.get_via_lexical
SETTING # Lexical lookup finds Inner::SETTING
end
def self.get_via_dynamic
const_get(:SETTING) # Dynamic lookup finds Inner::SETTING
end
def self.get_outer_via_dynamic
# This doesn't work as expected - finds Inner::SETTING, not Outer::SETTING
const_get(:SETTING) # Still finds local constant
end
def self.get_outer_correctly
Outer.const_get(:SETTING) # Explicit receiver required
end
end
end
Outer::Inner.get_via_lexical # => "inner"
Outer::Inner.get_via_dynamic # => "inner"
Outer::Inner.get_outer_via_dynamic # => "inner" (unexpected!)
Outer::Inner.get_outer_correctly # => "outer"
Constant autoloading interactions with constant queries create timing-dependent bugs. Rails autoloading can cause constants to become defined between checks, leading to race conditions in constant query operations.
# Problematic pattern in Rails applications
module ServiceLoader
def self.load_service(service_name)
service_class_name = "#{service_name.to_s.camelize}Service"
# This check might pass...
unless Object.const_defined?(service_class_name)
raise ArgumentError, "Service #{service_class_name} not found"
end
# But this might trigger autoloading and succeed
Object.const_get(service_class_name)
end
# Better approach accounts for autoloading
def self.load_service_safely(service_name)
service_class_name = "#{service_name.to_s.camelize}Service"
begin
Object.const_get(service_class_name)
rescue NameError
raise ArgumentError, "Service #{service_class_name} not available"
end
end
end
# In Rails, these might behave differently:
# ServiceLoader.load_service(:user) # Might raise then work
# ServiceLoader.load_service_safely(:user) # Handles autoloading correctly
String versus symbol constant names produce inconsistent behavior across Ruby versions and methods. While most constant query methods accept both strings and symbols, subtle differences exist in how they handle namespaced constant names and case sensitivity.
module TestModule
CONSTANT_VALUE = "test"
module NestedModule
NESTED_VALUE = "nested"
end
end
# Symbol vs string behavior
TestModule.const_defined?(:CONSTANT_VALUE) # => true
TestModule.const_defined?("CONSTANT_VALUE") # => true
# Nested constant access differences
TestModule.const_get("NestedModule::NESTED_VALUE") # => "nested" (works)
TestModule.const_get(:"NestedModule::NESTED_VALUE") # => "nested" (works)
# Case sensitivity issues
TestModule.const_defined?(:constant_value) # => false (wrong case)
TestModule.const_defined?("Constant_Value") # => false (wrong case)
# Namespace separator confusion
TestModule.const_get("NestedModule/NESTED_VALUE") # NameError (wrong separator)
Constant mutation through const_set
creates unexpected behavior when working with frozen or immutable constants. Ruby allows constant reassignment but issues warnings, and the behavior differs between mutable and immutable constant values.
module ConfigModule
# Immutable constant
VERSION = "1.0.0".freeze
# Mutable constant
FEATURES = ["authentication", "logging"]
def self.update_version(new_version)
# This generates warnings but works
const_set(:VERSION, new_version)
end
def self.add_feature(feature)
# This modifies the constant's content, no warnings
FEATURES << feature
end
end
original_version = ConfigModule::VERSION
ConfigModule.update_version("2.0.0") # Warning: already initialized constant
ConfigModule::VERSION # => "2.0.0"
# But the original string might still exist elsewhere
original_version # => "1.0.0"
# Mutable constant modification happens silently
ConfigModule.add_feature("caching")
ConfigModule::FEATURES # => ["authentication", "logging", "caching"]
Thread safety issues arise when multiple threads simultaneously query and modify constants. While Ruby's constant table operations are generally thread-safe, the combination of query-then-set operations creates race conditions.
module ThreadSafeConfig
@mutex = Mutex.new
def self.ensure_constant(name, default_value)
@mutex.synchronize do
unless const_defined?(name, false)
const_set(name, default_value)
end
const_get(name)
end
end
# Unsafe version - race condition possible
def self.unsafe_ensure_constant(name, default_value)
unless const_defined?(name, false) # Thread A checks
# Thread B might set constant here
const_set(name, default_value) # Thread A sets, might override B's value
end
const_get(name)
end
end
# Multiple threads calling unsafe version can cause conflicts
threads = 10.times.map do |i|
Thread.new do
ThreadSafeConfig.unsafe_ensure_constant(:COUNTER, i)
end
end
threads.each(&:join)
# COUNTER value is unpredictable due to race conditions
Reference
Core Methods
Method | Parameters | Returns | Description |
---|---|---|---|
const_defined?(name, inherit=true) |
name (String/Symbol), inherit (Boolean) |
Boolean | Checks if constant exists in module scope |
const_get(name, inherit=true) |
name (String/Symbol), inherit (Boolean) |
Object | Retrieves constant value from module scope |
constants(inherit=true) |
inherit (Boolean) |
Array of Symbols | Lists all constants in module scope |
const_set(name, value) |
name (String/Symbol), value (Object) |
Object | Defines or reassigns constant in module |
const_missing(name) |
name (Symbol) |
Object | Called when constant lookup fails |
remove_const(name) |
name (String/Symbol) |
Object | Removes constant definition (private method) |
Inheritance Parameters
Parameter Value | Scope | Behavior |
---|---|---|
true (default) |
Full hierarchy | Searches receiver, ancestors, and included modules |
false |
Local only | Searches only the receiver module's constants |
Exception Types
Exception | Cause | Common Scenarios |
---|---|---|
NameError |
Constant not found in lookup path | Undefined constant access, typos in constant names |
ArgumentError |
Invalid constant name format | Empty strings, invalid characters, namespace violations |
TypeError |
Invalid constant value type | Setting constants to inappropriate object types |
Constant Name Formats
Format | Example | Usage |
---|---|---|
Simple name | :VERSION |
Single constant in current module |
String name | "VERSION" |
Alternative to symbol notation |
Nested path | "Database::HOST" |
Namespaced constant access |
Absolute path | "::Object" |
Root-level constant access |
Lookup Resolution Order
- Receiver module - Constants defined directly in the target module
- Included modules - Constants from modules included via
include
- Prepended modules - Constants from modules added via
prepend
- Superclass chain - Constants from parent classes (classes only)
- Lexical scope - Constants from enclosing modules (lexical access only)
- Object constants - Top-level constants defined on Object
Method Availability
Context | Available Methods | Restrictions |
---|---|---|
All modules | const_defined? , const_get , constants , const_set |
Public interface |
Module owners | remove_const |
Private method, requires explicit receiver |
Subclasses | All methods | Inherited from Module |
Eigenclass | All methods | Available on singleton classes |
Thread Safety Characteristics
Operation | Thread Safety | Notes |
---|---|---|
const_defined? |
Safe | Read-only operation |
const_get |
Safe | Read-only operation |
constants |
Safe | Read-only operation, returns snapshot |
const_set |
Atomic | Individual assignment is safe |
remove_const |
Atomic | Individual removal is safe |
Check-then-set | Unsafe | Requires synchronization |
Performance Characteristics
Operation | Time Complexity | Memory Impact |
---|---|---|
const_defined? |
O(depth) | None |
const_get |
O(depth) | None |
constants |
O(n) | Creates new array |
const_set |
O(1) | Stores reference |
Nested path lookup | O(depth × segments) | None |
Common Patterns
# Safe constant access with fallback
value = Module.const_defined?(:CONST) ? Module.const_get(:CONST) : default
# Bulk constant processing
Module.constants.each { |name| process(Module.const_get(name)) }
# Conditional constant definition
Module.const_set(:CONST, value) unless Module.const_defined?(:CONST)
# Inheritance-aware constant checking
if Module.const_defined?(:CONST, false) # Local only
# Handle local definition
elsif Module.const_defined?(:CONST) # Check inheritance
# Handle inherited definition
end