state_machines 0.101.0 → 0.200.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/state_machines/async_mode/async_transition_collection.rb +19 -24
- data/lib/state_machines/eval_helpers.rb +7 -23
- data/lib/state_machines/machine/callbacks.rb +301 -27
- data/lib/state_machines/machine/event_methods.rb +380 -3
- data/lib/state_machines/machine/state_methods.rb +297 -0
- data/lib/state_machines/machine/utilities.rb +4 -3
- data/lib/state_machines/machine.rb +0 -1031
- data/lib/state_machines/test_helper.rb +107 -227
- data/lib/state_machines/transition.rb +16 -32
- data/lib/state_machines/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 10897c2b3fdaea1f4728eb063c09f7996c6164f938d814dc486c746c4e91c081
|
|
4
|
+
data.tar.gz: e7e89af7bfa089d2f1a7c7324f028223e530231e808e34398a2665c11992c168
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cb901d7e14ae688dbfec57c312a6edc338156dc0b239a9f56f6898443bbb3ed33e1aad85a260b5ab6c6aa21f3513fdac1e19f24b09418d8994be6f6f80a50a41
|
|
7
|
+
data.tar.gz: ca11e59b75a28e0c3a9f27fae6a9ac194082a9a1a034d1160050f21c8645f69111e6179e64c5d0048143f5d095f789207372c9d2ef3b3d2a0a07e10cd154438d
|
|
@@ -31,18 +31,7 @@ module StateMachines
|
|
|
31
31
|
# Create async tasks for each transition
|
|
32
32
|
tasks = map do |transition|
|
|
33
33
|
Async do
|
|
34
|
-
|
|
35
|
-
transition.transient = true
|
|
36
|
-
transition.machine.write_safely(object, :event_transition, transition)
|
|
37
|
-
run_actions
|
|
38
|
-
transition
|
|
39
|
-
else
|
|
40
|
-
within_transaction do
|
|
41
|
-
catch(:halt) { run_callbacks(&block) }
|
|
42
|
-
rollback unless success?
|
|
43
|
-
end
|
|
44
|
-
transition
|
|
45
|
-
end
|
|
34
|
+
execute_transition(transition, &block)
|
|
46
35
|
end
|
|
47
36
|
end
|
|
48
37
|
|
|
@@ -80,18 +69,7 @@ module StateMachines
|
|
|
80
69
|
each do |transition|
|
|
81
70
|
threads << Thread.new do
|
|
82
71
|
begin
|
|
83
|
-
result =
|
|
84
|
-
transition.transient = true
|
|
85
|
-
transition.machine.write_safely(object, :event_transition, transition)
|
|
86
|
-
run_actions
|
|
87
|
-
transition
|
|
88
|
-
else
|
|
89
|
-
within_transaction do
|
|
90
|
-
catch(:halt) { run_callbacks(&block) }
|
|
91
|
-
rollback unless success?
|
|
92
|
-
end
|
|
93
|
-
transition
|
|
94
|
-
end
|
|
72
|
+
result = execute_transition(transition, &block)
|
|
95
73
|
|
|
96
74
|
results_mutex.with_write_lock { results << result }
|
|
97
75
|
rescue StandardError => e
|
|
@@ -112,6 +90,23 @@ module StateMachines
|
|
|
112
90
|
|
|
113
91
|
private
|
|
114
92
|
|
|
93
|
+
# Runs a single transition either via event attributes or the standard
|
|
94
|
+
# callback/transaction flow, returning the transition on completion
|
|
95
|
+
def execute_transition(transition, &block)
|
|
96
|
+
if use_event_attributes? && !block_given?
|
|
97
|
+
transition.transient = true
|
|
98
|
+
transition.machine.write_safely(object, :event_transition, transition)
|
|
99
|
+
run_actions
|
|
100
|
+
else
|
|
101
|
+
within_transaction do
|
|
102
|
+
catch(:halt) { run_callbacks(&block) }
|
|
103
|
+
rollback unless success?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
transition
|
|
108
|
+
end
|
|
109
|
+
|
|
115
110
|
# Override run_actions to be thread-safe when needed
|
|
116
111
|
def run_actions(&block)
|
|
117
112
|
catch_exceptions do
|
|
@@ -163,41 +163,25 @@ module StateMachines
|
|
|
163
163
|
# Symbol methods currently don't support event arguments
|
|
164
164
|
# This maintains backward compatibility
|
|
165
165
|
evaluate_method(object, method)
|
|
166
|
-
in Proc =>
|
|
167
|
-
arity =
|
|
166
|
+
in Proc | Method => callable
|
|
167
|
+
arity = callable.arity
|
|
168
168
|
|
|
169
169
|
# Arity-based decision for backward compatibility using pattern matching
|
|
170
170
|
case arity
|
|
171
171
|
in 0
|
|
172
|
-
|
|
172
|
+
callable.call
|
|
173
173
|
in 1
|
|
174
|
-
|
|
174
|
+
callable.call(object)
|
|
175
175
|
in -1
|
|
176
176
|
# Splat parameters: object + all event args
|
|
177
|
-
|
|
177
|
+
callable.call(object, *event_args)
|
|
178
178
|
in arity if arity > 1
|
|
179
179
|
# Explicit parameters: object + limited event args
|
|
180
180
|
args_needed = arity - 1 # Subtract 1 for the object parameter
|
|
181
|
-
|
|
181
|
+
callable.call(object, *event_args[0, args_needed])
|
|
182
182
|
else
|
|
183
183
|
# Negative arity other than -1 (unlikely but handle gracefully)
|
|
184
|
-
|
|
185
|
-
end
|
|
186
|
-
in Method => meth
|
|
187
|
-
arity = meth.arity
|
|
188
|
-
|
|
189
|
-
case arity
|
|
190
|
-
in 0
|
|
191
|
-
meth.call
|
|
192
|
-
in 1
|
|
193
|
-
meth.call(object)
|
|
194
|
-
in -1
|
|
195
|
-
meth.call(object, *event_args)
|
|
196
|
-
in arity if arity > 1
|
|
197
|
-
args_needed = arity - 1
|
|
198
|
-
meth.call(object, *event_args[0, args_needed])
|
|
199
|
-
else
|
|
200
|
-
meth.call(object, *event_args)
|
|
184
|
+
callable.call(object, *event_args)
|
|
201
185
|
end
|
|
202
186
|
in String
|
|
203
187
|
# String evaluation doesn't support event arguments for security
|
|
@@ -5,46 +5,320 @@ module StateMachines
|
|
|
5
5
|
module Callbacks
|
|
6
6
|
# Creates a callback that will be invoked *before* a transition is
|
|
7
7
|
# performed so long as the given requirements match the transition.
|
|
8
|
+
#
|
|
9
|
+
# == The callback
|
|
10
|
+
#
|
|
11
|
+
# Callbacks must be defined as either an argument, in the :do option, or
|
|
12
|
+
# as a block. For example,
|
|
13
|
+
#
|
|
14
|
+
# class Vehicle
|
|
15
|
+
# state_machine do
|
|
16
|
+
# before_transition :set_alarm
|
|
17
|
+
# before_transition :set_alarm, all => :parked
|
|
18
|
+
# before_transition all => :parked, :do => :set_alarm
|
|
19
|
+
# before_transition all => :parked do |vehicle, transition|
|
|
20
|
+
# vehicle.set_alarm
|
|
21
|
+
# end
|
|
22
|
+
# ...
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# Notice that the first three callbacks are the same in terms of how the
|
|
27
|
+
# methods to invoke are defined. However, using the <tt>:do</tt> can
|
|
28
|
+
# provide for a more fluid DSL.
|
|
29
|
+
#
|
|
30
|
+
# In addition, multiple callbacks can be defined like so:
|
|
31
|
+
#
|
|
32
|
+
# class Vehicle
|
|
33
|
+
# state_machine do
|
|
34
|
+
# before_transition :set_alarm, :lock_doors, all => :parked
|
|
35
|
+
# before_transition all => :parked, :do => [:set_alarm, :lock_doors]
|
|
36
|
+
# before_transition :set_alarm do |vehicle, transition|
|
|
37
|
+
# vehicle.lock_doors
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# Notice that the different ways of configuring methods can be mixed.
|
|
43
|
+
#
|
|
44
|
+
# == State requirements
|
|
45
|
+
#
|
|
46
|
+
# Callbacks can require that the machine be transitioning from and to
|
|
47
|
+
# specific states. These requirements use a Hash syntax to map beginning
|
|
48
|
+
# states to ending states. For example,
|
|
49
|
+
#
|
|
50
|
+
# before_transition :parked => :idling, :idling => :first_gear, :do => :set_alarm
|
|
51
|
+
#
|
|
52
|
+
# In this case, the +set_alarm+ callback will only be called if the machine
|
|
53
|
+
# is transitioning from +parked+ to +idling+ or from +idling+ to +parked+.
|
|
54
|
+
#
|
|
55
|
+
# To help define state requirements, a set of helpers are available for
|
|
56
|
+
# slightly more complex matching:
|
|
57
|
+
# * <tt>all</tt> - Matches every state/event in the machine
|
|
58
|
+
# * <tt>all - [:parked, :idling, ...]</tt> - Matches every state/event except those specified
|
|
59
|
+
# * <tt>any</tt> - An alias for +all+ (matches every state/event in the machine)
|
|
60
|
+
# * <tt>same</tt> - Matches the same state being transitioned from
|
|
61
|
+
#
|
|
62
|
+
# See StateMachines::MatcherHelpers for more information.
|
|
63
|
+
#
|
|
64
|
+
# Examples:
|
|
65
|
+
#
|
|
66
|
+
# before_transition :parked => [:idling, :first_gear], :do => ... # Matches from parked to idling or first_gear
|
|
67
|
+
# before_transition all - [:parked, :idling] => :idling, :do => ... # Matches from every state except parked and idling to idling
|
|
68
|
+
# before_transition all => :parked, :do => ... # Matches all states to parked
|
|
69
|
+
# before_transition any => same, :do => ... # Matches every loopback
|
|
70
|
+
#
|
|
71
|
+
# == Event requirements
|
|
72
|
+
#
|
|
73
|
+
# In addition to state requirements, an event requirement can be defined so
|
|
74
|
+
# that the callback is only invoked on specific events using the +on+
|
|
75
|
+
# option. This can also use the same matcher helpers as the state
|
|
76
|
+
# requirements.
|
|
77
|
+
#
|
|
78
|
+
# Examples:
|
|
79
|
+
#
|
|
80
|
+
# before_transition :on => :ignite, :do => ... # Matches only on ignite
|
|
81
|
+
# before_transition :on => all - :ignite, :do => ... # Matches on every event except ignite
|
|
82
|
+
# before_transition :parked => :idling, :on => :ignite, :do => ... # Matches from parked to idling on ignite
|
|
83
|
+
#
|
|
84
|
+
# == Verbose Requirements
|
|
85
|
+
#
|
|
86
|
+
# Requirements can also be defined using verbose options rather than the
|
|
87
|
+
# implicit Hash syntax and helper methods described above.
|
|
88
|
+
#
|
|
89
|
+
# Configuration options:
|
|
90
|
+
# * <tt>:from</tt> - One or more states being transitioned from. If none
|
|
91
|
+
# are specified, then all states will match.
|
|
92
|
+
# * <tt>:to</tt> - One or more states being transitioned to. If none are
|
|
93
|
+
# specified, then all states will match.
|
|
94
|
+
# * <tt>:on</tt> - One or more events that fired the transition. If none
|
|
95
|
+
# are specified, then all events will match.
|
|
96
|
+
# * <tt>:except_from</tt> - One or more states *not* being transitioned from
|
|
97
|
+
# * <tt>:except_to</tt> - One more states *not* being transitioned to
|
|
98
|
+
# * <tt>:except_on</tt> - One or more events that *did not* fire the transition
|
|
99
|
+
#
|
|
100
|
+
# Examples:
|
|
101
|
+
#
|
|
102
|
+
# before_transition :from => :ignite, :to => :idling, :on => :park, :do => ...
|
|
103
|
+
# before_transition :except_from => :ignite, :except_to => :idling, :except_on => :park, :do => ...
|
|
104
|
+
#
|
|
105
|
+
# == Conditions
|
|
106
|
+
#
|
|
107
|
+
# In addition to the state/event requirements, a condition can also be
|
|
108
|
+
# defined to help determine whether the callback should be invoked.
|
|
109
|
+
#
|
|
110
|
+
# Configuration options:
|
|
111
|
+
# * <tt>:if</tt> - A method, proc or string to call to determine if the
|
|
112
|
+
# callback should occur (e.g. :if => :allow_callbacks, or
|
|
113
|
+
# :if => lambda {|user| user.signup_step > 2}). The method, proc or string
|
|
114
|
+
# should return or evaluate to a true or false value.
|
|
115
|
+
# * <tt>:unless</tt> - A method, proc or string to call to determine if the
|
|
116
|
+
# callback should not occur (e.g. :unless => :skip_callbacks, or
|
|
117
|
+
# :unless => lambda {|user| user.signup_step <= 2}). The method, proc or
|
|
118
|
+
# string should return or evaluate to a true or false value.
|
|
119
|
+
#
|
|
120
|
+
# Examples:
|
|
121
|
+
#
|
|
122
|
+
# before_transition :parked => :idling, :if => :moving?, :do => ...
|
|
123
|
+
# before_transition :on => :ignite, :unless => :seatbelt_on?, :do => ...
|
|
124
|
+
#
|
|
125
|
+
# == Accessing the transition
|
|
126
|
+
#
|
|
127
|
+
# In addition to passing the object being transitioned, the actual
|
|
128
|
+
# transition describing the context (e.g. event, from, to) can be accessed
|
|
129
|
+
# as well. This additional argument is only passed if the callback allows
|
|
130
|
+
# for it.
|
|
131
|
+
#
|
|
132
|
+
# For example,
|
|
133
|
+
#
|
|
134
|
+
# class Vehicle
|
|
135
|
+
# # Only specifies one parameter (the object being transitioned)
|
|
136
|
+
# before_transition all => :parked do |vehicle|
|
|
137
|
+
# vehicle.set_alarm
|
|
138
|
+
# end
|
|
139
|
+
#
|
|
140
|
+
# # Specifies 2 parameters (object being transitioned and actual transition)
|
|
141
|
+
# before_transition all => :parked do |vehicle, transition|
|
|
142
|
+
# vehicle.set_alarm(transition)
|
|
143
|
+
# end
|
|
144
|
+
# end
|
|
145
|
+
#
|
|
146
|
+
# *Note* that the object in the callback will only be passed in as an
|
|
147
|
+
# argument if callbacks are configured to *not* be bound to the object
|
|
148
|
+
# involved. This is the default and may change on a per-integration basis.
|
|
149
|
+
#
|
|
150
|
+
# See StateMachines::Transition for more information about the
|
|
151
|
+
# attributes available on the transition.
|
|
152
|
+
#
|
|
153
|
+
# == Usage with delegates
|
|
154
|
+
#
|
|
155
|
+
# As noted above, state_machine uses the callback method's argument list
|
|
156
|
+
# arity to determine whether to include the transition in the method call.
|
|
157
|
+
# If you're using delegates, such as those defined in ActiveSupport or
|
|
158
|
+
# Forwardable, the actual arity of the delegated method gets masked. This
|
|
159
|
+
# means that callbacks which reference delegates will always get passed the
|
|
160
|
+
# transition as an argument. For example:
|
|
161
|
+
#
|
|
162
|
+
# class Vehicle
|
|
163
|
+
# extend Forwardable
|
|
164
|
+
# delegate :refresh => :dashboard
|
|
165
|
+
#
|
|
166
|
+
# state_machine do
|
|
167
|
+
# before_transition :refresh
|
|
168
|
+
# ...
|
|
169
|
+
# end
|
|
170
|
+
#
|
|
171
|
+
# def dashboard
|
|
172
|
+
# @dashboard ||= Dashboard.new
|
|
173
|
+
# end
|
|
174
|
+
# end
|
|
175
|
+
#
|
|
176
|
+
# class Dashboard
|
|
177
|
+
# def refresh(transition)
|
|
178
|
+
# # ...
|
|
179
|
+
# end
|
|
180
|
+
# end
|
|
181
|
+
#
|
|
182
|
+
# In the above example, <tt>Dashboard#refresh</tt> *must* defined a
|
|
183
|
+
# +transition+ argument. Otherwise, an +ArgumentError+ exception will get
|
|
184
|
+
# raised. The only way around this is to avoid the use of delegates and
|
|
185
|
+
# manually define the delegate method so that the correct arity is used.
|
|
186
|
+
#
|
|
187
|
+
# == Examples
|
|
188
|
+
#
|
|
189
|
+
# Below is an example of a class with one state machine and various types
|
|
190
|
+
# of +before+ transitions defined for it:
|
|
191
|
+
#
|
|
192
|
+
# class Vehicle
|
|
193
|
+
# state_machine do
|
|
194
|
+
# # Before all transitions
|
|
195
|
+
# before_transition :update_dashboard
|
|
196
|
+
#
|
|
197
|
+
# # Before specific transition:
|
|
198
|
+
# before_transition [:first_gear, :idling] => :parked, :on => :park, :do => :take_off_seatbelt
|
|
199
|
+
#
|
|
200
|
+
# # With conditional callback:
|
|
201
|
+
# before_transition all => :parked, :do => :take_off_seatbelt, :if => :seatbelt_on?
|
|
202
|
+
#
|
|
203
|
+
# # Using helpers:
|
|
204
|
+
# before_transition all - :stalled => same, :on => any - :crash, :do => :update_dashboard
|
|
205
|
+
# ...
|
|
206
|
+
# end
|
|
207
|
+
# end
|
|
208
|
+
#
|
|
209
|
+
# As can be seen, any number of transitions can be created using various
|
|
210
|
+
# combinations of configuration options.
|
|
8
211
|
def before_transition(*args, **options, &)
|
|
9
|
-
|
|
10
|
-
parsed_options = parse_callback_arguments(args, options)
|
|
11
|
-
|
|
12
|
-
# Only validate callback-specific options, not state transition requirements
|
|
13
|
-
callback_options = parsed_options.slice(:do, :if, :unless, :bind_to_object, :terminator)
|
|
14
|
-
StateMachines::OptionsValidator.assert_valid_keys!(callback_options, :do, :if, :unless, :bind_to_object, :terminator)
|
|
15
|
-
|
|
16
|
-
add_callback(:before, parsed_options, &)
|
|
212
|
+
add_transition_callback(:before, args, options, &)
|
|
17
213
|
end
|
|
18
214
|
|
|
19
215
|
# Creates a callback that will be invoked *after* a transition is
|
|
20
216
|
# performed so long as the given requirements match the transition.
|
|
217
|
+
#
|
|
218
|
+
# See +before_transition+ for a description of the possible configurations
|
|
219
|
+
# for defining callbacks.
|
|
21
220
|
def after_transition(*args, **options, &)
|
|
22
|
-
|
|
23
|
-
parsed_options = parse_callback_arguments(args, options)
|
|
24
|
-
|
|
25
|
-
# Only validate callback-specific options, not state transition requirements
|
|
26
|
-
callback_options = parsed_options.slice(:do, :if, :unless, :bind_to_object, :terminator)
|
|
27
|
-
StateMachines::OptionsValidator.assert_valid_keys!(callback_options, :do, :if, :unless, :bind_to_object, :terminator)
|
|
28
|
-
|
|
29
|
-
add_callback(:after, parsed_options, &)
|
|
221
|
+
add_transition_callback(:after, args, options, &)
|
|
30
222
|
end
|
|
31
223
|
|
|
32
|
-
# Creates a callback that will be invoked *around* a transition so long
|
|
33
|
-
#
|
|
224
|
+
# Creates a callback that will be invoked *around* a transition so long as
|
|
225
|
+
# the given requirements match the transition.
|
|
226
|
+
#
|
|
227
|
+
# == The callback
|
|
228
|
+
#
|
|
229
|
+
# Around callbacks wrap transitions, executing code both before and after.
|
|
230
|
+
# These callbacks are defined in the exact same manner as before / after
|
|
231
|
+
# callbacks with the exception that the transition must be yielded to in
|
|
232
|
+
# order to finish running it.
|
|
233
|
+
#
|
|
234
|
+
# If defining +around+ callbacks using blocks, you must yield within the
|
|
235
|
+
# transition by directly calling the block (since yielding is not allowed
|
|
236
|
+
# within blocks).
|
|
237
|
+
#
|
|
238
|
+
# For example,
|
|
239
|
+
#
|
|
240
|
+
# class Vehicle
|
|
241
|
+
# state_machine do
|
|
242
|
+
# around_transition do |block|
|
|
243
|
+
# Benchmark.measure { block.call }
|
|
244
|
+
# end
|
|
245
|
+
#
|
|
246
|
+
# around_transition do |vehicle, block|
|
|
247
|
+
# logger.info "vehicle was #{state}..."
|
|
248
|
+
# block.call
|
|
249
|
+
# logger.info "...and is now #{state}"
|
|
250
|
+
# end
|
|
251
|
+
#
|
|
252
|
+
# around_transition do |vehicle, transition, block|
|
|
253
|
+
# logger.info "before #{transition.event}: #{vehicle.state}"
|
|
254
|
+
# block.call
|
|
255
|
+
# logger.info "after #{transition.event}: #{vehicle.state}"
|
|
256
|
+
# end
|
|
257
|
+
# end
|
|
258
|
+
# end
|
|
259
|
+
#
|
|
260
|
+
# Notice that referencing the block is similar to doing so within an
|
|
261
|
+
# actual method definition in that it is always the last argument.
|
|
262
|
+
#
|
|
263
|
+
# On the other hand, if you're defining +around+ callbacks using method
|
|
264
|
+
# references, you can yield like normal:
|
|
265
|
+
#
|
|
266
|
+
# class Vehicle
|
|
267
|
+
# state_machine do
|
|
268
|
+
# around_transition :benchmark
|
|
269
|
+
# ...
|
|
270
|
+
# end
|
|
271
|
+
#
|
|
272
|
+
# def benchmark
|
|
273
|
+
# Benchmark.measure { yield }
|
|
274
|
+
# end
|
|
275
|
+
# end
|
|
276
|
+
#
|
|
277
|
+
# See +before_transition+ for a description of the possible configurations
|
|
278
|
+
# for defining callbacks.
|
|
34
279
|
def around_transition(*args, **options, &)
|
|
280
|
+
add_transition_callback(:around, args, options, &)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Creates a callback that will be invoked *after* a transition failures to
|
|
284
|
+
# be performed so long as the given requirements match the transition.
|
|
285
|
+
#
|
|
286
|
+
# See +before_transition+ for a description of the possible configurations
|
|
287
|
+
# for defining callbacks. *Note* however that you cannot define the state
|
|
288
|
+
# requirements in these callbacks. You may only define event requirements.
|
|
289
|
+
#
|
|
290
|
+
# = The callback
|
|
291
|
+
#
|
|
292
|
+
# Failure callbacks get invoked whenever an event fails to execute. This
|
|
293
|
+
# can happen when no transition is available, a +before+ callback halts
|
|
294
|
+
# execution, or the action associated with this machine fails to succeed.
|
|
295
|
+
# In any of these cases, any failure callback that matches the attempted
|
|
296
|
+
# transition will be run.
|
|
297
|
+
#
|
|
298
|
+
# For example,
|
|
299
|
+
#
|
|
300
|
+
# class Vehicle
|
|
301
|
+
# state_machine do
|
|
302
|
+
# after_failure do |vehicle, transition|
|
|
303
|
+
# logger.error "vehicle #{vehicle} failed to transition on #{transition.event}"
|
|
304
|
+
# end
|
|
305
|
+
#
|
|
306
|
+
# after_failure :on => :ignite, :do => :log_ignition_failure
|
|
307
|
+
#
|
|
308
|
+
# ...
|
|
309
|
+
# end
|
|
310
|
+
# end
|
|
311
|
+
def after_failure(*args, **options, &)
|
|
35
312
|
# Extract legacy positional arguments and merge with keyword options
|
|
36
313
|
parsed_options = parse_callback_arguments(args, options)
|
|
314
|
+
StateMachines::OptionsValidator.assert_valid_keys!(parsed_options, :on, :do, :if, :unless)
|
|
37
315
|
|
|
38
|
-
|
|
39
|
-
callback_options = parsed_options.slice(:do, :if, :unless, :bind_to_object, :terminator)
|
|
40
|
-
StateMachines::OptionsValidator.assert_valid_keys!(callback_options, :do, :if, :unless, :bind_to_object, :terminator)
|
|
41
|
-
|
|
42
|
-
add_callback(:around, parsed_options, &)
|
|
316
|
+
add_callback(:failure, parsed_options, &)
|
|
43
317
|
end
|
|
44
318
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def
|
|
319
|
+
private
|
|
320
|
+
|
|
321
|
+
def add_transition_callback(type, args, options, &)
|
|
48
322
|
# Extract legacy positional arguments and merge with keyword options
|
|
49
323
|
parsed_options = parse_callback_arguments(args, options)
|
|
50
324
|
|
|
@@ -52,7 +326,7 @@ module StateMachines
|
|
|
52
326
|
callback_options = parsed_options.slice(:do, :if, :unless, :bind_to_object, :terminator)
|
|
53
327
|
StateMachines::OptionsValidator.assert_valid_keys!(callback_options, :do, :if, :unless, :bind_to_object, :terminator)
|
|
54
328
|
|
|
55
|
-
add_callback(
|
|
329
|
+
add_callback(type, parsed_options, &)
|
|
56
330
|
end
|
|
57
331
|
end
|
|
58
332
|
end
|