How to Declare a Rake Task That Depends on a Parameterized Task

How do you declare a Rake task that depends on a parameterized task?

Args are passed down through the call stack. You just need to make sure your top-level task captures all the arguments all of the dependencies require. In your case you'll want to put first_name and last_name on the send_letter task.

Here is an example that shows named arguments defined elsewhere flowing into the dependency (even if they aren't defined in the dependency), but the argument that doesn't match the name of the top-level task argument is nil.

desc 'Bar'
task :bar, :nom do |task, args|
puts "BAR NOM: #{args[:nom]}"
puts "BAR NAME: #{args[:name]}"
end

desc 'Foo'
task :foo, [:name] => :bar do |task, args|
puts "FOO NAME: #{args[:name]}"
end

Running rake foo[baz] yields

BAR NOM: 
BAR NAME: baz
FOO NAME: baz

It is interesting to note that using args.with_defaults(nom: 'Jaques') in the foo task has no effect on the dependent task -- nom is still nil.

Rake version: rake, version 10.0.3

Ruby version: ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]

How can I write a Rake task which depends on task with namespace and parameter

You can modify that task as:

desc 'Some description'
# setup_dev_env is dependent on neo4j:start
task :setup_dev_env,[:stable] => :environment do |t, arg|
param = arg[:stable].nil? ? 'stable' : arg[:stable]
Rake::Task['neo4j:start'].invoke(param)
puts "Created Rake task"
end

How to define a Rake task with arguments and dependencies

The answer is in a comment documenting the 'resolve_args_with_dependencies(args, hash)' method in the lib/rake/task_manager.rb file.

# The patterns recognized by this argument resolving function are:
#
# task :t => [:d]
# task :t, [a] => [:d]

So this means you need to declare the task as follows

# :spec task depends on clean and 'test-reports' tasks
# and takes args for tags and triggers
RSpec::Core::RakeTask.new(:spec, [:tag, :triggers] => [:clean, 'test-reports']) do |task, args|
task.rspec_opts = "--tag #{args[:tag]}"
# args is a Rake::TaskArguments object (NOT a hash)
ENV[TRIGGERS] = args[:triggers]
end

rake pass parameters to dependent tasks

Inside the rake file

task :test, [:dir] => [:prepare_testdir] do |t,args|
puts args.inspect # {:dir=>"foo"}
end

task :prepare_testdir, :dir do |t, args|
puts args.inspect # {:dir=>"foo"}
end

Invocation

rake test[foo]

Running a Rake task with parameters

As I mentioned in a comment, the task isn't being invoked from the test because of the way you're stubbing here:

    expect(Rake::Task['myapp:seed:all']).to receive(:invoke)

Although this checks whether invoke was called, it doesn't actually invoke invoke (actually, it makes the method return nil). To change that, you can either:

  1. tack on an and_return(<something>)
  2. tack on and_call_original.

Probably in this case you'd want to use and_call_original since you want to investigate what actually happens in the task. In order to stub individual method calls in the task, the approach you have been using (expect_any_instance_of(Object).to receive(:system)) will technically work, but could probably be refactored to be more decoupled from the code.

For example, you could separate each system call into its own method (available to the rake task), and then call those from the test. Then in order to stub it you only need to pass the method name. If you want, you can then go and unit test each of those methods individually, putting the system call expectation in there.

I don't recall where exactly but I've heard it advised to not do any acual programming in Rake tasks. Put your code somewhere in your regular codebase, and call those methods from the rake task. This can be seen as an example of a more general pattern which is to refactor large methods into smaller ones. Writing code this way (and also with a functional style, but I won't get into that) makes your life easier when testing.


onto your followup question:

as you can see in the test case's failure message, the only difference between the actual and expected is that one is a regex and the other is a string.

A simple fix for this is to change this line:

    expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:drop/).and_return(true)

so that the with() argument is a string, not a regex

How to pass command line arguments to a rake task

Options and dependencies need to be inside arrays:

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

Then

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

NOTE: variable task is the task object, not very helpful unless you know/care about Rake internals.

RAILS NOTE:

If running the task from Rails, it's best to preload the environment by adding => [:environment] which is a way to setup dependent tasks.

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

Rake task depend on other rake task

Typically you just declare dependencies like this:

task :pull => :clone do
# ...
end

Or in the case of multiple dependencies:

task :status => [ :clone, :pull ] do
# ...
end

How do I have the :default Rake task depend on a task with arguments?

I think you might have to use the old style parameter passing, eg:

nicholas@hal:/tmp$ cat Rakefile
task :default => :all

deploy_path = ENV['deploy_path'] || "c:/some_path"

task :all do |t, args|
puts deploy_path.inspect
end

And invoke with:

nicholas@hal:/tmp$ rake
(in /tmp)
"c:/some_path"

Or, to override the path:

nicholas@hal:/tmp$ rake deploy_path=c:/other_path
(in /tmp)
"c:/other_path"

How to pass arguments into a Rake task with environment in Rails?

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] 


Related Topics



Leave a reply



Submit