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:
- tack on an
and_return(<something>)
- 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
Get All Local Variables or Available Methods from Irb
Stub Method Only on The First Call with Rspec
Ways to Define a Global Method in Ruby
How to Completely Remove Webpack and All Its Dependencies from Rails App
Using Ruby CSV to Extract One Column
What Is The Logic Behind This Result
Properly Using Log4R in Ruby Application
Ror, Can't Iterate from Datetime/Timewithzone
How to Get The Pty.Spawn Child Exit Code
Does Anyone Know How to Appropriately Deal with User Timezones in Rails 2.3
Use Same Controller for Multiple Routes
Ruby on Rails:How to Implement Cancel Button in Form_Tag
Ruby Rake Load Tasks from a Gem
Ip Range to Cidr in Ruby/Rails
Using Negative Conditions Within Regular Expressions
How to Make a Custom Environment in Rails a Default Environment