Statum

Build Status Coverage Status Gem Version

Finite state machine for your objects

Tested on Ruby 2.2.0 +

Installation

Add this line to your application's Gemfile:

gem 'statum'

And then execute:

$ bundle

Or install it yourself as:

$ gem install statum

Usage

Basic usage

Statum provides a DSL for defining state machine for your class

class Car
  include Statum

  attr_accessor :state

  statum :state, initial: :idle do
    state :idle
    state :riding

    event :ride, idle: :riding
  end
end

Then Car object will receive following methods:

car = Car.new
car.idle? # => true
car.riding? # => false
car.ride! # changes idle state to riding

You can define an array of states which will be able to fire event:

class Car
  include Statum

  attr_accessor :state

  statum :state, initial: :idle do
    state :idle
    state :parked
    state :riding

    event :ride, %i[idle parked] => :riding
  end
end

Also you can use any_state helper to say, that event can be fired from any of defined states

class Car
  include Statum

  attr_accessor :state

  statum :state, initial: :idle do
    state :idle
    state :parked
    state :riding

    event :ride, any_state => :riding
  end
end

Multiple state machines

You can define more than one state machine on your object.

IMPORTANT use unique fields to work with two or more states

class Car
  include Statum

  attr_accessor :state, :engine

  statum :state do
    state :riding
    state :idle

    event :ride, idle: :riding
  end

  statum :engine do
    state :stopped
    state :started

    event :start, stopped: :started
  end
end
car = Car.new
car.start! # changes engine to started
car.ride! # changes state to riding

Hooks

You can be able to execute some procs before and after event will be fired

class Car
  include Statum

  attr_accessor :state, :started

  statum :state, initial: :idle do
    state :idle
    state :riding

    event :ride, idle: :riding,
                 before: -> { self.started = true },
                 after: :stop_engine
  end

  def stop_engine
    self.started = false
  end
end

And then before state changes will be executed before proc, and after changing - after proc (in instance context).

If you will wait for argument in hook - the instance will be passed.

...
event :ride, idle: :riding, 
             before: -> (instance) { instance.started = true}
...

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/nulldef/statum.

License

The gem is available as open source under the terms of the MIT License.