• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Blog
  • Careers

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Working with constants in Ruby

    Brian Buchalter

    By Brian Buchalter
    December 5, 2011

    Ruby is designed to put complete power into the programmer’s hands and with great power comes great responsibility! This includes the responsibility for freezing constants. Here’s an example of what someone might THINK is happening by default with a constant.

    class Foo
      DEFAULTS = [:a, :b]
    end
    
    #irb
    default = Foo::DEFAULTS
    default << :c
    Foo::DEFAULTS #=> [:a, :b, :c]  WHOOPS!
    

    As you can see, assigning a new variable from a constant lets you modify what you thought was a constant! Needless to say, such an assumption would be very difficult to track down in a real application. Let’s see how we might improve on this design. First, let’s freeze our constant.

    class Foo
      DEFAULTS = [:a, :b].freeze
    end
    
    #irb
    default = Foo::DEFAULTS
    default << :c #=>  ERROR can't modify frozen array
    

    Now we’ll get very specific feedback about offending code. The question is how can we use our constant now as a starting point for array, and still be able to modify it later? Let’s look at some more code.

    Foo::DEFAULTS.frozen? #=> true
    Foo::DEFAULTS.clone.frozen? #=> true, this was my first guess, but it turns out we need...
    Foo::DEFAULTS.dup.frozen? #=> false
    

    It’s worth reading the docs on clone and dup to understand there difference, but in short, clone replicates the internal state of the object while dup creates a new instance of the object. There was one more question I needed to answer; what would happen when I wanted to append another frozen array to a non-frozen array? Let’s look to the code again!

    default = Foo::DEFAULTS.dup  #not frozen
    new_default = default + [:c].frozen
    new_default.frozen? # false
    

    So it seems that the initial state of the object carries the frozen state, allowing you to append frozen arrays without having to dup them. The moral of the story here is don’t make assumptions about Ruby! One of the best ways to challenge your assumptions is with unit tests.

    ruby


    Comments