Столкновение пространства имен методов при запуске rake-задач в Rails - PullRequest
11 голосов
/ 24 августа 2011

Использование Rails 2.3.10 Если моя библиотека / задачи выглядит следующим образом

lib/tasks
- a.rake
- b.rake

a.rake выглядит так:

namespace :a do
    desc "Task A"
    task(:a=>:environment)do
      msg('I AM TASK A')
    end

    def msg(msg)
      puts "MSG SENT FROM Task A: #{msg}"
    end
end

b.rake выглядит так

namespace :b do
    desc "Task B"
    task(:b=>:environment)do
      msg('I AM TASK B')
    end

    def msg(msg)
      puts "MSG SENT FROM Task B: #{msg}"
    end
end

Затем, когда я запускаю задание

rake a RAILS_ENV=sandbox

Выход "MSG ОТПРАВЛЕНО ИЗ ЗАДАЧИ B: Я ЗАДАЧА A"

Таким образом, вспомогательный метод msg (), определенный в a.rake, не вызывается. Скорее то, что определено в b.rake, вызывается. (Более того, если у меня есть c.rake - тогда это вспомогательный метод msg вызывается при запуске задачи a.

Является ли пространство имен этого метода конфликтующим известным поведением? Я бы подумал, что пространство имен предотвратило бы это.

Спасибо

Ответы [ 3 ]

14 голосов
/ 22 апреля 2016

Вы наблюдаете, что методы в пространствах имен файлов 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, но мы еще не там.

0 голосов
/ 22 апреля 2016

Пространство имен rake предназначено только для задач rake. Смотрите документацию Rake: The NameSpace class will lookup task names in the the scope defined by a namespace command.

Вы можете создать модуль вместе с вашим пространством имен rake для решения этой проблемы:

module A do

    module_functions

    def msg(msg)
      puts "MSG SENT FROM Task A: #{msg}"
    end
end

namespace :a do
    desc "Task A"
    task(:a=>:environment)do
       A.msg('I AM TASK A')
    end
end
0 голосов
/ 24 августа 2011

Используйте пространство имен следующим образом:

namespace :rake_a do
  desc "Task A"
  task(:a=>:environment)do
    msg('I AM TASK A')
  end

  def msg(msg)
    puts "MSG SENT FROM Task A: #{msg}"
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...