Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 在Rails中运行rake任务时方法命名空间冲突_Ruby On Rails_Rake - Fatal编程技术网

Ruby on rails 在Rails中运行rake任务时方法命名空间冲突

Ruby on rails 在Rails中运行rake任务时方法命名空间冲突,ruby-on-rails,rake,Ruby On Rails,Rake,使用Rails 2.3.10 如果我的库/任务如下所示 lib/tasks - a.rake - 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 a、 rake看起来像这样: namesp

使用Rails 2.3.10 如果我的库/任务如下所示

lib/tasks
- a.rake
- 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
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看起来像这样

lib/tasks
- a.rake
- 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
输出是 “从任务B发送的消息:我是任务A”

因此,不会调用a.rake中定义的msg()helper方法。而是调用b.rake中定义的。(更重要的是,如果我有一个c.rake,那么当我运行任务a时,就会调用它的msg helper方法

此方法命名空间是否与已知行为冲突? 我本以为名称空间会阻止这种情况发生


感谢使用如下名称空间:

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

rake名称空间仅用于rake任务。 请参阅Rake文档:
NameSpace类将在NameSpace命令定义的范围内查找任务名称。

您可以创建一个模块以及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

您观察到的是,rake文件命名空间中的方法重新定义了以前定义的同名方法。原因是rake
命名空间
与Ruby命名空间(类或模块)非常不同事实上,它们只充当其中定义的任务名称的名称空间,而不是其他名称空间。因此,如果将任务
a
放置在
a
名称空间中,但任务之外的其他代码共享同一全局名称空间,则任务
a
将成为任务
a:a

这一事实,加上Rake在运行给定任务之前加载所有任务的事实,解释了该方法被重新定义的原因

TL;DR:名称冲突的解决方案/提示 您不能期望将两个具有相同名称(或任何其他代码)的方法放在单独的
名称空间中,但放在任务之外的方法能够正常工作。不过,以下是一些解决这种情况的提示:

  • 将方法放在任务中。如果在
    a:a
    b:b
    任务中定义了两个
    msg
    方法,则两个rake任务都将正常运行并显示预期的消息

  • < LI> < P>如果您需要使用RAKE的代码>命名空间< /代码>在多个RAKE任务中,<强>将方法/代码提取到一个真正的Ruby命名空间,如两个模块,和<代码>包含< /代码>需要的任务中的代码。考虑重写样本RAKES:

    # 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
名称空间
名称不必与Rake文件名相同,因此不能从任务/名称空间名称推断Rake文件名

证明rake的
名称空间
s不构成真正的类Ruby名称空间 加载rake文件后,将执行rake DSL语句以及
名称空间
方法。该方法获取其中定义的代码块并执行它(使用
yield
Rake.application
对象的上下文中,该对象是所有Rake任务共享的
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
类(或类似类)的一个独立对象,并在该对象的上下文中运行任务代码。对象的创建在任务管理器中的中完成:

def intern(task_class, task_name)
  @tasks[task_name.to_s] ||= task_class.new(task_name, self)
end
一段来自Rake作者的引文 最后,所有这一切都被一个非常相似和相关的问题所证实,我们可以引用《Rake》的原始作者Jim Weirich的话:

由于名称空间不引入真正的方法作用域,因此作用域的唯一可能是DSL模块

也许有一天,Rake名称空间将成为全类范围的实体,有一个地方可以挂起lazy-let定义,但我们还没有做到这一点


是的,这就是我一直在做的——虽然我的代码片段没有显示出来(我会更新它们)。rake名称空间没有任何效果。