Ruby Constants

I was doing some refactoring recently and made some interesting discoveries about how constants work in ruby.

What tripped me up was how much of a mess this code made:

Can you spot the problems here? The first obvious one is merge with a bang, and the other I will explain below.

What is a constant?

Constants in ruby are anything starting with a capital letter. So class names are constants as well as all capital variables.

Constants aren’t completely similar in other language. For example in Java and PHP you cannot re-assign or change a constant. In ruby you can:

Now you do get a warning, but its not an error and will not stop you from continuing. In the first example, the merge! actually modified the original constant so this was applied too all other classes using this constant.

There is one thing you can do if you want to ensure that the Object that the constant holds will not be modified, and that is by using the method freeze.

http://ruby-doc.org/core-2.1.0/Object.html#method-i-freeze

But as you can see below, the Constant is still able to be re-assigned (but still gives us the warning).

As pointed out by Andrew, you can freeze the class constant that an object refers to which will stop it from being modified.

Some other useful things about class constants is how easily they can be accessed. Constants defined in a class can even be reached without creating an instance of the class. You can even dynamically call the constant if you have a reference to the Class variable that contains the constant. For example:

Im still not certain on how useful constants are for settings that never change but I prefer using them to class methods that re-define hashes every time they are called or yaml files. They can sometimes make testing easier and other times harder.

Do you use constants much and how do you use them? Reply in the comments below.

4 Comments

  1. kris January 29, 2014 at 3:13 am #

    I prefer to not use constants and instead have a class method that returns the data I need.

  2. Andrew Grimm January 29, 2014 at 7:33 am #

    You can make it impossible to modify which object a constant refers to, if you so desire:

    class Foo
    BAR = 1
    end

    # Works
    class Foo
    BAR = 2
    end

    Foo.freeze

    # Doesn’t work
    class Foo
    BAR = 3
    end

    RuntimeError: can’t modify frozen Class

  3. Kieran Andrews January 29, 2014 at 8:04 am #

    @Andrew Thanks for that. I didn’t think about freezing the object that the constants where in. Will add this to the post.

    @kris I found when running benchmarks that defining a class method will redefine the hash contained in it, and this is less performant (although not really something you would notice)

  4. waxjar January 29, 2014 at 10:19 am #

    Constants are not class methods.

    class Test
    CONSTANT = "hello"
    end

    Test.CONSTANT
    # => NoMethodError: undefined method
    CONSTANT’ for X:Class
    `

Leave a Reply