5

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?

Hubro
  • 56,214
  • 69
  • 228
  • 381

2 Answers2

8

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
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • 2
    Sounds like a bug to me :P But very informative response, thank you – Hubro Dec 17 '15 at 15:46
  • I would say I consider it a bug, has anyone found a bug report in ruby about this? I'd like to contribute to discussion and look at what would be involved in fixing this. – Mike H-R Jan 27 '17 at 11:39
  • Note: found a link stating that it's intentional [here](https://bugs.ruby-lang.org/issues/11705) – Mike H-R Jan 27 '17 at 11:44
  • 1
    [This](https://bugs.ruby-lang.org/issues/6810) appears to be the bug report for this behaviour. Let's hope Matz decides to pick it up. :) – Mike H-R Jan 27 '17 at 11:59
1

This is expected. Using :: changes the scope of constant lookup and expects Test to be defined under Top::Foo.

To get the expected result, you could write:

module Top::Foo
  class SomeTest < Top::Test
  end
end

or:

module Top
  class Foo::SomeTest < Test
  end
end

or even:

class Top::Foo::SomeTest < Top::Test
end
Stefan
  • 109,145
  • 14
  • 143
  • 218