transitions 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -1 +1,3 @@
1
- rvm: 1.9.3
1
+ rvm:
2
+ - 2.0.0
3
+ - 1.9.3
data/CHANGELOG.md CHANGED
@@ -1,39 +1,46 @@
1
- # 1.0.8
1
+ # 0.1.9
2
+
3
+ * (barelyknow) add can_transition? and cant_transition? methods
4
+ * (barelyknow) add available_events method to class with state machine
5
+ * (Sean Devine) fire enter on create
6
+ * (jotto) change scopes to use proc so it works in rails 3 and 4
7
+
8
+ # 0.1.8
2
9
 
3
10
  * (phillipp | Phillipp Röll) Fixes a wrong select for scopes if the state machines attribute_name is set to something other than the default
4
11
 
5
- # 1.0.7
12
+ # 0.1.7
6
13
 
7
14
  * (cmw) Better prevention of name clashes when creating initializer methods
8
15
 
9
- # 1.0.6
16
+ # 0.1.6
10
17
 
11
18
  * (troessner) Revert 'Fixing set_initial_state with Mongoid' because of https://github.com/troessner/transitions/issues/76
12
19
  * (divins) Multiple success callbacks
13
20
  * (cstrahan) Pass additional args to guard function
14
21
  * (cmw) Support for configurable column names
15
22
 
16
- # 1.0.5
23
+ # 0.1.5
17
24
 
18
25
  * (troessner) Fix unhelpful error message when event can not be fired.
19
26
  * (simonc) Fixing set_initial_state with Mongoid
20
27
 
21
- # 1.0.4
28
+ # 0.1.4
22
29
 
23
30
  (troessner)
24
31
 
25
32
  * Raise exception if we try to overwrite existing instance methods when defining state predicate methods
26
33
  * Improve exception message if we try to overwrite existing class methods when ActiveRecord scopes
27
34
 
28
- # 1.0.3
35
+ # 0.1.3
29
36
 
30
37
  (troessner) Make sure `empty?` works on symbols.
31
38
 
32
- # 1.0.2
39
+ # 0.1.2
33
40
 
34
41
  (troessner) Slightly improved handling of current states.
35
42
 
36
- # 1.0.1
43
+ # 0.1.1
37
44
 
38
45
  * (troessner) Remove feature:
39
46
  `Do not override existing methods when defining state query methods but warn the user.`
@@ -41,7 +48,7 @@
41
48
  https://github.com/troessner/transitions/issues/62 for details.
42
49
  * (bnmrrs) Add helper methods to check if a given transition is possible.
43
50
 
44
- # 1.0.0
51
+ # 0.1.0
45
52
 
46
53
  (troessner) Remove suppport for multipe state machines
47
54
 
data/README.md ADDED
@@ -0,0 +1,291 @@
1
+ ### Overview
2
+
3
+ [<img
4
+ src="https://secure.travis-ci.org/troessner/transitions.png?branch=master"/>](
5
+ http://travis-ci.org/troessner/transitions) [<img
6
+ src="https://badge.fury.io/rb/transitions.png" alt="Gem Version"
7
+ />](http://badge.fury.io/rb/transitions)
8
+
9
+ ### Synopsis
10
+
11
+ `transitions` is a ruby state machine implementation.
12
+
13
+ ### Compatibility
14
+
15
+ Supported ruby versions:
16
+
17
+ * 1.9.3
18
+ * 2.0
19
+
20
+ `transitions` does not work with ruby 1.8.7 (see [this
21
+ issue](https://github.com/troessner/transitions/issues/86) for example).
22
+
23
+ ### Installation
24
+
25
+ #### Rails
26
+
27
+ This goes into your Gemfile:
28
+
29
+ gem "transitions", :require => ["transitions", "active_model/transitions"]
30
+
31
+ … and this into your ORM model:
32
+
33
+ include ActiveModel::Transitions
34
+
35
+ #### Standalone
36
+
37
+ gem install transitions
38
+
39
+ ### Using transitions
40
+
41
+ class Product
42
+ include ActiveModel::Transitions
43
+
44
+ state_machine do
45
+ state :available # first one is initial state
46
+ state :out_of_stock, :exit => :exit_out_of_stock
47
+ state :discontinued, :enter => lambda { |product| product.cancel_orders }
48
+
49
+ event :discontinued do
50
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
51
+ end
52
+ event :out_of_stock, :success => :reorder do
53
+ transitions :to => :out_of_stock, :from => [:available, :discontinued]
54
+ end
55
+ event :available do
56
+ transitions :to => :available, :from => [:out_of_stock], :guard => lambda { |product| product.in_stock > 0 }
57
+ end
58
+ end
59
+ end
60
+
61
+ In this example we assume that you are in a rails project using Bundler, which
62
+ would automatically require `transitions`. If this is not the case for you you
63
+ have to add
64
+
65
+ require 'transitions'
66
+
67
+ wherever you load your dependencies in your application.
68
+
69
+ **Known limitations:**
70
+
71
+ * You can only use one state machine per model. While in theory you can
72
+ define two or more, this won't work as you would expect. Not supporting
73
+ this was intentional, if you're interested in the ratione look up version
74
+ 1.0.0 in the CHANGELOG.
75
+
76
+ * Use symbols, not strings for declaring the state machine. Using strings is
77
+ **not** supported as is using whitespace in names (because `transitions`
78
+ possibly generates methods out of this).
79
+
80
+ ### Features
81
+
82
+ #### Getting and setting the current state
83
+
84
+ Use the (surprise ahead) `current_state` method - in case you didn't set a
85
+ state explicitly you'll get back the state that you defined as initial state.
86
+
87
+ >> Product.new.current_state
88
+ => :available
89
+
90
+ You can also set a new state explicitly via `update_current_state(new_state,
91
+ persist = true / false)` but you should never do this unless you really know
92
+ what you're doing and why - rather use events / state transitions (see below).
93
+
94
+ Predicate methods are also available using the name of the state.
95
+
96
+ >> Product.new.available?
97
+ => true
98
+
99
+ #### Events
100
+
101
+ When you declare an event, say `discontinue`, three methods are declared for
102
+ you: `discontinue`, `discontinue!` and `can_discontinue?`. The first two
103
+ events will modify the `state` attribute on successful transition, but only
104
+ the bang(!)-version will call `save!`. The `can_discontinue?` method will not
105
+ modify state but instead returns a boolean letting you know if a given
106
+ transition is possible.
107
+
108
+ In addition, a `can_transition?` method is added to the object that expects one or more event names as arguments. This semi-verbose method name is used to avoid collission with [https://github.com/ryanb/cancan](the authorization gem CanCan).
109
+
110
+ >> Product.new.can_transition? :out_of_stock
111
+ => true
112
+
113
+ #### Automatic scope generation
114
+
115
+ `transitions` will automatically generate scopes for you if you are using
116
+ ActiveRecord and tell it to do so via the `auto_scopes` option:
117
+
118
+ Given a model like this:
119
+
120
+ class Order < ActiveRecord::Base
121
+ include ActiveModel::Transitions
122
+ state_machine :auto_scopes => true do
123
+ state :pick_line_items
124
+ state :picking_line_items
125
+ event :move_cart do
126
+ transitions to: :pick_line_items, from: :picking_line_items
127
+ end
128
+ end
129
+ end
130
+
131
+ you can use this feature a la:
132
+
133
+ >> Order.pick_line_items
134
+ => []
135
+ >> Order.create!
136
+ => #<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">
137
+ >> Order.pick_line_items
138
+ => [#<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">]
139
+
140
+ #### Using `guard`
141
+
142
+ Each event definition takes an optional `guard` argument, which acts as a
143
+ predicate for the transition.
144
+
145
+ You can pass in a Symbol, a String, or a Proc like this:
146
+
147
+ event :discontinue do
148
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
149
+ end
150
+
151
+ Any arguments passed to the event method will be passed on to the `guard`
152
+ predicate.
153
+
154
+ #### Using `on_transition`
155
+
156
+ Each event definition takes an optional `on_transition` argument, which allows
157
+ you to execute methods on transition.
158
+
159
+ You can pass in a Symbol, a String, a Proc or an Array containing method names
160
+ as Symbol or String like this:
161
+
162
+ event :discontinue do
163
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => [:do_discontinue, :notify_clerk]
164
+ end
165
+
166
+ Any arguments passed to the event method will be passed on to the `on_transition` callback.
167
+
168
+ `on_transition` is called after `guard` and before `enter` on the state that it is transitioning to.
169
+
170
+ #### Using `enter` and `exit`
171
+
172
+ If you want to trigger a method call when the object enters or exits a state regardless
173
+ of the transition that made that happen, use `enter` and `exit`.
174
+
175
+ `exit` will be called before the transition out of the state is executed. If you want the method
176
+ to only be called if the transition is successful, then use another approach.
177
+
178
+ `enter` will be called after the transition has been made but before the object is persisted. If you want
179
+ the method to only be called after a successful transition to a new state including persistence,
180
+ use the `success` argument to an event instead.
181
+
182
+ #### Using `success`
183
+
184
+ In case you need to trigger a method call after a successful transition you
185
+ can use `success`. This will be called after the `save!` is complete (if you
186
+ use the `state_name!` method) and should be used for any methods that require
187
+ that the object be persisted.
188
+
189
+ event :discontinue, :success => :notfiy_admin do
190
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
191
+ end
192
+
193
+ In addition to just specify the method name on the record as a symbol you can
194
+ pass a lambda to perfom some more complex success callbacks:
195
+
196
+ event :discontinue, :success => lambda { |order| AdminNotifier.notify_about_discontinued_order(order) } do
197
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
198
+ end
199
+
200
+ If you need it, you can even call multiple methods or lambdas just passing an
201
+ array:
202
+
203
+ event :discontinue, :success => [:notify_admin, lambda { |order| AdminNotifier.notify_about_discontinued_order(order) }] do
204
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
205
+ end
206
+
207
+ #### Timestamps
208
+
209
+ If you'd like to note the time of a state change, Transitions comes with
210
+ timestamps free! To activate them, simply pass the `timestamp` option to the
211
+ event definition with a value of either true or the name of the timestamp
212
+ column. *NOTE - This should be either true, a String or a Symbol*
213
+
214
+ # This will look for an attribute called exploded_at or exploded_on (in that order)
215
+ # If present, it will be updated
216
+ event :explode, :timestamp => true do
217
+ transitions :from => :complete, :to => :exploded
218
+ end
219
+
220
+ # This will look for an attribute named repaired_on to update upon save
221
+ event :rebuild, :timestamp => :repaired_on do
222
+ transitions :from => :exploded, :to => :rebuilt
223
+ end
224
+
225
+ #### Using `event_fired` and `event_failed`
226
+
227
+ In case you define `event_fired` and / or `event_failed`, `transitions` will
228
+ use those callbacks correspondingly.
229
+
230
+ You can use those callbacks like this:
231
+
232
+ def event_fired(current_state, new_state, event)
233
+ MyLogger.info "Event fired #{event.inspect}"
234
+ end
235
+
236
+ def event_failed(event)
237
+ MyLogger.warn "Event failed #{event.inspect}"
238
+ end
239
+
240
+ #### Listing all the available states and events
241
+
242
+ You can easily get a listing of all available states:
243
+
244
+ Order.available_states # Uses the <tt>default</tt> state machine
245
+ # => [:pick_line_items, :picking_line_items]
246
+
247
+ Same goes for the available events:
248
+
249
+ Order.available_events
250
+ # => [:move_cart]
251
+
252
+ #### Explicitly setting the initial state with the `initial` option
253
+
254
+ state_machine :initial => :closed do
255
+ state :open
256
+ state :closed
257
+ end
258
+
259
+ ### Configuring a different column name with ActiveRecord
260
+
261
+ To use a different column than `state` to track it's value simply do this:
262
+
263
+ class Product < ActiveRecord::Base
264
+ include Transitions
265
+
266
+ state_machine :attribute_name => :different_column do
267
+
268
+ ...
269
+
270
+ end
271
+ end
272
+
273
+ ### Known bugs / limitations
274
+
275
+ * Right now it seems like `transitions` does not play well with `mongoid`. A
276
+ possible fix had to be rolled back due to other side effects:
277
+ https://github.com/troessner/transitions/issues/76. Since I know virtually
278
+ zero about mongoid, a pull request would be highly appreciated.
279
+ * Multiple state machines are and will not be supported. For the rationale
280
+ behind this see the Changelog.
281
+
282
+
283
+ ### Documentation, Guides & Examples
284
+
285
+ * [Online API Documentation](http://rdoc.info/github/troessner/transitions/master/Transitions)
286
+ * [Railscasts #392: A Tour of State Machines](http://railscasts.com/episodes/392-a-tour-of-state-machines) (requires Pro subscription)
287
+
288
+
289
+ ### Copyright
290
+
291
+ Copyright (c) 2010 Jakub Kuźma, Timo Rößner. See LICENSE for details.
@@ -26,7 +26,7 @@ module ActiveModel
26
26
 
27
27
  included do
28
28
  class ::Transitions::Machine
29
- unless instance_methods.include?(:new_transitions_initialize) || instance_methods.include?(:new_transitions_update)
29
+ unless method_defined?(:new_transitions_initialize) || method_defined?(:new_transitions_update)
30
30
  attr_reader :attribute_name
31
31
  alias :old_transitions_initialize :initialize
32
32
  alias :old_transitions_update :update
@@ -88,6 +88,7 @@ module ActiveModel
88
88
 
89
89
  def set_initial_state
90
90
  self[transitions_state_column_name] ||= self.class.get_state_machine.initial_state.to_s if self.has_attribute?(transitions_state_column_name)
91
+ self.class.get_state_machine.state_index[self[transitions_state_column_name].to_sym].call_action(:enter, self)
91
92
  end
92
93
 
93
94
  def state_presence
data/lib/transitions.rb CHANGED
@@ -51,6 +51,10 @@ module Transitions
51
51
  def available_states
52
52
  @state_machine.states.map(&:name).sort_by {|x| x.to_s}
53
53
  end
54
+
55
+ def available_events
56
+ @state_machine.events.keys.sort
57
+ end
54
58
  end
55
59
 
56
60
  def self.included(base)
@@ -68,6 +72,16 @@ module Transitions
68
72
  instance_variable_set(ivar, new_state)
69
73
  end
70
74
 
75
+ def can_transition?(*events)
76
+ events.all? do |event|
77
+ self.class.get_state_machine.events_for(current_state).include?(event.to_sym)
78
+ end
79
+ end
80
+
81
+ def cant_transition?(*events)
82
+ !can_transition?(*events)
83
+ end
84
+
71
85
  def current_state
72
86
  sm = self.class.get_state_machine
73
87
  ivar = sm.current_state_variable
@@ -102,7 +102,7 @@ module Transitions
102
102
  if @klass.respond_to?(state_name)
103
103
  raise InvalidMethodOverride, "Transitions: Can not define scope `#{state_name}` because there is already an equally named method defined - either rename the existing method or the state."
104
104
  end
105
- @klass.scope state_name, @klass.where(@klass.state_machine.attribute_name => state_name)
105
+ @klass.scope state_name, -> { @klass.where(@klass.state_machine.attribute_name => state_name) }
106
106
  end
107
107
  end
108
108
  end
@@ -67,7 +67,7 @@ module Transitions
67
67
  private
68
68
  def define_state_query_method(machine)
69
69
  method_name, state_name = "#{@name}?", @name # Instance vars are out of scope when calling define_method below, so we use local variables.
70
- if machine.klass.instance_methods.include?(method_name.to_sym)
70
+ if machine.klass.method_defined?(method_name.to_sym)
71
71
  raise InvalidMethodOverride, "Transitions: Can not define method `#{method_name}` because it is already defined - either rename the existing method or the state."
72
72
  end
73
73
  machine.klass.send :define_method, method_name do
@@ -1,3 +1,3 @@
1
1
  module Transitions
2
- VERSION = '0.1.8'
2
+ VERSION = '0.1.9'
3
3
  end
@@ -24,9 +24,10 @@ set_up_db CreateDifferentTrafficLights
24
24
 
25
25
  class TrafficLight < ActiveRecord::Base
26
26
  include ActiveModel::Transitions
27
+ attr_reader :power
27
28
 
28
29
  state_machine :auto_scopes => true do
29
- state :off
30
+ state :off, enter: :turn_power_on
30
31
 
31
32
  state :red
32
33
  state :green
@@ -48,6 +49,11 @@ class TrafficLight < ActiveRecord::Base
48
49
  transitions :to => :red, :from => [:off]
49
50
  end
50
51
  end
52
+
53
+ def turn_power_on
54
+ raise "the power should not have been on already" if @power == :on
55
+ @power = :on
56
+ end
51
57
  end
52
58
 
53
59
  class ProtectedTrafficLight < TrafficLight
@@ -82,6 +88,11 @@ class TestActiveRecord < Test::Unit::TestCase
82
88
  assert_equal :off, @light.current_state
83
89
  end
84
90
 
91
+ test "calls enter when setting the initial state" do
92
+ @new_light = TrafficLight.new
93
+ assert_equal :on, @new_light.power
94
+ end
95
+
85
96
  test "transition to a valid state" do
86
97
  @light.reset
87
98
  assert @light.red?
@@ -7,6 +7,10 @@ class Bender
7
7
  state :drinking
8
8
  state :smoking
9
9
  state :gambling
10
+
11
+ event :cough do
12
+ transitions from: :smoking, to: :gambling
13
+ end
10
14
  end
11
15
  end
12
16
 
@@ -14,4 +18,7 @@ class TestAvailableStatesListing < Test::Unit::TestCase
14
18
  test 'available_states should return the states for the state machine' do
15
19
  assert_equal [:drinking, :gambling, :smoking], Bender.available_states
16
20
  end
21
+ test 'available_events should return the events for the state machine' do
22
+ assert_equal [:cough], Bender.available_events
23
+ end
17
24
  end
@@ -14,6 +14,10 @@ class MachineTestSubject
14
14
  event :timeout do
15
15
  transitions :from => :open, :to => :closed
16
16
  end
17
+
18
+ event :restart do
19
+ transitions :from => :closed, to: :open
20
+ end
17
21
  end
18
22
  end
19
23
 
@@ -32,6 +36,17 @@ class TransitionsMachineTest < Test::Unit::TestCase
32
36
  assert events.include?(:timeout)
33
37
  end
34
38
 
39
+ test "knows that it can use a transition when it is available" do
40
+ machine = MachineTestSubject.new
41
+ machine.restart
42
+ assert machine.can_transition?(:shutdown)
43
+ end
44
+
45
+ test "knows that it can't use a transition when it is unavailable" do
46
+ machine = MachineTestSubject.new
47
+ assert machine.cant_transition?(:shutdown)
48
+ end
49
+
35
50
  test "test fire_event" do
36
51
  pend "Implement me"
37
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transitions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-03-14 00:00:00.000000000 Z
13
+ date: 2013-05-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -136,7 +136,7 @@ files:
136
136
  - CHANGELOG.md
137
137
  - Gemfile
138
138
  - LICENSE
139
- - README.rdoc
139
+ - README.md
140
140
  - Rakefile
141
141
  - lib/active_model/transitions.rb
142
142
  - lib/active_record/transitions.rb
@@ -179,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
179
  version: '0'
180
180
  segments:
181
181
  - 0
182
- hash: 604342113
182
+ hash: -611477343
183
183
  required_rubygems_version: !ruby/object:Gem::Requirement
184
184
  none: false
185
185
  requirements:
@@ -188,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
188
  version: 1.3.6
189
189
  requirements: []
190
190
  rubyforge_project: transitions
191
- rubygems_version: 1.8.24
191
+ rubygems_version: 1.8.25
192
192
  signing_key:
193
193
  specification_version: 3
194
194
  summary: State machine extracted from ActiveModel
data/README.rdoc DELETED
@@ -1,214 +0,0 @@
1
- === Travis Build Status
2
-
3
- {<img src="https://secure.travis-ci.org/troessner/transitions.png?branch=master"/>}[http://travis-ci.org/troessner/transitions]
4
-
5
- === Synopsis
6
-
7
- <tt>transitions</tt> is a ruby state machine implementation.
8
-
9
- === Installation
10
-
11
- ==== Rails
12
-
13
- This goes into your Gemfile:
14
-
15
- gem "transitions", :require => ["transitions", "active_model/transitions"]
16
-
17
- … and this into your ORM model:
18
-
19
- include ActiveModel::Transitions
20
-
21
- ==== Standalone
22
-
23
- gem install transitions
24
-
25
- === Using transitions
26
-
27
- class Product
28
- include ActiveModel::Transitions
29
-
30
- state_machine do
31
- state :available # first one is initial state
32
- state :out_of_stock, :exit => :exit_out_of_stock
33
- state :discontinued, :enter => lambda { |product| product.cancel_orders }
34
-
35
- event :discontinued do
36
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
37
- end
38
- event :out_of_stock do
39
- transitions :to => :out_of_stock, :from => [:available, :discontinued]
40
- end
41
- event :available do
42
- transitions :to => :available, :from => [:out_of_stock], :guard => lambda { |product| product.in_stock > 0 }
43
- end
44
- end
45
- end
46
-
47
- In this example we assume that you are in a rails project using Bundler, which would automatically require `transitions`.
48
- If this is not the case for you you have to add
49
-
50
- require 'transitions'
51
-
52
- whereever you load your dependencies in your application.
53
-
54
- <b>Known limitations:</b>
55
-
56
- * You can only use one state machine per model. While in theory you can define two or more, this won't work as you would expect. Not supporting this was intentional, if you're interested in the ratione look up version 1.0.0 in the CHANGELOG.
57
-
58
- * 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).
59
-
60
- === Features
61
-
62
- ==== Getting and setting the current state
63
-
64
- Use the (surprise ahead) `current_state` method - in case you didn't set a state explicitly you'll get back the state that you defined as initial state.
65
-
66
- >> Product.new.current_state
67
- => :available
68
-
69
- You can also set a new state explicitly via `update_current_state(new_state, persist = true / false)` but you should never do this unless you really know what you're doing and why - rather use events / state transitions (see below).
70
-
71
- ==== Events
72
-
73
- When you declare an event, say <tt>discontinue</tt>, three methods are declared for
74
- you: <tt>discontinue</tt>, <tt>discontinue!</tt> and <tt>can_discontinue?</tt>. The first two events will modify the <tt>state</tt> attribute on successful transition,
75
- but only the bang(!)-version will call <tt>save!</tt>. The <tt>can_discontinue?</tt> method will
76
- not modify state but instead returns a boolean letting you know if a given transition is possible.
77
-
78
- ==== Automatic scope generation
79
-
80
- <tt>transitions</tt> will automatically generate scopes for you if you are using ActiveRecord and tell it to do so via the <tt>auto_scopes</tt> option:
81
-
82
- Given a model like this:
83
-
84
- class Order < ActiveRecord::Base
85
- include ActiveModel::Transitions
86
- state_machine :auto_scopes => true do
87
- state :pick_line_items
88
- state :picking_line_items
89
- end
90
- end
91
-
92
- you can use this feature a la:
93
-
94
- >> Order.pick_line_items
95
- => []
96
- >> Order.create!
97
- => #<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">
98
- >> Order.pick_line_items
99
- => [#<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">]
100
-
101
- ==== Using <tt>guard</tt>
102
-
103
- Each event definition takes an optional "guard" argument, which acts as a predicate for the transition.
104
- You can pass in a Symbol, a String, or a Proc like this:
105
-
106
- event :discontinue do
107
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
108
- end
109
-
110
- Any arguments passed to the event method will be passed on to the <tt>guard</tt> predicate.
111
-
112
- ==== Using <tt>on_transition</tt>
113
-
114
- Each event definition takes an optional "on_transition" argument, which allows you to execute methods on transition.
115
- You can pass in a Symbol, a String, a Proc or an Array containing method names as Symbol or String like this:
116
-
117
- event :discontinue do
118
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => [:do_discontinue, :notify_clerk]
119
- end
120
-
121
- Any arguments passed to the event method will be passed on to the <tt>on_transition</tt> callback.
122
-
123
- ==== Using <tt>success</tt>
124
-
125
- In case you need to trigger a method call after a successful transition you can use <tt>success</tt>:
126
-
127
- event :discontinue, :success => :notfiy_admin do
128
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
129
- end
130
-
131
- In addition to just specify the method name on the record as a symbol you can pass a lambda to
132
- perfom some more complex success callbacks:
133
-
134
- event :discontinue, :success => lambda { |order| AdminNotifier.notify_about_discontinued_order(order) } do
135
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
136
- end
137
-
138
- If you need it, you can even call multiple methods or lambdas just passing an array:
139
-
140
- event :discontinue, :success => [:notify_admin, lambda { |order| AdminNotifier.notify_about_discontinued_order(order) }] do
141
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
142
- end
143
-
144
- ==== Timestamps
145
-
146
- If you'd like to note the time of a state change, Transitions comes with timestamps free!
147
- To activate them, simply pass the :timestamp option to the event definition with a value of either true or
148
- the name of the timestamp column.
149
- *NOTE - This should be either true, a String or a Symbol*
150
-
151
- # This will look for an attribute called exploded_at or exploded_on (in that order)
152
- # If present, it will be updated
153
- event :explode, :timestamp => true do
154
- transitions :from => :complete, :to => :exploded
155
- end
156
-
157
- # This will look for an attribute named repaired_on to update upon save
158
- event :rebuild, :timestamp => :repaired_on do
159
- transitions :from => :exploded, :to => :rebuilt
160
- end
161
-
162
- ==== Using <tt>event_fired</tt> and <tt>event_failed</tt>
163
-
164
- In case you define `event_fired` and / or `event_failed`, `transitions` will use those callbacks correspondingly.
165
- You can use those callbacks like this:
166
-
167
- def event_fired(current_state, new_state, event)
168
- MyLogger.info "Event fired #{event.inspect}"
169
- end
170
-
171
- def event_failed(event)
172
- MyLogger.warn "Event failed #{event.inspect}"
173
- end
174
-
175
- ==== Listing all the available states
176
-
177
- You can easily get a listing of all available states:
178
-
179
- Order.available_states # Uses the <tt>default</tt> state machine
180
- # => [:pick_line_items, :picking_line_items]
181
-
182
- ==== Explicitly setting the initial state with the <tt>initial</tt> option
183
-
184
- state_machine :initial => :closed do
185
- state :open
186
- state :closed
187
- end
188
-
189
- === Configuring a different column name with ActiveRecord
190
-
191
- To use a different column than <tt>state</tt> to track it's value simply do this:
192
-
193
- class Product < ActiveRecord::Base
194
- include Transitions
195
-
196
- state_machine :attribute_name => :different_column do
197
-
198
- ...
199
-
200
- end
201
- end
202
-
203
- === Known bugs / limitations
204
-
205
- - 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.
206
- - Multiple state machines are and will not be supported. For the rationale behind this see the Changelog.
207
-
208
- === Documentation, Guides & Examples
209
-
210
- - {Online API Documentation}[http://rdoc.info/github/troessner/transitions/master/Transitions]
211
-
212
- === Copyright
213
-
214
- Copyright (c) 2010 Jakub Kuźma, Timo Rößner. See LICENSE for details.