Почему Ruby не находит классы в более высокой области видимости, когда модуль указан с помощью :: ?

#ruby

#ruby #ruby-2.2

Вопрос:

Я просто застрял на этом на некоторое время. Возьмите эту базу:

 module Top
  class Test
  end

  module Foo
  end
end
  

Позже я могу определить классы внутри Foo , которые расширяются Test , выполнив это:

 module Top
  module Foo
    class SomeTest < Test
    end
  end
end
  

Однако, если я попытаюсь минимизировать отступ, используя :: для указания модуля:

 module Top::Foo
  class Failure < Test
  end
end
  

Это не удается с:

Ошибка имени: неинициализированная константа Top::Foo::Test

Это ошибка или просто логическое следствие того, как Ruby разрешает имена переменных?

Ответ №1:

Это ошибка или это просто логическое следствие

Это «причуда». Некоторые считают это ошибкой.

Родительские области, используемые для поиска неразрешенных констант, определяются вложенностью модуля. Так уж получилось, что при использовании module Top::Foo он создает только один уровень вложенности вместо двух. Обратите внимание:

 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
  

Комментарии:

1. Для меня это звучит как ошибка: P, но очень информативный ответ, спасибо

2. Я бы сказал, что считаю это ошибкой, кто-нибудь нашел сообщение об ошибке в ruby об этом? Я хотел бы внести свой вклад в обсуждение и посмотреть, что будет связано с исправлением этого.

3. Примечание: найдена ссылка, указывающая, что это намеренно здесь

4. Похоже, это сообщение об ошибке для такого поведения. Будем надеяться, что Matz решит забрать его. 🙂

Ответ №2:

Это ожидаемо. Использование :: изменяет область постоянного поиска и ожидает Test , что она будет определена в Top::Foo .

Чтобы получить ожидаемый результат, вы могли бы написать:

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

или:

 module Top
  class Foo::SomeTest < Test
  end
end
  

или даже:

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