ZetCode

Ruby Constants

last modified October 18, 2026

Constants in Ruby are variables whose names begin with an uppercase letter. They hold values that are meant to stay fixed during program execution. This guide covers defining constants, how Ruby looks them up through lexical scope and namespaces, freezing values to prevent changes, and inspecting constants dynamically with reflection methods.

Defining Constants

A constant name must begin with an uppercase letter; by convention, constant names are written in all uppercase with underscores separating words. Ruby allows constants to be assigned values of any type. Their values can be read anywhere the constant is visible, and the defined? keyword reports whether a name is a constant.

define.rb
# Constants start with an uppercase letter
MAX_USERS = 100
APP_NAME = "Inventory"
PI_APPROX = 3.14159

puts "App: #{APP_NAME}"
puts "Max users: #{MAX_USERS}"
puts "Pi: #{PI_APPROX}"
puts MAX_USERS.class
puts defined?(MAX_USERS)
puts defined?(MISSING).inspect

The names MAX_USERS, APP_NAME, and PI_APPROX are constants holding an integer, a string, and a float. defined?(MAX_USERS) returns "constant", while defined?(MISSING) returns nil for a name that was never defined.

$ ./define.rb
App: Inventory
Max users: 100
Pi: 3.14159
Integer
constant
nil

Reassignment Warning

Ruby does not forbid reassigning a constant, but it prints a warning to standard error when a constant that already has a value is assigned again. The program keeps running, and the new value replaces the old one. The warning is a signal that the code is probably wrong, not an error that stops execution.

reassign.rb
LIMIT = 10
puts "Before: #{LIMIT}"
LIMIT = 20
puts "After: #{LIMIT}"

The second assignment to LIMIT triggers a warning that includes the line of the new and the previous definition. Running with 2>&1 captures the warning, which is written to standard error, together with the normal output.

reassign.rb output (ruby reassign.rb 2>&1)
$ ./reassign.rb
reassign.rb:3: warning: already initialized constant LIMIT
reassign.rb:1: warning: previous definition of LIMIT was here
Before: 10
After: 20

Constant Scope and Lookup

Ruby resolves a constant name first through the lexical scope: the module and class bodies that textually enclose the code. If it is not found there, Ruby checks the ancestors of the enclosing class and finally the top-level (Object) namespace. A constant can be read explicitly with Module::NAME, and ::NAME refers to a top-level constant even when a nearer constant with the same name shadows it.

scope.rb
GREETING = "Hello from top level"

module Outer
  GREETING = "Hello from Outer"

  class Greeter
    def say
      GREETING   # lexical lookup: finds Outer::GREETING
    end
  end

  module Inner
    def self.say
      GREETING
    end
  end
end

class Other
  def say
    GREETING   # not nested in Outer, finds top-level constant
  end
end

puts Outer::Greeter.new.say
puts Outer::Inner.say
puts Other.new.say
puts Outer::GREETING
puts ::GREETING
puts Outer.const_get(:GREETING)

Inside Outer, both Greeter and Inner find Outer::GREETING because they are lexically nested within Outer. The class Other is not nested there, so the lookup reaches the top-level constant. Outer::GREETING reads the constant through its namespace, and ::GREETING forces the lookup to the top level.

$ ./scope.rb
Hello from Outer
Hello from Outer
Hello from top level
Hello from Outer
Hello from top level
Hello from Outer

Constants in Classes and Modules

Constants defined in a class or module belong to that namespace and are read with the :: operator from outside. Inside the class body and its methods, they can be used directly. Modules are a common place to group related configuration values, and constants lists the names defined in a given namespace.

classes.rb
module Config
  VERSION = "1.2.0"
  DEFAULTS = { timeout: 30, retries: 3 }.freeze
end

class Server
  PORT = 8080
  ROLES = %w[admin user].freeze

  def info
    "Port #{PORT}, roles #{ROLES.join(', ')}, version #{Config::VERSION}"
  end
end

puts Server::PORT
puts Server.new.info
puts Config::DEFAULTS[:timeout]
puts Server.constants.sort.inspect
puts Config.constants.sort.inspect

Server defines PORT and ROLES, which its instance method uses directly. The method also reads Config::VERSION through the module's namespace. The constants method returns the names defined directly in each namespace as symbols.

$ ./classes.rb
8080
Port 8080, roles admin, user, version 1.2.0
30
[:PORT, :ROLES]
[:DEFAULTS, :VERSION]

Freezing Constants

Assigning a constant only prevents rebinding its name, not changing the object it refers to. Calling freeze makes an object immutable. Attempting to modify a frozen object raises FrozenError. The freeze is shallow: it applies only to the object itself, so nested strings or arrays remain mutable unless they are frozen too.

freeze.rb
COLORS = %w[red green blue].freeze
puts COLORS.frozen?

begin
  COLORS << "yellow"
rescue FrozenError => e
  puts "FrozenError: #{e.message}"
end

# freeze is shallow: the array cannot change, but the string inside it can
NAMES = ["alice", "bob"].freeze
NAMES[0] << " smith"
puts NAMES.inspect

# Freezing the hash and the string it holds
SETTINGS = { host: "localhost".freeze }.freeze
begin
  SETTINGS[:host] << "!"
rescue FrozenError => e
  puts "FrozenError: #{e.message}"
end
puts "string frozen? #{SETTINGS[:host].frozen?}"

Appending to the frozen COLORS array raises a FrozenError that names the frozen class and its contents. The NAMES array is frozen, but its first string is not, so it can still be changed. SETTINGS freezes both the hash and its string value, which makes the string immutable too.

$ ./freeze.rb
true
FrozenError: can't modify frozen Array: ["red", "green", "blue"]
["alice smith", "bob"]
FrozenError: can't modify frozen String: "localhost"
string frozen? true

Dynamic Constant Access

Ruby provides reflection methods for constants. const_get reads a constant by name, const_set defines one at runtime, and const_defined? checks whether a name exists. Calling Object.const_defined? checks the top-level namespace. A missing constant raises NameError.

dynamic.rb
module Limits
  MIN = 1
  MAX = 99
end

puts Limits.constants.sort.inspect
puts Limits.const_get(:MAX)
Limits.const_set(:STEP, 5)
puts Limits::STEP
puts Limits.const_defined?(:MIN)
puts Object.const_defined?(:Limits)
puts Object.const_defined?(:Nope)
puts Object.const_get("Limits::MIN")

begin
  Limits.const_get(:Missing)
rescue NameError => e
  puts "NameError: #{e.message}"
end

const_set adds STEP to Limits at runtime, after which it appears in constants and can be read normally. Object.const_defined?(:Limits) is true because modules are top-level constants, while Object.const_defined?(:Nope) is false. const_get also accepts a string with :: separators for nested lookup.

$ ./dynamic.rb
[:MAX, :MIN]
99
5
true
true
false
1
NameError: uninitialized constant Limits::Missing

Best Practices

Use uppercase names with underscores for constants and group related values in a module or class. Freeze collections and strings that should not change, and remember that freeze is shallow. Avoid reassigning constants; if a value must change, use a regular variable or an attribute instead. Prefer explicit Module::NAME references when a constant comes from another namespace, so the source of the value is clear.

Source References

Learn more from these resources: Ruby Assignment Documentation, Module Documentation, and FrozenError Documentation.

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.