<img src=“” />

A simple DSL to decorate existing methods with state transition guards.

Instead of using a DSL to define events, SimpleStateMachine decorates methods to help you encapsulate state and guard state transitions.

It supports exception rescuing, google chart visualization and mountable state machines.


Define an event and specify how the state should transition. If we want the state to change from pending to active we write:

event :activate_account, :pending => :active

That’s it. You can now call activate_account and the state will automatically change. If the state change is not allowed, a SimpleStateMachine::IllegalStateTransitionError is raised.

Methods with arguments

If you want to pass arguments and call other methods before the state transition, define your event as a method.

def activate_account(activation_code)
  # call other methods, no need to add these in callbacks

Now mark the method as an event and specify how the state should transition when the method is called.

event :activate_account, :pending => :active

Basic example

class LampSwitch

   extend SimpleStateMachine

   def initialize
     self.state = 'off'

   event :push_switch, :off => :on,
                       :on  => :off


lamp =
lamp.state          # => 'off'           # => true
lamp.push_switch    #
lamp.state          # => 'on'
lamp.on?            # => true
lamp.push_switch    #           # => true


For ActiveRecord methods are decorated with state transition guards and persistence. Methods marked as events behave like ActiveRecord save and save!.


To add a state machine to an ActiveRecord class, you will have to:

  • extend SimpleStateMachine::ActiveRecord,

  • set the initial state in after_initialize,

  • turn methods into events

    class User < ActiveRecord::Base
      extend SimpleStateMachine::ActiveRecord
      after_initialize do
        self.state ||= 'pending'
      def invite
        self.activation_code = Digest::SHA1.hexdigest("salt #{}")
      event :invite, :pending => :invited
    user =
    user.pending?         # => true
    user.invite           # => true
    user.invited?         # => true
    user.activation_code  # => 'SOMEDIGEST'

For the invite method this generates the following event methods

  • invite (behaves like ActiveRecord save )

  • invite! (behaves like ActiveRecord save!)

If you want to be more verbose you can also use:

  • invite_and_save (alias for invite)

  • invite_and_save! (alias for invite!)

Using ActiveRecord / ActiveModel validations

When using ActiveRecord / ActiveModel you can add an error to the errors object. This will prevent the state from being changed.

If we add an activate_account method to User

class User  < ActiveRecord::Base
  def activate_account(activation_code)
    if activation_code_invalid?(activation_code)
      errors.add(:activation_code, 'Invalid')
  event :activate_account, :invited => :confirmed

user.confirm_invitation!('INVALID') # => raises ActiveRecord::RecordInvalid,
                                    #           "Validation failed: Activation code is invalid"
user.confirmed?                     # => false
user.confirmed?                     # => true

Mountable StateMachines

If you like to separate your state machine from your model class, you can do so as following:

class MyStateMachine < SimpleStateMachine::StateMachineDefinition

  event :invite,             :new     => :invited
  event :confirm_invitation, :invited => :active

  def decorator_class

class User < ActiveRecord::Base

  extend SimpleStateMachine::Mountable
  mount_state_machine MyStateMachine

  after_initialize do
    self.state ||= 'new'



Catching all from states

If an event should transition from all other defined states, you can use the :all state:

event :suspend, :all => :suspended

Catching exceptions

You can let the state machine handle exceptions by specifying the failure state for an Error:

def download_data
  raise Service::ConnectionError, "Uhoh"
event :download_data, Service::ConnectionError => :download_failed

download_data               # catches Service::ConnectionError
state                       # => "download_failed"
state_machine.raised_error  # "Uhoh"

Default error state

To automatically catch all exceptions to a default error state use default_error_state:

state_machine_definition.default_error_state = :failed


If you want to run events in transactions run them in a transaction block:

user.transaction { user.invite! }


Generating state diagrams

When using Rails/ActiveRecord you can generate a state diagram of the state machine via the built in rake tasks. For details run:

rake -T ssm

A Googlechart example:


Use gem install:

gem install simple_state_machine

Or add it to your Gemfile:

gem 'simple_state_machine'

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 Marek & Petrik. See LICENSE for details.