Comment passer des arguments de ligne de commande à une tâche de râteau

1096

J'ai une tâche de râteau qui doit insérer une valeur dans plusieurs bases de données.

Je voudrais passer cette valeur dans la tâche de râteau à partir de la ligne de commande ou d' une autre tâche de râteau.

Comment puis-je faire ceci?

Tilendor
la source
4
rakefile rdoc
Brian Maltzan
3
Les documents ont été mis en miroir par SeattleRb.
Jonathan Allard
1
Pouvez-vous s'il vous plaît remplacer la réponse acceptée par celle de @BlairAnderson, car la réponse acceptée est très obsolète maintenant. Cette question apparaît haut sur Google pour ce sujet!
rmcsharry

Réponses:

377

les options et les dépendances doivent être à l'intérieur des tableaux:

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

alors

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

REMARQUE: la variable taskest l'objet de tâche, pas très utile à moins que vous ne connaissiez / vous souciez des internes de Rake.

NOTE SUR LES RAILS:

Si vous exécutez la tâche à partir de rails, il est préférable de précharger l'environnement en ajoutant => [:environment]un moyen de configurer les tâches dépendantes .

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end
Blair Anderson
la source
28
Assurez-vous également de ne pas utiliser d'espaces entre les arguments. Par exemple, ne faites pas cela: rake thing:work[1, 2, 3]car cela ne fonctionnera pas et vous obtiendrez une erreurDon't know how to build task
rpbaltazar
9
Assurez-vous également de placer l'argument dans une chaîne. Par exemple, à partir de votre ligne de commande, exécutez la tâche de râteau comme ceci rake thing:work'[1,2,3]'
theterminalguy
36
Malheureusement, zsh ne peut pas analyser l'appel correctement, vous devez taper la commande sur zsh comme ceci rake thing:work\[1,2,3\]rake 'thing:work[1,2,3]'
:,
1
@sakurashinken, vous pouvez supprimer le :environmentsymbole de votre tâche. les applications de rails ont une tâche d'environnement ...
Blair Anderson
3
Au lieu d'avoir une note pour expliquer ce que cela tsignifie task, pourquoi ne pas simplement utiliser taskcomme nom de paramètre?
Joshua Pinter
1132

Vous pouvez spécifier des arguments formels dans rake en ajoutant des arguments de symbole à l'appel de tâche. Par exemple:

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

Ensuite, à partir de la ligne de commande:

> ratisser ma_tâche [1, faux]
Les arguments étaient: {: arg1 => "1",: arg2 => "false"} de la classe Rake :: TaskArguments
arg1 était: '1' de la classe String
arg2 était: 'faux' de la classe String

> ratisser "ma_tâche [1, 2]"
Les arguments étaient: {: arg1 => "1",: arg2 => "2"}

> râteau invoke_my_task
Les arguments étaient: {: arg1 => "1",: arg2 => "2"}

> râteau invoke_my_task_2
Les arguments étaient: {: arg1 => 3,: arg2 => 4}

> ratisser avec_préalable [5,6]
Les arguments étaient: {: arg1 => "5",: arg2 => "6"}

> ratisser with_defaults
Les arguments avec des valeurs par défaut étaient: {: arg1 =>: default_1,: arg2 =>: default_2}

> ratisser avec_defaults ['x', 'y']
Les arguments avec des valeurs par défaut étaient: {: arg1 => "x",: arg2 => "y"}

Comme illustré dans le deuxième exemple, si vous souhaitez utiliser des espaces, les guillemets autour du nom cible sont nécessaires pour empêcher le shell de fractionner les arguments dans l'espace.

En regardant le code dans rake.rb , il semble que rake n'analyse pas les chaînes de tâches pour extraire les arguments des conditions préalables, vous ne pouvez donc pas le faire task :t1 => "dep[1,2]". La seule façon de spécifier différents arguments pour une condition préalable serait de l'invoquer explicitement dans l'action de tâche dépendante, comme dans :invoke_my_tasket :invoke_my_task_2.

Notez que certains shells (comme zsh) nécessitent que vous échappiez aux crochets: rake my_task\['arg1'\]

Nick Desjardins
la source
5
Pour appeler une tâche dans un espace de noms, faites simplement: Rake :: Task ['namespace: task'].
Invoke
1
C'est une question distincte, Igoru, mais la raison pour laquelle votre appel à invoquer ne s'exécute qu'une seule fois est que le râteau est orienté sur les dépendances, il n'exécutera donc une tâche que si elle est nécessaire. Pour les tâches génériques, cela signifie que s'il n'a pas déjà été exécuté. Pour exécuter explicitement une tâche indépendamment de ses dépendances ou si elle est nécessaire, appelez execute au lieu d'invoquer.
Nick Desjardins du
10
Remarque: Selon rake, cette syntaxe d'acceptation de variables dans les tâches est obsolète:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32
57
Notez que zsh ne parvient pas à analyser les arguments de ligne de commande correctement ( zsh: no matches found: ...), de sorte que vous devez échapper aux crochets: rake my_task\['arg1'\]. De robots.thoughtbot.com/post/18129303042/…
Seth Bro
2
@SethBro OUI. Si seulement votre commentaire n'avait pas été caché derrière le lien "Voir plus de commentaires", je n'aurais pas perdu 10 minutes pour ne pas réussir.
GMA
342

En plus de répondre par kch (je n'ai pas trouvé comment laisser un commentaire à ça, désolé):

Vous n'avez pas besoin de spécifier des variables en tant que ENVvariables avant la rakecommande. Vous pouvez simplement les définir comme des paramètres de ligne de commande habituels comme ça:

rake mytask var=foo

et accédez à celles de votre fichier rake en tant que variables ENV comme celles-ci:

p ENV['var'] # => "foo"
timurb
la source
2
C'est la meilleure réponse IMO la plus simple. Ça a marché tout de suite. Qu'est-ce que cela psignifie exactement ?
stevec
1
@ user5783745 Comme put, mais au lieu de consigner value.to_s en standard, il appelle Obj.inspect et le connecte en standard. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef
Et remplacer les variables d'environnement? Fantastique!
Damien Roche
Rake est un désordre complètement sur-conçu et c'est le seul moyen qui a fonctionné. Et ce n'est pas seulement moi, cette réponse a le même nombre de votes que la «bonne» réponse.
lzap
108

Si vous voulez passer des arguments nommés (par exemple avec standard OptionParser), vous pouvez utiliser quelque chose comme ceci:

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

notez le --, qui est nécessaire pour contourner les arguments Rake standard. Devrait fonctionner avec Rake 0.9.x , <= 10.3.x .

Newer Rake a changé son analyse de --, et maintenant vous devez vous assurer qu'il n'est pas passé à la OptionParser#parseméthode, par exemple avecparser.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 à la fin s'assurera que les arguments supplémentaires ne seront pas interprétés comme une tâche Rake.

Le raccourci pour les arguments devrait également fonctionner:

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

Lorsque les scripts de rake ressemblent à ceci, il est peut-être temps de chercher un autre outil qui permettrait cela tout de suite.

Tombart
la source
13
De mon point de vue, c'est vraiment la meilleure réponse. Contourner les variables d'environnement kludges, étrange syntaxe avec des arguments de tâche, l'avantage supplémentaire pour la norme --option-names. Ma seule suggestion serait d'utiliser exitplutôt que abortde abortvous laisser avec un code retour de 1 au shell. Si la tâche de râteau fait partie d'un script de niveau supérieur, il est plus courant de supposer qu'une sortie non nulle est un type d'erreur.
Joe
1
Je suis d'accord avec Joe, c'est la meilleure réponse. La chose naturelle est d'utiliser la même interface pour passer des options à ratisser comme vous le feriez lorsque vous passez des options à un script.
Rik Smith-Unna
1
Je suis d'accord que c'est la meilleure réponse. N'y a-t-il pas un moyen de contourner le laid --? Comme passer des rakearguments à la tâche réelle ou quelque chose? Comme task :my_task, :*args do |t, args|ou quelque chose?
Augustin Riedinger
1
D'ailleurs, je ne comprends pas à quoi {username}ça sert. Où est-il utilisé? Pourquoi n'est-il pas là -u {username}? Santé
Augustin Riedinger
2
La façon dont Rake analyse ARGV a été modifiée 10.4.1et rétablie 10.4.2. github.com/ruby/rake/commit/…
Tombart
58

J'ai trouvé la réponse sur ces deux sites: Net Maniac et Aimred .

Vous devez avoir la version> 0.8 de rake pour utiliser cette technique

La description de la tâche de râteau normale est la suivante:

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

Pour passer des arguments, faites trois choses:

  1. Ajoutez les noms d'arguments après le nom de la tâche, séparés par des virgules.
  2. Mettez les dépendances à la fin en utilisant: needs => [...]
  3. Lieu | t, args | après le faire. (t est l'objet de cette tâche)

Pour accéder aux arguments du script, utilisez 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

Pour appeler cette tâche à partir de la ligne de commande, passez-lui les arguments dans [] s

rake task_name['Hello',4]

affichera

Hello
Hello
Hello
Hello

et si vous voulez appeler cette tâche à partir d'une autre tâche et lui passer des arguments, utilisez invoke

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

puis la commande

rake caller

affichera

In Caller
hi
hi

Je n'ai pas trouvé de moyen de passer des arguments dans le cadre d'une dépendance, car le code suivant se casse:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end
Tilendor
la source
15
Le format de cette fonctionnalité a changé car cet avertissement indique: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh
29

Une autre option couramment utilisée consiste à transmettre des variables d'environnement. Dans votre code, vous les lisez via ENV['VAR']et pouvez les transmettre juste avant la rakecommande, comme

$ VAR=foo rake mytask
kch
la source
Franchement, j'espérais une tâche de râteau - ces - aller - à un programme et ma tâche pourrait les obtenir de l'ARGV. Malheureusement, je ne sais pas si c'est possible, mais j'utilise actuellement votre solution: rake var1 = val1 var2 = val2
JasonSmith
3
@jhs: rake blah -- --these --go --to --a-program(notez le --pour indiquer à rake que ses commutateurs sont terminés), voir stackoverflow.com/questions/5086224/…
mu est trop court le
28

En fait, @Nick Desjardins a répondu parfaitement. Mais juste pour l'éducation: vous pouvez utiliser une approche sale: utiliser l' ENVargument

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

rake my_task myvar=10
#=> myvar: 10
fl00r
la source
28

Je ne pouvais pas comprendre comment passer les arguments et aussi l'environnement: jusqu'à ce que je résolve ceci:

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

Et puis j'appelle comme ça:

rake db:export['foo, /tmp/']
Nate Flink
la source
Merci pour cela, excellente solution tout en maintenant l'environnement:
Olivier JM
24
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end
Feng
la source
Pour appeler cela, allez:rake task_name[hello, world]
Dex
2
from rake.rubyforge.org/files/doc/rakefile_rdoc.html "Juste quelques mots de prudence. Le nom de la tâche de râteau et ses arguments doivent être un seul argument de ligne de commande pour râteau. Cela signifie généralement pas d'espaces. Si des espaces sont nécessaires , alors toute la chaîne rake + argument doit être citée. Quelque chose comme ceci: rake "name [billy bob, smith]" "
Gayle
19

Je voulais juste pouvoir courir:

$ rake some:task arg1 arg2

C'est simple, non? (Nan!)

Rake interprète arg1et en arg2tant que tâches et essaie de les exécuter. Nous avortons donc juste avant.

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

    # your task...

    exit
  end
end

Prenez ça, crochets!

Avertissement : je voulais pouvoir le faire dans un joli petit projet pour animaux de compagnie. Non destiné à un usage «réel» car vous perdez la capacité de chaîner des tâches de râteau (c. rake task1 task2 task3-à-d.). L'OMI n'en vaut pas la peine. Utilisez simplement le laid rake task[arg1,arg2].

jassa
la source
3
Nécessaire pour faire cela _, arg1, arg2 = ARGVcar le premier argument était considéré comme le nom de la tâche de râteau. Mais c'est exitune astuce intéressante.
gras
rake task[arg1,arg2] && rake task2 && rake task3Je ne sais pas si c'est moins moche que rake task[arg1,arg2] task2 task3. Probablement moins efficace cependant.
Nuclearman
_, *args = ARGVest parfait pour capturer tous les arguments suivants! Merci des tas!
XtraSimplicity
12

J'utilise un argument ruby ​​régulier dans le fichier rake:

DB = ARGV[1]

puis je stub sur les tâches de râteau au bas du fichier (puisque râteau cherchera une tâche basée sur ce nom d'argument).

task :database_name1
task :database_name2

ligne de commande:

rake mytask db_name

cela me semble plus propre que le var = foo ENV var et la tâche args [blah, blah2] solutions.
le talon est un peu jenky, mais pas trop mal si vous n'avez que quelques environnements qui sont une configuration unique

djburdick
la source
2
Pour éviter les problèmes de chaînes figées, utilisez dupà la fin: db = ARGV [1] .dup
Juanda
Événement mieux db = ARGV[1].dup unless ARGV[1].nil?pour éviter l'exception de duper un néant.
Andre Figueiredo
5

Les façons de passer l'argument sont correctes dans la réponse ci-dessus. Cependant, pour exécuter la tâche de râteau avec des arguments, une petite technicité est impliquée dans la nouvelle version des rails

Cela fonctionnera avec le rake "namespace: taskname ['argument1']"

Notez les guillemets inversés lors de l'exécution de la tâche à partir de la ligne de commande.

Asim Mushtaq
la source
3

Pour passer des arguments à la tâche par défaut, vous pouvez faire quelque chose comme ça. Par exemple, dites "version" est votre argument:

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

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

Ensuite, vous pouvez l'appeler ainsi:

$ 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

Cependant, je n'ai pas trouvé de moyen d'éviter de spécifier le nom de la tâche (par défaut ou build) en passant des arguments. J'adorerais entendre si quelqu'un connaît un moyen.

Fille
la source
3

J'aime la syntaxe "querystring" pour le passage d'arguments, surtout quand il y a beaucoup d'arguments à passer.

Exemple:

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

La "chaîne de requête" étant:

width=10&height=20

Attention: notez que la syntaxe est rake "mytask[foo=bar]"et NON rake mytask["foo=bar"]

Une fois analysé à l'intérieur de la tâche de râteau en utilisant Rack::Utils.parse_nested_query, nous obtenons Hash:

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

(Ce qui est cool, c'est que vous pouvez passer des hachages et des tableaux, plus ci-dessous)

Voici comment y parvenir:

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

Voici un exemple plus étendu que j'utilise avec Rails dans ma gemme delay_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]"

Analysé de la même manière que ci-dessus, avec une dépendance à l'environnement (pour charger l'environnement 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

Donne ce qui suit dans options

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

Si vous ne pouvez pas vous soucier de savoir quelle position d'argument est pour quoi et que vous voulez faire quelque chose comme un hachage d'argument rubis. Vous pouvez utiliser un argument pour passer une chaîne, puis regex cette chaîne dans un hachage d'options.

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

Et sur la ligne de commande, vous obtenez.

$ 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
la source
1
Votre code a besoin de quelques lignes vides bien placées. Je ne sais pas comment tu lis ce mur de texte.
Joshua Pinter
2

La plupart des méthodes décrites ci-dessus ne fonctionnaient pas pour moi, peut-être qu'elles sont obsolètes dans les versions plus récentes. Le guide à jour peut être trouvé ici: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

un copier-coller ans du guide est ici:

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

Invoquez-le comme ça

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

Pour exécuter des tâches de râteau avec un style d'arguments traditionnel:

rake task arg1 arg2

Et puis utilisez:

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

Ajoutez le patch suivant de 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
Daniel Garmoshka
la source
-5

En passant des paramètres, il est préférable que l'option soit un fichier d'entrée, peut-il être un excel un json ou tout ce dont vous avez besoin et à partir de là, lisez la structure de données et les variables dont vous avez besoin, y compris le nom de la variable selon les besoins. Pour lire un fichier peut avoir la structure suivante.

  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

Exemple json

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

Exécution

rake :name_task 
tundervirld
la source
4
Si vous avez besoin d'un fichier d'instructions JSON pour votre tâche Rake, vous faites probablement trop de choses dans votre tâche Rake.
ZiggyTheHamster
C'est bien trop compliquer quelque chose qui est incroyablement simple.
jeffdill2