Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
State machine extracted from ActiveModel
Ruby
branch: master

This branch is 190 commits behind troessner:master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
test
.gitignore
.rvmrc
.travis.yml
Gemfile
Gemfile.lock
LICENSE
README.rdoc
Rakefile
transitions.gemspec

README.rdoc

Travis Build Status

What is transitions?

transitions is a ruby state machine implementation based on Rick Olson’s ActiveModel::StateMachine. It was extracted from ActiveModel and turned into a gem when it got the axe in commit db49c706b.

Quick Example

require 'transitions'

class Product
  include Transitions

  state_machine do
    state :available # first one is initial state
    state :out_of_stock, :exit => :exit_out_of_stock
    state :discontinued, :enter => lambda { |product| product.cancel_orders }

    event :discontinued do
      transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
    end
    event :out_of_stock do
      transitions :to => :out_of_stock, :from => [:available, :discontinued]
    end
    event :available do
      transitions :to => :available, :from => [:out_of_stock], :guard => lambda { |product| product.in_stock > 0 }
    end
  end
end

Automatic scope generation

`transitions` will automatically generate scopes for you if you are using AR:

Given a model like this:

class Order < ActiveRecord::Base
  include ActiveRecord::Transitions
  state_machine do
    state :pick_line_items
    state :picking_line_items
  end
end

you can use this feature a la:

>> Order.pick_line_items
=> []
>> Order.create!
=> #<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">
>> Order.pick_line_items
=> [#<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">]

Using `on_transition`

Each event definition takes an optional “on_transition” argument, which allows you to execute methods on transition. You can pass in a Symbol, a String, a Proc or an Array containing method names as Symbol or String like this:

event :discontinue do
  transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => [:do_discontinue, :notify_clerk]
end

Using `success`

In case you need to trigger a method call after a successful transition you can use `success`:

event :discontinue, :success => :notfiy_admin do
  transitions :to => :discontinued, :from => [:available, :out_of_stock]
end

Timestamps

If you'd like to note the time of a state change, Transitions comes with timestamps free! To activate them, simply pass the :timestamp option to the event definition with a value of either true or the name of the timestamp column. *NOTE - This should be either true, a String or a Symbol*

# This will look for an attribute called exploded_at or exploded_on (in that order)
# If present, it will be updated
event :explode, :timestamp => true do
  transitions :from => :complete, :to => :exploded
end

# This will look for an attribute named repaired_on to update upon save
event :rebuild, :timestamp => :repaired_on do
  transiions :from => :exploded, :to => :rebuilt
end

Using with Rails

This goes into your Gemfile:

gem "transitions", :require => ["transitions", "active_record/transitions"]

… and this into your AR model:

include ActiveRecord::Transitions

A note about persistence

The property used to persist the models’ state is named state (really!), which should be a string column wide enough to fit your longest state name. It should also be mentioned that #save! is called after every successful event.

Event execution flow

On an event, with our quick example product going from :available to :discontinued it looks like this:

  1. baby_ninja.discontinue!(:reason => :pirates)

  2. call :exit handler of :available state

  3. call :guard of :available to :discontinue transition within #discontinue event

  4. call #event_failed(:event) and abort unless 3. returned true

  5. call :on_transition(:reason => :pirates) of :available to :discontinue transition within #discontinue event

  6. call :enter handler of :discontinue

  7. call #event_fired(:available, :discontinue)

  8. call #write_state(machine, :discontinue)

  9. call #write_state_without_persistence(machine, :discontinue)

  10. call baby_ninja#:success handler method of #discontinue event

A note about events

When you declare an event discontinue, two methods are declared for you: discontinue and discontinue!. Both events will call write_state_without_persistence on successful transition, but only the bang(!)-version will call write_state.

Documentation, Guides & Examples

Copyright

Copyright © 2010 Jakub Kuźma, Timo Rößner. See LICENSE for details.

Something went wrong with that request. Please try again.