我有一个rake任务,需要将一个值插入多个数据库。

我想从命令行或从另一个rake任务中将这个值传递给rake任务。

我该怎么做呢?


当前回答

使用传统的参数样式运行rake任务:

rake task arg1 arg2

然后使用:

task :task do |_, args|
  puts "This is argument 1: #{args.first}"
end

添加以下rake gem补丁:

Rake::Application.class_eval do

  alias origin_top_level top_level

  def top_level
    @top_level_tasks = [top_level_tasks.join(' ')]
    origin_top_level
  end

  def parse_task_string(string) # :nodoc:
    parts = string.split ' '
    return parts.shift, parts
  end

end

Rake::Task.class_eval do

  def invoke(*args)
    invoke_with_call_chain(args, Rake::InvocationChain::EMPTY)
  end

end

其他回答

如果你想传递命名参数(例如使用标准OptionParser),你可以使用这样的东西:

$ rake user:create -- --user test@example.com --pass 123

注意——,这是绕过标准Rake参数所必需的。应该适用于Rake 0.9。X, <= 10.3.x。

更新的Rake已经改变了对——的解析,现在你必须确保它没有传递给OptionParser#parse方法,例如使用parser.parse!(ARGV[2..-1])

require 'rake'
require 'optparse'
# Rake task for creating an account

namespace :user do |args|
  desc 'Creates user account with given credentials: rake user:create'
  # environment is required to have access to Rails models
  task :create do
    options = {}
    OptionParser.new(args) do |opts|
      opts.banner = "Usage: rake user:create [options]"
      opts.on("-u", "--user {username}","User's email address", String) do |user|
        options[:user] = user
      end
      opts.on("-p", "--pass {password}","User's password", String) do |pass|
        options[:pass] = pass
      end
    end.parse!

    puts "creating user account..."
    u = Hash.new
    u[:email] = options[:user]
    u[:password] = options[:pass]
    # with some DB layer like ActiveRecord:
    # user = User.new(u); user.save!
    puts "user: " + u.to_s
    puts "account created."
    exit 0
  end
end

exit将确保额外的参数不会被解释为Rake任务。

参数的快捷方式也可以工作:

 rake user:create -- -u test@example.com -p 123

当rake脚本看起来像这样时,也许是时候寻找另一种工具来实现这一点了。

我喜欢参数传递的“querystring”语法,特别是当有很多参数要传递时。

例子:

rake "mytask[width=10&height=20]"

“querystring”是:

width=10&height=20

警告:注意语法是rake“mytask[foo=bar]”而不是rake mytask["foo=bar"]

当在rake任务中使用Rack::Utils进行解析时。parse_nested_query,得到一个哈希值:

=> {"width"=>"10", "height"=>"20"}

(最酷的事情是你可以传递哈希和数组,更多如下)

以下是如何实现这一点:

require 'rack/utils'

task :mytask, :args_expr do |t,args|
  args.with_defaults(:args_expr => "width=10&height=10")
  options = Rack::Utils.parse_nested_query(args[:args_expr])
end

下面是我在delayed_job_active_record_threading gem中使用的一个更扩展的示例:

bundle exec rake "dj:start[ebooks[workers_number]=16&ebooks[worker_timeout]=60&albums[workers_number]=32&albums[worker_timeout]=120]"

以与上面相同的方式解析,使用环境依赖项(为了加载Rails环境)

namespace :dj do
  task :start, [ :args_expr ] => :environment do |t, args|
    # defaults here...
    options = Rack::Utils.parse_nested_query(args[:args_expr])  
  end
end

给出以下选项

=> {"ebooks"=>{"workers_number"=>"16", "worker_timeout"=>"60"}, "albums"=>{"workers_number"=>"32", "worker_timeout"=>"120"}}

我在rake文件中使用了一个常规的ruby参数:

DB = ARGV[1]

然后我在文件底部去掉rake任务(因为rake将根据参数名查找任务)。

task :database_name1
task :database_name2

命令行:

rake mytask db_name

这感觉比我的var=foo ENV var和任务参数[blah, blah2]解决方案更干净。 存根有点麻烦,但如果您只有几个一次性设置的环境,那也不算太糟

除了kch的回答(我不知道如何留下评论,对不起):

在rake命令之前,您不必将变量指定为ENV变量。你可以像设置命令行参数一样设置它们:

rake mytask var=foo

然后从rake文件中访问这些ENV变量,就像这样:

p ENV['var'] # => "foo"

使用传统的参数样式运行rake任务:

rake task arg1 arg2

然后使用:

task :task do |_, args|
  puts "This is argument 1: #{args.first}"
end

添加以下rake gem补丁:

Rake::Application.class_eval do

  alias origin_top_level top_level

  def top_level
    @top_level_tasks = [top_level_tasks.join(' ')]
    origin_top_level
  end

  def parse_task_string(string) # :nodoc:
    parts = string.split ' '
    return parts.shift, parts
  end

end

Rake::Task.class_eval do

  def invoke(*args)
    invoke_with_call_chain(args, Rake::InvocationChain::EMPTY)
  end

end