state_machines 0.20.0 → 0.30.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +124 -13
  3. data/lib/state_machines/branch.rb +12 -13
  4. data/lib/state_machines/callback.rb +11 -12
  5. data/lib/state_machines/core.rb +0 -1
  6. data/lib/state_machines/error.rb +5 -4
  7. data/lib/state_machines/eval_helpers.rb +83 -45
  8. data/lib/state_machines/event.rb +23 -26
  9. data/lib/state_machines/event_collection.rb +4 -5
  10. data/lib/state_machines/extensions.rb +5 -5
  11. data/lib/state_machines/helper_module.rb +1 -1
  12. data/lib/state_machines/integrations/base.rb +1 -1
  13. data/lib/state_machines/integrations.rb +11 -14
  14. data/lib/state_machines/machine/action_hooks.rb +53 -0
  15. data/lib/state_machines/machine/callbacks.rb +59 -0
  16. data/lib/state_machines/machine/class_methods.rb +25 -11
  17. data/lib/state_machines/machine/configuration.rb +124 -0
  18. data/lib/state_machines/machine/event_methods.rb +59 -0
  19. data/lib/state_machines/machine/helper_generators.rb +125 -0
  20. data/lib/state_machines/machine/integration.rb +70 -0
  21. data/lib/state_machines/machine/parsing.rb +77 -0
  22. data/lib/state_machines/machine/rendering.rb +17 -0
  23. data/lib/state_machines/machine/scoping.rb +44 -0
  24. data/lib/state_machines/machine/state_methods.rb +101 -0
  25. data/lib/state_machines/machine/utilities.rb +85 -0
  26. data/lib/state_machines/machine/validation.rb +39 -0
  27. data/lib/state_machines/machine.rb +73 -617
  28. data/lib/state_machines/machine_collection.rb +18 -14
  29. data/lib/state_machines/macro_methods.rb +2 -2
  30. data/lib/state_machines/matcher.rb +6 -6
  31. data/lib/state_machines/matcher_helpers.rb +1 -1
  32. data/lib/state_machines/node_collection.rb +18 -17
  33. data/lib/state_machines/path.rb +2 -4
  34. data/lib/state_machines/path_collection.rb +2 -3
  35. data/lib/state_machines/state.rb +6 -5
  36. data/lib/state_machines/state_collection.rb +3 -3
  37. data/lib/state_machines/state_context.rb +6 -7
  38. data/lib/state_machines/stdio_renderer.rb +16 -16
  39. data/lib/state_machines/syntax_validator.rb +57 -0
  40. data/lib/state_machines/test_helper.rb +290 -27
  41. data/lib/state_machines/transition.rb +43 -41
  42. data/lib/state_machines/transition_collection.rb +22 -25
  43. data/lib/state_machines/version.rb +1 -1
  44. metadata +23 -9
@@ -29,17 +29,30 @@ module StateMachines
29
29
  # Assert that an object is in a specific state for a given state machine
30
30
  #
31
31
  # @param object [Object] The object with state machines
32
- # @param machine_name [Symbol] The name of the state machine
33
32
  # @param expected_state [Symbol] The expected state
33
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
34
34
  # @param message [String, nil] Custom failure message
35
35
  # @return [void]
36
36
  # @raise [AssertionError] If the state doesn't match
37
37
  #
38
38
  # @example
39
39
  # user = User.new
40
- # assert_state(user, :status, :active)
41
- def assert_state(object, machine_name, expected_state, message = nil)
42
- actual = object.send("#{machine_name}_name")
40
+ # assert_sm_state(user, :active) # Uses default :state machine
41
+ # assert_sm_state(user, :active, machine_name: :status) # Uses :status machine
42
+ def assert_sm_state(object, expected_state, machine_name: :state, message: nil)
43
+ name_method = "#{machine_name}_name"
44
+
45
+ # Handle the case where machine_name doesn't have a corresponding _name method
46
+ unless object.respond_to?(name_method)
47
+ available_machines = begin
48
+ object.class.state_machines.keys
49
+ rescue StandardError
50
+ []
51
+ end
52
+ raise ArgumentError, "No state machine '#{machine_name}' found. Available machines: #{available_machines.inspect}"
53
+ end
54
+
55
+ actual = object.send(name_method)
43
56
  default_message = "Expected #{object.class}##{machine_name} to be #{expected_state}, but was #{actual}"
44
57
 
45
58
  if defined?(::Minitest)
@@ -55,16 +68,30 @@ module StateMachines
55
68
  #
56
69
  # @param object [Object] The object with state machines
57
70
  # @param event [Symbol] The event name
71
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
58
72
  # @param message [String, nil] Custom failure message
59
73
  # @return [void]
60
74
  # @raise [AssertionError] If the transition is not available
61
75
  #
62
76
  # @example
63
77
  # user = User.new
64
- # assert_can_transition(user, :activate)
65
- def assert_can_transition(object, event, message = nil)
66
- can_method = "can_#{event}?"
67
- default_message = "Expected to be able to trigger event :#{event}, but #{can_method} returned false"
78
+ # assert_sm_can_transition(user, :activate) # Uses default :state machine
79
+ # assert_sm_can_transition(user, :activate, machine_name: :status) # Uses :status machine
80
+ def assert_sm_can_transition(object, event, machine_name: :state, message: nil)
81
+ # Try different method naming patterns
82
+ possible_methods = [
83
+ "can_#{event}?", # Default state machine or non-namespaced
84
+ "can_#{event}_#{machine_name}?" # Namespaced events
85
+ ]
86
+
87
+ can_method = possible_methods.find { |method| object.respond_to?(method) }
88
+
89
+ unless can_method
90
+ available_methods = object.methods.grep(/^can_.*\?$/).sort
91
+ raise ArgumentError, "No transition method found for event :#{event} on machine :#{machine_name}. Available methods: #{available_methods.first(10).inspect}"
92
+ end
93
+
94
+ default_message = "Expected to be able to trigger event :#{event} on #{machine_name}, but #{can_method} returned false"
68
95
 
69
96
  if defined?(::Minitest)
70
97
  assert object.send(can_method), message || default_message
@@ -79,16 +106,30 @@ module StateMachines
79
106
  #
80
107
  # @param object [Object] The object with state machines
81
108
  # @param event [Symbol] The event name
109
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
82
110
  # @param message [String, nil] Custom failure message
83
111
  # @return [void]
84
112
  # @raise [AssertionError] If the transition is available
85
113
  #
86
114
  # @example
87
115
  # user = User.new
88
- # assert_cannot_transition(user, :delete)
89
- def assert_cannot_transition(object, event, message = nil)
90
- can_method = "can_#{event}?"
91
- default_message = "Expected not to be able to trigger event :#{event}, but #{can_method} returned true"
116
+ # assert_sm_cannot_transition(user, :delete) # Uses default :state machine
117
+ # assert_sm_cannot_transition(user, :delete, machine_name: :status) # Uses :status machine
118
+ def assert_sm_cannot_transition(object, event, machine_name: :state, message: nil)
119
+ # Try different method naming patterns
120
+ possible_methods = [
121
+ "can_#{event}?", # Default state machine or non-namespaced
122
+ "can_#{event}_#{machine_name}?" # Namespaced events
123
+ ]
124
+
125
+ can_method = possible_methods.find { |method| object.respond_to?(method) }
126
+
127
+ unless can_method
128
+ available_methods = object.methods.grep(/^can_.*\?$/).sort
129
+ raise ArgumentError, "No transition method found for event :#{event} on machine :#{machine_name}. Available methods: #{available_methods.first(10).inspect}"
130
+ end
131
+
132
+ default_message = "Expected not to be able to trigger event :#{event} on #{machine_name}, but #{can_method} returned true"
92
133
 
93
134
  if defined?(::Minitest)
94
135
  refute object.send(can_method), message || default_message
@@ -103,18 +144,19 @@ module StateMachines
103
144
  #
104
145
  # @param object [Object] The object with state machines
105
146
  # @param event [Symbol] The event to trigger
106
- # @param machine_name [Symbol] The name of the state machine
107
147
  # @param expected_state [Symbol] The expected state after transition
148
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
108
149
  # @param message [String, nil] Custom failure message
109
150
  # @return [void]
110
151
  # @raise [AssertionError] If the transition fails or results in wrong state
111
152
  #
112
153
  # @example
113
154
  # user = User.new
114
- # assert_transition(user, :activate, :status, :active)
115
- def assert_transition(object, event, machine_name, expected_state, message = nil)
155
+ # assert_sm_transition(user, :activate, :active) # Uses default :state machine
156
+ # assert_sm_transition(user, :activate, :active, machine_name: :status) # Uses :status machine
157
+ def assert_sm_transition(object, event, expected_state, machine_name: :state, message: nil)
116
158
  object.send("#{event}!")
117
- assert_state(object, machine_name, expected_state, message)
159
+ assert_sm_state(object, expected_state, machine_name: machine_name, message: message)
118
160
  end
119
161
 
120
162
  # === Extended State Machine Assertions ===
@@ -205,11 +247,11 @@ module StateMachines
205
247
  end
206
248
  alias assert_sm_transition_not_allowed refute_sm_transition_allowed
207
249
 
208
- def assert_sm_event_triggers(object, event, message = nil)
209
- initial_state = object.state
250
+ def assert_sm_event_triggers(object, event, machine_name = :state, message = nil)
251
+ initial_state = object.send(machine_name)
210
252
  object.send("#{event}!")
211
- state_changed = initial_state != object.state
212
- default_message = "Expected event #{event} to trigger state change"
253
+ state_changed = initial_state != object.send(machine_name)
254
+ default_message = "Expected event #{event} to trigger state change on #{machine_name}"
213
255
 
214
256
  if defined?(::Minitest)
215
257
  assert state_changed, message || default_message
@@ -220,12 +262,12 @@ module StateMachines
220
262
  end
221
263
  end
222
264
 
223
- def refute_sm_event_triggers(object, event, message = nil)
224
- initial_state = object.state
265
+ def refute_sm_event_triggers(object, event, machine_name = :state, message = nil)
266
+ initial_state = object.send(machine_name)
225
267
  begin
226
268
  object.send("#{event}!")
227
- state_unchanged = initial_state == object.state
228
- default_message = "Expected event #{event} to not trigger state change"
269
+ state_unchanged = initial_state == object.send(machine_name)
270
+ default_message = "Expected event #{event} to not trigger state change on #{machine_name}"
229
271
 
230
272
  if defined?(::Minitest)
231
273
  assert state_unchanged, message || default_message
@@ -288,10 +330,26 @@ module StateMachines
288
330
  end
289
331
  alias assert_sm_callback_not_executed refute_sm_callback_executed
290
332
 
291
- def assert_sm_state_persisted(record, expected:, message: nil)
333
+ # Assert that a record's state is persisted correctly for a specific state machine
334
+ #
335
+ # @param record [Object] The record to check (should respond to reload)
336
+ # @param expected [String, Symbol] The expected persisted state
337
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
338
+ # @param message [String, nil] Custom failure message
339
+ # @return [void]
340
+ # @raise [AssertionError] If the persisted state doesn't match
341
+ #
342
+ # @example
343
+ # # Default state machine
344
+ # assert_sm_state_persisted(user, "active")
345
+ #
346
+ # # Specific state machine
347
+ # assert_sm_state_persisted(ship, "up", :shields)
348
+ # assert_sm_state_persisted(ship, "armed", :weapons)
349
+ def assert_sm_state_persisted(record, expected, machine_name = :state, message = nil)
292
350
  record.reload if record.respond_to?(:reload)
293
- actual_state = record.state
294
- default_message = "Expected persisted state #{expected} but got #{actual_state}"
351
+ actual_state = record.send(machine_name)
352
+ default_message = "Expected persisted state #{expected} for #{machine_name} but got #{actual_state}"
295
353
 
296
354
  if defined?(::Minitest)
297
355
  assert_equal expected, actual_state, message || default_message
@@ -301,5 +359,210 @@ module StateMachines
301
359
  raise default_message unless expected == actual_state
302
360
  end
303
361
  end
362
+
363
+ # Assert that executing a block triggers one or more expected events
364
+ #
365
+ # @param object [Object] The object with state machines
366
+ # @param expected_events [Symbol, Array<Symbol>] The event(s) expected to be triggered
367
+ # @param machine_name [Symbol] The name of the state machine (defaults to :state)
368
+ # @param message [String, nil] Custom failure message
369
+ # @return [void]
370
+ # @raise [AssertionError] If the expected events were not triggered
371
+ #
372
+ # @example
373
+ # # Single event
374
+ # assert_sm_triggers_event(vehicle, :crash) { vehicle.redline }
375
+ #
376
+ # # Multiple events
377
+ # assert_sm_triggers_event(vehicle, [:crash, :emergency]) { vehicle.emergency_stop }
378
+ #
379
+ # # Specific machine
380
+ # assert_sm_triggers_event(vehicle, :disable, machine_name: :alarm) { vehicle.turn_off_alarm }
381
+ def assert_sm_triggers_event(object, expected_events, machine_name: :state, message: nil)
382
+ expected_events = Array(expected_events)
383
+ triggered_events = []
384
+
385
+ # Get the state machine
386
+ machine = object.class.state_machines[machine_name]
387
+ raise ArgumentError, "No state machine found for #{machine_name}" unless machine
388
+
389
+ # Save original callbacks to restore later
390
+ machine.callbacks[:before].dup
391
+
392
+ # Add a temporary callback to track triggered events
393
+ temp_callback = machine.before_transition do |_obj, transition|
394
+ triggered_events << transition.event if transition.event
395
+ end
396
+
397
+ begin
398
+ # Execute the block
399
+ yield
400
+
401
+ # Check if expected events were triggered
402
+ missing_events = expected_events - triggered_events
403
+ extra_events = triggered_events - expected_events
404
+
405
+ unless missing_events.empty? && extra_events.empty?
406
+ default_message = "Expected events #{expected_events.inspect} to be triggered, but got #{triggered_events.inspect}"
407
+ default_message += ". Missing: #{missing_events.inspect}" if missing_events.any?
408
+ default_message += ". Extra: #{extra_events.inspect}" if extra_events.any?
409
+
410
+ if defined?(::Minitest)
411
+ assert false, message || default_message
412
+ elsif defined?(::RSpec)
413
+ raise message || default_message
414
+ else
415
+ raise default_message
416
+ end
417
+ end
418
+ ensure
419
+ # Restore original callbacks by removing the temporary one
420
+ machine.callbacks[:before].delete(temp_callback)
421
+ end
422
+ end
423
+
424
+ # Assert that a before_transition callback is defined with expected arguments
425
+ #
426
+ # @param machine_or_class [StateMachines::Machine, Class] The machine or class to check
427
+ # @param options [Hash] Expected callback options (on:, from:, to:, do:, if:, unless:)
428
+ # @param message [String, nil] Custom failure message
429
+ # @return [void]
430
+ # @raise [AssertionError] If the callback is not defined
431
+ #
432
+ # @example
433
+ # # Check for specific transition callback
434
+ # assert_before_transition(Vehicle, on: :crash, do: :emergency_stop)
435
+ #
436
+ # # Check with from/to states
437
+ # assert_before_transition(Vehicle.state_machine, from: :parked, to: :idling, do: :start_engine)
438
+ #
439
+ # # Check with conditions
440
+ # assert_before_transition(Vehicle, on: :ignite, if: :seatbelt_on?)
441
+ def assert_before_transition(machine_or_class, options = {}, message = nil)
442
+ _assert_transition_callback(:before, machine_or_class, options, message)
443
+ end
444
+
445
+ # Assert that an after_transition callback is defined with expected arguments
446
+ #
447
+ # @param machine_or_class [StateMachines::Machine, Class] The machine or class to check
448
+ # @param options [Hash] Expected callback options (on:, from:, to:, do:, if:, unless:)
449
+ # @param message [String, nil] Custom failure message
450
+ # @return [void]
451
+ # @raise [AssertionError] If the callback is not defined
452
+ #
453
+ # @example
454
+ # # Check for specific transition callback
455
+ # assert_after_transition(Vehicle, on: :crash, do: :tow)
456
+ #
457
+ # # Check with from/to states
458
+ # assert_after_transition(Vehicle.state_machine, from: :stalled, to: :parked, do: :log_repair)
459
+ def assert_after_transition(machine_or_class, options = {}, message = nil)
460
+ _assert_transition_callback(:after, machine_or_class, options, message)
461
+ end
462
+
463
+ # RSpec-style aliases for event triggering (for consistency with RSpec expectations)
464
+ alias expect_to_trigger_event assert_sm_triggers_event
465
+ alias have_triggered_event assert_sm_triggers_event
466
+
467
+ private
468
+
469
+ # Internal helper for checking transition callbacks
470
+ def _assert_transition_callback(callback_type, machine_or_class, options, message)
471
+ # Get the machine
472
+ machine = machine_or_class.is_a?(StateMachines::Machine) ? machine_or_class : machine_or_class.state_machine
473
+ raise ArgumentError, 'No state machine found' unless machine
474
+
475
+ callbacks = machine.callbacks[callback_type] || []
476
+
477
+ # Extract expected conditions
478
+ expected_event = options[:on]
479
+ expected_from = options[:from]
480
+ expected_to = options[:to]
481
+ expected_method = options[:do]
482
+ expected_if = options[:if]
483
+ expected_unless = options[:unless]
484
+
485
+ # Find matching callback
486
+ matching_callback = callbacks.find do |callback|
487
+ branch = callback.branch
488
+
489
+ # Check event requirement
490
+ if expected_event
491
+ event_requirement = branch.event_requirement
492
+ event_matches = if event_requirement && event_requirement.respond_to?(:values)
493
+ event_requirement.values.include?(expected_event)
494
+ else
495
+ false
496
+ end
497
+ next false unless event_matches
498
+ end
499
+
500
+ # Check state requirements (from/to)
501
+ if expected_from || expected_to
502
+ state_matches = false
503
+ branch.state_requirements.each do |req|
504
+ from_matches = !expected_from || (req[:from] && req[:from].respond_to?(:values) && req[:from].values.include?(expected_from))
505
+ to_matches = !expected_to || (req[:to] && req[:to].respond_to?(:values) && req[:to].values.include?(expected_to))
506
+
507
+ if from_matches && to_matches
508
+ state_matches = true
509
+ break
510
+ end
511
+ end
512
+ next false unless state_matches
513
+ end
514
+
515
+ # Check method requirement
516
+ if expected_method
517
+ methods = callback.instance_variable_get(:@methods) || []
518
+ method_matches = methods.any? do |method|
519
+ (method.is_a?(Symbol) && method == expected_method) ||
520
+ (method.is_a?(String) && method.to_sym == expected_method) ||
521
+ (method.respond_to?(:call) && method.respond_to?(:source_location))
522
+ end
523
+ next false unless method_matches
524
+ end
525
+
526
+ # Check if condition
527
+ if expected_if
528
+ if_condition = branch.if_condition
529
+ if_matches = (if_condition.is_a?(Symbol) && if_condition == expected_if) ||
530
+ (if_condition.is_a?(String) && if_condition.to_sym == expected_if) ||
531
+ if_condition.respond_to?(:call)
532
+ next false unless if_matches
533
+ end
534
+
535
+ # Check unless condition
536
+ if expected_unless
537
+ unless_condition = branch.unless_condition
538
+ unless_matches = (unless_condition.is_a?(Symbol) && unless_condition == expected_unless) ||
539
+ (unless_condition.is_a?(String) && unless_condition.to_sym == expected_unless) ||
540
+ unless_condition.respond_to?(:call)
541
+ next false unless unless_matches
542
+ end
543
+
544
+ true
545
+ end
546
+
547
+ return if matching_callback
548
+
549
+ expected_parts = []
550
+ expected_parts << "on: #{expected_event.inspect}" if expected_event
551
+ expected_parts << "from: #{expected_from.inspect}" if expected_from
552
+ expected_parts << "to: #{expected_to.inspect}" if expected_to
553
+ expected_parts << "do: #{expected_method.inspect}" if expected_method
554
+ expected_parts << "if: #{expected_if.inspect}" if expected_if
555
+ expected_parts << "unless: #{expected_unless.inspect}" if expected_unless
556
+
557
+ default_message = "Expected #{callback_type}_transition callback with #{expected_parts.join(', ')} to be defined, but it was not found"
558
+
559
+ if defined?(::Minitest)
560
+ assert false, message || default_message
561
+ elsif defined?(::RSpec)
562
+ raise message || default_message
563
+ else
564
+ raise default_message
565
+ end
566
+ end
304
567
  end
305
568
  end
@@ -33,11 +33,11 @@ module StateMachines
33
33
  # Determines whether the current ruby implementation supports pausing and
34
34
  # resuming transitions
35
35
  def self.pause_supported?
36
- %w(ruby maglev).include?(RUBY_ENGINE)
36
+ %w[ruby maglev].include?(RUBY_ENGINE)
37
37
  end
38
38
 
39
39
  # Creates a new, specific transition
40
- def initialize(object, machine, event, from_name, to_name, read_state = true) #:nodoc:
40
+ def initialize(object, machine, event, from_name, to_name, read_state = true) # :nodoc:
41
41
  @object = object
42
42
  @machine = machine
43
43
  @args = []
@@ -136,7 +136,7 @@ module StateMachines
136
136
  # transition = StateMachines::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
137
137
  # transition.attributes # => {:object => #<Vehicle:0xb7d60ea4>, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
138
138
  def attributes
139
- @attributes ||= {object: object, attribute: attribute, event: event, from: from, to: to}
139
+ @attributes ||= { object: object, attribute: attribute, event: event, from: from, to: to }
140
140
  end
141
141
 
142
142
  # Runs the actual transition and any before/after callbacks associated
@@ -162,16 +162,14 @@ module StateMachines
162
162
  self.args = args
163
163
 
164
164
  # Run the transition
165
- !!TransitionCollection.new([self], {use_transactions: machine.use_transactions, actions: run_action}).perform
165
+ !!TransitionCollection.new([self], { use_transactions: machine.use_transactions, actions: run_action }).perform
166
166
  end
167
167
 
168
168
  # Runs a block within a transaction for the object being transitioned.
169
169
  # By default, transactions are a no-op unless otherwise defined by the
170
170
  # machine's integration.
171
- def within_transaction
172
- machine.within_transaction(object) do
173
- yield
174
- end
171
+ def within_transaction(&)
172
+ machine.within_transaction(object, &)
175
173
  end
176
174
 
177
175
  # Runs the before / after callbacks for this transition. If a block is
@@ -186,7 +184,7 @@ module StateMachines
186
184
  # This will return true if all before callbacks gets executed. After
187
185
  # callbacks will not have an effect on the result.
188
186
  def run_callbacks(options = {}, &block)
189
- options = {before: true, after: true}.merge(options)
187
+ options = { before: true, after: true }.merge(options)
190
188
  @success = false
191
189
 
192
190
  halted = pausable { before(options[:after], &block) } if options[:before]
@@ -219,10 +217,10 @@ module StateMachines
219
217
  #
220
218
  # vehicle.state # => 'idling'
221
219
  def persist
222
- unless @persisted
223
- machine.write(object, :state, to)
224
- @persisted = true
225
- end
220
+ return if @persisted
221
+
222
+ machine.write(object, :state, to)
223
+ @persisted = true
226
224
  end
227
225
 
228
226
  # Rolls back changes made to the object's state via this transition. This
@@ -265,11 +263,11 @@ module StateMachines
265
263
  # and event involved in the transition are equal
266
264
  def ==(other)
267
265
  other.instance_of?(self.class) &&
268
- other.object == object &&
269
- other.machine == machine &&
270
- other.from_name == from_name &&
271
- other.to_name == to_name &&
272
- other.event == event
266
+ other.object == object &&
267
+ other.machine == machine &&
268
+ other.from_name == from_name &&
269
+ other.to_name == to_name &&
270
+ other.event == event
273
271
  end
274
272
 
275
273
  # Generates a nicely formatted description of this transitions's contents.
@@ -279,10 +277,10 @@ module StateMachines
279
277
  # transition = StateMachines::Transition.new(object, machine, :ignite, :parked, :idling)
280
278
  # transition # => #<StateMachines::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
281
279
  def inspect
282
- "#<#{self.class} #{%w(attribute event from from_name to to_name).map { |attr| "#{attr}=#{send(attr).inspect}" } * ' '}>"
280
+ "#<#{self.class} #{%w[attribute event from from_name to to_name].map { |attr| "#{attr}=#{send(attr).inspect}" } * ' '}>"
283
281
  end
284
282
 
285
- private
283
+ private
286
284
 
287
285
  # Runs a block that may get paused. If the block doesn't pause, then
288
286
  # execution will continue as normal. If the block gets paused, then it
@@ -292,13 +290,16 @@ module StateMachines
292
290
  # getting paused.
293
291
  def pausable
294
292
  begin
295
- halted = !catch(:halt) { yield; true }
296
- rescue => error
293
+ halted = !catch(:halt) do
294
+ yield
295
+ true
296
+ end
297
+ rescue StandardError => e
297
298
  raise unless @resume_block
298
299
  end
299
300
 
300
301
  if @resume_block
301
- @resume_block.call(halted, error)
302
+ @resume_block.call(halted, e)
302
303
  else
303
304
  halted
304
305
  end
@@ -310,12 +311,12 @@ module StateMachines
310
311
  def pause
311
312
  raise ArgumentError, 'around_transition callbacks cannot be called in multiple execution contexts in java implementations of Ruby. Use before/after_transitions instead.' unless self.class.pause_supported?
312
313
 
313
- unless @resume_block
314
- require 'continuation' unless defined?(callcc)
315
- callcc do |block|
316
- @paused_block = block
317
- throw :halt, true
318
- end
314
+ return if @resume_block
315
+
316
+ require 'continuation' unless defined?(callcc)
317
+ callcc do |block|
318
+ @paused_block = block
319
+ throw :halt, true
319
320
  end
320
321
  end
321
322
 
@@ -372,8 +373,9 @@ module StateMachines
372
373
  @before_run = true
373
374
  end
374
375
 
375
- action = {success: true}.merge(block_given? ? yield : {})
376
- @result, @success = action[:result], action[:success]
376
+ action = { success: true }.merge(block_given? ? yield : {})
377
+ @result = action[:result]
378
+ @success = action[:success]
377
379
  end
378
380
 
379
381
  # Runs the machine's +after+ callbacks for this transition. Only
@@ -390,17 +392,17 @@ module StateMachines
390
392
  # exception will not bubble up to the caller since +after+ callbacks
391
393
  # should never halt the execution of a +perform+.
392
394
  def after
393
- unless @after_run
394
- # First resume previously paused callbacks
395
- if resume
396
- catch(:halt) do
397
- type = @success ? :after : :failure
398
- machine.callbacks[type].each { |callback| callback.call(object, context, self) }
399
- end
400
- end
395
+ return if @after_run
401
396
 
402
- @after_run = true
397
+ # First resume previously paused callbacks
398
+ if resume
399
+ catch(:halt) do
400
+ type = @success ? :after : :failure
401
+ machine.callbacks[type].each { |callback| callback.call(object, context, self) }
402
+ end
403
403
  end
404
+
405
+ @after_run = true
404
406
  end
405
407
 
406
408
  # Gets a hash of the context defining this unique transition (including
@@ -412,7 +414,7 @@ module StateMachines
412
414
  # transition = StateMachines::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
413
415
  # transition.context # => {:on => :ignite, :from => :parked, :to => :idling}
414
416
  def context
415
- @context ||= {on: event, from: from_name, to: to_name}
417
+ @context ||= { on: event, from: from_name, to: to_name }
416
418
  end
417
419
  end
418
420
  end