README.md in transitions-0.1.12 vs README.md in transitions-0.1.13
- old
+ new
@@ -1,24 +1,33 @@
### Overview
[![Build status](https://secure.travis-ci.org/troessner/transitions.png?branch=master)](http://travis-ci.org/troessner/transitions)
[![Gem Version](https://badge.fury.io/rb/transitions.png)](http://badge.fury.io/rb/transitions)
[![Code Climate](https://codeclimate.com/github/troessner/transitions.png)](https://codeclimate.com/github/troessner/transitions)
+[![Dependency Status](https://gemnasium.com/troessner/transitions.png)](https://gemnasium.com/troessner/transitions)
+[![Inline docs](http://inch-ci.org/github/troessner/transitions.png)](http://inch-ci.org/github/troessner/transitions)
+
### Synopsis
`transitions` is a ruby state machine implementation.
-### Compatibility
+### Ruby Compatibility
-Supported ruby versions:
+Supported versions:
* 1.9.3
* 2.0
+* 2.1
`transitions` does not work with ruby 1.8.7 (see [this
issue](https://github.com/troessner/transitions/issues/86) for example).
+### Supported Rails versions:
+
+* 3
+* 4
+
### Installation
#### Rails
This goes into your Gemfile:
@@ -117,66 +126,78 @@
If you need to get all available transitions for current state you can simply call:
```ruby
>> Product.new.available_transitions
=> [:discontinued, :out_of_stock]
```
-
-#### 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:
-```ruby
-class Order < ActiveRecord::Base
- include ActiveModel::Transitions
- state_machine :auto_scopes => true do
- state :pick_line_items
- state :picking_line_items
- event :move_cart do
- transitions to: :pick_line_items, from: :picking_line_items
- end
- end
-end
-```
+#### Callback overview
-you can use this feature a la:
-```ruby
->> 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">]
-```
+`transitions` offers you the possibility to define a couple of callbacks during the different stages / places of transitioning from one state to another. So let's say you have an event `discontinue` which transitions the current state from `in_stock` to `sold_out`. The callback sequence would look like this:
-#### Using `guard`
-Each event definition takes an optional `guard` argument, which acts as a
-predicate for the transition.
+ | discontinue event |
+ |
+ |
+ |
+ | current_state `in_stock` | ----> executes `exit` callback
+ |
+ |
+ |
+ | current_state `in_stock` | ----> executes `on_transition` callback if and only the `guard` check was successfull. If not successfull, the chain aborts here and the `event_failed` callback is executed
+ |
+ |
+ |
+ | current_state `in_stock` | ----> executes `enter` callback for new state `sold_out`
+ |
+ |
+ |
+ | current_state `in_stock` | ----> executes `event_fired` callback
+ |
+ |
+ |
+ | current_state `in_stock` | ----> move state from `in_stock` to `sold_out`
+ |
+ |
+ |
+ | current_state `sold_out` | ----> executes `success` callback of the `discontinue` event
-You can pass in Symbols, Strings, or Procs like this:
-```ruby
-event :discontinue do
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
-end
-```
-or
+
+This all looks very complicated (I know), but don't worry, in 99% of all cases you don't have to care about the details and the usage itself is straightforward as you can see in the examples below where each callback is explained a little more throrough.
+
+
+#### Callback # 1: State callbacks `enter` and `exit`
+
+If you want to trigger a method call when the object enters or exits a state regardless
+of the transition that made that happen, use `enter` and `exit`.
+
+`exit` will be called before the transition out of the state is executed. If you want the method
+to only be called if the transition is successful, then use another approach.
+
+`enter` will be called after the transition has been made but before the object is persisted. If you want
+the method to only be called after a successful transition to a new state including persistence,
+use the `success` argument to an event instead.
+
+An example:
+
```ruby
-event :discontinue do
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => [:can_discontinue, :super_sure?]
+class Motor < ActiveRecord::Base
+ include ActiveModel::Transitions
+
+ state_machine do
+ state :off, enter: :turn_power_off
+ state :on, exit: :prepare_shutdown
+ end
end
```
-Any arguments passed to the event method will be passed on to the `guard`
-predicate.
+#### Callback # 2: Transition callback `on_transition`
-#### Using `on_transition`
Each event definition takes an optional `on_transition` argument, which allows
-you to execute methods on transition.
+you to execute code on transition. This callback is executed after the `exit` callback of the former state (if it has been defined) but before the `enter` callback of the new state and only if the `guard` check succeeds. There is no check if the callback itself succeeds (meaning that `transitions` does not evaluate its return value somewhere). However, you can easily add some properly abstracted error handling yourself by raising an exception in this callback and then handling this exception in the (also defined by you) `event_failed` callback (see below and / or the wonderful ascii diagram above).
You can pass in a Symbol, a String, a Proc or an Array containing method names
as Symbol or String like this:
```ruby
event :discontinue do
@@ -184,26 +205,13 @@
end
```
Any arguments passed to the event method will be passed on to the `on_transition` callback.
-`on_transition` is called after `guard` and before `enter` on the state that it is transitioning to.
-#### Using `enter` and `exit`
+#### Callback #3 : Event callback `success`
-If you want to trigger a method call when the object enters or exits a state regardless
-of the transition that made that happen, use `enter` and `exit`.
-
-`exit` will be called before the transition out of the state is executed. If you want the method
-to only be called if the transition is successful, then use another approach.
-
-`enter` will be called after the transition has been made but before the object is persisted. If you want
-the method to only be called after a successful transition to a new state including persistence,
-use the `success` argument to an event instead.
-
-#### Using `success`
-
In case you need to trigger a method call after a successful transition you
can use `success`. This will be called after the `save!` is complete (if you
use the `state_name!` method) and should be used for any methods that require
that the object be persisted.
```ruby
@@ -226,10 +234,75 @@
event :discontinue, :success => [:notify_admin, lambda { |order| AdminNotifier.notify_about_discontinued_order(order) }] do
transitions :to => :discontinued, :from => [:available, :out_of_stock]
end
```
+#### Callback caveats
+
+Since callbacks will not be called by you but by `transitions` the scope is different when they are called and you'll run into problems if you use classes / modules in those callbacks that have the same names like `transitions` ones, e.g. "Event":
+
+```Ruby
+def event_fired(current_state, new_state, event)
+ Event.create!
+end
+```
+
+This will crash because `transitions` uses an Event class as well, and, since the scope has changed when `transitions` calls this method, `transitions` will use it's own Event class here, not yours.
+In this case you can try to prefix your models with the "::" operator and see if that solves your problems. See https://github.com/troessner/transitions/issues/123 for details.
+
+#### 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:
+```ruby
+class Order < ActiveRecord::Base
+ include ActiveModel::Transitions
+ state_machine :auto_scopes => true do
+ state :pick_line_items
+ state :picking_line_items
+ event :move_cart do
+ transitions to: :pick_line_items, from: :picking_line_items
+ end
+ end
+end
+```
+
+you can use this feature a la:
+```ruby
+>> 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 `guard`
+
+Each event definition takes an optional `guard` argument, which acts as a
+predicate for the transition.
+
+You can pass in Symbols, Strings, or Procs like this:
+```ruby
+event :discontinue do
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
+end
+```
+
+or
+```ruby
+event :discontinue do
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => [:can_discontinue, :super_sure?]
+end
+```
+
+Any arguments passed to the event method will be passed on to the `guard`
+predicate.
+
+
#### 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
@@ -283,10 +356,13 @@
state :open
state :closed
end
```
+The explicitly specified state **must** be one of the states listed in the state definition below, otherwise `transitions` will raise a rather unhelpful exception like "NoMethodError: undefined method `call_action' for nil:NilClass" (there's a ticket to fix this already: https://github.com/troessner/transitions/issues/112)
+
+
### Configuring a different column name with ActiveRecord
To use a different column than `state` to track it's value simply do this:
```ruby
class Product < ActiveRecord::Base
@@ -304,10 +380,10 @@
* Right now it seems like `transitions` does not play well with `mongoid`. A
possible fix had to be rolled back due to other side effects:
https://github.com/troessner/transitions/issues/76. Since I know virtually
zero about mongoid, a pull request would be highly appreciated.
-* Multiple state machines are and will not be supported. For the rationale
+* Multiple state machines are not and will not be supported. For the rationale
behind this see the Changelog.
### Documentation, Guides & Examples