Синтаксис вложенных модулей в Ruby (и Rails) - PullRequest
29 голосов
/ 19 мая 2010

Мне интересно, в чем разница между следующими двумя модулями

# First Example
module Parent
  module Child
  end
end

и

# Second Example
module Parent::Child
end

Используя второй метод, создается впечатление, что родительский модуль должен быть предварительно определен, в противном случае я получаю ошибку «неинициализированная константа»

Учитывая это, каков предпочтительный способ определения таких модулей, как этот, и последующего добавления вложенных потомков с учетом синтаксиса и структуры файлов (т. Е. Папок и т. Д.). Ссылка на путь Rails будет принята с благодарностью.

Являются ли эти два примера для всех намерений и целей эквивалентными?

Ответы [ 4 ]

31 голосов
/ 20 мая 2010

В первом примере он определяет модуль Parent, а затем модуль Child. Во втором примере, как вы говорите сами, модуль Parent должен быть определен заранее. За счет еще одной строки кода вы гарантируете, что модуль, в который вы вкладываете, используя ваш первый пример, всегда будет определяться.

В качестве примера Rails давайте рассмотрим файл railties / lib / rails / engine.rb , в котором повторно открывает модуль Rails , а затем определяет Engine класс внутри него. Это можно было сделать просто:

class Rails::Engine

Но вместо этого, возможно, по причинам, изложенным выше, а также, возможно, для ясности, сначала был определен модуль, а затем класс внутри.

13 голосов
/ 20 мая 2010

Я предпочитаю второй метод (если я уверен, что Parent уже определен), потому что он выглядит чище, особенно. когда вложение очень глубокое.

Однако у 1-го метода есть некоторые преимущества, одно из которых еще не обсуждалось: вложенный модуль получает доступ к любым лексически доступным константам во вложенном модуле.

7 голосов
/ 20 мая 2010

Вообще говоря, вы не хотите определять модуль, используя синтаксис модуля Parent :: Child, если вы не можете быть абсолютно уверены, что Parent уже существует. Подмодуль может быть определен с использованием синтаксиса ::, только если определен родительский модуль. В вашем примере, если вы сделаете следующее, вы не получите неинициализированную постоянную ошибку.

module Parent
end

module Parent::Child
end
3 голосов
/ 22 ноября 2011

Кажется, что ответ Банистера также является причиной такого поведения:

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 можно найти только в глобальной области видимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...