Skip to content

odlp/oops_a_rake

Repository files navigation

OOP(s) a Rake

Write your Rake tasks as plain-old Ruby objects.

Setup

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

Usage

Simple task with a description

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.

Task with arguments

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!

Task with prequisites

class ComplexSetupTask
  include OopsARake::Task

  prerequisites :task_one, :task_two

  def call
    # Your implementation
  end
end

Namespaced task

class Admin::SpecialTask
  include OopsARake::Task

  def call
    # Your implementation
  end
end

Invocation:

bundle exec rake admin:special

Task with a custom name

class ObscureClassNameTask
  include OopsARake::Task.with_options(name: "custom_name")

  def call
    puts "Hello"
  end
end

Invocation:

bundle exec rake custom_name

Motivation

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

About

Write your Rake tasks as plain-old Ruby objects

Topics

Resources

License

Stars

Watchers

Forks

Languages