Вы наблюдаете, что методы в пространствах имен файлов rake переопределяют ранее определенные методы с тем же именем.Причина этого заключается в том, что Rake namespace
очень отличается от пространств имен Ruby (классов или модулей), фактически они служат только в качестве пространства имен для имен заданных в них задач , но ничего больше .Таким образом, задача a
становится задачей a:a
, если она помещена в пространство имен a
, но другой код вне задач разделяет общее пространство имен .
Этот факт вместе с фактомчто Rake загружает все задачи перед запуском данной задачи, объясняет, почему метод переопределяется.
TL; DR: Решения / подсказки для столкновения имен
Вы не можете ожидать, что два метода с одинаковым именем (или любым другим кодом) будут помещены в отдельные namespace
s, но внешние задачи будутработать должным образом.Тем не менее, вот несколько советов по решению такой ситуации:
Поместите методы в задачи .Если оба метода msg
были определены внутри задач a:a
и b:b
, тогда обе задачи rake будут работать правильно и отображать ожидаемые сообщения.
Если вам нужно использовать код из namespace
граблей в нескольких задачах рейка, извлекает методы / код в реальное пространство имен Ruby , например, двамодули и include
код в задачах, которые в нем нуждаются.Рассмотрим переписывание примеров граблей:
# lib/tasks/a.rake:
module HelperMethodsA
def msg(msg)
puts "MSG SENT FROM Task A: #{msg}"
end
end
namespace :a do
desc "Task A"
task(:a => :environment) do
include HelperMethodsA
msg('I AM TASK A')
end
end
# lib/tasks/b.rake:
module HelperMethodsB
def msg(msg)
puts "MSG SENT FROM Task B: #{msg}"
end
end
namespace :b do
desc "Task B"
task(:b => :environment) do
include HelperMethodsB
msg('I AM TASK B')
end
end
Поскольку два модуля имеют разные имена и потому что в соответствующих задачах они равны include
d, обе задачи rake снова будут работать, как и ожидалось.
Теперь давайте докажем вышеуказанные утверждения с помощью исходного кода ...
Доказательство того, что Rake сначала загружает все задачи и почему это так
Это легко.В главном Rakefile
вы всегда найдете следующую строку:
Rails.application.load_tasks
Этот метод в конечном итоге вызывает следующий код из движка Rails:
def run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
Таким образом, он ищетlib/tasks
каталоги для всех файлов rake и загружают их один за другим, в отсортированном порядке.Вот почему файл b.rake
будет загружен после a.rake
, и все, что находится внутри него, потенциально переопределит код из a.rake
и всех ранее загруженных файлов rake.
Rake должен загрузить все файлы rake просто потому, чтоимена rake namespace
не обязательно должны совпадать с именами rake-файлов, поэтому имя файла rake не может быть выведено из имени задачи / пространства имен.
Доказательство того, что namespace
rake не являются реальнымиRuby-подобное пространство имен
После загрузки файлов rake выполняются операторы Rake DSL, а также метод namespace
.Метод берет блок кода, определенный внутри него, и выполняет его (используя yield
) в контексте Rake.application
объекта , который является одноэлементным объектом класса Rake::Application
, который является общим для всехграбли задачи.Для пространства имен не создан динамический модуль / класс, он просто выполняется в контексте основного объекта.
# this reuses the shared Rake.application object and executes the namespace code inside it
def namespace(name=nil, &block)
# ...
Rake.application.in_namespace(name, &block)
end
# the namespace block is yielded in the context of Rake.application shared object
def in_namespace(name)
# ...
@scope = Scope.new(name, @scope)
ns = NameSpace.new(self, @scope)
yield(ns)
# ...
end
См. Соответствующие источники здесь и здесь .
Задачи Rake ДОЛЖНЫ представлять собой пространства имен ruby
Однако в самих задачах Rake ситуация иная.Для каждой задачи создается отдельный объект класса Rake::Task
(или аналогичного класса), и код задачи запускается в контексте этого объекта.Создание объекта выполняется в intern
методе в диспетчере задач:
def intern(task_class, task_name)
@tasks[task_name.to_s] ||= task_class.new(task_name, self)
end
Цитата из автора Рейка
Наконец, все этоПодтверждено этим интересным обсуждением на github , в котором рассматривается очень похожая и связанная с этим проблема, и из которой мы можем процитировать Джима Вейриха, первоначального автора Rake:
Поскольку пространства именНе представляйте реальные области применения метода, единственная реальная возможность для области - это модуль DSL.
...
Возможно, когда-нибудь пространства имен Rake станут полноправными объектами с областью, в которой можно разместить ленивые определения let, но мы еще не там.