Blo*_*ngs 13 rake ruby-on-rails
使用Rails 2.3.10如果我的lib/tasks看起来像这样
lib/tasks
- a.rake
- b.rake
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
然后,当我运行任务a
rake a RAILS_ENV=sandbox
Run Code Online (Sandbox Code Playgroud)
输出是"MSG SENT FROM Task B:I am TASK A"
因此,a.rake中定义的msg()辅助方法不会被调用.而是在b.rake中定义的那个被调用.(更重要的是,如果我有一个c.rake - 那么当我运行任务时,它会调用msg辅助方法.
此方法命名空间是否与已知行为发生冲突?我原本以为命名空间会阻止这个.
谢谢
Bor*_*aMa 16
您观察到的是rake文件命名空间中的方法重新定义了以前定义的具有相同名称的方法.原因是Rake namespace
与Ruby命名空间(类或模块)非常不同,实际上它们只是作为其中定义的任务名称的命名空间,而不是其他任何东西.因此,如果放在命名空间中,任务a
就变成了任务a:a
,a
但任务之外的其他代码共享同一个全局命名空间.
这个事实以及Rake在运行给定任务之前加载所有任务这一事实解释了该方法重新定义的原因.
您不能指望两个具有相同名称(或任何其他代码)的方法放在单独的namespace
s但外部任务中才能正常工作.不过,这里有几个提示可以解决这种情况:
将方法放在任务中.如果两个msg
方法都在a:a
和b:b
任务中定义,则两个rake任务都将正常运行并显示预期的消息.
如果您需要namespace
在多个rake任务中使用rake的代码,请将方法/代码提取到真正的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
Run Code Online (Sandbox Code Playgroud)
因为这两个模块具有不同的名称,并且因为它们分别include
位于相应的任务中,所以两个rake任务将再次按预期运行.
现在,让我们在源代码的帮助下证明上述声明......
这个很容易.在主要内容中,Rakefile
您总能找到以下行:
Rails.application.load_tasks
Run Code Online (Sandbox Code Playgroud)
此方法最终从Rails引擎调用以下代码:
def run_tasks_blocks(*) #:nodoc:
super
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
Run Code Online (Sandbox Code Playgroud)
因此,它在lib/tasks
目录中搜索所有rake文件,并按排序顺序一个接一个地加载它们.这就是为什么b.rake
文件将被加载后a.rake
,内部的任何内容都可能重新定义代码a.rake
和所有以前加载的rake文件.
Rake必须加载所有rake文件,因为rake namespace
名称不必与rake文件名相同,因此无法从任务/命名空间名称推断rake文件名.
namespace
不构成真正的类Ruby命名空间加载rake文件后,执行Rake DSL语句,以及namespace
方法.该方法获取在其中定义的代码块并在对象的上下文中执行它(使用yield
),该Rake.application
对象Rake::Application
是在所有rake任务之间共享的类的单个对象.没有为命名空间创建动态模块/类,它只是在主对象上下文中执行.
# 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
Run Code Online (Sandbox Code Playgroud)
有了Rake任务本身,情况就不同了.对于每个任务,创建Rake::Task
类(或类似类)的单独对象,并在该对象的上下文中运行任务代码.对象的创建在任务管理器中的intern
方法中完成:
def intern(task_class, task_name)
@tasks[task_name.to_s] ||= task_class.new(task_name, self)
end
Run Code Online (Sandbox Code Playgroud)
最后,所有这些都得到了关于github的有趣讨论的证实,该讨论涉及一个非常相似和相关的问题,我们可以从中引用Rake的原作者Jim Weirich:
由于名称空间不引入实际方法范围,因此范围的唯一真正可能性是DSL模块.
...
也许,有一天,Rake命名空间将成为完整的类范围实体,并且可以挂起懒惰的定义,但我们还没有.
使用如下所示的命名空间:
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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2005 次 |
最近记录: |