desenv-web-rp.com

Como passar argumentos de linha de comando para uma tarefa de rake

Eu tenho uma tarefa de rake que precisa inserir um valor em vários bancos de dados. 

Eu gostaria de passar este valor para a tarefa rake a partir da linha de comando, ou da tarefa another rake.

Como posso fazer isso?

998
Tilendor

opções e dependências precisam estar dentro de matrizes:

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

Então

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"}

NOTA: a variável task é o objeto da tarefa, não é muito útil a menos que você saiba/se preocupe com os internos do Rake. 

Trilhos NOTA:

Se estiver executando a tarefa do Rails, é melhor pré-carregar o ambiente adicionando => [:environment] que é uma maneira de configurar as tarefas dependent

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end
282
Blair Anderson

Você pode especificar argumentos formais no rake adicionando argumentos de símbolo à chamada de tarefa. Por exemplo:

require 'rake'

task :my_task, [:arg1, :arg2] do |t, args|
  puts "Args were: #{args}"
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

Então, na linha de comando:

> rake my_task [1,2] 
 Args foram: {: arg1 => "1",: arg2 => "2"} 
 
> ancinho "my_task [1, 2]" 
 Args eram: {: arg1 => "1",: arg2 => "2"} 
 
> rake invoke_my_task 
 Args eram: {: arg1 => "1",: arg2 => "2"} 
 
> Rake invoke_my_task_2 
 Os Args eram: {: arg1 => 3,: arg2 => 4} 
 
> rake with_prerequisite [5,6] 
 Args eram: {: arg1 => "5",: arg2 => "6"} 
 
> rake with_defaults 
 Args com padrões eram: {: arg1 =>: default_1,: arg2 =>: default_2} 
 
> rake com_defaults [ 'x', 'y'] 
 Args com padrões eram: {: arg1 => "x",: arg2 => "y"} 

Como demonstrado no segundo exemplo, se você deseja usar espaços, as aspas em torno do nome de destino são necessárias para impedir que o Shell divida os argumentos no espaço.

Olhando para o código em rake.rb , parece que o rake não analisa strings de tarefas para extrair argumentos para os pré-requisitos, então você não pode fazer task :t1 => "dep[1,2]". A única maneira de especificar argumentos diferentes para um pré-requisito seria invocá-lo explicitamente na ação de tarefa dependente, como em :invoke_my_task e :invoke_my_task_2.

Note que algumas shells (como zsh) requerem que você escape dos colchetes: rake my_task\['arg1'\]

1095
Nick Desjardins

Além de responder por kch (não achei como deixar um comentário para isso, desculpe):

Você não precisa especificar variáveis ​​como variáveis ​​ENV antes do comando rake. Você pode apenas configurá-los como parâmetros de linha de comando normais, como:

rake mytask var=foo

e acessar aqueles do seu arquivo rake como variáveis ​​ENV como:

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

Se você quiser passar argumentos nomeados (por exemplo, com o padrão OptionParser), você pode usar algo assim:

$ rake user:create -- --user [email protected] --pass 123

observe o --, que é necessário para ignorar os argumentos padrão do Rake. Deve funcionar com o Rake 0.9.x , <= 10.3.x .

O novo Rake mudou sua análise de --, e agora você precisa se certificar de que não é passado para o método OptionParser#parse, por exemplo, com 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 no final garantirá que os argumentos extras não sejam interpretados como tarefas Rake. 

Além disso, o atalho para argumentos deve funcionar:

 rake user:create -- -u [email protected] -p 123

Quando os scripts do rake se parecem com isso, talvez seja hora de procurar outra ferramenta que permita isso apenas fora da caixa.

102
Tombart

Eu encontrei a resposta destes dois sites: Net Maniac e Aimred .

Você precisa ter versão> 0.8 do rake para usar esta técnica

A descrição normal da tarefa de rake é esta:

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

Para passar argumentos, faça três coisas:

  1. Adicione os nomes dos argumentos após o nome da tarefa, separados por vírgulas.
  2. Coloque as dependências no final usando: needs => [...]
  3. Coloque | t, args | após o fazer. (t é o objeto para esta tarefa)

Para acessar os argumentos no script, use 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

Para chamar esta tarefa a partir da linha de comando, passe os argumentos em [] s

rake task_name['Hello',4]

vai sair

Hello
Hello
Hello
Hello

e se você quiser chamar essa tarefa de outra tarefa e passar argumentos, use invoke

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

então o comando

rake caller

vai sair

In Caller
hi
hi

Eu não encontrei uma maneira de passar argumentos como parte de uma dependência, como o seguinte código quebra:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end
57
Tilendor

Outra opção comumente usada é passar variáveis ​​de ambiente. No seu código você os lê via ENV['VAR'], e pode passá-los logo antes do comando rake, como

$ VAR=foo rake mytask
29
kch

Na verdade, @Nick Desjardins respondeu perfeito. Mas apenas para educação: você pode usar abordagem suja: usando o argumento ENV

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

rake my_task myvar=10
#=> myvar: 10
29
fl00r

Não consegui descobrir como passar args e também o: environment até que resolvi:

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

E então eu chamo assim:

rake db:export['foo, /tmp/']
25
Nate Flink
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end
22
Feng

Eu só queria poder correr:

$ rake some:task arg1 arg2

Simples, certo? (Não!)

O Rake interpreta arg1 e arg2 como tarefas e tenta executá-las. Então, nós apenas abortamos antes disso.

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

    # your task...

    exit
  end
end

Tome isso, suportes!

Disclaimer : Eu queria ser capaz de fazer isso em um projeto de estimação muito pequeno. Não destinado ao uso do "mundo real", já que você perde a capacidade de executar tarefas de rake (isto é, rake task1 task2 task3). IMO não vale a pena. Apenas use o rake task[arg1,arg2] feio.

16
jassa

Eu uso um argumento Ruby regular no arquivo rake:

DB = ARGV[1]

então retiro as tarefas do rake na parte inferior do arquivo (já que o rake irá procurar por uma tarefa baseada no nome desse argumento).

task :database_name1
task :database_name2

linha de comando:

rake mytask db_name

isso parece mais limpo para mim do que as soluções var = foo ENV var e a tarefa args [blah, blah2].
o esboço é um pouco maluco, mas não tão ruim se você tiver apenas alguns ambientes que são uma configuração única

12
djburdick

As maneiras de passar o argumento estão corretas na resposta acima. No entanto, para executar a tarefa de rake com argumentos, há um pequeno detalhe técnico envolvido na versão mais recente do Rails

Ele funcionará com rake "namespace: taskname ['argument1']"

Observe as aspas invertidas ao executar a tarefa a partir da linha de comando. 

5
Asim Mushtaq

Eu gosto da sintaxe "querystring" para passagem de argumentos, especialmente quando há muitos argumentos a serem passados. 

Exemplo:

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

A "querystring" é:

width=10&height=20

Atenção: note que a sintaxe é rake "mytask[foo=bar]" e NOTrake mytask["foo=bar"]

Quando analisado dentro da tarefa de rake usando Rack::Utils.parse_nested_query, obtemos uma Hash:

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

(O legal é que você pode passar hashes e matrizes, mais abaixo)

Isto é como conseguir isto:

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

Aqui está um exemplo mais extenso que estou usando com o Rails em meu delayed_job_active_record_threaded gem:

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

Analisado da mesma forma acima, com uma dependência de ambiente (para carregar o ambiente 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

Dá o seguinte em options

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

Para passar argumentos para a tarefa padrão, você pode fazer algo assim. Por exemplo, diga "Versão" é o seu argumento:

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

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

Então você pode chamar assim:

$ rake
no version passed

ou 

$ rake default[3.2.1]
version is 3.2.1

ou

$ rake build[3.2.1]
version is 3.2.1

No entanto, não encontrei uma maneira de evitar a especificação do nome da tarefa (padrão ou compilação) ao passar argumentos. Adoraria ouvir se alguém souber de uma maneira.

3
Gal

A maioria dos métodos descritos acima não funcionou para mim, talvez eles estejam obsoletos nas versões mais recentes. O guia atualizado pode ser encontrado aqui: http://guides.rubyonrails.org/ command_line.html # custom-rake-tasks

um ans de copiar e colar do guia está aqui:

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

Invoque-o assim

bin/rake "task_name[value 1]" # entire argument string should be quoted
2
hexinpeter

Se você não pode se incomodar em lembrar qual posição de argumento é para o que e você quer fazer algo como um hash de argumento Ruby. Você pode usar um argumento para passar uma string e, em seguida, regexar essa string em um hash de opções. 

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

E na linha de comando você pega.

$ 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
2
xander-miller

Para executar tarefas de rake com estilo de argumentos tradicionais:

rake task arg1 arg2

E então use:

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

Adicione o seguinte patch da jóia do rake:

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
0
Dan Key