Cómo pasar argumentos de línea de comando a una tarea de rastrillo

1096

Tengo una tarea de rastrillo que necesita insertar un valor en múltiples bases de datos.

Me gustaría pasar este valor a la tarea de rastrillo desde la línea de comando, o desde otra tarea de rastrillo.

¿Cómo puedo hacer esto?

Tilendor
fuente
44
rakefile rdoc
Brian Maltzan
3
Los documentos han sido reflejados por SeattleRb.
Jonathan Allard
1
¿Puede cambiar la respuesta aceptada a la de @BlairAnderson, ya que la respuesta aceptada está muy desactualizada ahora? ¡Esta pregunta aparece en Google para este tema!
rmcsharry

Respuestas:

377

Las opciones y dependencias deben estar dentro de las matrices:

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

Entonces

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: la variable taskes el objeto de la tarea, no es muy útil a menos que conozca / se preocupe por las partes internas de Rake.

NOTA DE RAILS:

Si ejecuta la tarea desde rieles, es mejor precargar el entorno agregando => [:environment]cuál es una forma de configurar tareas dependientes .

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end
Blair Anderson
fuente
28
Además, asegúrese de no usar espacios entre los argumentos. Por ejemplo, no hagas esto: rake thing:work[1, 2, 3]ya que no funcionará y obtendrás un errorDon't know how to build task
rpbaltazar
99
Además, asegúrese de encerrar el argumento en una cadena. por ejemplo, desde su línea de comando ejecute la tarea de rastrillo así rake thing:work'[1,2,3]'
theterminalguy
36
Desafortunadamente, zsh no puede analizar la llamada correctamente, debe escribir el comando en zsh de esta manera: rake thing:work\[1,2,3\]o estorake 'thing:work[1,2,3]'
hutusi
1
@sakurashinken puede eliminar el :environmentsímbolo de su tarea. aplicaciones de rieles tienen una: tarea de entorno ...
Blair Anderson
3
En lugar de tener una nota para explicar que los tmedios task, ¿por qué no usar simplemente taskcomo el param name?
Joshua Pinter
1132

Puede especificar argumentos formales en rake agregando argumentos de símbolo a la llamada de tarea. Por ejemplo:

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

Luego, desde la línea de comando:

> rastrillar mi_tarea [1, falso]
Los argumentos fueron: {: arg1 => "1",: arg2 => "false"} de la clase Rake :: TaskArguments
arg1 era: '1' de la clase String
arg2 era: 'falso' de la clase String

> rastrillo "my_task [1, 2]"
Los argumentos fueron: {: arg1 => "1",: arg2 => "2"}

> rastrillo invoke_my_task
Los argumentos fueron: {: arg1 => "1",: arg2 => "2"}

> rastrillo invoke_my_task_2
Los argumentos fueron: {: arg1 => 3,: arg2 => 4}

> rastrillo con requisito previo [5,6]
Los argumentos fueron: {: arg1 => "5",: arg2 => "6"}

> rake with_defaults
Los argumentos con valores predeterminados fueron: {: arg1 =>: default_1,: arg2 =>: default_2}

> rake with_defaults ['x', 'y']
Los argumentos con valores predeterminados fueron: {: arg1 => "x",: arg2 => "y"}

Como se demostró en el segundo ejemplo, si desea usar espacios, las comillas alrededor del nombre de destino son necesarias para evitar que el shell divida los argumentos en el espacio.

Al observar el código en rake.rb , parece que rake no analiza las cadenas de tareas para extraer argumentos de requisitos previos, por lo que no puede hacerlo task :t1 => "dep[1,2]". La única forma de especificar diferentes argumentos para un requisito previo sería invocarlo explícitamente dentro de la acción de la tarea dependiente, como en :invoke_my_tasky :invoke_my_task_2.

Tenga en cuenta que algunos shells (como zsh) requieren que escape de los corchetes: rake my_task\['arg1'\]

Nick Desjardins
fuente
55
Para invocar una tarea dentro de un espacio de nombres simplemente haga: Rake :: Task ['namespace: task'].
Invoke
1
Esa es una pregunta separada, Igoru, pero la razón por la que su llamado a invocar solo se ejecuta una vez es que el rastrillo está orientado a la dependencia, por lo que solo ejecutará una tarea si es necesario. Para tareas genéricas, eso significa si aún no se ha ejecutado. Para ejecutar explícitamente una tarea independientemente de sus dependencias o si es necesario, llame a execute en lugar de invocar.
Nick Desjardins
10
Nota: Según rake, esta sintaxis para aceptar variables en tareas está en desuso:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32
57
Tenga en cuenta que zsh no puede analizar los argumentos de línea de comandos correctamente ( zsh: no matches found: ...), por lo que necesita para escapar de los soportes: rake my_task\['arg1'\]. De robots.thoughtbot.com/post/18129303042/…
Seth Bro el
2
@SethBro SÍ. Si solo tu comentario no hubiera estado oculto detrás del enlace "Ver más comentarios", no habría perdido 10 minutos sin poder hacer que esto funcione.
GMA
342

Además de responder por kch (no encontré cómo dejar un comentario a eso, lo siento):

No tiene que especificar variables como ENVvariables antes del rakecomando. Simplemente puede configurarlos como parámetros de línea de comandos habituales como ese:

rake mytask var=foo

y acceda a los de su archivo rake como variables ENV como tales:

p ENV['var'] # => "foo"
timurb
fuente
2
Esta es la mejor respuesta más simple de la OMI. Funcionó de inmediato. ¿Qué significa exactamente el p?
stevec
1
@ user5783745 Me gusta pone pero en lugar de registrar value.to_s a la salida estándar, llama a Obj.inspect y lo registra a la salida estándar. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef
¿Y anular las variables de entorno? ¡Fantástico!
Damien Roche el
El rastrillo es un desastre completamente diseñado y esta es la única forma en que funcionó. Y no soy solo yo, esta respuesta tiene la misma cantidad de votos que la respuesta "correcta".
lzap
108

Si desea pasar argumentos con nombre (por ejemplo, con estándar OptionParser) puede usar algo como esto:

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

tenga en cuenta --que es necesario para pasar por alto los argumentos estándar de Rake. En caso de trabajar con el rastrillo 0.9.x , <= 10.3.x .

Newer Rake ha cambiado su análisis --, y ahora debe asegurarse de que no se haya pasado al OptionParser#parsemétodo, por ejemplo conparser.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 al final se asegurará de que los argumentos adicionales no se interpreten como tarea Rake.

También el atajo para argumentos debería funcionar:

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

Cuando los scripts de rastrillo se ven así, tal vez sea hora de buscar otra herramienta que permita esto de manera inmediata.

Tombart
fuente
13
Desde mi punto de vista, esta es realmente la mejor respuesta. Omitir kludges variables de entorno, sintaxis extraña con argumentos de tareas, el beneficio adicional para el estándar --option-names. Mi única sugerencia sería usar en exitlugar de abortcomo abortlo dejará con un código de retorno de 1 al shell. Si la tarea de rake es parte de un script de nivel superior, es más común asumir que una salida distinta de cero es algún tipo de error.
Joe
1
Estoy de acuerdo con Joe, esta es la mejor respuesta. Lo natural es usar la misma interfaz para pasar opciones al rastrillo como lo haría al pasar opciones a un script.
Rik Smith-Unna
1
Estoy de acuerdo en que esta es la mejor respuesta. ¿No hay una manera de evitar lo feo --? ¿Como pasar rakeargumentos a la tarea real o algo así? ¿Me gusta task :my_task, :*args do |t, args|o algo?
Augustin Riedinger
1
Además, no entiendo para qué {username}está aquí. Donde se usa ¿Por qué no está allí -u {username}? Saludos
Augustin Riedinger
2
La forma en que Rake analiza ARGV cambió 10.4.1y se revirtió 10.4.2. github.com/ruby/rake/commit/…
Tombart
58

He encontrado la respuesta de estos dos sitios web: Net Maniac y Aimred .

Necesitas tener una versión> 0.8 de rastrillo para usar esta técnica

La descripción normal de la tarea de rastrillo es la siguiente:

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

Para pasar argumentos, haga tres cosas:

  1. Agregue los nombres de los argumentos después del nombre de la tarea, separados por comas.
  2. Ponga las dependencias al final usando: needs => [...]
  3. Lugar | t, args | después del do. (t es el objeto para esta tarea)

Para acceder a los argumentos en el 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 llamar a esta tarea desde la línea de comando, pásele los argumentos en [] s

rake task_name['Hello',4]

saldrá

Hello
Hello
Hello
Hello

y si desea llamar a esta tarea desde otra tarea y pasarle argumentos, use invoke

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

entonces el comando

rake caller

saldrá

In Caller
hi
hi

No he encontrado una manera de pasar argumentos como parte de una dependencia, ya que el siguiente código se rompe:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end
Tilendor
fuente
15
El formato de esta funcionalidad ha cambiado cuando esta advertencia indica: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh
29

Otra opción comúnmente utilizada es pasar variables de entorno. En su código, los lee ENV['VAR']y puede pasarlos justo antes del rakecomando, como

$ VAR=foo rake mytask
kch
fuente
Francamente, esperaba una tarea de rastrillo - estos --go - a - un programa y mi tarea podría obtenerlos de ARGV. Lamentablemente, no estoy seguro de si eso es posible, sin embargo, actualmente estoy usando su solución: rake var1 = val1 var2 = val2
JasonSmith
3
@jhs: rake blah -- --these --go --to --a-program(observe el --rastrillo para decirle que sus interruptores han terminado), consulte stackoverflow.com/questions/5086224/…
mu es demasiado corto
28

En realidad, @Nick Desjardins respondió perfecto. Pero solo por educación: puedes usar un enfoque sucio: usar ENVargumentos

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

rake my_task myvar=10
#=> myvar: 10
fl00r
fuente
28

No pude entender cómo pasar args y también el entorno: hasta que resolví esto:

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

Y luego llamo así:

rake db:export['foo, /tmp/']
Nate Flink
fuente
Gracias por esto, excelente solución mientras se mantiene el: medio ambiente
Olivier JM hace
24
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end
Feng
fuente
Para llamar a esto, vaya:rake task_name[hello, world]
Dex
2
de rake.rubyforge.org/files/doc/rakefile_rdoc.html "Solo unas pocas palabras de precaución. El nombre de la tarea de rastrillo y sus argumentos deben ser un argumento de línea de comando único para rastrillar. Esto generalmente significa que no hay espacios. Si se necesitan espacios , entonces se debe citar toda la cadena de argumento + rastrillo. Algo así: rastrillo "nombre [billy bob, smith]" "
Gayle
19

Solo quería poder ejecutar:

$ rake some:task arg1 arg2

Simple, ¿verdad? (¡No!)

Rake interpreta arg1y arg2como tareas, e intenta ejecutarlas. Entonces abortamos antes de que lo haga.

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

    # your task...

    exit
  end
end

¡Toma eso, paréntesis!

Descargo de responsabilidad : quería poder hacer esto en un proyecto de mascotas bastante pequeño. No está destinado al uso en el "mundo real" ya que pierde la capacidad de encadenar tareas de rastrillo (es decir rake task1 task2 task3). OMI no vale la pena. Solo usa lo feo rake task[arg1,arg2].

jassa
fuente
3
Necesitaba hacer esto _, arg1, arg2 = ARGVya que el primer argumento era el nombre de la tarea de rastrillo. Pero ese exites un buen truco.
graso
rake task[arg1,arg2] && rake task2 && rake task3No estoy seguro si eso es menos feo que rake task[arg1,arg2] task2 task3. Probablemente menos eficiente sin embargo.
Nuclearman
_, *args = ARGV¡Es perfecto para capturar todos los argumentos posteriores! ¡Montones de gracias!
XtraSimplicity
12

Utilizo un argumento ruby ​​regular en el archivo rastrillo:

DB = ARGV[1]

luego apago las tareas de rastrillo en la parte inferior del archivo (ya que el rastrillo buscará una tarea basada en ese nombre de argumento).

task :database_name1
task :database_name2

línea de comando:

rake mytask db_name

esto me parece más limpio que el var = foo ENV var y la tarea argumenta [bla, blah2] soluciones.
el trozo es un poco jenky, pero no está mal si solo tienes algunos entornos que son una configuración única

djburdick
fuente
2
Para evitar problemas de cadenas congeladas, use dupal final: db = ARGV [1] .dup
Juanda
Evento mejor db = ARGV[1].dup unless ARGV[1].nil?para evitar la excepción de duplicar un cero.
Andre Figueiredo
5

Las formas de pasar el argumento son correctas en la respuesta anterior. Sin embargo, para ejecutar la tarea de rake con argumentos, hay un pequeño tecnicismo involucrado en la versión más nueva de rails

Funcionará con rake "espacio de nombres: nombre de tarea ['argumento1']"

Tenga en cuenta las comillas invertidas al ejecutar la tarea desde la línea de comandos.

Asim Mushtaq
fuente
3

Para pasar argumentos a la tarea predeterminada, puede hacer algo como esto. Por ejemplo, decir "versión" es su argumento:

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

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

Entonces puedes llamarlo así:

$ rake
no version passed

o

$ rake default[3.2.1]
version is 3.2.1

o

$ rake build[3.2.1]
version is 3.2.1

Sin embargo, no he encontrado una manera de evitar especificar el nombre de la tarea (predeterminado o compilación) al pasar argumentos. Me encantaría saber si alguien sabe de alguna manera.

Galón
fuente
3

Me gusta la sintaxis de "cadena de consulta" para pasar argumentos, especialmente cuando hay muchos argumentos para pasar.

Ejemplo:

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

La "cadena de consulta" es:

width=10&height=20

Advertencia: tenga en cuenta que la sintaxis es rake "mytask[foo=bar]"y NO rake mytask["foo=bar"]

Cuando se analiza dentro de la tarea de rastrillo usando Rack::Utils.parse_nested_query, obtenemos un Hash:

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

(Lo bueno es que puedes pasar hash y matrices, más abajo)

Así es como lograr esto:

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

Aquí hay un ejemplo más extenso que estoy usando con Rails en mi gema retrayed_job_active_record_threaded :

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

Analizado de la misma manera que arriba, con una dependencia del entorno (para cargar el entorno 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

Da lo siguiente en options

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

Si no puede molestarse en recordar qué posición de argumento es para qué y desea hacer algo como un hash de argumento de rubí. Puede usar un argumento para pasar una cadena y luego volver a expresar esa cadena en un hash de opciones.

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

Y en la línea de comando que obtienes.

$ 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
xander-miller
fuente
1
Su código necesita algunas líneas vacías bien colocadas. No sé cómo lees ese muro de texto.
Joshua Pinter
2

La mayoría de los métodos descritos anteriormente no funcionaron para mí, tal vez están en desuso en las versiones más recientes. La guía actualizada se puede encontrar aquí: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

Copie y pegue la respuesta de la guía aquí:

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

Invocarlo así

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

Para ejecutar tareas de rastrillo con estilo de argumentos tradicionales:

rake task arg1 arg2

Y luego usa:

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

Agregue el siguiente parche de gema de rastrillo:

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
Daniel Garmoshka
fuente
-5

Al pasar los parámetros, es mejor que la opción sea un archivo de entrada, puede ser un excel a json o lo que necesite y, a partir de ahí, lea la estructura de datos y las variables que necesita, incluido el nombre de la variable según sea necesario. Para leer un archivo puede tener la siguiente estructura.

  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

Ejemplo json

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

Ejecución

rake :name_task 
tundervirld
fuente
44
Si necesita un archivo de instrucciones JSON para su tarea Rake, probablemente esté haciendo demasiadas cosas en su tarea Rake.
ZiggyTheHamster
Esto es así complicar algo que es increíblemente simple.
jeffdill2