14 February 2017

A Gotcha in Ruby Constant Lookup

Given a namespace Foo (it can be a class or a module),

module Foo
  MEOW = "meow"
end

…there are two ways to open a class within that namespace.

class Foo::Bar
  # ...
end

# --- or ---

module Foo
  class Bar
    # ...
  end
end

However, the first version does not have access to constants attached to the outer namespace,

module Foo
  MEOW = "meow"
end

class Foo::Bar
  def get_meow
    MEOW
  end
end

Foo::Bar.new.get_meow
#=> NameError: uninitialized constant Foo::Bar::MEOW

…whereas the second version does.

module Foo
  MEOW = "meow"
end

module Foo
  class Bar
    def get_meow
      MEOW
    end
  end
end

Foo::Bar.new.get_meow
#=> "meow"

Explanation

Module.nesting allows you to inspect the list of namespaces that will be searched through during constant lookup. Using it, we can see that the first version does not add Foo to the lookup list,

class Foo::Bar
  Module.nesting
end
#=> [Foo::Bar]

…whereas the second version does.

class Foo
  class Bar
    Module.nesting
  end
end
#=> [Foo::Bar, Foo]