-
Notifications
You must be signed in to change notification settings - Fork 4
Home
Commander Adama is a command pattern library heavily inspired by Collective Idea's Interactor gem.
Commands are small classes that represent individual units of work. Each command is executed by a client "calling" it. An invoker is a class responsible for the execution of one or more commands.
Add Commander Adama to your Gemfile and bundle install
.
gem 'adama'
To create a command, include the Adama::Command
module in your command's class definition:
class DestroyCylons
include Adama::Command
end
Including the Adama::Command
module extends the class with the .call
class method. So you would execute this command like so:
DestroyCylons.call(captain: :apollo)
The above .call
method creates an instance of the DestroyCylons
class, then calls the #call
instance method. If the #call
method fails, the #rollback
method is then called.
At this point our command DestroyCylons
doesn't do much. As explained above, the Adama::Command
module has two instance methods: call
and rollback
. By default these methods are empty and should be overridden like this:
class DestroyCylons
include Adama::Command
def call
got_destroy_cylons(kwargs[:captain])
end
def rollback
retreat_and_jump_away()
end
end
The kwargs are available within the #call
and #rollback
instance methods due to an attr_reader
in the Adama::Command
module.
To create an invoker, include the Adama::Invoker
module in your invoker's class definition:
class RebuildHumanRace
include Adama::Invoker
end
Because the Adama::Invoker
module extends Adama::Command
you can execute an invoker in the exact same way you execute a command, with the .call
class method:
RebuildHumanRace.call(captain: :apollo, president: :laura)
The Adama::Invoker
module also extends your invoker class with the .invoke
class method, which allows you to specify a list of commands to run in sequence, e.g.:
class RebuildHumanRace
include Adama::Invoker
invoke(
GetArrowOfApollo,
DestroyCylons,
FindEarth,
)
end
Now, when you run RebuildHumanRace.call(captain: :apollo, president: :laura)
it will execute GetArrowOfApollo
, then DestroyCylons
, then finally FindEarth
commands in order.
If there is an error in any of those commands, the invoker will call FindEarth.rollback
, then DestroyCylons.rollback
, then GetArrowOfApollo.rollback
leaving everything just as it was in the beginning.
A Adama::Command
or Adama::Invoker
will always raise an error of type Adama::Errors::BaseError
.
More specifically:
If a command fails, it will raise Adama::Errors::CommandError
.
If a command fails while being called in an invoker, the commands will be rolled back and the invoker will raise Adama::Errors::InvokerError
.
If a command fails while rolling back within the invoker, the invoker will raise Adama::Errors::InvokerRollbackError
.
The base error type Adama::Errors::Adama
is designed to be initialized with three optional non-positional keyword args error
, command
, invoker
. The error is the original exception that was rescued in the command or invoker, the command is the command that failed, and if the command or rollback failed in an invoker, the invoker field is set to the instance of the failed invoker.
module Adama
module Errors
class BaseError < StandardError
attr_reader :error, :command, :invoker
def initialize(error: nil, command: nil, invoker: nil)
@error = error
@command = command
@invoker = invoker
end
end
end
end