In Progress
Unit 1, Lesson 1
In Progress

# Top Level Constant

Video transcript & code

I live in Eastern Tennessee, right next to Great Smoky Mountains National Park. The Smoky Mountains are renowned for the rich variety of wildflowers that bloom in the spring. And since it's prime wildlfower season as I'm making this episode, I thought we could talk about flowers today.

Oh, and also about Ruby constants.

Here's a class named `Child`. Which subclasses, unsurprisingly enough, a class called `Parent`

This child, like most children, loves flowers. And particularly loves to pick dandelions.

So if we ask him to name a flower he likes, and he happens to be holding a dandelion, that's what he's likely to say.

``````FLOWER = "Clover"

module Neighborhood
FLOWER = "Dogwood"

module Yard
FLOWER = "Rose"

class Parent
FLOWER = "Mountain Laurel"
end

class Child < Parent
FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Dandelion"
``````

But if he didn't have a particular flower in mind…

…he might then look around the yard, and name a flower he saw there. Our yard happens to be full of roses at this time of year.

And indeed, when we execute this code, that's exactly what we see.

``````FLOWER = "Clover"

module Neighborhood
FLOWER = "Dogwood"

module Yard
FLOWER = "Rose"

class Parent
FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Rose"
``````

But supposing you ask him while he's not at home.

If he were still in the neighborhood…

…he might look around at what's blooming, and take note of dogwood flowers.

And that's the answer we see when we run this code.

``````FLOWER = "Clover"

module Neighborhood
FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent
FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Dogwood"
``````

But perhaps you catch him when there are no flowers nearby.

If he can't immediately think of a flower, he might ask one of his parents for a suggestion.

I'm a parent, and personally, I'm partial to the flower of the Mountain Laurel.

And indeed, that's the answer we now get.

``````FLOWER = "Clover"

module Neighborhood
# FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent
FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Mountain Laurel"
``````

Now, if he isn't holding a flower, he can't see any flowers nearby, and he doesn't have one of his parents handy, he might just think of one of the flowers he sees most often in the world around him: the clover.

When we run this code, we see that the answer changes to "Clover".

That's the definition I've set at the top-level, outside of any module or class namespace.

``````FLOWER = "Clover"

module Neighborhood
# FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent
# FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Clover"
``````

What is this "top-level" constant environment, anyway? Is it just some kind of nameless, all-encompassing module?

It turns out that it's not that simple. Because in fact, when we define constants in the top-level evaluation environment, we're really defining them inside the `Object` class.

Don't believe me? Let's ask the `Object` class about all of the constants defined within it.

OK, that's a really long list. Let's just search for `FLOWER`.

Sure enough, there it is. Along with all of Ruby's other standard class constants, we have our `FLOWER` constant.

``````FLOWER = "Clover"

Object.constants
# => [:Object, :Module, :Class, :BasicObject, :Kernel, :NilClass, :NIL, :Data...

Object.constants.grep(/FLOWER/)
# => [:FLOWER]
``````

Having top-level constants actually be inside of the `Object` class makes a weird sort of sense. Because the Ruby top-level is actually evaluated in the context of a special object called `main`.

And `main` is an instance of—can you guess?—=Object=.

``````self                            # => main
self.class                      # => Object
``````

From what we've seen so far, we can start to get a picture of how Ruby looks up constants.

First, Ruby looks for a constant defined in the class of the current object.

If it can't find a definition there, it starts working its way outwards, through containing modules.

When it runs out of surrounding modules, it shifts direction from out to up. It looks for constant definitions in its parent class…

And then in that class' parent, and so on up to the `Object` class.

So we can sum up Ruby's constant search path very simply as "out, then up".

And that's the end of it, right?

…not quite.

Because `Object` itself has a superclass: `BasicObject`.

``````Object.superclass               # => BasicObject
``````

And in certain cases, we explictly base our objects on `BasicObject` instead of on `Object`.

Let's say the `Parent` class is descended directly from `BasicObject`.

When we run this code, Ruby can't find a definiton for the `FLOWER` constant.

Even though there's a top-level defition of it!

``````FLOWER = "Clover"

module Neighborhood
# FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent < BasicObject
# FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# =>

# ~> NameError
# ~> uninitialized constant Neighborhood::Yard::Child::FLOWER
# ~>
# ~> xmptmp-in36362wj.rb:18:in `flower'
# ~> xmptmp-in36362wj.rb:23:in `<main>'
``````

As we just saw a moment ago, Ruby's constant lookup usually ends at the `Object` class. When we inherit from `BasicObject`, we put ourselves outside this world of commonly defined constants.

When we run into an error like this, the first thing we might think to do is to explicitly qualify our reference to `FLOWER` with the `Object` class, where we know it is defined.

But this too fails!

Why? Because we made reference to the constant `Object`… which is also defined in the top-level constant scope… which `BasicObject` doesn't have access to!

``````FLOWER = "Clover"

module Neighborhood
# FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent < BasicObject
# FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
Object::FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# =>

# ~> NameError
# ~> uninitialized constant Neighborhood::Yard::Child::Object
# ~>
# ~> xmptmp-in36364yx.rb:17:in `flower'
# ~> xmptmp-in36364yx.rb:22:in `<main>'
``````

It's a classic catch-22. Fortunately, Ruby provides a way out. By using double-colons at the beginning of a constant name, we force Ruby to look that constant up in the standard top-level constant namespace.

This is known as "fully-qualifying" the constant name.

``````FLOWER = "Clover"

module Neighborhood
# FLOWER = "Dogwood"

module Yard
# FLOWER = "Rose"

class Parent < BasicObject
# FLOWER = "Mountain Laurel"
end

class Child < Parent
# FLOWER = "Dandelion"

def flower
::FLOWER
end
end
end
end
Neighborhood::Yard::Child.new.flower
# => "Clover"
``````

In other words, we force it to look up the constant in the `Object` class. As a special syntactical rule, this works anywhere, even inside `BasicObject`-derived objects.

And this is important knowledge to have. Because if you ever find yourself working on `BasicObject`-inheriting classes, you may find yourself with some very strange errors when you least expect it.

For instance, check out this code.

It uses a pretty typical Ruby idiom to switch on the type of an input object.

And yet, when we run it, we get a `NameError`.

``````class Hyperbole < BasicObject
def exaggerate(obj)
case obj
when String then obj.upcase
when Integer then obj * 2
end
end
end

Hyperbole.new.exaggerate(23)
# =>

# ~> NameError
# ~> uninitialized constant Hyperbole::String
# ~>
# ~> xmptmp-in3636nD1.rb:4:in `exaggerate'
# ~> xmptmp-in3636nD1.rb:10:in `<main>'
``````

When we're not expecting it, this type of error can make it seem like Ruby has suddenly lost its mind. What do you mean, you don't recognize the constant `String`???

The trick, of course, is to fully-qualify the constant references inside of a `BasicObject`-derived object.

When we run this, everything works fine.

``````class Hyperbole < BasicObject
def exaggerate(obj)
case obj
when ::String then obj.upcase
when ::Integer then obj * 2
end
end
end

Hyperbole.new.exaggerate(23)
# => 46
``````

And that is how constant lookup works in Ruby, and how it is possible to find oursleves outside the usual world of constant definitions. Happy hacking!