aasm 3.0.16 → 3.0.17
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +2 -1
- data/API +34 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -1
- data/HOWTO +12 -0
- data/README.md +57 -4
- data/aasm.gemspec +2 -0
- data/lib/aasm.rb +5 -4
- data/lib/aasm/aasm.rb +50 -75
- data/lib/aasm/base.rb +22 -18
- data/lib/aasm/event.rb +130 -0
- data/lib/aasm/instance_base.rb +87 -0
- data/lib/aasm/localizer.rb +54 -0
- data/lib/aasm/persistence.rb +22 -14
- data/lib/aasm/persistence/active_record_persistence.rb +38 -69
- data/lib/aasm/persistence/base.rb +42 -2
- data/lib/aasm/persistence/mongoid_persistence.rb +33 -64
- data/lib/aasm/state.rb +78 -0
- data/lib/aasm/state_machine.rb +2 -2
- data/lib/aasm/transition.rb +49 -0
- data/lib/aasm/version.rb +1 -1
- data/spec/models/active_record/api.rb +75 -0
- data/spec/models/auth_machine.rb +1 -1
- data/spec/models/bar.rb +15 -0
- data/spec/models/foo.rb +34 -0
- data/spec/models/mongoid/simple_mongoid.rb +10 -0
- data/spec/models/mongoid/{mongoid_models.rb → simple_new_dsl_mongoid.rb} +1 -12
- data/spec/models/persistence.rb +2 -1
- data/spec/models/this_name_better_not_be_in_use.rb +11 -0
- data/spec/schema.rb +1 -1
- data/spec/spec_helper.rb +8 -1
- data/spec/unit/api_spec.rb +72 -0
- data/spec/unit/callbacks_spec.rb +2 -2
- data/spec/unit/event_spec.rb +269 -0
- data/spec/unit/inspection_spec.rb +43 -5
- data/spec/unit/{supporting_classes/localizer_spec.rb → localizer_spec.rb} +2 -2
- data/spec/unit/memory_leak_spec.rb +12 -12
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -40
- data/spec/unit/persistence/mongoid_persistance_spec.rb +3 -2
- data/spec/unit/simple_example_spec.rb +6 -0
- data/spec/unit/{supporting_classes/state_spec.rb → state_spec.rb} +2 -2
- data/spec/unit/{supporting_classes/state_transition_spec.rb → transition_spec.rb} +18 -18
- metadata +127 -38
- data/lib/aasm/persistence/read_state.rb +0 -40
- data/lib/aasm/supporting_classes/event.rb +0 -146
- data/lib/aasm/supporting_classes/localizer.rb +0 -56
- data/lib/aasm/supporting_classes/state.rb +0 -80
- data/lib/aasm/supporting_classes/state_transition.rb +0 -51
- data/spec/spec_helpers/models_spec_helper.rb +0 -64
- data/spec/unit/supporting_classes/event_spec.rb +0 -203
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/API
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
Overwrite method to read the current state. Used to provide another storage mechanism,
|
3
|
+
different from the standard Rails read_attribute method.
|
4
|
+
|
5
|
+
class MyClass
|
6
|
+
include AASM
|
7
|
+
|
8
|
+
def aasm_read_state
|
9
|
+
# retrieve the current state manually
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
Overwrite method to write the current state (and actually persist it). Used to provide
|
15
|
+
another storage mechanism, different from the standard Rails write_attribute method.
|
16
|
+
|
17
|
+
class MyClass
|
18
|
+
include AASM
|
19
|
+
|
20
|
+
def aasm_write_state
|
21
|
+
# store and persist the current state manually
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
Overwrite method to write the current state (without persisting it).
|
27
|
+
|
28
|
+
class MyClass
|
29
|
+
include AASM
|
30
|
+
|
31
|
+
def aasm_write_state_without_persistence
|
32
|
+
# store the current state manually
|
33
|
+
end
|
34
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
## 3.0.17
|
6
|
+
|
7
|
+
* supporting instance level inspection for states (including permissible state, see issue #54)
|
8
|
+
* added autocreation of constants for each state ([@jherdman](https://github.com/jherdman))
|
9
|
+
|
3
10
|
## 3.0.16
|
4
11
|
|
5
12
|
* added autocreation of state scopes for Mongoid (thanks to [@jonnyshields](https://github.com/johnnyshields))
|
data/Gemfile
CHANGED
data/HOWTO
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# AASM - Ruby state machines [![Build Status](https://secure.travis-ci.org/aasm/aasm.png)](http://travis-ci.org/aasm/aasm) [![Code Climate](https://codeclimate.com/
|
1
|
+
# AASM - Ruby state machines [![Build Status](https://secure.travis-ci.org/aasm/aasm.png)](http://travis-ci.org/aasm/aasm) [![Code Climate](https://codeclimate.com/github/aasm/aasm.png)](https://codeclimate.com/github/aasm/aasm) [![Coverage Status](https://coveralls.io/repos/aasm/aasm/badge.png?branch=master)](https://coveralls.io/r/aasm/aasm)
|
2
2
|
|
3
3
|
This package contains AASM, a library for adding finite state machines to Ruby classes.
|
4
4
|
|
@@ -81,14 +81,24 @@ class Job
|
|
81
81
|
state :running
|
82
82
|
|
83
83
|
event :run, :after => :notify_somebody do
|
84
|
-
transitions :from => :sleeping, :to => :running
|
84
|
+
transitions :from => :sleeping, :to => :running, :on_transition => Proc.new {|obj, *args| obj.set_process(*args) }
|
85
85
|
end
|
86
86
|
|
87
87
|
event :sleep do
|
88
|
+
after do
|
89
|
+
...
|
90
|
+
end
|
91
|
+
error do |e|
|
92
|
+
...
|
93
|
+
end
|
88
94
|
transitions :from => :running, :to => :sleeping
|
89
95
|
end
|
90
96
|
end
|
91
97
|
|
98
|
+
def set_process(name)
|
99
|
+
...
|
100
|
+
end
|
101
|
+
|
92
102
|
def do_something
|
93
103
|
...
|
94
104
|
end
|
@@ -116,6 +126,18 @@ Here you can see a list of all possible callbacks, together with their order of
|
|
116
126
|
event:after
|
117
127
|
```
|
118
128
|
|
129
|
+
Also, you can pass parameters to events:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
job = Job.new
|
133
|
+
job.run(:running, :defragmentation)
|
134
|
+
```
|
135
|
+
|
136
|
+
In this case the `set_process` would be called with `:defagmentation` argument.
|
137
|
+
|
138
|
+
In case of an error during the event processing the error is rescued and passed to `:error`
|
139
|
+
callback, which can handle it or re-raise it for further propagation.
|
140
|
+
|
119
141
|
### Guards
|
120
142
|
|
121
143
|
Let's assume you want to allow particular transitions only if a defined condition is
|
@@ -277,12 +299,36 @@ class AddJobState < ActiveRecord::Migration
|
|
277
299
|
end
|
278
300
|
|
279
301
|
def self.down
|
280
|
-
remove_column :
|
302
|
+
remove_column :jobs, :aasm_state
|
281
303
|
end
|
282
304
|
end
|
283
305
|
```
|
284
306
|
|
285
|
-
|
307
|
+
### <a id="inspection">Inspection
|
308
|
+
|
309
|
+
AASM supports a couple of methods to find out which states or events are provided or permissible.
|
310
|
+
|
311
|
+
Given the `Job` class from above:
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
job = Job.new
|
315
|
+
|
316
|
+
job.states
|
317
|
+
=> [:sleeping, :running, :cleaning]
|
318
|
+
|
319
|
+
job.states(:permissible => true)
|
320
|
+
=> [:running]
|
321
|
+
job.run
|
322
|
+
job.states(:permissible => true)
|
323
|
+
=> [:cleaning, :sleeping]
|
324
|
+
|
325
|
+
job.events
|
326
|
+
=> [:run, :clean, :sleep]
|
327
|
+
```
|
328
|
+
|
329
|
+
|
330
|
+
|
331
|
+
## <a id="installation">Installation ##
|
286
332
|
|
287
333
|
### Manually from RubyGems.org ###
|
288
334
|
|
@@ -308,6 +354,13 @@ gem 'aasm'
|
|
308
354
|
|
309
355
|
Look at the [CHANGELOG](https://github.com/aasm/aasm/blob/master/CHANGELOG.md) for details.
|
310
356
|
|
357
|
+
## Questions? ##
|
358
|
+
|
359
|
+
Feel free to
|
360
|
+
|
361
|
+
* [create an issue on GitHub](https://github.com/aasm/aasm/issues)
|
362
|
+
* [ask a question on StackOverflow](http://stackoverflow.com) (tag with `aasm`)
|
363
|
+
* send us a tweet [@aasm](http://twitter.com/aasm)
|
311
364
|
|
312
365
|
## Authors ##
|
313
366
|
|
data/aasm.gemspec
CHANGED
@@ -23,7 +23,9 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_development_dependency 'sqlite3'
|
24
24
|
s.add_development_dependency 'minitest'
|
25
25
|
# s.add_development_dependency 'debugger'
|
26
|
+
# s.add_development_dependency 'pry'
|
26
27
|
s.add_development_dependency 'ruby-debug-completion'
|
28
|
+
s.add_development_dependency 'coveralls'
|
27
29
|
|
28
30
|
s.files = `git ls-files`.split("\n")
|
29
31
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/aasm.rb
CHANGED
@@ -4,10 +4,11 @@ require 'ostruct'
|
|
4
4
|
require File.join(File.dirname(__FILE__), 'aasm', 'version')
|
5
5
|
require File.join(File.dirname(__FILE__), 'aasm', 'errors')
|
6
6
|
require File.join(File.dirname(__FILE__), 'aasm', 'base')
|
7
|
-
require File.join(File.dirname(__FILE__), 'aasm', '
|
8
|
-
require File.join(File.dirname(__FILE__), 'aasm', '
|
9
|
-
require File.join(File.dirname(__FILE__), 'aasm', '
|
10
|
-
require File.join(File.dirname(__FILE__), 'aasm', '
|
7
|
+
require File.join(File.dirname(__FILE__), 'aasm', 'instance_base')
|
8
|
+
require File.join(File.dirname(__FILE__), 'aasm', 'transition')
|
9
|
+
require File.join(File.dirname(__FILE__), 'aasm', 'event')
|
10
|
+
require File.join(File.dirname(__FILE__), 'aasm', 'state')
|
11
|
+
require File.join(File.dirname(__FILE__), 'aasm', 'localizer')
|
11
12
|
require File.join(File.dirname(__FILE__), 'aasm', 'state_machine')
|
12
13
|
require File.join(File.dirname(__FILE__), 'aasm', 'persistence')
|
13
14
|
require File.join(File.dirname(__FILE__), 'aasm', 'aasm')
|
data/lib/aasm/aasm.rb
CHANGED
@@ -3,18 +3,19 @@ module AASM
|
|
3
3
|
def self.included(base) #:nodoc:
|
4
4
|
base.extend AASM::ClassMethods
|
5
5
|
AASM::StateMachine[base] ||= AASM::StateMachine.new('')
|
6
|
-
AASM::Persistence.
|
6
|
+
AASM::Persistence.load_persistence(base)
|
7
7
|
super
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
11
|
|
12
12
|
# make sure inheritance (aka subclassing) works with AASM
|
13
|
-
def inherited(
|
14
|
-
AASM::StateMachine[
|
13
|
+
def inherited(base)
|
14
|
+
AASM::StateMachine[base] = AASM::StateMachine[self].clone
|
15
15
|
super
|
16
16
|
end
|
17
17
|
|
18
|
+
# this is the entry point for all state and event definitions
|
18
19
|
def aasm(options={}, &block)
|
19
20
|
@aasm ||= AASM::Base.new(self, options)
|
20
21
|
@aasm.instance_eval(&block) if block # new DSL
|
@@ -31,6 +32,7 @@ module AASM
|
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
35
|
+
# is this better?: aasm.states.name.from_states
|
34
36
|
def aasm_from_states_for_state(state, options={})
|
35
37
|
if options[:transition]
|
36
38
|
aasm.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten
|
@@ -71,100 +73,73 @@ module AASM
|
|
71
73
|
|
72
74
|
# aasm.event(:event_name).human?
|
73
75
|
def aasm_human_event_name(event) # event_name?
|
74
|
-
AASM::
|
76
|
+
AASM::Localizer.new.human_event_name(self, event)
|
75
77
|
end
|
76
78
|
end # ClassMethods
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
@aasm_current_state ||=
|
81
|
-
aasm_persistable? ? aasm_read_state : aasm_enter_initial_state
|
82
|
-
end
|
83
|
-
|
84
|
-
# private?
|
85
|
-
def aasm_enter_initial_state
|
86
|
-
state_name = aasm_determine_state_name(self.class.aasm_initial_state)
|
87
|
-
state = aasm_state_object_for_state(state_name)
|
88
|
-
|
89
|
-
state.fire_callbacks(:before_enter, self)
|
90
|
-
state.fire_callbacks(:enter, self)
|
91
|
-
self.aasm_current_state = state_name
|
92
|
-
state.fire_callbacks(:after_enter, self)
|
93
|
-
|
94
|
-
state_name
|
80
|
+
def aasm
|
81
|
+
@aasm ||= AASM::InstanceBase.new(self)
|
95
82
|
end
|
96
83
|
|
97
|
-
#
|
98
|
-
def
|
99
|
-
|
84
|
+
# may be overwritten by persistence mixins
|
85
|
+
def aasm_read_state
|
86
|
+
aasm.enter_initial_state
|
100
87
|
end
|
101
88
|
|
102
|
-
#
|
103
|
-
|
104
|
-
|
105
|
-
aasm_events_for_current_state.select{ |e| self.send(("may_" + e.to_s + "?").to_sym) }
|
89
|
+
# may be overwritten by persistence mixins
|
90
|
+
def aasm_write_state(new_state)
|
91
|
+
true
|
106
92
|
end
|
107
93
|
|
108
|
-
|
109
|
-
|
110
|
-
|
94
|
+
# may be overwritten by persistence mixins
|
95
|
+
def aasm_write_state_without_persistence(new_state)
|
96
|
+
true
|
111
97
|
end
|
112
98
|
|
113
|
-
|
114
|
-
|
99
|
+
# deprecated
|
100
|
+
def aasm_current_state
|
101
|
+
# warn "#aasm_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.state instead!"
|
102
|
+
aasm.current_state
|
115
103
|
end
|
116
104
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
105
|
+
# deprecated
|
106
|
+
def aasm_enter_initial_state
|
107
|
+
# warn "#aasm_enter_initial_state is deprecated and will be removed in version 3.2.0; please use #aasm.enter_initial_state instead!"
|
108
|
+
aasm.enter_initial_state
|
121
109
|
end
|
122
110
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
128
|
-
self.aasm_current_state = state if save_success
|
129
|
-
|
130
|
-
save_success
|
111
|
+
# deprecated
|
112
|
+
def aasm_events_for_current_state
|
113
|
+
# warn "#aasm_events_for_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.events instead!"
|
114
|
+
aasm.events(aasm.current_state)
|
131
115
|
end
|
132
116
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
@aasm_current_state = state
|
117
|
+
# deprecated
|
118
|
+
def aasm_permissible_events_for_current_state
|
119
|
+
# warn "#aasm_permissible_events_for_current_state is deprecated and will be removed in version 3.2.0; please use #aasm.permissible_events instead!"
|
120
|
+
aasm.permissible_events
|
138
121
|
end
|
139
122
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
when Proc
|
145
|
-
state.call(self)
|
146
|
-
else
|
147
|
-
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
148
|
-
end
|
123
|
+
# deprecated
|
124
|
+
def aasm_events_for_state(state_name)
|
125
|
+
# warn "#aasm_events_for_state(state_name) is deprecated and will be removed in version 3.2.0; please use #aasm.events(state_name) instead!"
|
126
|
+
aasm.events(state_name)
|
149
127
|
end
|
150
128
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
129
|
+
# deprecated
|
130
|
+
def aasm_human_state
|
131
|
+
# warn "#aasm_human_state is deprecated and will be removed in version 3.2.0; please use #aasm.human_state instead!"
|
132
|
+
aasm.human_state
|
155
133
|
end
|
156
134
|
|
157
|
-
|
158
|
-
event = self.class.aasm_events[name]
|
159
|
-
event.may_fire?(self, *args)
|
160
|
-
end
|
135
|
+
private
|
161
136
|
|
162
137
|
def aasm_fire_event(name, options, *args)
|
163
138
|
persist = options[:persist]
|
164
139
|
|
165
140
|
event = self.class.aasm_events[name]
|
166
141
|
begin
|
167
|
-
old_state =
|
142
|
+
old_state = aasm.state_object_for_name(aasm.current_state)
|
168
143
|
|
169
144
|
|
170
145
|
old_state.fire_callbacks(:exit, self)
|
@@ -173,7 +148,7 @@ private
|
|
173
148
|
event.fire_callbacks(:before, self)
|
174
149
|
|
175
150
|
if new_state_name = event.fire(self, *args)
|
176
|
-
new_state =
|
151
|
+
new_state = aasm.state_object_for_name(new_state_name)
|
177
152
|
|
178
153
|
# new before_ callbacks
|
179
154
|
old_state.fire_callbacks(:before_exit, self)
|
@@ -183,10 +158,10 @@ private
|
|
183
158
|
|
184
159
|
persist_successful = true
|
185
160
|
if persist
|
186
|
-
persist_successful =
|
187
|
-
event.
|
161
|
+
persist_successful = aasm.set_current_state_with_persistence(new_state_name)
|
162
|
+
event.fire_callbacks(:success, self) if persist_successful
|
188
163
|
else
|
189
|
-
|
164
|
+
aasm.current_state = new_state_name
|
190
165
|
end
|
191
166
|
|
192
167
|
if persist_successful
|
@@ -194,7 +169,7 @@ private
|
|
194
169
|
new_state.fire_callbacks(:after_enter, self)
|
195
170
|
event.fire_callbacks(:after, self)
|
196
171
|
|
197
|
-
self.aasm_event_fired(name, old_state.name,
|
172
|
+
self.aasm_event_fired(name, old_state.name, aasm.current_state) if self.respond_to?(:aasm_event_fired)
|
198
173
|
else
|
199
174
|
self.aasm_event_failed(name, old_state.name) if self.respond_to?(:aasm_event_failed)
|
200
175
|
end
|
@@ -207,13 +182,13 @@ private
|
|
207
182
|
end
|
208
183
|
|
209
184
|
if AASM::StateMachine[self.class].config.whiny_transitions
|
210
|
-
raise AASM::InvalidTransition, "Event '#{event.name}' cannot transition from '#{
|
185
|
+
raise AASM::InvalidTransition, "Event '#{event.name}' cannot transition from '#{aasm.current_state}'"
|
211
186
|
else
|
212
187
|
false
|
213
188
|
end
|
214
189
|
end
|
215
190
|
rescue StandardError => e
|
216
|
-
event.
|
191
|
+
event.fire_callbacks(:error, self, e) || raise(e)
|
217
192
|
end
|
218
193
|
end
|
219
194
|
end
|
data/lib/aasm/base.rb
CHANGED
@@ -3,50 +3,54 @@ module AASM
|
|
3
3
|
|
4
4
|
def initialize(clazz, options={}, &block)
|
5
5
|
@clazz = clazz
|
6
|
-
|
7
|
-
|
6
|
+
@state_machine = AASM::StateMachine[@clazz]
|
7
|
+
@state_machine.config.column = options[:column].to_sym if options[:column]
|
8
8
|
|
9
9
|
if options.key?(:whiny_transitions)
|
10
|
-
|
11
|
-
elsif
|
12
|
-
|
10
|
+
@state_machine.config.whiny_transitions = options[:whiny_transitions]
|
11
|
+
elsif @state_machine.config.whiny_transitions.nil?
|
12
|
+
@state_machine.config.whiny_transitions = true # this is the default, so let's cry
|
13
13
|
end
|
14
14
|
|
15
15
|
if options.key?(:skip_validation_on_save)
|
16
|
-
|
17
|
-
elsif
|
18
|
-
|
16
|
+
@state_machine.config.skip_validation_on_save = options[:skip_validation_on_save]
|
17
|
+
elsif @state_machine.config.skip_validation_on_save.nil?
|
18
|
+
@state_machine.config.skip_validation_on_save = false # this is the default, so don't store any new state if the model is invalid
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def initial_state
|
23
|
-
|
23
|
+
@state_machine.initial_state
|
24
24
|
end
|
25
25
|
|
26
|
+
# define a state
|
26
27
|
def state(name, options={})
|
27
28
|
# @clazz.aasm_state(name, options)
|
28
|
-
|
29
|
-
|
30
|
-
sm.initial_state = name if options[:initial] || !sm.initial_state
|
29
|
+
@state_machine.add_state(name, @clazz, options)
|
30
|
+
@state_machine.initial_state = name if options[:initial] || !@state_machine.initial_state
|
31
31
|
|
32
32
|
@clazz.send(:define_method, "#{name.to_s}?") do
|
33
33
|
aasm_current_state == name
|
34
34
|
end
|
35
|
+
|
36
|
+
unless @clazz.const_defined?("STATE_#{name.to_s.upcase}")
|
37
|
+
@clazz.const_set("STATE_#{name.to_s.upcase}", name)
|
38
|
+
end
|
35
39
|
end
|
36
40
|
|
41
|
+
# define an event
|
37
42
|
def event(name, options={}, &block)
|
38
43
|
# @clazz.aasm_event(name, options, &block)
|
39
|
-
sm = AASM::StateMachine[@clazz]
|
40
44
|
|
41
|
-
unless
|
42
|
-
|
45
|
+
unless @state_machine.events.has_key?(name)
|
46
|
+
@state_machine.events[name] = AASM::Event.new(name, options, &block)
|
43
47
|
end
|
44
48
|
|
45
49
|
# an addition over standard aasm so that, before firing an event, you can ask
|
46
50
|
# may_event? and get back a boolean that tells you whether the guard method
|
47
51
|
# on the transition will let this happen.
|
48
52
|
@clazz.send(:define_method, "may_#{name.to_s}?") do |*args|
|
49
|
-
|
53
|
+
aasm.may_fire_event?(name, *args)
|
50
54
|
end
|
51
55
|
|
52
56
|
@clazz.send(:define_method, "#{name.to_s}!") do |*args|
|
@@ -59,11 +63,11 @@ module AASM
|
|
59
63
|
end
|
60
64
|
|
61
65
|
def states
|
62
|
-
|
66
|
+
@state_machine.states
|
63
67
|
end
|
64
68
|
|
65
69
|
def events
|
66
|
-
|
70
|
+
@state_machine.events
|
67
71
|
end
|
68
72
|
|
69
73
|
def states_for_select
|