Can I catch errors and continue to next Rake task? - ruby

I invoke Rake tasks in another Rake task like following:
namespace :test do
task :migrate do
Rake::Task['A:migrate'].invoke
Rake::Task['B:migrate'].invoke
end
end
What I want to know is whether I can continue to the next Rake task even if the earlier one fails. For instance, there is an error in A:migrate, I want to catch it and log it but move ahead with B:migrate. How can I do this?

Rake is just Ruby, so you can use Ruby's error handling feature.
namespace :test do
task :migrate do
begin
Rake::Task['A:migrate'].invoke
rescue => e
log(e)
end
Rake::Task['B:migrate'].invoke
end
end

Related

Resque tasks always fail - uninitialized job constants?

I've tried using Resque before and was met with unmitigated failure. I'm revisiting it again with the same results...
resque.rake:
require "resque/tasks"
task "resque:setup" => :environment
test.rb:
require 'resque'
class FileWorker
#queue = :save_to_file
def self.perform(str)
File.open('./' + Time.now.to_s + '.txt', 'w+') do |f|
f << "test 123"
end
end
end
Resque.enqueue(FileWorker, "12345567".split('').shuffle.join)
Gemfile:
gem 'resque'
gem 'rake'
It seems like running test.rb on its own successfully queues the job:
However, running rake resque:work QUEUE='*' in the same folder results in a warning,
WARNING: This way of doing signal handling is now deprecated. Please
see http://hone.heroku.com/resque/2012/08/21/resque-signals.html for
more info.
As well as the task being added to "failed" queue with the following reason: "exception":"NameError","error":"uninitialized constant FileWorker"
How do I get this to work? Seems like something quite obvious but there's tons of tutorials about Resque spanning many years - some painfully out of date and none explaining how to run workers so they don't fail.
Thanks in advance.
When you enqueue a task with Resque, what is stored on Redis is just the name of the job class (as a string) along with the arguments (again as strings) in a JSON object.
When a worker then tries to perform the task, it needs to be able to create an instance of the job class. It does this by using const_get and const_missing. This is where the error you are seeing occurs, since the worker does not have the definition of FileWorker available to it.
The error is the same as if you tried to get an unknown constant in irb:
> Object.const_missing "FileWorker"
NameError: uninitialized constant FileWorker
The solution is to make sure the definition of FileWorker is available to your workers. The simplest way to do this would be to just require test.rb from your Rakefile (or resque.rake). In your code this would involve adding another task to the queue, so you might want to move the FileWorker code to its own file where it can be required by both the rake file and the code enqueuing jobs.
test.rb:
require 'resque'
require './file_worker'
Resque.enqueue(FileWorker, "12345567".split('').shuffle.join)
Rakefile (note the :environment task only makes sense if you are using Rails and will give errors otherwise):
require "resque/tasks"
require "./file_worker"
file_worker.rb:
class FileWorker
#queue = :save_to_file
def self.perform(str)
File.open('./' + Time.now.to_s + '.txt', 'w+') do |f|
f << "test 123"
end
end
end
Now the workers will be able to create instances of FileWorker to complete the tasks.
The way to avoid the warning about signals is given in the page linked to in the message. Simply set the environment variable TERM_CHILD when calling rake:
$ rake resque:work QUEUE='*' TERM_CHILD=1

Invoking the same rake task twice in RSpec

I am trying to test a rake task with rspec, and for that purpose I need to invoke it twice but
it is only being invoked once.
it 'first test' do
Rake::Task['my_rake_task'].invoke
# rake task was processed
end
it 'second test' do
Rake::Task['my_rake_task'].invoke
# rake task was NOT processed
end
if a rake task has already been invoked once it won't run again unless you call:
#rake[#task_name].reenable
or invoke it with
#rake[#task_name].execute
To adding to Guy Segev answer I prefer adding this to your spec file
after(:each) do
Rake::Task["task:name"].reenable
end

How can I have an rcov task know if a subtask failed?

I have this task:
task :all => ['foo', 'bar', 'announce_success']
If foo and bar don't raise exceptions, then announce_success happens. How can I have a particular task or code block execute if they do raise exceptions?
The way you have defined your tasks will cause rake to exit as soon as one of the dependencies fails/raises and exception. This is the core functionality of rake.
One way to work around though is to do something like
task :all do
task :tmp => ['foo','bar']
begin
Rake::Task[:tmp].invoke
rescue
#do something with the exception
end
end
Unfortunately that goes against the grain of Rake.
Ruby has an at_exit hook you can add a block of code to, if you want to run a bit of cleanup when Rake terminates. You can combine rake-tasks and at_exit hook like this:
task :cleanup do
at_exit {
# cleanup code here
}
end
Just make sure :cleanup is executed early in the list of dependencies.

Namespaces in Ruby Rake Tasks

Are the following equivalent?
namespace :resque do
task setup: :environment do
end
end
task "resque:setup" => :environment do
end
In short: yes. When running rake resque:setup both of these tasks will be invoked.
Rake will merge these tasks. You can test this by doing the following:
p Rake.application.tasks
Which in this case would return something like
[<Rake::Task resque:setup => [environment]>]
Which is simply an Array holding a single Rake::Task object. You can also check the scope or list of namespaces for a task by doing:
p Rake.application.tasks.first.scope
#=> ["resque"]
If you want to learn a little more on how the internals of Rake work, check out Rake::Task and Rake::TaskManager

Rescue RuntimeError in Rake

I have a custom Rakefile which calls different file tasks. Sometimes a file that is expected doesn't exist, and rake throws a RuntimeError and fails. However, I'd like to do a few things before it fails. So is there any way I could rescue a RuntimeError? Or is there some sort of a magic task which gets called before a complete fail?
I haven't run into this issue with rake myself, but you could try simply wrapping your call to the file tasks in a begin-rescue block, i.e.
begin
file_task
rescue RuntimeError => e
puts e
end
and then do your rescuing in the rescue block.

Resources