An open API service indexing awesome lists of open source software.

https://github.com/odlp/oops_a_rake

Write your Rake tasks as plain-old Ruby objects
https://github.com/odlp/oops_a_rake

rake

Last synced: about 1 year ago
JSON representation

Write your Rake tasks as plain-old Ruby objects

Awesome Lists containing this project

README

          

# OOP(s) a Rake

Write your Rake tasks as plain-old Ruby objects.

## Setup

Add the gem to your Gemfile and `bundle install`:

```ruby
gem "oops_a_rake"
```

In your Rakefile, require `oops_a_rake` and require your tasks:

```ruby
# 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`

```ruby
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:

```sh
$ 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.

```ruby
class PersonalizedGreetingTask
include OopsARake::Task

def call(name)
puts "Hello #{name}!"
end
end
```

Invocation:

```sh
bundle exec rake "personalized_greeting[Bob]"
# => Hello Bob!
```

### Task with prequisites

```ruby
class ComplexSetupTask
include OopsARake::Task

prerequisites :task_one, :task_two

def call
# Your implementation
end
end
```

### Namespaced task

```ruby
class Admin::SpecialTask
include OopsARake::Task

def call
# Your implementation
end
end
```

Invocation:

```sh
bundle exec rake admin:special
```

### Task with a custom name

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

def call
puts "Hello"
end
end
```

Invocation:

```sh
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][testing-tasks]
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:

```ruby
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:

```ruby
# 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:

```ruby
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:

```ruby
class HardWorker
include Sidekiq::Worker

def perform(name, count)
# do something
end
end
```

[testing-tasks]: https://thoughtbot.com/blog/test-rake-tasks-like-a-boss