Write your Rake tasks as plain-old Ruby objects.
Add the gem to your Gemfile and bundle install:
gem "oops_a_rake"In your Rakefile, require oops_a_rake and require your tasks:
# Rakefile
require "oops_a_rake"
Dir.glob("lib/tasks/**/*.rb").each { |task| require_relative(task) }Write a class which:
- responds to
#call - includes
OopsARake::Task
class GreetingTask
include OopsARake::Task
description "An enthusiastic greeting"
def call
puts "Hello!"
end
endWhen you list all the Rake tasks in the project:
$ bundle exec rake --tasksYou should see the greeting task listed (note the optional 'task' suffix from
the class name is omitted):
rake greeting # An enthusiastic greeting
N.B. Unless you include a description for a task then Rake won't list it by
default. Run bundle exec rake --tasks --all to see tasks without descriptions.
Note: Only positional arguments are supported.
class PersonalizedGreetingTask
include OopsARake::Task
def call(name)
puts "Hello #{name}!"
end
endInvocation:
bundle exec rake "personalized_greeting[Bob]"
# => Hello Bob!class ComplexSetupTask
include OopsARake::Task
prerequisites :task_one, :task_two
def call
# Your implementation
end
endclass Admin::SpecialTask
include OopsARake::Task
def call
# Your implementation
end
endInvocation:
bundle exec rake admin:specialclass ObscureClassNameTask
include OopsARake::Task.with_options(name: "custom_name")
def call
puts "Hello"
end
endInvocation:
bundle exec rake custom_nameRake is an omnipresent tool in the Ruby world. It has some drawbacks – the main issue I've heard repeatedly is how difficult it is to test Rake tasks.
Testing Rake tasks isn't impossible, but it's complex and requires some familiarity with how Rake works (see Test Rake Tasks Like a BOSS for an excellent guide).
As a result I've seen many codebases which opt for writing thin Rake tasks that call a plain Ruby object, which is tested in isolation:
task :greeting do |_, args|
SomeObject.new(*args).call
endInstead of writing this glue-code by hand it's cleaner to write your tasks as objects:
# lib/tasks/greeting_task.rb
class GreetingTask
include OopsARake::Task
def call(name)
puts "Hello #{name}"
end
endTo test this task you can then initialize a new instance and invoke #call.
This side-steps any requirement to manage Rake's world in tests. For example in
RSpec:
require "tasks/greeting_task"
RSpec.describe GreetingTask do
it "personalizes the greeting" do
task = described_class.new
task.call("Bob")
# ... rest of your test
end
endThis approach is heavily inspired by Sidekiq, which allows jobs to be tested the same way:
class HardWorker
include Sidekiq::Worker
def perform(name, count)
# do something
end
end