state_machines 0.20.0 → 0.31.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/README.md +154 -18
- data/lib/state_machines/branch.rb +30 -17
- data/lib/state_machines/callback.rb +12 -13
- data/lib/state_machines/core.rb +0 -1
- data/lib/state_machines/error.rb +5 -4
- data/lib/state_machines/eval_helpers.rb +178 -49
- data/lib/state_machines/event.rb +31 -32
- data/lib/state_machines/event_collection.rb +4 -5
- data/lib/state_machines/extensions.rb +5 -5
- data/lib/state_machines/helper_module.rb +1 -1
- data/lib/state_machines/integrations/base.rb +1 -1
- data/lib/state_machines/integrations.rb +11 -14
- data/lib/state_machines/machine/action_hooks.rb +53 -0
- data/lib/state_machines/machine/callbacks.rb +59 -0
- data/lib/state_machines/machine/class_methods.rb +25 -11
- data/lib/state_machines/machine/configuration.rb +124 -0
- data/lib/state_machines/machine/event_methods.rb +59 -0
- data/lib/state_machines/machine/helper_generators.rb +125 -0
- data/lib/state_machines/machine/integration.rb +70 -0
- data/lib/state_machines/machine/parsing.rb +77 -0
- data/lib/state_machines/machine/rendering.rb +17 -0
- data/lib/state_machines/machine/scoping.rb +44 -0
- data/lib/state_machines/machine/state_methods.rb +101 -0
- data/lib/state_machines/machine/utilities.rb +85 -0
- data/lib/state_machines/machine/validation.rb +39 -0
- data/lib/state_machines/machine.rb +75 -619
- data/lib/state_machines/machine_collection.rb +18 -14
- data/lib/state_machines/macro_methods.rb +2 -2
- data/lib/state_machines/matcher.rb +6 -6
- data/lib/state_machines/matcher_helpers.rb +1 -1
- data/lib/state_machines/node_collection.rb +18 -17
- data/lib/state_machines/path.rb +2 -4
- data/lib/state_machines/path_collection.rb +2 -3
- data/lib/state_machines/state.rb +14 -7
- data/lib/state_machines/state_collection.rb +3 -3
- data/lib/state_machines/state_context.rb +6 -7
- data/lib/state_machines/stdio_renderer.rb +16 -16
- data/lib/state_machines/syntax_validator.rb +57 -0
- data/lib/state_machines/test_helper.rb +290 -27
- data/lib/state_machines/transition.rb +57 -46
- data/lib/state_machines/transition_collection.rb +22 -25
- data/lib/state_machines/version.rb +1 -1
- metadata +23 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcd04394640ccea1429d2e121a46e2c705f8c16b2987b142858ad2f151c9c287
|
4
|
+
data.tar.gz: 58cfb25ff972b1b2802730d9084d3c3b27c59a82e694646ce676e38fc661cbb9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9328eea4f9d8d9e57124571de54bb8f5160a044ef913d3e515bcaac933b7355ccf987c10699c71e94145f44bc1e2387342f350dcbff0100d76a5c1c06645321
|
7
|
+
data.tar.gz: 07c7bfad14f7ce103eec5d6dfa48a9117bc0a73cb5433447c4bf7b6cdea1b87b894a12ad6b85b5a0cb05d63dd68e291f980eeca40e60754c13d5b5c44a7fcf29
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|

|
2
|
+
|
2
3
|
# State Machines
|
3
4
|
|
4
5
|
State Machines adds support for creating state machines for attributes on any Ruby class.
|
@@ -9,15 +10,21 @@ State Machines adds support for creating state machines for attributes on any Ru
|
|
9
10
|
|
10
11
|
Add this line to your application's Gemfile:
|
11
12
|
|
12
|
-
|
13
|
+
```ruby
|
14
|
+
gem 'state_machines'
|
15
|
+
```
|
13
16
|
|
14
17
|
And then execute:
|
15
18
|
|
16
|
-
|
19
|
+
```sh
|
20
|
+
bundle
|
21
|
+
```
|
17
22
|
|
18
23
|
Or install it yourself as:
|
19
24
|
|
20
|
-
|
25
|
+
```sh
|
26
|
+
gem install state_machines
|
27
|
+
```
|
21
28
|
|
22
29
|
## Usage
|
23
30
|
|
@@ -38,11 +45,11 @@ Class definition:
|
|
38
45
|
|
39
46
|
```ruby
|
40
47
|
class Vehicle
|
41
|
-
attr_accessor :seatbelt_on, :time_used, :auto_shop_busy
|
48
|
+
attr_accessor :seatbelt_on, :time_used, :auto_shop_busy, :parking_meter_number
|
42
49
|
|
43
50
|
state_machine :state, initial: :parked do
|
44
51
|
before_transition parked: any - :parked, do: :put_on_seatbelt
|
45
|
-
|
52
|
+
|
46
53
|
after_transition on: :crash, do: :tow
|
47
54
|
after_transition on: :repair, do: :fix
|
48
55
|
after_transition any => :parked do |vehicle, transition|
|
@@ -61,6 +68,18 @@ class Vehicle
|
|
61
68
|
transition [:idling, :first_gear] => :parked
|
62
69
|
end
|
63
70
|
|
71
|
+
before_transition on: :park do |vehicle, transition|
|
72
|
+
# If using Rails:
|
73
|
+
# options = transition.args.extract_options!
|
74
|
+
|
75
|
+
options = transition.args.last.is_a?(Hash) ? transition.args.pop : {}
|
76
|
+
meter_number = options[:meter_number]
|
77
|
+
|
78
|
+
unless meter_number.nil?
|
79
|
+
vehicle.parking_meter_number = meter_number
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
64
83
|
event :ignite do
|
65
84
|
transition stalled: same, parked: :idling
|
66
85
|
end
|
@@ -130,6 +149,7 @@ class Vehicle
|
|
130
149
|
@seatbelt_on = false
|
131
150
|
@time_used = 0
|
132
151
|
@auto_shop_busy = true
|
152
|
+
@parking_meter_number = nil
|
133
153
|
super() # NOTE: This *must* be called, otherwise states won't get initialized
|
134
154
|
end
|
135
155
|
|
@@ -200,6 +220,11 @@ vehicle.park! # => StateMachines:InvalidTransition: Cannot tra
|
|
200
220
|
vehicle.state?(:parked) # => false
|
201
221
|
vehicle.state?(:invalid) # => IndexError: :invalid is an invalid name
|
202
222
|
|
223
|
+
# Transition callbacks can receive arguments
|
224
|
+
vehicle.park(meter_number: '12345') # => true
|
225
|
+
vehicle.parked? # => true
|
226
|
+
vehicle.parking_meter_number # => "12345"
|
227
|
+
|
203
228
|
# Namespaced machines have uniquely-generated methods
|
204
229
|
vehicle.alarm_state # => 1
|
205
230
|
vehicle.alarm_state_name # => :active
|
@@ -256,30 +281,36 @@ vehicle.state_name # => :parked
|
|
256
281
|
|
257
282
|
## Testing
|
258
283
|
|
259
|
-
State Machines provides
|
284
|
+
State Machines provides an optional `TestHelper` module with assertion methods to make testing state machines easier and more expressive.
|
285
|
+
|
286
|
+
**Note: TestHelper is not required by default** - you must explicitly require it in your test files.
|
260
287
|
|
261
288
|
### Setup
|
262
289
|
|
263
|
-
|
290
|
+
First, require the test helper module, then include it in your test class:
|
264
291
|
|
265
292
|
```ruby
|
266
293
|
# For Minitest
|
294
|
+
require 'state_machines/test_helper'
|
295
|
+
|
267
296
|
class VehicleTest < Minitest::Test
|
268
297
|
include StateMachines::TestHelper
|
269
|
-
|
298
|
+
|
270
299
|
def test_initial_state
|
271
300
|
vehicle = Vehicle.new
|
272
|
-
|
301
|
+
assert_sm_state vehicle, :parked
|
273
302
|
end
|
274
303
|
end
|
275
304
|
|
276
|
-
# For RSpec
|
305
|
+
# For RSpec
|
306
|
+
require 'state_machines/test_helper'
|
307
|
+
|
277
308
|
RSpec.describe Vehicle do
|
278
309
|
include StateMachines::TestHelper
|
279
|
-
|
310
|
+
|
280
311
|
it "starts in parked state" do
|
281
312
|
vehicle = Vehicle.new
|
282
|
-
|
313
|
+
assert_sm_state vehicle, :parked
|
283
314
|
end
|
284
315
|
end
|
285
316
|
```
|
@@ -292,10 +323,17 @@ The TestHelper provides both basic assertions and comprehensive state machine-sp
|
|
292
323
|
|
293
324
|
```ruby
|
294
325
|
vehicle = Vehicle.new
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
326
|
+
|
327
|
+
# New standardized API (all methods prefixed with assert_sm_)
|
328
|
+
assert_sm_state(vehicle, :parked) # Uses default :state machine
|
329
|
+
assert_sm_state(vehicle, :parked, machine_name: :status) # Specify machine explicitly
|
330
|
+
assert_sm_can_transition(vehicle, :ignite) # Test transition capability
|
331
|
+
assert_sm_cannot_transition(vehicle, :shift_up) # Test transition restriction
|
332
|
+
assert_sm_transition(vehicle, :ignite, :idling) # Test actual transition
|
333
|
+
|
334
|
+
# Multi-FSM examples
|
335
|
+
assert_sm_state(vehicle, :inactive, machine_name: :insurance_state) # Test insurance state
|
336
|
+
assert_sm_can_transition(vehicle, :buy_insurance, machine_name: :insurance_state)
|
299
337
|
```
|
300
338
|
|
301
339
|
#### Extended State Machine Assertions
|
@@ -308,7 +346,7 @@ vehicle = Vehicle.new
|
|
308
346
|
assert_sm_states_list machine, [:parked, :idling, :stalled]
|
309
347
|
assert_sm_initial_state machine, :parked
|
310
348
|
|
311
|
-
# Event behavior
|
349
|
+
# Event behavior
|
312
350
|
assert_sm_event_triggers vehicle, :ignite
|
313
351
|
refute_sm_event_triggers vehicle, :shift_up
|
314
352
|
assert_sm_event_raises_error vehicle, :invalid_event, StateMachines::InvalidTransition
|
@@ -317,8 +355,106 @@ assert_sm_event_raises_error vehicle, :invalid_event, StateMachines::InvalidTran
|
|
317
355
|
assert_sm_state_persisted record, expected: :active
|
318
356
|
```
|
319
357
|
|
358
|
+
#### Indirect Event Testing
|
359
|
+
|
360
|
+
Test that methods trigger state machine events indirectly:
|
361
|
+
|
362
|
+
```ruby
|
363
|
+
# Minitest style
|
364
|
+
vehicle = Vehicle.new
|
365
|
+
vehicle.ignite # Put in idling state
|
366
|
+
|
367
|
+
# Test that a custom method triggers a specific event
|
368
|
+
assert_sm_triggers_event(vehicle, :crash) do
|
369
|
+
vehicle.redline # Custom method that calls crash! internally
|
370
|
+
end
|
371
|
+
|
372
|
+
# Test multiple events
|
373
|
+
assert_sm_triggers_event(vehicle, [:crash, :emergency]) do
|
374
|
+
vehicle.emergency_stop
|
375
|
+
end
|
376
|
+
|
377
|
+
# Test on specific state machine (multi-FSM support)
|
378
|
+
assert_sm_triggers_event(vehicle, :disable, machine_name: :alarm) do
|
379
|
+
vehicle.turn_off_alarm
|
380
|
+
end
|
381
|
+
```
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
# RSpec style (coming soon with proper matcher support)
|
385
|
+
RSpec.describe Vehicle do
|
386
|
+
include StateMachines::TestHelper
|
387
|
+
|
388
|
+
it "triggers crash when redlining" do
|
389
|
+
vehicle = Vehicle.new
|
390
|
+
vehicle.ignite
|
391
|
+
|
392
|
+
expect_to_trigger_event(vehicle, :crash) do
|
393
|
+
vehicle.redline
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
#### Callback Definition Testing (TDD Support)
|
400
|
+
|
401
|
+
Verify that callbacks are properly defined in your state machine:
|
402
|
+
|
403
|
+
```ruby
|
404
|
+
# Test after_transition callbacks
|
405
|
+
assert_after_transition(Vehicle, on: :crash, do: :tow)
|
406
|
+
assert_after_transition(Vehicle, from: :stalled, to: :parked, do: :log_repair)
|
407
|
+
|
408
|
+
# Test before_transition callbacks
|
409
|
+
assert_before_transition(Vehicle, from: :parked, do: :put_on_seatbelt)
|
410
|
+
assert_before_transition(Vehicle, on: :ignite, if: :seatbelt_on?)
|
411
|
+
|
412
|
+
# Works with machine instances too
|
413
|
+
machine = Vehicle.state_machine(:state)
|
414
|
+
assert_after_transition(machine, on: :crash, do: :tow)
|
415
|
+
```
|
416
|
+
|
417
|
+
#### Multiple State Machine Support
|
418
|
+
|
419
|
+
The TestHelper fully supports objects with multiple state machines:
|
420
|
+
|
421
|
+
```ruby
|
422
|
+
# Example: StarfleetShip with 3 state machines
|
423
|
+
ship = StarfleetShip.new
|
424
|
+
|
425
|
+
# Test states on different machines
|
426
|
+
assert_sm_state(ship, :docked, machine_name: :status) # Main ship status
|
427
|
+
assert_sm_state(ship, :down, machine_name: :shields) # Shield system
|
428
|
+
assert_sm_state(ship, :standby, machine_name: :weapons) # Weapons system
|
429
|
+
|
430
|
+
# Test transitions on specific machines
|
431
|
+
assert_sm_transition(ship, :undock, :impulse, machine_name: :status)
|
432
|
+
assert_sm_transition(ship, :raise_shields, :up, machine_name: :shields)
|
433
|
+
assert_sm_transition(ship, :arm_weapons, :armed, machine_name: :weapons)
|
434
|
+
|
435
|
+
# Test event triggering across multiple machines
|
436
|
+
assert_sm_triggers_event(ship, :red_alert, machine_name: :status) do
|
437
|
+
ship.engage_combat_mode # Custom method affecting multiple systems
|
438
|
+
end
|
439
|
+
|
440
|
+
assert_sm_triggers_event(ship, :raise_shields, machine_name: :shields) do
|
441
|
+
ship.engage_combat_mode
|
442
|
+
end
|
443
|
+
|
444
|
+
# Test callback definitions on specific machines
|
445
|
+
shields_machine = StarfleetShip.state_machine(:shields)
|
446
|
+
assert_before_transition(shields_machine, from: :down, to: :up, do: :power_up_shields)
|
447
|
+
|
448
|
+
# Test persistence across multiple machines
|
449
|
+
assert_sm_state_persisted(ship, "impulse", :status)
|
450
|
+
assert_sm_state_persisted(ship, "up", :shields)
|
451
|
+
assert_sm_state_persisted(ship, "armed", :weapons)
|
452
|
+
```
|
453
|
+
|
320
454
|
The test helper works with both Minitest and RSpec, automatically detecting your testing framework.
|
321
455
|
|
456
|
+
**Note:** All methods use consistent keyword arguments with `machine_name:` as the last parameter, making the API intuitive and Grep-friendly.
|
457
|
+
|
322
458
|
## Additional Topics
|
323
459
|
|
324
460
|
### Explicit vs. Implicit Event Transitions
|
@@ -679,7 +815,7 @@ For RSpec testing, use the custom RSpec matchers:
|
|
679
815
|
|
680
816
|
## Contributing
|
681
817
|
|
682
|
-
1. Fork it ( https://github.com/state-machines/state_machines/fork )
|
818
|
+
1. Fork it ( <https://github.com/state-machines/state_machines/fork> )
|
683
819
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
684
820
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
685
821
|
4. Push to the branch (`git push origin my-new-feature`)
|
@@ -8,7 +8,6 @@ module StateMachines
|
|
8
8
|
# state of the transition match, in addition to if/unless conditionals for
|
9
9
|
# an object's state.
|
10
10
|
class Branch
|
11
|
-
|
12
11
|
include EvalHelpers
|
13
12
|
|
14
13
|
# The condition that must be met on an object
|
@@ -31,7 +30,7 @@ module StateMachines
|
|
31
30
|
attr_reader :known_states
|
32
31
|
|
33
32
|
# Creates a new branch
|
34
|
-
def initialize(options = {})
|
33
|
+
def initialize(options = {}) # :nodoc:
|
35
34
|
# Build conditionals
|
36
35
|
@if_condition = options.delete(:if)
|
37
36
|
@unless_condition = options.delete(:unless)
|
@@ -39,9 +38,9 @@ module StateMachines
|
|
39
38
|
# Build event requirement
|
40
39
|
@event_requirement = build_matcher(options, :on, :except_on)
|
41
40
|
|
42
|
-
if (options.keys - [
|
41
|
+
if (options.keys - %i[from to on except_from except_to except_on]).empty?
|
43
42
|
# Explicit from/to requirements specified
|
44
|
-
@state_requirements = [{from: build_matcher(options, :from, :except_from), to: build_matcher(options, :to, :except_to)}]
|
43
|
+
@state_requirements = [{ from: build_matcher(options, :from, :except_from), to: build_matcher(options, :to, :except_to) }]
|
45
44
|
else
|
46
45
|
# Separate out the event requirement
|
47
46
|
options.delete(:on)
|
@@ -51,7 +50,7 @@ module StateMachines
|
|
51
50
|
@state_requirements = options.collect do |from, to|
|
52
51
|
from = WhitelistMatcher.new(from) unless from.is_a?(Matcher)
|
53
52
|
to = WhitelistMatcher.new(to) unless to.is_a?(Matcher)
|
54
|
-
{from: from, to: to}
|
53
|
+
{ from: from, to: to }
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
@@ -59,7 +58,7 @@ module StateMachines
|
|
59
58
|
# on the priority in which tracked states should be added.
|
60
59
|
@known_states = []
|
61
60
|
@state_requirements.each do |state_requirement|
|
62
|
-
[
|
61
|
+
%i[from to].each { |option| @known_states |= state_requirement[option].values }
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
@@ -109,25 +108,27 @@ module StateMachines
|
|
109
108
|
# * <tt>:guard</tt> - Whether to guard matches with the if/unless
|
110
109
|
# conditionals defined for this branch. Default is true.
|
111
110
|
#
|
111
|
+
# Event arguments are passed to guard conditions if they accept multiple parameters.
|
112
|
+
#
|
112
113
|
# == Examples
|
113
114
|
#
|
114
115
|
# branch = StateMachines::Branch.new(:parked => :idling, :on => :ignite)
|
115
116
|
#
|
116
117
|
# branch.match(object, :on => :ignite) # => {:to => ..., :from => ..., :on => ...}
|
117
118
|
# branch.match(object, :on => :park) # => nil
|
118
|
-
def match(object, query = {})
|
119
|
+
def match(object, query = {}, event_args = [])
|
119
120
|
StateMachines::OptionsValidator.assert_valid_keys!(query, :from, :to, :on, :guard)
|
120
121
|
|
121
|
-
|
122
|
-
|
123
|
-
|
122
|
+
return unless (match = match_query(query)) && matches_conditions?(object, query, event_args)
|
123
|
+
|
124
|
+
match
|
124
125
|
end
|
125
126
|
|
126
127
|
def draw(graph, event, valid_states, io = $stdout)
|
127
128
|
machine.renderer.draw_branch(self, graph, event, valid_states, io)
|
128
129
|
end
|
129
130
|
|
130
|
-
|
131
|
+
protected
|
131
132
|
|
132
133
|
# Builds a matcher strategy to use for the given options. If neither a
|
133
134
|
# whitelist nor a blacklist option is specified, then an AllMatcher is
|
@@ -168,7 +169,7 @@ module StateMachines
|
|
168
169
|
# matching requirement is found, then it is returned.
|
169
170
|
def match_states(query)
|
170
171
|
state_requirements.detect do |state_requirement|
|
171
|
-
[
|
172
|
+
%i[from to].all? { |option| matches_requirement?(query, option, state_requirement[option]) }
|
172
173
|
end
|
173
174
|
end
|
174
175
|
|
@@ -179,11 +180,23 @@ module StateMachines
|
|
179
180
|
end
|
180
181
|
|
181
182
|
# Verifies that the conditionals for this branch evaluate to true for the
|
182
|
-
# given object
|
183
|
-
def matches_conditions?(object, query)
|
184
|
-
query[:guard]
|
185
|
-
|
186
|
-
|
183
|
+
# given object. Event arguments are passed to guards that accept multiple parameters.
|
184
|
+
def matches_conditions?(object, query, event_args = [])
|
185
|
+
case [query[:guard], if_condition, unless_condition]
|
186
|
+
in [false, _, _]
|
187
|
+
true
|
188
|
+
in [_, nil, nil]
|
189
|
+
true
|
190
|
+
in [_, if_conds, nil] if if_conds
|
191
|
+
Array(if_conds).all? { |condition| evaluate_method_with_event_args(object, condition, event_args) }
|
192
|
+
in [_, nil, unless_conds] if unless_conds
|
193
|
+
Array(unless_conds).none? { |condition| evaluate_method_with_event_args(object, condition, event_args) }
|
194
|
+
in [_, if_conds, unless_conds] if if_conds || unless_conds
|
195
|
+
Array(if_conds).all? { |condition| evaluate_method_with_event_args(object, condition, event_args) } &&
|
196
|
+
Array(unless_conds).none? { |condition| evaluate_method_with_event_args(object, condition, event_args) }
|
197
|
+
else
|
198
|
+
true
|
199
|
+
end
|
187
200
|
end
|
188
201
|
end
|
189
202
|
end
|
@@ -124,7 +124,7 @@ module StateMachines
|
|
124
124
|
# callback can be found in their attribute definitions.
|
125
125
|
def initialize(type, *args, &block)
|
126
126
|
@type = type
|
127
|
-
raise ArgumentError, 'Type must be :before, :after, :around, or :failure' unless [
|
127
|
+
raise ArgumentError, 'Type must be :before, :after, :around, or :failure' unless %i[before after around failure].include?(type)
|
128
128
|
|
129
129
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
130
130
|
@methods = args
|
@@ -132,7 +132,7 @@ module StateMachines
|
|
132
132
|
@methods << block if block_given?
|
133
133
|
raise ArgumentError, 'Method(s) for callback must be specified' unless @methods.any?
|
134
134
|
|
135
|
-
options = {bind_to_object: self.class.bind_to_object, terminator: self.class.terminator}.merge(options)
|
135
|
+
options = { bind_to_object: self.class.bind_to_object, terminator: self.class.terminator }.merge(options)
|
136
136
|
|
137
137
|
# Proxy lambda blocks so that they're bound to the object
|
138
138
|
bind_to_object = options.delete(:bind_to_object)
|
@@ -156,16 +156,16 @@ module StateMachines
|
|
156
156
|
#
|
157
157
|
# If a terminator has been configured and it matches the result from the
|
158
158
|
# evaluated method, then the callback chain should be halted.
|
159
|
-
def call(object, context = {},
|
159
|
+
def call(object, context = {}, *, &)
|
160
160
|
if @branch.matches?(object, context)
|
161
|
-
run_methods(object, context, 0,
|
161
|
+
run_methods(object, context, 0, *, &)
|
162
162
|
true
|
163
163
|
else
|
164
164
|
false
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
|
-
|
168
|
+
private
|
169
169
|
|
170
170
|
# Runs all of the methods configured for this callback.
|
171
171
|
#
|
@@ -179,7 +179,7 @@ module StateMachines
|
|
179
179
|
def run_methods(object, context = {}, index = 0, *args, &block)
|
180
180
|
if type == :around
|
181
181
|
current_method = @methods[index]
|
182
|
-
if
|
182
|
+
if current_method
|
183
183
|
yielded = false
|
184
184
|
evaluate_method(object, current_method, *args) do
|
185
185
|
yielded = true
|
@@ -187,13 +187,13 @@ module StateMachines
|
|
187
187
|
end
|
188
188
|
|
189
189
|
throw :halt unless yielded
|
190
|
-
|
191
|
-
yield
|
190
|
+
elsif block_given?
|
191
|
+
yield
|
192
192
|
end
|
193
193
|
else
|
194
194
|
@methods.each do |method|
|
195
195
|
result = evaluate_method(object, method, *args)
|
196
|
-
throw :halt if @terminator
|
196
|
+
throw :halt if @terminator&.call(result)
|
197
197
|
end
|
198
198
|
end
|
199
199
|
end
|
@@ -206,13 +206,12 @@ module StateMachines
|
|
206
206
|
arity += 1 if arity >= 0 # Make sure the object gets passed
|
207
207
|
arity += 1 if arity == 1 && type == :around # Make sure the block gets passed
|
208
208
|
|
209
|
-
method =
|
210
|
-
|
209
|
+
method = ->(object, *args) { object.instance_exec(*args, &block) }
|
211
210
|
|
212
211
|
# Proxy arity to the original block
|
213
212
|
(
|
214
|
-
class << method
|
215
|
-
self
|
213
|
+
class << method
|
214
|
+
self
|
216
215
|
end).class_eval do
|
217
216
|
define_method(:arity) { arity }
|
218
217
|
end
|
data/lib/state_machines/core.rb
CHANGED
data/lib/state_machines/error.rb
CHANGED
@@ -6,7 +6,7 @@ module StateMachines
|
|
6
6
|
# The object that failed
|
7
7
|
attr_reader :object
|
8
8
|
|
9
|
-
def initialize(object, message = nil)
|
9
|
+
def initialize(object, message = nil) # :nodoc:
|
10
10
|
@object = object
|
11
11
|
|
12
12
|
super(message)
|
@@ -49,12 +49,13 @@ module StateMachines
|
|
49
49
|
# The event that was attempted to be run
|
50
50
|
attr_reader :event
|
51
51
|
|
52
|
-
def initialize(object, event_name)
|
52
|
+
def initialize(object, event_name) # :nodoc:
|
53
53
|
@event = event_name
|
54
54
|
|
55
55
|
super(object, "#{event.inspect} is an unknown state machine event")
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
58
59
|
# An invalid transition was attempted
|
59
60
|
class InvalidTransition < Error
|
60
61
|
# The machine attempting to be transitioned
|
@@ -63,7 +64,7 @@ module StateMachines
|
|
63
64
|
# The current state value for the machine
|
64
65
|
attr_reader :from
|
65
66
|
|
66
|
-
def initialize(object, machine, event)
|
67
|
+
def initialize(object, machine, event) # :nodoc:
|
67
68
|
@machine = machine
|
68
69
|
@from_state = machine.states.match!(object)
|
69
70
|
@from = machine.read(object, :state)
|
@@ -101,7 +102,7 @@ module StateMachines
|
|
101
102
|
# The set of events that failed the transition(s)
|
102
103
|
attr_reader :events
|
103
104
|
|
104
|
-
def initialize(object, events)
|
105
|
+
def initialize(object, events) # :nodoc:
|
105
106
|
@events = events
|
106
107
|
|
107
108
|
super(object, "Cannot run events in parallel: #{events * ', '}")
|