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 +3 -1
- data/CHANGELOG.md +16 -9
- data/README.md +291 -0
- data/lib/active_model/transitions.rb +2 -1
- data/lib/transitions.rb +14 -0
- data/lib/transitions/machine.rb +1 -1
- data/lib/transitions/state.rb +1 -1
- data/lib/transitions/version.rb +1 -1
- data/test/active_record/test_active_record.rb +12 -1
- data/test/machine/test_available_states_listing.rb +7 -0
- data/test/machine/test_machine.rb +15 -0
- metadata +5 -5
- data/README.rdoc +0 -214
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,39 +1,46 @@
|
|
1
|
-
#
|
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.
|
12
|
+
# 0.1.7
|
6
13
|
|
7
14
|
* (cmw) Better prevention of name clashes when creating initializer methods
|
8
15
|
|
9
|
-
# 1.
|
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.
|
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.
|
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.
|
35
|
+
# 0.1.3
|
29
36
|
|
30
37
|
(troessner) Make sure `empty?` works on symbols.
|
31
38
|
|
32
|
-
# 1.
|
39
|
+
# 0.1.2
|
33
40
|
|
34
41
|
(troessner) Slightly improved handling of current states.
|
35
42
|
|
36
|
-
#
|
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
|
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
|
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
|
data/lib/transitions/machine.rb
CHANGED
@@ -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
|
data/lib/transitions/state.rb
CHANGED
@@ -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.
|
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
|
data/lib/transitions/version.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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:
|
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.
|
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.
|