This is a loose translation of my post from 2018.
Ruby is a very complex programming language. It’s incredibly beautiful and expressive. However, it also has lots of traits and implementation details that even some of the experienced Ruby programmers may not know. One of these is constant lookup.
I’m not gonna try to explain the lookup algorithm works. I just want to raise some awareness of the topic. Mostly, it’s just a rant.
Example
Let’s make a little demonstration. Let’s start with a few constants:
We have a mixin module M
and a module Namespace
with a subclass C
. Each
module defines a constant A
which we are going to try and access.
A quiz. What will the code below print out? I’ll put the answers a bit further so they don’t spoil the fun.
Let’s also define a few methods:
What do you think? Are they different?
Answers
Here’s the full code from our demo with answers in it:
So the output will be:
A from M
A from Namespace
A from Namespace
A from M
A from M
Short explanation
Simply speaking, constant lookup includes a few steps:
- Lexical scope search. I.e. the lookup context will depend on the place where
the line of code is. For example, in the first
puts
the execution context is the top-level so it uses the constantNamespace::C::A
. During the secondputs
, it first changes the context toNamespace
module, then theC
class, and only then looksA
up. You can find more about that if you read about nesting andModule.nesting
method. - If the first step wasn’t successful, the interpreter starts going through mixins and parent classes for every module from the first step.
- If even that didn’t help, the top-level gets checked. Technically, it’s
included in the step 2 because top-level is an
Object
instance. - At this stage the constant is considered missing and the
const_missing
method is called (similarly tomethod_missing
).
So:
Outro
Ruby Style Guide has a good rule of thumb: define and extend classes/modules using explicit nesting, i.e. never write `class A::B`. Following this simple rule is, practically, enough to avoid surprises and to be oblivious of constant lookup completely.
What else you can read on the subject:
- Chapter 7.9 of “The Ruby Programming Language” - book written by Matz.
- Autoloading and Reloading Constants in Rails guides
- Ruby Style Guide
- Play around with Module.nesting
Update
User @DsideSPb left a useful comment about an additional feature of constant lookup. However, it was deleted in the last ruby release (2.5.0).
I don’t know all the details but in some cases if you specify wrong way to the constant, interpreter may replace it with one from top-level.