Posted about 6 years ago. Visible to the public. Repeats.

Ruby constant lookup: The good, the bad and the ugly

In Ruby, classes and modules are called constants. This card explains how Ruby resolves the meaning of a constant.

The good

E. g. in the following example, Array could mean either Foo::Array or simply Array:

class Foo def list end end

What Ruby does here is to see if the name Array makes sense inside of Foo::, and if that fails, resolves it to ::Array (without a namespace).

The bad

This is relevant for old Ruby versions. Ruby 2.5+ removes top-level constant lookup which means that Array::String will not resolve to String and raise an error instead.

You might be surprised that these are all valid ways to reference Ruby's String class:

String Array::String Array::Hash::String

When you see Array::String, do not think "a class String inside the Array namespace". Rather think: "What does String resolve to from the viewpoint of the Array class?".

When you do this, Ruby will print a warning:

warning: toplevel constant String referenced by Array::String

However, that warning is usually lost in a sea of log messages. And here is where it gets ugly (see below).

The ugly

This part is a bit lengthy, but it allows you to debug strange bugs with Rails autoloading the wrong constants.

During development, Rails unloads all classes after every request. This way code changes take effect immediately, without requiring you to restart the server. If you have been working with Rails for a while, you might even have forgotten that this is a feature of Rails. In the regular world of Ruby, classes don't refresh themselves automatically after a change.

Rails also autoloads classes on demand during development. This means every request begins with none of your models, controllers, loaded. Whenever Ruby encounters a constant name it doesn't know yet, it guesses the correct .rb file and requires the file. Again, this is a feature activated by Rails. If you write a regular Ruby script without Rails, you need to require all the files you want to use. Classes don't magically load themselves from disk.

Unfortunately, due to the constant lookup rules above, Rails sometimes guesses wrong and loads the wrong file. This happens when you heavily namespace your models.

Take this example with some constants referencing each other:

# app/models/contract/document/uploader.rb class Contract::Document::Uploader < CarrierWave::Uploader::Base ... end # app/models/document/uploader.rb class Document::Uploader < CarrierWave::Uploader::Base ... end # app/models/document.rb class Document < ActiveRecord::Base mount_uploader Document::Uploader end # app/models/contract.rb class Contract < ActiveRecord::Base mount_uploader Contract::Document::Uploader end

With Rails autoloading during development, it can happen that the last class actually mounts the uploader Document::Uploader instead of the requested Contract::Document::Uploader. This is because when you haven't required 'app/models/contract/document/uploader.rb', Contract::Document::Uploader is actually a valid way to reference Document::Uploader. The constant lookup never fails, thus the file is never required.


  • You can't fix it with require
  • Never make a class name for which an existing class name is a suffix. E.g. don't create a class Api::User when you also have a ::User.
  • A workaround when you have control over every involved class name:
    • prefix the root level class with 'Generic' e.g. GenericDocument
    • then the names Contract::Document and Deal::Document are available
  • If you want to know more about constant lookup in ruby read "Everything you ever wanted to know about constant lookup in Ruby"

Once an application no longer requires constant development, it needs periodic maintenance for stable and secure operation. makandra offers monthly maintenance contracts that let you focus on your business while we make sure the lights stay on.

Owner of this card:

Henning Koch
Last edit:
about 23 hours ago
by Henning Koch
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandra dev
This website uses cookies to improve usability and analyze traffic.
Accept or learn more