1168

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

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

我怎样才能做到这一点?

4

20 回答 20

1159

您可以通过在任务调用中添加符号参数来指定 rake 中的正式参数。例如:

require 'rake'

task :my_task, [:arg1, :arg2] do |t, args|
  puts "Args were: #{args} of class #{args.class}"
  puts "arg1 was: '#{args[:arg1]}' of class #{args[:arg1].class}"
  puts "arg2 was: '#{args[:arg2]}' of class #{args[:arg2].class}"
end

task :invoke_my_task do
  Rake.application.invoke_task("my_task[1, 2]")
end

# or if you prefer this syntax...
task :invoke_my_task_2 do
  Rake::Task[:my_task].invoke(3, 4)
end

# a task with prerequisites passes its 
# arguments to it prerequisites
task :with_prerequisite, [:arg1, :arg2] => :my_task #<- name of prerequisite task

# to specify default values, 
# we take advantage of args being a Rake::TaskArguments object
task :with_defaults, :arg1, :arg2 do |t, args|
  args.with_defaults(:arg1 => :default_1, :arg2 => :default_2)
  puts "Args with defaults were: #{args}"
end

然后,从命令行:

> 耙 my_task[1,false]
Args 是:Rake::TaskArguments 类的 {:arg1=>"1", :arg2=>"false"}
arg1 是:String 类的“1”
arg2 是:String 类的“假”

> 耙 "my_task[1, 2]"
参数为:{:arg1=>"1", :arg2=>"2"}

> 耙invoke_my_task
参数为:{:arg1=>"1", :arg2=>"2"}

> 耙invoke_my_task_2
参数为:{:arg1=>3, :arg2=>4}

> 用_prerequisite[5,6] 耙
参数为:{:arg1=>"5", :arg2=>"6"}

> 用_defaults 耙
默认参数为:{:arg1=>:default_1, :arg2=>:default_2}

> 用_defaults['x','y'] 耙
默认参数为:{:arg1=>"x", :arg2=>"y"}

如第二个示例所示,如果要使用空格,则需要在目标名称周围加上引号,以防止 shell 拆分空格处的参数。

查看rake.rb中的代码,rake 似乎没有解析任务字符串来提取先决条件的参数,所以你不能这样做task :t1 => "dep[1,2]"。为先决条件指定不同参数的唯一方法是在依赖任务操作中显式调用它,如:invoke_my_task:invoke_my_task_2

请注意,某些 shell(如 zsh)要求您转义括号:rake my_task\['arg1'\]

于 2009-05-05T16:45:25.480 回答
438

选项和依赖项需要在数组中:

namespace :thing do
  desc "it does a thing"
  task :work, [:option, :foo, :bar] do |task, args|
    puts "work", args
  end
  
  task :another, [:option, :foo, :bar] do |task, args|
    puts "another #{args}"
    Rake::Task["thing:work"].invoke(args[:option], args[:foo], args[:bar])
    # or splat the args
    # Rake::Task["thing:work"].invoke(*args)
  end

end

然后

rake thing:work[1,2,3]
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

rake thing:another[1,2,3]
=> another {:option=>"1", :foo=>"2", :bar=>"3"}
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

注意:变量task是任务对象,除非您了解/关心 Rake 内部,否则它不是很有帮助。

导轨注意:

如果从 Rails 运行任务,最好通过添加=> [:environment]which 来预加载环境,这是一种设置相关任务的方法。

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end
于 2015-04-07T21:56:36.850 回答
367

除了 kch 的回答(我没有找到如何对此发表评论,抱歉):

您不必在命令ENV之前将变量指定为变量rake。您可以将它们设置为通常的命令行参数,如下所示:

rake mytask var=foo

并从您的 rake 文件中访问这些作为 ENV 变量,如下所示:

p ENV['var'] # => "foo"
于 2011-02-19T11:05:41.060 回答
117

如果你想传递命名参数(例如使用标准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 脚本看起来像这样时,也许是时候寻找另一个工具来允许它开箱即用了。

于 2013-10-19T13:09:57.043 回答
58

我从这两个网站找到了答案:Net ManiacAimred

您需要 rake 版本 > 0.8 才能使用此技术

正常的 rake 任务描述是这样的:

desc 'Task Description'
task :task_name => [:depends_on_taskA, :depends_on_taskB] do
  #interesting things
end

要传递参数,请做三件事:

  1. 在任务名称后添加参数名称,以逗号分隔。
  2. 使用 :needs => [...] 将依赖项放在最后
  3. 位置 |t, args| 做完之后。(t 是这个任务的对象)

要访问脚本中的参数,请使用 args.arg_name

desc 'Takes arguments task'
task :task_name, :display_value, :display_times, :needs => [:depends_on_taskA, :depends_on_taskB] do |t, args|
  args.display_times.to_i.times do
    puts args.display_value
  end
end

要从命令行调用此任务,请将 []s 中的参数传递给它

rake task_name['Hello',4]

将输出

Hello
Hello
Hello
Hello

如果你想从另一个任务调用这个任务,并传递参数,使用调用

task :caller do
  puts 'In Caller'
  Rake::Task[:task_name].invoke('hi',2)
end

然后命令

rake caller

将输出

In Caller
hi
hi

我还没有找到将参数作为依赖项的一部分传递的方法,因为以下代码会中断:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end
于 2009-05-05T16:51:45.543 回答
33

在我解决这个问题之前,我无法弄清楚如何传递 args 和 :environment:

namespace :db do
  desc 'Export product data'
  task :export, [:file_token, :file_path] => :environment do |t, args|
    args.with_defaults(:file_token => "products", :file_path => "./lib/data/")

       #do stuff [...]

  end
end

然后我这样打电话:

rake db:export['foo, /tmp/']
于 2014-11-29T19:38:18.303 回答
32

实际上@Nick Desjardins 回答得很完美。但仅用于教育:您可以使用肮脏的方法:使用ENV参数

task :my_task do
  myvar = ENV['myvar']
  puts "myvar: #{myvar}"
end 

rake my_task myvar=10
#=> myvar: 10
于 2011-02-28T20:06:33.597 回答
29

另一个常用的选项是传递环境变量。在您的代码中,您可以通过 阅读它们,并且可以在命令ENV['VAR']之前传递它们,例如rake

$ VAR=foo rake mytask
于 2009-05-05T16:53:13.107 回答
24
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end
于 2012-10-16T07:36:27.603 回答
22

我只是想能够运行:

$ rake some:task arg1 arg2

很简单,对吧?(没有!)

Rake 将arg1and解释arg2为任务,并尝试运行它们。所以我们只是在它发生之前中止。

namespace :some do
  task task: :environment do
    arg1, arg2 = ARGV

    # your task...

    exit
  end
end

拿那个,括号!

免责声明:我希望能够在一个非常小的宠物项目中做到这一点。不适合“现实世界”使用,因为您失去了链接 rake 任务的能力(即rake task1 task2 task3)。海事组织不值得。只用丑陋的rake task[arg1,arg2]

于 2016-04-29T03:29:33.750 回答
13

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

DB = ARGV[1]

然后我将文件底部的 rake 任务存根(因为 rake 将根据该参数名称查找任务)。

task :database_name1
task :database_name2

命令行:

rake mytask db_name

这对我来说比 var=foo ENV var 和任务 args[blah, blah2] 解决方案更干净。
存根有点笨拙,但如果您只有几个一次性设置的环境,那还不错

于 2012-12-29T02:48:52.867 回答
5

上述答案中传递参数的方法是正确的。但是,要使用参数运行 rake 任务,新版本的 rails 涉及到一个小的技术性

它将与 rake "namespace:taskname['argument1']" 一起使用

请注意从命令行运行任务时的倒引号。

于 2014-04-24T14:23:15.857 回答
3

要将参数传递给默认任务,您可以执行以下操作。例如,说“版本”是您的论点:

task :default, [:version] => [:build]

task :build, :version do |t,args|
  version = args[:version]
  puts version ? "version is #{version}" : "no version passed"
end

然后你可以这样称呼它:

$ rake
no version passed

或者

$ rake default[3.2.1]
version is 3.2.1

或者

$ rake build[3.2.1]
version is 3.2.1

但是,我还没有找到一种方法来避免在传递参数时指定任务名称(默认或构建)。很想听听是否有人知道方法。

于 2013-10-20T19:03:37.573 回答
3

我喜欢参数传递的“查询字符串”语法,特别是当有很多参数要传递时。

例子:

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

“查询字符串”是:

width=10&height=20

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

在 rake 任务中使用 解析时Rack::Utils.parse_nested_query,我们得到Hash

=> {"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

这是我在我的延迟作业活动记录线程gem中与Rails一起使用的一个更扩展的示例

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

给出以下内容options

=> {"ebooks"=>{"workers_number"=>"16", "worker_timeout"=>"60"}, "albums"=>{"workers_number"=>"32", "worker_timeout"=>"120"}}
于 2014-02-16T22:05:07.223 回答
3

上面描述的大多数方法对我都不起作用,也许它们在较新的版本中已被弃用。最新指南可以在这里找到:http: //guides.rubyonrails.org/command_line.html#custom-rake-tasks

指南中的复制和粘贴答案在这里:

task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
  # You can use args from here
end

像这样调用它

bin/rake "task_name[value 1]" # entire argument string should be quoted
于 2015-02-03T01:15:17.420 回答
3

如果您不想记住参数位置是什么,并且您想做一些类似 ruby​​ 参数散列的事情。您可以使用一个参数传入一个字符串,然后将该字符串正则表达式转换为选项哈希。

namespace :dummy_data do
  desc "Tests options hash like arguments"
  task :test, [:options] => :environment do |t, args|
    arg_options = args[:options] || '' # nil catch incase no options are provided
    two_d_array = arg_options.scan(/\W*(\w*): (\w*)\W*/)
    puts two_d_array.to_s + ' # options are regexed into a 2d array'
    string_key_hash = two_d_array.to_h
    puts string_key_hash.to_s + ' # options are in a hash with keys as strings'
    options = two_d_array.map {|p| [p[0].to_sym, p[1]]}.to_h
    puts options.to_s + ' # options are in a hash with symbols'
    default_options = {users: '50', friends: '25', colour: 'red', name: 'tom'}
    options = default_options.merge(options)
    puts options.to_s + ' # default option values are merged into options'
  end
end

在命令行上你得到。

$ rake dummy_data:test["users: 100 friends: 50 colour: red"]
[["users", "100"], ["friends", "50"], ["colour", "red"]] # options are regexed into a 2d array
{"users"=>"100", "friends"=>"50", "colour"=>"red"} # options are in a hash with keys as strings
{:users=>"100", :friends=>"50", :colour=>"red"} # options are in a hash with symbols
{:users=>"100", :friends=>"50", :colour=>"red", :name=>"tom"} # default option values are merged into options
于 2017-05-12T01:05:57.930 回答
2

要使用传统的参数样式运行 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
于 2019-02-13T06:40:14.250 回答
2

我在这里看不到的一件事是如何处理任意参数。如果您传递任务定义中未列出的参数,它们仍然可以在以下位置访问args.extras

task :thing, [:foo] do |task, args|
  puts args[:foo]     # named argument
  puts args.extras    # any additional arguments that were passed
end
于 2020-11-07T21:37:46.330 回答
1
namespace :namespace1 do
  task :task1, [:arg1, :arg2, :arg3] => :environment do |_t, args|
    p args[:arg1]
  end
end

打电话

rake namespace1:task1["1","2","3"]

调用时无需提供环境

在 zsh 中需要将调用括在引号中

'rake namespace1:task1["1","2","3"]'

谢谢

于 2022-02-14T11:52:18.133 回答
-5

在传递参数时,更好的选择是输入文件,可以是 excel 或 json 或任何您需要的文件,并从那里读取您需要的数据结构和变量,包括需要的变量名。读取一个文件可以有如下结构。

  namespace :name_sapace_task do
    desc "Description task...."
      task :name_task  => :environment do
        data =  ActiveSupport::JSON.decode(File.read(Rails.root+"public/file.json")) if defined?(data)
    # and work whit yoour data, example is data["user_id"]

    end
  end

示例 json

{
  "name_task": "I'm a task",
  "user_id": 389,
  "users_assigned": [389,672,524],
  "task_id": 3
}

执行

rake :name_task 
于 2014-02-27T13:52:06.427 回答