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
end
When you list all the Rake tasks in the project:
$ bundle exec rake --tasks
You 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
end
Invocation:
bundle exec rake "personalized_greeting[Bob]"
# => Hello Bob!
class ComplexSetupTask
include OopsARake::Task
prerequisites :task_one, :task_two
def call
# Your implementation
end
end
class Admin::SpecialTask
include OopsARake::Task
def call
# Your implementation
end
end
Invocation:
bundle exec rake admin:special
class ObscureClassNameTask
include OopsARake::Task.with_options(name: "custom_name")
def call
puts "Hello"
end
end
Invocation:
bundle exec rake custom_name
Rake 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
end
Instead 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
end
To 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
end
This 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