Skywalker
Skywalker is a gem that provides a simple command pattern for applications that use transactions.
Why Skywalker?
It's impossible to come up with a single-word clever name for a gem about commands. If you can't achieve clever, achieve topicality.
What is a command?
A command is simply a series of instructions that should be run in sequence and considered a single unit. If one instruction fails, they should all fail.
That's a transaction, you say? You're correct! But there are some benefits of considering transactional blocks as objects:
Testability
With a command, you inject most any argument, which means that you can simulate the run of the command without providing real arguments. Best practice is to describe the operations in methods, which can then be stubbed out to test small portions in isolation.
This also allows you to make the reasonable inference that the command will abort
properly if one step raises an error, and by convention, the same method (on_failure)
will be called. In most cases, you can thereby verify happy path and a single bad path
through integration specs, and that will suffice.
Reasonability
The benefit of abstraction means that you can easily reason about a command without
having to know its internals. Standard caveats apply, but if you have a CreateGroup
command, you should be able to infer that calling the command with the correct arguments
will produce the expected result.
A gateway to harder architectures
It's not hard to create an Event class and step up toward full event sourcing, or to
go a bit further and implement full CQRS. This is the architectural pattern your parents
warned you about.
Installation
Add this line to your application's Gemfile:
gem 'skywalker'
And then execute:
$ bundle
Or install it yourself as:
$ gem install skywalker
Usage
Compose your commands:
class AddGroupCommand < Skywalker::Command
def execute!
# Your transactional operations go here. No need to open a transaction.
# Simply make sure each method raises an error when it fails.
end
end
Then call your commands:
command = AddGroupCommand.call(
on_success: method(:on_success),
on_failure: method(:on_failure)
)
You can pass any object responding to `#call` to the `on_success` and `on_failure` handlers, including procs, lambdas, controller methods, or other commands themselves.
Contributing
- Fork it ( https://github.com/robyurkowski/skywalker/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request