InterstateMachine

When state machine meets interactor. InterstateMachine is a simple state machine which use interactors to trigger transitions. Long story short, an object receives an event which is a interactor and you can do fantastic things with interactors. What is an interactor? "An interactor is a simple, single-purpose object."

Installation

gem install interstate_machine

Gemfile

gem 'interstate_machine', '~> 1.0.0'

Usage

class TrafficLight < ActiveRecord::Base
  include InterstateMachine

  initial_state :stop

  transition_table :stop, :proceed, :caution, :tilt, :broken do
    on event: :cycle do |event|
      allow event: event, transition_to: [:proceed], from: [:stop]
      allow event: event, transition_to: [:caution], from: [:proceed]
      allow event: event, transition_to: [:stop], from: [:caution]
    end
    on event: :tilt, transition_to: [:broken], from: [:proceed, :caution, :stop]
    on event: :repair, transition_to: [:stop], from: [:broken]
  end
end

If you want to use InterstateMachine in plain ruby, add attr_accessor :state to store the state. transition_table is where the state machine rules and states are defined. Each event represent an Interactor that is called to process the transition.

on can take a block which defines different transition(rules) for the same event or a single transition

In addition to the class where you define the state machine, you also need to create interactors for each event.

In this case we have an event cycle that trigger many transitions so we define three interactors for the cycle event and two for the remaining. CycleProceed, CycleCaution, CycleStop and then Tilt and Repair. Yes, when an event triggers a transition to a single state and it's not a block, you have to name the class like the event name.

class CycleProceed
  include Interactor

  before :validate_transition

  def call
    # do stuff needed when this state happen
    'yeah'
  end

  private

  def validate_transition
    context.fail!(error: 'there is no power') if context.object.power == 0
  end
end

Note: You can use all the magics like hooks. Whoop!

You can access the class where you have included InterstateMachine by context.object

When transition is allowed:

t = TrafficLight.new
t.cycle
#=> :proceed

When transition can't happen because something wrong executing the event

t.power = 0
t.cycle
#=> 'there is no power'
t.state
#=> :stop

When transition is no allowed

t.state
#=> :stop
t.repair
#=> RuntimeError Exception:

Contributing

Feel free to play around, fork, add, pull request, and get a hug. If you decide to pull request please add tests