Skip to content
Famo edited this page Oct 26, 2017 · 2 revisions

Commander Adama

Adama

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.

Getting Started

Add Commander Adama to your Gemfile and bundle install.

gem 'adama'

Usage

Command

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.

Invoker

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.

Errors

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
Clone this wiki locally