rubyruby-2.2

Why doesn't Ruby find classes in a higher scope when module is specified using ::?


I just got stuck on this for a while. Take this base:

module Top
  class Test
  end

  module Foo
  end
end

Later, I can define classes inside Foo that extends Test by doing this:

module Top
  module Foo
    class SomeTest < Test
    end
  end
end

However, if I try to minimize indentation by using :: to specify the module:

module Top::Foo
  class Failure < Test
  end
end

This fails with:

NameError: uninitialized constant Top::Foo::Test

Is this a bug, or is it just a logical consequence of the way Ruby resolves variable names?


Solution

  • Is this a bug, or is it just a logical consequence

    It's a "quirk". Some consider it a bug.

    Parent scopes used for looking up unresolved constants are determined by module nesting. It just so happens that when you use module Top::Foo, it creates just one level of nesting instead of two. Observe:

    module Top
      module Foo
        class SomeTest
          Module.nesting # => [Top::Foo::SomeTest, Top::Foo, Top]
        end
      end
    end
    
    module Top::Foo
      class SomeTest
        Module.nesting # => [Top::Foo::SomeTest, Top::Foo]
      end
    end