=== Travis Build Status
{}[http://travis-ci.org/troessner/transitions]
=== Synopsis
transitions is a ruby state machine implementation.
=== Installation
==== Rails
This goes into your Gemfile:
gem "transitions", :require => ["transitions", "active_model/transitions"]
… and this into your ORM model:
include ActiveModel::Transitions
==== Standalone
gem install transitions
=== Using transitions
class Product
include ActiveModel::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
In this example we assume that you are in a rails project using Bundler, which would automitcally require `transitions`.
If this is not the case for you you have to add
require 'transitions'
whereever you load your dependencies in your application.
A word of warning: Use symbols, not strings for declaring the state machine. Using strings is *not* supported as is using whitespace in names (because `transitions` possibly generates methods out of this).
=== Features
==== Events
When you declare an event, say discontinue, three methods are declared for
you: discontinue, discontinue! and can_discontinue?. The first two events will modify the state attribute on successful transition,
but only the bang(!)-version will call save!. The can_discontinue? method will
not modify state but instead returns a boolean letting you know if a given transition is possible.
==== Automatic scope generation
transitions will automatically generate scopes for you if you are using ActiveRecord and tell it to do so via the auto_scopes option:
Given a model like this:
class Order < ActiveRecord::Base
include ActiveModel::Transitions
state_machine :auto_scopes => true 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.pick_line_items
=> [#]
==== 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
In addition to just specify the method name on the record as a symbol you can pass a lambda to
perfom some more complex success callbacks:
event :discontinue, :success => lambda { |order) AdminNotifier.notify_about_discontinued_order(order) } 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
transitions :from => :exploded, :to => :rebuilt
end
==== Using event_fired and event_failed
In case you define `event_fired` and / or `event_failed`, `transitions` will use those callbacks correspondingly.
You can use those callbacks like this:
def event_fired(current_state, new_state, event)
MyLogger.info "Event fired #{event.inspect}"
end
def event_failed(event)
MyLogger.warn "Event failed #{event.inspect}"
end
==== Listing all the available states
You can easily get a listing of all available states:
Order.available_states # Uses the default state machine
# => [:pick_line_items, :picking_line_items]
==== Explicitly setting the initial state with the initial option
state_machine :initial => :closed do
state :open
state :closed
end
=== Documentation, Guides & Examples
- {Online API Documentation}[http://rdoc.info/github/troessner/transitions/master/Transitions]
=== Copyright
Copyright (c) 2010 Jakub Kuźma, Timo Rößner. See LICENSE for details.