196

This question already has an answer here:

I am able to pass in arguments as follows:

desc "Testing args"
task: :hello, :user, :message do |t, args|
  args.with_defaults(:message => "Thanks for logging on")
  puts "Hello #{args[:user]}. #{:message}"
end

I am also able to load the current environment for a Rails application

desc "Testing environment"
task: :hello => :environment do 
  puts "Hello #{User.first.name}."
end

What I would like to do is be able to have variables and environment

desc "Testing environment and variables"
task: :hello => :environment, :message do |t, args|
  args.with_defaults(:message => "Thanks for logging on")
  puts "Hello #{User.first.name}. #{:message}"
end

But that is not a valid task call. Does anyone know how I can achieve this?


  • Can you please update to accept inger's newer answer. - mahemoff

5 답변


106

TLDR;

task :t, [args] => [deps] 

Original Answer

When you pass in arguments to rake tasks, you can require the environment using the :needs option. For example:


desc "Testing environment and variables"
task :hello, :message, :needs => :environment do |t, args|
  args.with_defaults(:message => "Thanks for logging on")
  puts "Hello #{User.first.name}. #{args.message}"
end

Updated per @Peiniau's comment below

As for Rails > 3.1

task :t, arg, :needs => [deps] # deprecated

Please use

task :t, [args] => [deps] 


  • Note that the most current docs describe the :needs method this way: "That format is still supported for compatability, but it is not recommended for use." (I mention this only for future browsers, since it suggests that eventually :needs won't be supported any longer. The section is called "Deprecated Task Paramaters Format"...) - Telemachus
  • Bug in that code: puts "Hello #{User.first.name}. #{:message}" ... this does not print "Thanks for logging on", just "message". Fix: #{args.message} for the last substition. - inger
  • As for Rails 3.1 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead. - Peiniau
  • To clarify the previous comment with an example, I believe this would work out to task :hello, [:message] => [:environment] do |t, args|. - Jason Swett
  • Additional clarification: to pass multiple arguments, task :hello, [:message1, :message2] => [:environment] do |t, args| works, and to invoke use rake hello['message #1','message #2']. Warning: I found that rake would fail if the argument list contained any clear text, e.g rake hello['message #1', 'message #2'] (note the space after the comma). - Steve Wilhelm

354

Just to follow up on this old topic; here's what I think a current Rakefile (since a long ago) should do there. It's an upgraded and bugfixed version of the current winning answer (hgimenez):

desc "Testing environment and variables"
task :hello, [:message]  => :environment  do |t, args|
  args.with_defaults(:message => "Thanks for logging on")
  puts "Hello #{User.first.name}. #{args.message}"   # Q&A above had a typo here : #{:message}
end

This is how you invoke it (http://guides.rubyonrails.org/v4.2/command_line.html#rake):

  rake "hello[World]" 

For multiple arguments, just add their keywords in the array of the task declaration (task :hello, [:a,:b,:c]...), and pass them comma separated:

  rake "hello[Earth,Mars,Sun,Pluto]" 

Note: the number of arguments is not checked, so the odd planet is left out:)


  • This answer should be the accepted one. - samvermette
  • Man...rake always feels so un-rubyish to me. Most things in ruby your first guess is how it actually works. This syntax is just bizarre. </rant> - Brian Armstrong
  • To send 2 arguments, task :hello, [:arg1, :arg2] => :environment do |t, args| - konyak
  • To send 2 arguments, without spaces between [:arg1,:arg2] - hbin
  • @hbin Ah so this was when you invoked rake not the declaration within the Rakefile(which ChaseT.'s comment referred to).. Yep, in that case shell rules, so you either write the thing as one shell-word(no space) or quote the spaces, or use quotes around the whole thing like rake 'webp:convert["hello", "world"]' .. Warning: I haven't tried this one now, but it should work. - inger

57

Just for completeness, here the example from the docs mentioned above:

   task :name, [:first_name, :last_name] => [:pre_name] do |t, args|
     args.with_defaults(:first_name => "John", :last_name => "Dough")
     puts "First name is #{args.first_name}"
     puts "Last  name is #{args.last_name}"
   end

Notes:

  • You may omit the #with_defaults call, obviously.
  • You have to use an Array for your arguments, even if there is only one.
  • The prerequisites do not need to be an Array.
  • args is an instance of Rake::TaskArguments.
  • t is an instance of Rake::Task.


  • This explains why args.empty? was giving me weird results. args looks like a hash, but it's a Rake::TaskArguments. So if you want special behaviour when no args are passed, call #to_hash or #to_s before checking if args is empty. - Dennis

18

An alternate way to go about this: use OS environment variables. Benefits of this approach:

  • All dependent rake tasks get the options.
  • The syntax is a lot simpler, not depending on the rake DSL which is hard to figure out and changes over time.

I have a rake task which requires three command-line options. Here's how I invoke it:

$ rake eaternet:import country=us region=or agency=multco

That's very clean, simple, and just bash syntax, which I like. Here's my rake task. Also very clean and no magic:

task import: [:environment] do
  agency = agency_to_import
  puts "Importing data for #{agency}..."
  agency.import_businesses
end

def agency_to_import
  country_code = ENV['country'] or raise "No country specified"
  region_slug  = ENV['region']  or raise "No region specified"
  agency_slug  = ENV['agency']  or raise "No agency specified"
  Agency.from_slugs(country_code, region_slug, agency_slug)
end

This particular example doesn't show the use of dependencies. But if the :import task did depend on others, they'd also have access to these options. But using the normal rake options method, they wouldn't.


  • This is definitely not environment variables! It is rake arguments even though rake shows it as environment variables. There is a huge concept (and practical) difference! Note that country=us ruby -e "puts ENV['country']" is not the same as ruby -e "puts ENV['country']" country=us (environment variable in the first versus argument in the second). - Djunzu

6

While these solutions work, in my opinion this is overly complicated.

Also, if you do it this way in zsh, you'll get errors if the brackets in your array aren't escaped with '\'.

I recommend using the ARGV array, which works fine, is much simpler, and is less prone to error. E.g:

namespace :my_example do
  desc "Something"
  task :my_task => :environment do
    puts ARGV.inspect
  end
end

then

rake my_example:my_task 1 2 3

#=>  ["my_example:my_task", "1", "2", "3"]

The only thing you need to keep in mind is that ARGV[0] is the process name, so use only ARGV[1..-1].

I realize that strictly speaking this does not answer the question, as it does not make use of :environment as part of the solution. But OP did not state why he included that stipulation so it might still apply to his use case.


  • :environment is needed to load Rails application code. And trying it on a Rails app I get: ["my_example:my_task", "1", "2", "3"] rake aborted! Don't know how to build task '1' (see --tasks) - Djunzu
  • this is exactly what i was looking for - Paul Brunache

Linked


Related

Latest