RubyGuides
Share this post!

Everything You Need to Know About Ruby Constants

There is a lot more to Ruby constants that you might think…

For example, you can change the value of a constant, unlike other languages like C or Java.

Since this is such an important topic let’s explore it in detail in this article!

Defining Constants

A constant doesn’t require any special symbol or syntax to declare. You just need to make the first letter an uppercase letter.

The following are valid constants:

ABC = 1
Goo = 2
Foo = 3

Notice that you can’t define constants inside a method.

You will get this cryptic error message:

def the_method
  ABC = 1
end

# "dynamic constant assignment"

So just define your constants outside methods, typically we want to have constant definitions at the top of your class so they are clearly visible.

class RubyBlog
  URL    = "rubyguides.com"
  AUTHOR = "Jesus Castello"
end

You can then access these constants inside the class methods or outside the class using the following syntax:

p RubyBlog::AUTHOR

# "Jesus Castello"

We are going to talk a bit more about the scope of constants later in this post, so stay tuned for that!

Uninitialized Constant

One common error that you may get is this:

puts Foo
# "uninitialized constant Foo (NameError)"

I want you to mentally translate this error to “constant not found Foo”. One important point to understand this error is that Ruby classes are constants.

Array
String
Hash

They are constants because the first letter is uppercase. This matters because the most likely reason that you are seeing this error is that you forgot to require some file or gem that defines the constant.

Or maybe you just misspelled the name of the constant.

So keep an eye open for that.

Constants Can Change

Like I mentioned in the introduction, Ruby constants can change.

ABC = 1
ABC = 2

But you will see this warning message:

2: warning: already initialized constant ABC

Your program will still work fine, but you want to avoid this.

There is no way to prevent a constant from changing because variables in Ruby are not containers, they are simply pointers to objects.

The best you can do is to use an immutable object.

Example:

AUTHOR = "Jesus Castello".freeze

AUTHOR << "o"
# RuntimeError: can't modify frozen String

Related article: Ruby mutability & the freeze method.

Notice that in this example you can still change what the AUTHOR constant is pointing to, the only thing freeze protects you from is from changing the object itself.

Constant Methods

There are a few methods dedicated to working with constants:

Method Description
constants Returns an array of symbols that represent the constants defined in a class
const_get Returns value for a constant. Takes a symbol or string as parameter
const_set Sets the value for a constant. Takes two parameters: constant name as a symbol & constant value
const_missing Same as method_missing but for constants
const_defined? Returns true if a given constant (as a symbol) has been defined
remove_const Removes a constant
private_constant Makes a constant private so it can't be accessed outside the class with Class::ABC syntax

There are a few metaprogramming tricks you can do using these methods.

Example:

module Food
  class Bacon; end
  class Chocolate; end
end

puts "Classes defined inside #{Food}:"

puts Food.constants

Also you can use a string like "Array" & get the actual class:

array_class = Object.const_get("Array")

But be careful with that one since a user could inject code if the string is coming from params or some other form of user input.

In Rails, there is the constantize method which basically does const_get for you, but keep in mind that it doesn't do any security checks.

Cheatsheet

cheatsheet-constant

Ruby Constant Scope

When you create a constant outside of any class, at the top-level of your code, that constant will be available anywhere.

Constants are also available in child classes.

class A
  FOO = 1
end

class B < A
  def foo
    puts FOO
  end
end

B.constants

# [:FOO]

Constants defined outside a nested module or class are also available inside the nested classes.

module Food
  STORE_ADDRESS = "The moon"

  class Bacon
    def foo; puts STORE_ADDRESS; end
  end
end

fb = Food::Bacon.new
fb.foo

# "The moon"

Module Mixing

Constants from mixed-in modules are also available:

module Mixin
  A = 123
end

class Product
  include Mixin

  puts A
end

# 123

Notice that this works when including the module, it won't work if you are extending it.

Example:

class Product
  extend Mixin

  puts A
end

#  uninitialized constant Product::A

Also when you use a method defined in the included method, it will use the constants defined in that module, even if the same constant is defined in the current class.

module Parent
  def print_value
    VALUE
  end
end

class Child
  include Parent

  VALUE = 1
end

# Works
p Child::VALUE

# uninitialized constant Parent::VALUE
p Child.new.print_value

Module Nesting

I want to show you one more example with nested classes (same for modules).

class A
  FOO = 1
end

class A::B
  class C
    puts FOO
  end
end

Notice the A::B notation here, which we tried to use as a shortcut. But the problem is that class C won't have access to FOO directly.

For that reason you want to stick to this kind of nesting:

class A
  FOO = 1
end

class A
  class B
    class C
      puts FOO
    end
  end
end

In the first example you can still do ::A::FOO to access the constant, but if the class name changes then you will get an error.

This ::A::FOO syntax works because it tells Ruby to look in the top-level scope, where constants like Array & String are defined.

Video

Summary

You learned about Ruby constants, a type of variable which has some interesting behavior. You can change the value of a constant but it will print a warning.

You also learned that class names are constants & that you should avoid const_get with user input.

If you enjoyed this post don't forget to share it so more people can understand how constants work.

2 comments
ANDRES says 11 months ago

Thanks for your post!

I just wanted to point out that it is possible to access a child-defined constant from the parent by prepending self.class:: to the constant:

module Parent
  def print_value
    self.class::VALUE
  end
end

class Child
  include Parent

VALUE = 1
end

Works

p Child::VALUE

Also works

p Child.new.print_value

    Jesus Castello says 11 months ago

    Thanks for reading & sharing this example with us 🙂

Comments are closed