Ruby constants

Posted February 10, 2022 - tagged ruby  ‐  3 min read

How to define constants in Ruby, and how to use them - with one tricky part.

Motivation

We don't have a lot of Ruby constants in the bootrAils starter. However, it's always good to know how they work when the need arises.
"Constants" are variables that are not meant to be changed. Let's see how. In this article we will use the ruby console, if you are in a Rails environment, you can get it by typing bin/rails console.

How to define a Ruby constant

Constants begin with an uppercase letter.

Example :

Boiling = 100 # valid
BOILING = 100 # valid
boiling = 100 # invalid

Constants defined within a class or module can be directly accessed from the inside. Those defined outside a class or module can still be accessed globally.

Example :

class Water
   ICE = 0
   BOIL = 100
   def show
      puts "Celsius temperature for becoming ice is #{ICE}"
      puts "Celsius temperature for becoming steam is #{BOIL}"
   end
end

As you can notice, ICE and BOIL are freely accessed from the class where they are defined. From the outside :

Water::ICE
# => 0
Water::BOIL
# => 100
Water.new.show
# => Celsius temperature for becoming ice is 0
# => Celsius temperature for becoming steam is 100

Ruby constants that will fail

Constants cannot be defined inside a method.

Example :

class Water
   def show
      ICE = 0
      BOIL = 100
      puts "Celsius temperature for becoming ice is #{ICE}"
      puts "Celsius temperature for becoming steam is #{BOIL}"
   end
end
# Traceback (most recent call last):
# SyntaxError ((irb):117: dynamic constant assignment)
# ICE = 0

Constants cannot start with a lower case.

class Water
   ice = 0 # ! fail ! Constant cannot start with lowercase
   boil = 100 # ! fail ! Constant cannot start with lowercase
   def show
      puts "Celsius temperature for becoming ice is #{ice}"
      puts "Celsius temperature for becoming steam is #{boil}"
   end
end

This will produce the following error on call :

Water::ice
Traceback (most recent call last):
        1: from (irb):87:in `<main>'
NoMethodError (undefined method `ice' for Water:Class)

Uninitialized Constant Error

This kind of error will often happen if you code with Ruby-on-Rails or any other framework. It often means a class or constant inside a module wasn't found. You can easily recreate this error on your computer :

class Water
   ICE = 0
   BOIL = 100
   def show
      puts "Celsius temperature for becoming ice is #{ICE}"
      puts "Celsius temperature for becoming steam is #{BOIL}"
   end
end

And then try simply to access a constant that doesn't exists :

irb(main):131:0> Water::ICE
0
irb(main):132:0> Water::UNEXISTING
Traceback (most recent call last):
        1: from (irb):132:in `<main>'
NameError (uninitialized constant Water::UNEXISTING)

Changing... A Ruby constant

This is something disturbing if you come from another language : Ruby constants can be changed without the raise of any error.

Water::ICE = 42
# => warning: already initialized constant Water::ICE
Water::ICE
# => 42

A warning is raised, but the program still continues.

If you want to avoid this default behaviour, you have to freeze your constant.

For this example, I'm going to freeze... the ice.

class Water
   ICE = 0.freeze
   BOIL = 100.freeze
   def show
      puts "Celsius temperature for becoming ice is #{ICE}"
      puts "Celsius temperature for becoming steam is #{BOIL}"
   end
end

It doesn't really work, because you can still make Water::ICE = 42 and have a simple warning.

A Ruby constant isn't actually immutable, and you can't freeze a variable.

See this excellent StackOverflow answer to circumvent this problem : https://stackoverflow.com/a/26542453/2595513

Build your MVP with Rails

👉 We are 2 Ruby-on-Rails lovers. By building BootrAils, we hope we will help you to launch your Startup idea (really) faster.
Take care ! Health first. 🙏
Shino & David.