/stateflow

State machine that allows dynamic transitions for business workflows

Primary LanguageRubyMIT LicenseMIT

Stateflow

Now Rails 5 ready :)

INSTALL

gem install stateflow

Usage

As you can see below, Stateflow’s API is very similar to AASM, but allows for a more dynamic state transition flow. Stateflow supports persistence/storage with Mongoid, MongoMapper, and ActiveRecord. Request any others or push them to me.

Stateflow defaults to ActiveRecord but you can set the persistence layer with:

Stateflow.persistence = :mongo_mapper

OR

Stateflow.persistence = :active_record

OR

Stateflow.persistence = :mongoid

Stateflow allows dynamic :to transitions with :decide. The result :decide returns needs to be one of the states listed in the :to array, otherwise it wont allow the transition. Please view the advanced example below for usage.

You can set the default column with the state_column function in the stateflow block. The default state column is “state”.

state_column :state

Rails 3

Stateflow now automatically tries to detect your persistence from your applications default ORM config. If the ORM you are using does not have a persistence layer it will default to ActiveRecord.

Basic Example

require 'rubygems'
require 'stateflow'

# No persistence
Stateflow.persistence = :none

class Stoplight
  include Stateflow

  stateflow do
    initial :green

    state :green, :yellow, :red

    event :change_color do
      transitions :from => :green, :to => :yellow
      transitions :from => :yellow, :to => :red
      transitions :from => :red, :to => :green
    end
  end
end

Advanced Example

require 'rubygems'
require 'stateflow'

# No persistence
Stateflow.persistence = :none

class Test
  include Stateflow

  stateflow do

    initial :love

    state :love do
      enter lambda { |t| p "Entering love" }
      exit :exit_love
    end

    state :hate do
      enter lambda { |t| p "Entering hate" }
      exit lambda { |t| p "Exiting hate" }
    end

    state :mixed do
      enter lambda { |t| p "Entering mixed" }
      exit lambda { |t| p "Exiting mixed" }
    end

    event :b do
      transitions :from => :love, :to => :hate, :if => :no_ice_cream
      transitions :from => :hate, :to => :love
    end

    event :a do
      transitions :from => :love, :to => [:hate, :mixed], :decide => :likes_ice_cream?
      transitions :from => [:hate, :mixed], :to => :love
    end
  end

  def likes_ice_cream?
    rand(10) > 5 ? :mixed : :hate
  end

  def exit_love
    p "Exiting love"
  end

  def no_ice_cream
    rand(4) > 2 ? true : false
  end
end

Bang event vs non-bang event

Bang events will save the model after call, where the non bang event will just update the state and call the transitions. (ie. model.change! vs model.change)

Extras

  • When transitioning states, the previous state from which you have transitioned to can be accessed via ‘_previous_state`. See tests for more information.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Ryan Oberholzer. See LICENSE for details.