Синтаксис вложенного модуля Ruby (и Rails)
Мне интересно, какая разница между двумя следующими модулями:
# First Example
module Parent
module Child
end
end
и
# Second Example
module Parent::Child
end
Используя второй метод, кажется, что родительский модуль должен быть предварительно определен, в противном случае я получаю ошибку "неинициализированной константы"
Учитывая это, что является предпочтительным способом определения таких модулей, а затем добавления вложенных дочерних элементов в отношении синтаксиса и файловой структуры (т.е. папок и т.д.). Приветствуем ссылку на путь Rails.
Являются ли эти два примера для всех целей и целей эквивалентными?
Ответы
Ответ 1
В первом примере он определяет модуль Parent
, а затем модуль Child
. Второй пример, как вы говорите, должен иметь модуль Parent
, определенный перед началом работы. За счет еще одной строки кода вы гарантируете, что модуль, который вы размещаете, используя ваш первый пример, всегда будет определен.
Для примера Rails рассмотрим файл railties/lib/rails/engine.rb, который повторно открывает модуль Rails
и то определяет класс Engine
внутри него. Это можно было бы сделать просто:
class Rails::Engine
Но вместо этого, по причинам, указанным выше и, возможно, также для ясности, сначала был определен модуль, затем класс внутри.
Ответ 2
Я предпочитаю второй метод (если я уверен, что Parent уже определен), потому что он выглядит более чистым, особенно. когда вложенность очень глубокая.
Однако у 1-го метода есть некоторые преимущества, еще не обсужденный, что вложенный модуль получает доступ к любым лексически доступным константам в охватывающем модуле.
Ответ 3
Вообще говоря, вы не хотите определять модуль с использованием синтаксиса Parent:: Child модуля, если вы не можете быть абсолютно уверены, что Parent уже существует. Подмодуль может быть определен только с помощью синтаксиса:: if, если родительский модуль определен. В вашем примере, если вы выполните следующее, вы не получите неинициализированную постоянную ошибку.
module Parent
end
module Parent::Child
end
Ответ 4
Кажется, что ответ Banister также является причиной такого поведения:
ruby-1.9.2-p290 :001 > module A; module A; A.ancestors; end; end
=> [A::A]
ruby-1.9.2-p290 :002 > module A::A; A.ancestors; end
=> [A]
Внутренний модуль A является константой внутри внешнего модуля A и, следовательно, не отображается с использованием второго метода.
Редактирование моего предыдущего комментария:
Это объясняется в 7.9 "Постоянный поиск" "Язык программирования Ruby" (первое издание). Соответствующая часть здесь - Module.nesting, которая не содержит внешний модуль во втором примере, таким образом, A может быть найдена только в глобальной области.