state_machine 0.7.5 → 0.7.6
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.
- data/CHANGELOG.rdoc +10 -0
- data/README.rdoc +1 -1
- data/Rakefile +9 -2
- data/lib/state_machine.rb +17 -12
- data/lib/state_machine/assertions.rb +4 -5
- data/lib/state_machine/callback.rb +27 -21
- data/lib/state_machine/condition_proxy.rb +1 -1
- data/lib/state_machine/eval_helpers.rb +1 -2
- data/lib/state_machine/event.rb +3 -3
- data/lib/state_machine/event_collection.rb +5 -6
- data/lib/state_machine/integrations/active_record.rb +25 -16
- data/lib/state_machine/integrations/active_record/locale.rb +1 -0
- data/lib/state_machine/integrations/data_mapper.rb +6 -9
- data/lib/state_machine/integrations/data_mapper/observer.rb +5 -5
- data/lib/state_machine/integrations/sequel.rb +5 -7
- data/lib/state_machine/machine.rb +65 -55
- data/lib/state_machine/machine_collection.rb +15 -17
- data/lib/state_machine/matcher_helpers.rb +1 -0
- data/lib/state_machine/state.rb +6 -4
- data/lib/state_machine/state_collection.rb +3 -3
- data/lib/state_machine/transition.rb +3 -3
- data/test/unit/eval_helpers_test.rb +18 -0
- data/test/unit/event_collection_test.rb +30 -0
- data/test/unit/integrations/active_record_test.rb +78 -8
- data/test/unit/integrations/data_mapper_test.rb +77 -0
- data/test/unit/integrations/sequel_test.rb +54 -2
- data/test/unit/machine_collection_test.rb +20 -0
- data/test/unit/machine_test.rb +154 -5
- data/test/unit/state_test.rb +17 -3
- data/test/unit/transition_test.rb +27 -0
- metadata +2 -2
@@ -109,19 +109,19 @@ module StateMachine
|
|
109
109
|
# state machines for each observed class.
|
110
110
|
def add_transition_callback(type, *args, &block)
|
111
111
|
if args.any? && !args.first.is_a?(Hash)
|
112
|
-
# Specific
|
113
|
-
|
112
|
+
# Specific machine(s) being targeted
|
113
|
+
names = args
|
114
114
|
args = args.last.is_a?(Hash) ? [args.pop] : []
|
115
115
|
else
|
116
116
|
# Target all state machines
|
117
|
-
|
117
|
+
names = nil
|
118
118
|
end
|
119
119
|
|
120
120
|
# Add the transition callback to each class being observed
|
121
121
|
observing.each do |klass|
|
122
122
|
state_machines =
|
123
|
-
if
|
124
|
-
|
123
|
+
if names
|
124
|
+
names.map {|name| klass.state_machines.fetch(name)}
|
125
125
|
else
|
126
126
|
klass.state_machines.values
|
127
127
|
end
|
@@ -84,17 +84,14 @@ module StateMachine
|
|
84
84
|
# you can build two state machines (one public and one protected) like so:
|
85
85
|
#
|
86
86
|
# class Vehicle < Sequel::Model
|
87
|
-
# # Allow both machines to share the same state
|
88
|
-
# alias_method :public_state, :state
|
89
|
-
# alias_method :public_state=, :state=
|
90
|
-
#
|
91
87
|
# set_restricted_columns :state_event # Prevent access to events in the first machine
|
92
88
|
#
|
93
89
|
# state_machine do
|
94
90
|
# # Define private events here
|
95
91
|
# end
|
96
92
|
#
|
97
|
-
#
|
93
|
+
# # Allow both machines to share the same state
|
94
|
+
# state_machine :public_state, :attribute => :state do
|
98
95
|
# # Define public events here
|
99
96
|
# end
|
100
97
|
# end
|
@@ -231,7 +228,7 @@ module StateMachine
|
|
231
228
|
|
232
229
|
# Adds a validation error to the given object
|
233
230
|
def invalidate(object, attribute, message, values = [])
|
234
|
-
object.errors.add(attribute, generate_message(message, values))
|
231
|
+
object.errors.add(self.attribute(attribute), generate_message(message, values))
|
235
232
|
end
|
236
233
|
|
237
234
|
# Resets any errors previously added when invalidating the given object
|
@@ -242,8 +239,9 @@ module StateMachine
|
|
242
239
|
protected
|
243
240
|
# Skips defining reader/writer methods since this is done automatically
|
244
241
|
def define_state_accessor
|
242
|
+
name = self.name
|
245
243
|
owner_class.validates_each(attribute) do |record, attr, value|
|
246
|
-
machine = record.class.state_machine(
|
244
|
+
machine = record.class.state_machine(name)
|
247
245
|
machine.invalidate(record, attr, :invalid) unless machine.states.match(record)
|
248
246
|
end
|
249
247
|
end
|
@@ -113,14 +113,14 @@ module StateMachine
|
|
113
113
|
#
|
114
114
|
# Callbacks are supported for hooking before and after every possible
|
115
115
|
# transition in the machine. Each callback is invoked in the order in which
|
116
|
-
# it was defined. See StateMachine::Machine#before_transition
|
117
|
-
#
|
118
|
-
#
|
116
|
+
# it was defined. See StateMachine::Machine#before_transition and
|
117
|
+
# StateMachine::Machine#after_transition for documentation on how to define
|
118
|
+
# new callbacks.
|
119
119
|
#
|
120
|
-
# *Note* that callbacks only get executed within the context of an event.
|
121
|
-
#
|
122
|
-
#
|
123
|
-
#
|
120
|
+
# *Note* that callbacks only get executed within the context of an event. As
|
121
|
+
# a result, if a class has an initial state when it's created, any callbacks
|
122
|
+
# that would normally get executed when the object enters that state will
|
123
|
+
# *not* get triggered.
|
124
124
|
#
|
125
125
|
# For example,
|
126
126
|
#
|
@@ -229,7 +229,7 @@ module StateMachine
|
|
229
229
|
#
|
230
230
|
# [Vehicle, Switch, Project].each do |klass|
|
231
231
|
# klass.state_machines.each do |attribute, machine|
|
232
|
-
# machine.before_transition
|
232
|
+
# machine.before_transition StateMachineObserver.method(:before_transition)
|
233
233
|
# end
|
234
234
|
# end
|
235
235
|
#
|
@@ -300,10 +300,10 @@ module StateMachine
|
|
300
300
|
# in the new owner class (the original will remain unchanged).
|
301
301
|
def find_or_create(owner_class, *args, &block)
|
302
302
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
303
|
-
|
303
|
+
name = args.first || :state
|
304
304
|
|
305
305
|
# Find an existing machine
|
306
|
-
if owner_class.respond_to?(:state_machines) && machine = owner_class.state_machines[
|
306
|
+
if owner_class.respond_to?(:state_machines) && machine = owner_class.state_machines[name]
|
307
307
|
# Only create a new copy if changes are being made to the machine in
|
308
308
|
# a subclass
|
309
309
|
if machine.owner_class != owner_class && (options.any? || block_given?)
|
@@ -316,7 +316,7 @@ module StateMachine
|
|
316
316
|
machine.instance_eval(&block) if block_given?
|
317
317
|
else
|
318
318
|
# No existing machine: create a new one
|
319
|
-
machine = new(owner_class,
|
319
|
+
machine = new(owner_class, name, options, &block)
|
320
320
|
end
|
321
321
|
|
322
322
|
machine
|
@@ -347,7 +347,7 @@ module StateMachine
|
|
347
347
|
end
|
348
348
|
|
349
349
|
# Draw each of the class's state machines
|
350
|
-
klass.state_machines.
|
350
|
+
klass.state_machines.each_value do |machine|
|
351
351
|
machine.draw(options)
|
352
352
|
end
|
353
353
|
end
|
@@ -365,8 +365,9 @@ module StateMachine
|
|
365
365
|
# The class that the machine is defined in
|
366
366
|
attr_accessor :owner_class
|
367
367
|
|
368
|
-
# The
|
369
|
-
|
368
|
+
# The name of the machine, used for scoping methods generated for the
|
369
|
+
# machine as a whole (not states or events)
|
370
|
+
attr_reader :name
|
370
371
|
|
371
372
|
# The events that trigger transitions. These are sorted, by default, in
|
372
373
|
# the order in which they were defined.
|
@@ -402,7 +403,7 @@ module StateMachine
|
|
402
403
|
# Creates a new state machine for the given attribute
|
403
404
|
def initialize(owner_class, *args, &block)
|
404
405
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
405
|
-
assert_valid_keys(options, :initial, :action, :plural, :namespace, :integration, :messages, :use_transactions)
|
406
|
+
assert_valid_keys(options, :attribute, :initial, :action, :plural, :namespace, :integration, :messages, :use_transactions)
|
406
407
|
|
407
408
|
# Find an integration that matches this machine's owner class
|
408
409
|
if integration = options[:integration] ? StateMachine::Integrations.find(options[:integration]) : StateMachine::Integrations.match(owner_class)
|
@@ -414,7 +415,8 @@ module StateMachine
|
|
414
415
|
options = {:use_transactions => true}.merge(options)
|
415
416
|
|
416
417
|
# Set machine configuration
|
417
|
-
@
|
418
|
+
@name = args.first || :state
|
419
|
+
@attribute = options[:attribute] || @name
|
418
420
|
@events = EventCollection.new(self)
|
419
421
|
@states = StateCollection.new(self)
|
420
422
|
@callbacks = {:before => [], :after => []}
|
@@ -467,10 +469,9 @@ module StateMachine
|
|
467
469
|
include instance_helper_module
|
468
470
|
end
|
469
471
|
|
470
|
-
# Record this machine as matched to the
|
471
|
-
#
|
472
|
-
|
473
|
-
owner_class.state_machines[attribute] = self
|
472
|
+
# Record this machine as matched to the name in the current owner class.
|
473
|
+
# This will override any machines mapped to the same name in any superclasses.
|
474
|
+
owner_class.state_machines[name] = self
|
474
475
|
end
|
475
476
|
|
476
477
|
# Sets the initial state of the machine. This can be either the static name
|
@@ -484,22 +485,27 @@ module StateMachine
|
|
484
485
|
states.each {|state| state.initial = (state.name == @initial_state)}
|
485
486
|
end
|
486
487
|
|
488
|
+
# Gets the actual name of the attribute on the machine's owner class that
|
489
|
+
# stores data with the given name.
|
490
|
+
def attribute(name = :state)
|
491
|
+
name == :state ? @attribute : :"#{self.name}_#{name}"
|
492
|
+
end
|
493
|
+
|
487
494
|
# Defines a new instance method with the given name on the machine's owner
|
488
495
|
# class. If the method is already defined in the class, then this will not
|
489
496
|
# override it.
|
490
497
|
#
|
491
498
|
# Example:
|
492
499
|
#
|
493
|
-
# attribute = machine.attribute
|
494
500
|
# machine.define_instance_method(:state_name) do |machine, object|
|
495
501
|
# machine.states.match(object)
|
496
502
|
# end
|
497
503
|
def define_instance_method(method, &block)
|
498
|
-
|
504
|
+
name = self.name
|
499
505
|
|
500
506
|
@instance_helper_module.class_eval do
|
501
507
|
define_method(method) do |*args|
|
502
|
-
block.call(self.class.state_machine(
|
508
|
+
block.call(self.class.state_machine(name), self, *args)
|
503
509
|
end
|
504
510
|
end
|
505
511
|
end
|
@@ -515,11 +521,11 @@ module StateMachine
|
|
515
521
|
# machine.states.keys
|
516
522
|
# end
|
517
523
|
def define_class_method(method, &block)
|
518
|
-
|
524
|
+
name = self.name
|
519
525
|
|
520
526
|
@class_helper_module.class_eval do
|
521
527
|
define_method(method) do |*args|
|
522
|
-
block.call(self.state_machine(
|
528
|
+
block.call(self.state_machine(name), self, *args)
|
523
529
|
end
|
524
530
|
end
|
525
531
|
end
|
@@ -616,7 +622,7 @@ module StateMachine
|
|
616
622
|
# end
|
617
623
|
#
|
618
624
|
# class Vehicle < ActiveRecord::Base
|
619
|
-
# state_machine :state_id, :initial => :parked do
|
625
|
+
# state_machine :attribute => :state_id, :initial => :parked do
|
620
626
|
# event :ignite do
|
621
627
|
# transition :parked => :idling
|
622
628
|
# end
|
@@ -824,7 +830,7 @@ module StateMachine
|
|
824
830
|
end
|
825
831
|
alias_method :other_states, :state
|
826
832
|
|
827
|
-
# Gets the current value stored in the given object's
|
833
|
+
# Gets the current value stored in the given object's attribute.
|
828
834
|
#
|
829
835
|
# For example,
|
830
836
|
#
|
@@ -834,13 +840,15 @@ module StateMachine
|
|
834
840
|
# end
|
835
841
|
# end
|
836
842
|
#
|
837
|
-
# vehicle = Vehicle.new
|
838
|
-
# Vehicle.state_machine.read(vehicle)
|
839
|
-
|
840
|
-
|
843
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7d94ab0 @state="parked">
|
844
|
+
# Vehicle.state_machine.read(vehicle, :state) # => "parked" # Equivalent to vehicle.state
|
845
|
+
# Vehicle.state_machine.read(vehicle, :event) # => nil # Equivalent to vehicle.state_event
|
846
|
+
def read(object, attribute, ivar = false)
|
847
|
+
attribute = self.attribute(attribute)
|
848
|
+
ivar ? object.instance_variable_get("@#{attribute}") : object.send(attribute)
|
841
849
|
end
|
842
850
|
|
843
|
-
# Sets a new value in the given object's
|
851
|
+
# Sets a new value in the given object's attribute.
|
844
852
|
#
|
845
853
|
# For example,
|
846
854
|
#
|
@@ -850,11 +858,13 @@ module StateMachine
|
|
850
858
|
# end
|
851
859
|
# end
|
852
860
|
#
|
853
|
-
# vehicle = Vehicle.new
|
854
|
-
# Vehicle.state_machine.write(vehicle, 'idling')
|
855
|
-
# vehicle
|
856
|
-
|
857
|
-
|
861
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7d94ab0 @state="parked">
|
862
|
+
# Vehicle.state_machine.write(vehicle, :state, 'idling') # => Equivalent to vehicle.state = 'idling'
|
863
|
+
# Vehicle.state_machine.write(vehicle, :event, 'park') # => Equivalent to vehicle.state_event = 'park'
|
864
|
+
# vehicle.state # => "idling"
|
865
|
+
# vehicle.event # => "park"
|
866
|
+
def write(object, attribute, value)
|
867
|
+
object.send("#{self.attribute(attribute)}=", value)
|
858
868
|
end
|
859
869
|
|
860
870
|
# Defines one or more events for the machine and the transitions that can
|
@@ -1112,12 +1122,12 @@ module StateMachine
|
|
1112
1122
|
#
|
1113
1123
|
# class Vehicle
|
1114
1124
|
# # Only specifies one parameter (the object being transitioned)
|
1115
|
-
# before_transition
|
1125
|
+
# before_transition all => :parked do |vehicle|
|
1116
1126
|
# vehicle.set_alarm
|
1117
1127
|
# end
|
1118
1128
|
#
|
1119
1129
|
# # Specifies 2 parameters (object being transitioned and actual transition)
|
1120
|
-
# before_transition
|
1130
|
+
# before_transition all => :parked do |vehicle, transition|
|
1121
1131
|
# vehicle.set_alarm(transition)
|
1122
1132
|
# end
|
1123
1133
|
# end
|
@@ -1143,7 +1153,7 @@ module StateMachine
|
|
1143
1153
|
# before_transition [:first_gear, :idling] => :parked, :on => :park, :do => :take_off_seatbelt
|
1144
1154
|
#
|
1145
1155
|
# # With conditional callback:
|
1146
|
-
# before_transition
|
1156
|
+
# before_transition all => :parked, :do => :take_off_seatbelt, :if => :seatbelt_on?
|
1147
1157
|
#
|
1148
1158
|
# # Using helpers:
|
1149
1159
|
# before_transition all - :stalled => same, :on => any - :crash, :do => :update_dashboard
|
@@ -1205,7 +1215,7 @@ module StateMachine
|
|
1205
1215
|
#
|
1206
1216
|
# Configuration options:
|
1207
1217
|
# * <tt>:name</tt> - The name of the file to write to (without the file extension).
|
1208
|
-
# Default is "#{owner_class.name}_#{
|
1218
|
+
# Default is "#{owner_class.name}_#{name}"
|
1209
1219
|
# * <tt>:path</tt> - The path to write the graph file to. Default is the
|
1210
1220
|
# current directory (".").
|
1211
1221
|
# * <tt>:format</tt> - The image format to generate the graph in.
|
@@ -1217,7 +1227,7 @@ module StateMachine
|
|
1217
1227
|
# * <tt>:output</tt> - Whether to generate the output of the graph
|
1218
1228
|
def draw(options = {})
|
1219
1229
|
options = {
|
1220
|
-
:name => "#{owner_class.name}_#{
|
1230
|
+
:name => "#{owner_class.name}_#{name}",
|
1221
1231
|
:path => '.',
|
1222
1232
|
:format => 'png',
|
1223
1233
|
:font => 'Arial',
|
@@ -1272,7 +1282,7 @@ module StateMachine
|
|
1272
1282
|
define_action_helpers if action
|
1273
1283
|
|
1274
1284
|
# Gets the state name for the current value
|
1275
|
-
define_instance_method(
|
1285
|
+
define_instance_method(attribute(:name)) do |machine, object|
|
1276
1286
|
machine.states.match!(object).name
|
1277
1287
|
end
|
1278
1288
|
end
|
@@ -1289,7 +1299,7 @@ module StateMachine
|
|
1289
1299
|
# Adds predicate method to the owner class for determining the name of the
|
1290
1300
|
# current state
|
1291
1301
|
def define_state_predicate
|
1292
|
-
define_instance_method("#{
|
1302
|
+
define_instance_method("#{name}?") do |machine, object, state|
|
1293
1303
|
machine.states.matches?(object, state)
|
1294
1304
|
end
|
1295
1305
|
end
|
@@ -1298,31 +1308,31 @@ module StateMachine
|
|
1298
1308
|
# events
|
1299
1309
|
def define_event_helpers
|
1300
1310
|
# Gets the events that are allowed to fire on the current object
|
1301
|
-
define_instance_method(
|
1311
|
+
define_instance_method(attribute(:events)) do |machine, object|
|
1302
1312
|
machine.events.valid_for(object).map {|event| event.name}
|
1303
1313
|
end
|
1304
1314
|
|
1305
1315
|
# Gets the next possible transitions that can be run on the current
|
1306
1316
|
# object
|
1307
|
-
define_instance_method(
|
1317
|
+
define_instance_method(attribute(:transitions)) do |machine, object, *args|
|
1308
1318
|
machine.events.transitions_for(object, *args)
|
1309
1319
|
end
|
1310
1320
|
|
1311
1321
|
# Add helpers for interacting with the action
|
1312
1322
|
if action
|
1313
|
-
attribute = self.attribute
|
1314
|
-
|
1315
1323
|
# Tracks the event / transition to invoke when the action is called
|
1324
|
+
event_attribute = attribute(:event)
|
1325
|
+
event_transition_attribute = attribute(:event_transition)
|
1316
1326
|
@instance_helper_module.class_eval do
|
1317
|
-
attr_writer
|
1327
|
+
attr_writer event_attribute
|
1318
1328
|
|
1319
1329
|
protected
|
1320
|
-
attr_accessor
|
1330
|
+
attr_accessor event_transition_attribute
|
1321
1331
|
end
|
1322
1332
|
|
1323
1333
|
# Interpret non-blank events as present
|
1324
|
-
define_instance_method(
|
1325
|
-
event =
|
1334
|
+
define_instance_method(attribute(:event)) do |machine, object|
|
1335
|
+
event = machine.read(object, :event, true)
|
1326
1336
|
event && !(event.respond_to?(:empty?) && event.empty?) ? event.to_sym : nil
|
1327
1337
|
end
|
1328
1338
|
end
|
@@ -1334,7 +1344,7 @@ module StateMachine
|
|
1334
1344
|
action = self.action
|
1335
1345
|
private_method = owner_class.private_method_defined?(action_hook)
|
1336
1346
|
|
1337
|
-
if (owner_class.method_defined?(action_hook) || private_method) && !owner_class.state_machines.any? {|
|
1347
|
+
if (owner_class.method_defined?(action_hook) || private_method) && !owner_class.state_machines.any? {|name, machine| machine.action == action && machine != self}
|
1338
1348
|
# Action is defined and hasn't already been overridden by another machine
|
1339
1349
|
@instance_helper_module.class_eval do
|
1340
1350
|
# Override the default action to invoke the before / after hooks
|
@@ -1358,9 +1368,9 @@ module StateMachine
|
|
1358
1368
|
# automatically determined by either calling +pluralize+ on the attribute
|
1359
1369
|
# name or adding an "s" to the end of the name.
|
1360
1370
|
def define_scopes(custom_plural = nil)
|
1361
|
-
plural = custom_plural || (
|
1371
|
+
plural = custom_plural || (name.to_s.respond_to?(:pluralize) ? name.to_s.pluralize : "#{name}s")
|
1362
1372
|
|
1363
|
-
[
|
1373
|
+
[name, plural].uniq.each do |name|
|
1364
1374
|
[:with, :without].each do |kind|
|
1365
1375
|
method = "#{kind}_#{name}"
|
1366
1376
|
|
@@ -5,9 +5,9 @@ module StateMachine
|
|
5
5
|
# values are only set if the machine's attribute doesn't already exist
|
6
6
|
# (which must mean the defaults are being skipped)
|
7
7
|
def initialize_states(object)
|
8
|
-
|
9
|
-
value = machine.read(object)
|
10
|
-
machine.write(object, machine.initial_state(object).value) if value.nil? || value.respond_to?(:empty?) && value.empty?
|
8
|
+
each_value do |machine|
|
9
|
+
value = machine.read(object, :state)
|
10
|
+
machine.write(object, :state, machine.initial_state(object).value) if value.nil? || value.respond_to?(:empty?) && value.empty?
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -17,19 +17,19 @@ module StateMachine
|
|
17
17
|
run_action = [true, false].include?(events.last) ? events.pop : true
|
18
18
|
|
19
19
|
# Generate the transitions to run for each event
|
20
|
-
transitions = events.collect do |
|
20
|
+
transitions = events.collect do |event_name|
|
21
21
|
# Find the actual event being run
|
22
22
|
event = nil
|
23
|
-
detect do |
|
24
|
-
event = machine.events[
|
23
|
+
detect do |name, machine|
|
24
|
+
event = machine.events[event_name, :qualified_name]
|
25
25
|
end
|
26
26
|
|
27
|
-
raise InvalidEvent, "#{
|
27
|
+
raise InvalidEvent, "#{event_name.inspect} is an unknown state machine event" unless event
|
28
28
|
|
29
29
|
# Get the transition that will be performed for the event
|
30
30
|
unless transition = event.transition_for(object)
|
31
31
|
machine = event.machine
|
32
|
-
machine.invalidate(object,
|
32
|
+
machine.invalidate(object, :state, :invalid_transition, [[:event, event_name]])
|
33
33
|
end
|
34
34
|
|
35
35
|
transition
|
@@ -107,7 +107,7 @@ module StateMachine
|
|
107
107
|
# vehicle.state_event_transition # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
|
108
108
|
def fire_event_attributes(object, action, complete = true)
|
109
109
|
# Get the transitions to fire for each applicable machine
|
110
|
-
transitions = map {|
|
110
|
+
transitions = map {|name, machine| machine.action == action ? machine.events.attribute_transition_for(object, true) : nil}.compact
|
111
111
|
return yield if transitions.empty?
|
112
112
|
|
113
113
|
# The value generated by the yielded block (the actual action)
|
@@ -118,27 +118,25 @@ module StateMachine
|
|
118
118
|
begin
|
119
119
|
result = Transition.perform(transitions, :after => complete) do
|
120
120
|
# Prevent events from being evaluated multiple times if actions are nested
|
121
|
-
transitions.each {|transition|
|
121
|
+
transitions.each {|transition| transition.machine.write(object, :event, nil)}
|
122
122
|
action_value = yield
|
123
123
|
end
|
124
124
|
rescue Exception
|
125
|
-
# Revert
|
125
|
+
# Revert object modifications
|
126
126
|
transitions.each do |transition|
|
127
|
-
|
128
|
-
|
127
|
+
transition.machine.write(object, :event, transition.event)
|
128
|
+
transition.machine.write(object, :event_transition, nil) if complete
|
129
129
|
end
|
130
130
|
|
131
131
|
raise
|
132
132
|
end
|
133
133
|
|
134
134
|
transitions.each do |transition|
|
135
|
-
attribute = transition.attribute
|
136
|
-
|
137
135
|
# Revert event unless transition was successful
|
138
|
-
|
136
|
+
transition.machine.write(object, :event, transition.event) unless complete && result
|
139
137
|
|
140
138
|
# Track transition if partial transition completed successfully
|
141
|
-
|
139
|
+
transition.machine.write(object, :event_transition, !complete && result ? transition : nil)
|
142
140
|
end
|
143
141
|
end
|
144
142
|
|