state_machines 0.6.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.
- checksums.yaml +4 -4
- data/README.md +205 -14
- data/lib/state_machines/branch.rb +20 -17
- data/lib/state_machines/callback.rb +13 -12
- data/lib/state_machines/core.rb +3 -3
- data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
- data/lib/state_machines/core_ext.rb +2 -0
- data/lib/state_machines/error.rb +7 -4
- data/lib/state_machines/eval_helpers.rb +93 -26
- data/lib/state_machines/event.rb +41 -29
- data/lib/state_machines/event_collection.rb +6 -5
- data/lib/state_machines/extensions.rb +7 -5
- data/lib/state_machines/helper_module.rb +3 -1
- data/lib/state_machines/integrations/base.rb +3 -1
- data/lib/state_machines/integrations.rb +13 -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 +93 -0
- 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 +83 -673
- data/lib/state_machines/machine_collection.rb +23 -15
- data/lib/state_machines/macro_methods.rb +4 -2
- data/lib/state_machines/matcher.rb +8 -5
- data/lib/state_machines/matcher_helpers.rb +3 -1
- data/lib/state_machines/node_collection.rb +23 -18
- data/lib/state_machines/options_validator.rb +72 -0
- data/lib/state_machines/path.rb +7 -5
- data/lib/state_machines/path_collection.rb +7 -4
- data/lib/state_machines/state.rb +76 -47
- data/lib/state_machines/state_collection.rb +5 -3
- data/lib/state_machines/state_context.rb +11 -8
- data/lib/state_machines/stdio_renderer.rb +74 -0
- data/lib/state_machines/syntax_validator.rb +57 -0
- data/lib/state_machines/test_helper.rb +568 -0
- data/lib/state_machines/transition.rb +45 -41
- data/lib/state_machines/transition_collection.rb +27 -26
- data/lib/state_machines/version.rb +3 -1
- data/lib/state_machines.rb +4 -1
- metadata +32 -16
- data/lib/state_machines/assertions.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c732f34387da18f4dc1c3d78cad1fbb111cc88d06d9fe3edcda912adc5e655a
|
4
|
+
data.tar.gz: ef0079a89a1b0e4ddea45dd4a79e11de12740d4eb0c2aa98ad95b5ca7ae3eab8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d71a5bd20b0de0b19e4684b4251954d0cf648e5454b57af96555372bbdee59a9ac855bb27d182e018f87ffa0525b5cd3daff5c3ce53c483f3dec4954d076a331
|
7
|
+
data.tar.gz: 179e0cb6c2a31d14c4078820d254ce35e26d9b5aaa2646e4766b842127411cf49e5ad697d0a764ba79a0036bce5dc288ef113b8bef57c3cac64de8816a23f49b
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|

|
2
|
-
[](https://codeclimate.com/github/state-machines/state_machines)
|
3
2
|
# State Machines
|
4
3
|
|
5
4
|
State Machines adds support for creating state machines for attributes on any Ruby class.
|
@@ -43,7 +42,7 @@ class Vehicle
|
|
43
42
|
|
44
43
|
state_machine :state, initial: :parked do
|
45
44
|
before_transition parked: any - :parked, do: :put_on_seatbelt
|
46
|
-
|
45
|
+
|
47
46
|
after_transition on: :crash, do: :tow
|
48
47
|
after_transition on: :repair, do: :fix
|
49
48
|
after_transition any => :parked do |vehicle, transition|
|
@@ -255,6 +254,182 @@ vehicle.state_name # => :parked
|
|
255
254
|
# vehicle.state = :parked
|
256
255
|
```
|
257
256
|
|
257
|
+
## Testing
|
258
|
+
|
259
|
+
State Machines provides an optional `TestHelper` module with assertion methods to make testing state machines easier and more expressive.
|
260
|
+
|
261
|
+
**Note: TestHelper is not required by default** - you must explicitly require it in your test files.
|
262
|
+
|
263
|
+
### Setup
|
264
|
+
|
265
|
+
First, require the test helper module, then include it in your test class:
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
# For Minitest
|
269
|
+
require 'state_machines/test_helper'
|
270
|
+
|
271
|
+
class VehicleTest < Minitest::Test
|
272
|
+
include StateMachines::TestHelper
|
273
|
+
|
274
|
+
def test_initial_state
|
275
|
+
vehicle = Vehicle.new
|
276
|
+
assert_sm_state vehicle, :parked
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# For RSpec
|
281
|
+
require 'state_machines/test_helper'
|
282
|
+
|
283
|
+
RSpec.describe Vehicle do
|
284
|
+
include StateMachines::TestHelper
|
285
|
+
|
286
|
+
it "starts in parked state" do
|
287
|
+
vehicle = Vehicle.new
|
288
|
+
assert_sm_state vehicle, :parked
|
289
|
+
end
|
290
|
+
end
|
291
|
+
```
|
292
|
+
|
293
|
+
### Available Assertions
|
294
|
+
|
295
|
+
The TestHelper provides both basic assertions and comprehensive state machine-specific assertions with `sm_` prefixes:
|
296
|
+
|
297
|
+
#### Basic Assertions
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
vehicle = Vehicle.new
|
301
|
+
|
302
|
+
# New standardized API (all methods prefixed with assert_sm_)
|
303
|
+
assert_sm_state(vehicle, :parked) # Uses default :state machine
|
304
|
+
assert_sm_state(vehicle, :parked, machine_name: :status) # Specify machine explicitly
|
305
|
+
assert_sm_can_transition(vehicle, :ignite) # Test transition capability
|
306
|
+
assert_sm_cannot_transition(vehicle, :shift_up) # Test transition restriction
|
307
|
+
assert_sm_transition(vehicle, :ignite, :idling) # Test actual transition
|
308
|
+
|
309
|
+
# Multi-FSM examples
|
310
|
+
assert_sm_state(vehicle, :inactive, machine_name: :insurance_state) # Test insurance state
|
311
|
+
assert_sm_can_transition(vehicle, :buy_insurance, machine_name: :insurance_state)
|
312
|
+
```
|
313
|
+
|
314
|
+
#### Extended State Machine Assertions
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
machine = Vehicle.state_machine(:state)
|
318
|
+
vehicle = Vehicle.new
|
319
|
+
|
320
|
+
# State configuration
|
321
|
+
assert_sm_states_list machine, [:parked, :idling, :stalled]
|
322
|
+
assert_sm_initial_state machine, :parked
|
323
|
+
|
324
|
+
# Event behavior
|
325
|
+
assert_sm_event_triggers vehicle, :ignite
|
326
|
+
refute_sm_event_triggers vehicle, :shift_up
|
327
|
+
assert_sm_event_raises_error vehicle, :invalid_event, StateMachines::InvalidTransition
|
328
|
+
|
329
|
+
# Persistence (with ActiveRecord integration)
|
330
|
+
assert_sm_state_persisted record, expected: :active
|
331
|
+
```
|
332
|
+
|
333
|
+
#### Indirect Event Testing
|
334
|
+
|
335
|
+
Test that methods trigger state machine events indirectly:
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
# Minitest style
|
339
|
+
vehicle = Vehicle.new
|
340
|
+
vehicle.ignite # Put in idling state
|
341
|
+
|
342
|
+
# Test that a custom method triggers a specific event
|
343
|
+
assert_sm_triggers_event(vehicle, :crash) do
|
344
|
+
vehicle.redline # Custom method that calls crash! internally
|
345
|
+
end
|
346
|
+
|
347
|
+
# Test multiple events
|
348
|
+
assert_sm_triggers_event(vehicle, [:crash, :emergency]) do
|
349
|
+
vehicle.emergency_stop
|
350
|
+
end
|
351
|
+
|
352
|
+
# Test on specific state machine (multi-FSM support)
|
353
|
+
assert_sm_triggers_event(vehicle, :disable, machine_name: :alarm) do
|
354
|
+
vehicle.turn_off_alarm
|
355
|
+
end
|
356
|
+
```
|
357
|
+
|
358
|
+
```ruby
|
359
|
+
# RSpec style (coming soon with proper matcher support)
|
360
|
+
RSpec.describe Vehicle do
|
361
|
+
include StateMachines::TestHelper
|
362
|
+
|
363
|
+
it "triggers crash when redlining" do
|
364
|
+
vehicle = Vehicle.new
|
365
|
+
vehicle.ignite
|
366
|
+
|
367
|
+
expect_to_trigger_event(vehicle, :crash) do
|
368
|
+
vehicle.redline
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
```
|
373
|
+
|
374
|
+
#### Callback Definition Testing (TDD Support)
|
375
|
+
|
376
|
+
Verify that callbacks are properly defined in your state machine:
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
# Test after_transition callbacks
|
380
|
+
assert_after_transition(Vehicle, on: :crash, do: :tow)
|
381
|
+
assert_after_transition(Vehicle, from: :stalled, to: :parked, do: :log_repair)
|
382
|
+
|
383
|
+
# Test before_transition callbacks
|
384
|
+
assert_before_transition(Vehicle, from: :parked, do: :put_on_seatbelt)
|
385
|
+
assert_before_transition(Vehicle, on: :ignite, if: :seatbelt_on?)
|
386
|
+
|
387
|
+
# Works with machine instances too
|
388
|
+
machine = Vehicle.state_machine(:state)
|
389
|
+
assert_after_transition(machine, on: :crash, do: :tow)
|
390
|
+
```
|
391
|
+
|
392
|
+
#### Multiple State Machine Support
|
393
|
+
|
394
|
+
The TestHelper fully supports objects with multiple state machines:
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
# Example: StarfleetShip with 3 state machines
|
398
|
+
ship = StarfleetShip.new
|
399
|
+
|
400
|
+
# Test states on different machines
|
401
|
+
assert_sm_state(ship, :docked, machine_name: :status) # Main ship status
|
402
|
+
assert_sm_state(ship, :down, machine_name: :shields) # Shield system
|
403
|
+
assert_sm_state(ship, :standby, machine_name: :weapons) # Weapons system
|
404
|
+
|
405
|
+
# Test transitions on specific machines
|
406
|
+
assert_sm_transition(ship, :undock, :impulse, machine_name: :status)
|
407
|
+
assert_sm_transition(ship, :raise_shields, :up, machine_name: :shields)
|
408
|
+
assert_sm_transition(ship, :arm_weapons, :armed, machine_name: :weapons)
|
409
|
+
|
410
|
+
# Test event triggering across multiple machines
|
411
|
+
assert_sm_triggers_event(ship, :red_alert, machine_name: :status) do
|
412
|
+
ship.engage_combat_mode # Custom method affecting multiple systems
|
413
|
+
end
|
414
|
+
|
415
|
+
assert_sm_triggers_event(ship, :raise_shields, machine_name: :shields) do
|
416
|
+
ship.engage_combat_mode
|
417
|
+
end
|
418
|
+
|
419
|
+
# Test callback definitions on specific machines
|
420
|
+
shields_machine = StarfleetShip.state_machine(:shields)
|
421
|
+
assert_before_transition(shields_machine, from: :down, to: :up, do: :power_up_shields)
|
422
|
+
|
423
|
+
# Test persistence across multiple machines
|
424
|
+
assert_sm_state_persisted(ship, "impulse", :status)
|
425
|
+
assert_sm_state_persisted(ship, "up", :shields)
|
426
|
+
assert_sm_state_persisted(ship, "armed", :weapons)
|
427
|
+
```
|
428
|
+
|
429
|
+
The test helper works with both Minitest and RSpec, automatically detecting your testing framework.
|
430
|
+
|
431
|
+
**Note:** All methods use consistent keyword arguments with `machine_name:` as the last parameter, making the API intuitive and Grep-friendly.
|
432
|
+
|
258
433
|
## Additional Topics
|
259
434
|
|
260
435
|
### Explicit vs. Implicit Event Transitions
|
@@ -428,7 +603,7 @@ easily migrate from a different library, you can do so as shown below:
|
|
428
603
|
```ruby
|
429
604
|
class Vehicle
|
430
605
|
state_machine initial: :parked do
|
431
|
-
...
|
606
|
+
# ...
|
432
607
|
|
433
608
|
state :parked do
|
434
609
|
transition to: :idling, :on => [:ignite, :shift_up], if: :seatbelt_on?
|
@@ -464,7 +639,7 @@ example below:
|
|
464
639
|
```ruby
|
465
640
|
class Vehicle
|
466
641
|
state_machine initial: :parked do
|
467
|
-
...
|
642
|
+
# ...
|
468
643
|
|
469
644
|
transition parked: :idling, :on => [:ignite, :shift_up]
|
470
645
|
transition first_gear: :second_gear, second_gear: :third_gear, on: :shift_up
|
@@ -496,12 +671,31 @@ class Vehicle
|
|
496
671
|
transition [:idling, :first_gear] => :parked
|
497
672
|
end
|
498
673
|
|
499
|
-
...
|
674
|
+
# ...
|
500
675
|
end
|
501
676
|
end
|
502
677
|
```
|
503
678
|
|
504
|
-
|
679
|
+
#### Draw state machines
|
680
|
+
|
681
|
+
State machines includes a default STDIORenderer for debugging state machines without external dependencies.
|
682
|
+
This renderer can be used to visualize the state machine in the console.
|
683
|
+
|
684
|
+
To use the renderer, simply call the `draw` method on the state machine:
|
685
|
+
|
686
|
+
```ruby
|
687
|
+
Vehicle.state_machine.draw # Outputs the state machine diagram to the console
|
688
|
+
```
|
689
|
+
|
690
|
+
You can customize the output by passing in options to the `draw` method, such as the output stream:
|
691
|
+
|
692
|
+
```ruby
|
693
|
+
Vehicle.state_machine.draw(io: $stderr) # Outputs the state machine diagram to stderr
|
694
|
+
```
|
695
|
+
|
696
|
+
#### Dynamic definitions
|
697
|
+
|
698
|
+
There may be cases where the definition of a state machine is **dynamic**.
|
505
699
|
This means that you don't know the possible states or events for a machine until
|
506
700
|
runtime. For example, you may allow users in your application to manage the
|
507
701
|
state machine of a project or task in your system. This means that the list of
|
@@ -580,22 +774,19 @@ transitions.
|
|
580
774
|
|
581
775
|
Ruby versions officially supported and tested:
|
582
776
|
|
583
|
-
* Ruby (MRI)
|
584
|
-
* JRuby
|
585
|
-
* Rubinius
|
777
|
+
* Ruby (MRI) 3.0.0+
|
586
778
|
|
587
779
|
For graphing state machine:
|
588
780
|
|
589
|
-
* [state_machines-graphviz](
|
781
|
+
* [state_machines-graphviz](https://github.com/state-machines/state_machines-graphviz)
|
590
782
|
|
591
783
|
For documenting state machines:
|
592
784
|
|
593
|
-
* [state_machines-yard](
|
594
|
-
|
785
|
+
* [state_machines-yard](https://github.com/state-machines/state_machines-yard)
|
595
786
|
|
596
|
-
|
787
|
+
For RSpec testing, use the custom RSpec matchers:
|
597
788
|
|
598
|
-
*
|
789
|
+
* [state_machines-rspec](https://github.com/state-machines/state_machines-rspec)
|
599
790
|
|
600
791
|
## Contributing
|
601
792
|
|
@@ -1,10 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
4
|
+
|
1
5
|
module StateMachines
|
2
6
|
# Represents a set of requirements that must be met in order for a transition
|
3
7
|
# or callback to occur. Branches verify that the event, from state, and to
|
4
8
|
# state of the transition match, in addition to if/unless conditionals for
|
5
9
|
# an object's state.
|
6
10
|
class Branch
|
7
|
-
|
8
11
|
include EvalHelpers
|
9
12
|
|
10
13
|
# The condition that must be met on an object
|
@@ -27,7 +30,7 @@ module StateMachines
|
|
27
30
|
attr_reader :known_states
|
28
31
|
|
29
32
|
# Creates a new branch
|
30
|
-
def initialize(options = {})
|
33
|
+
def initialize(options = {}) # :nodoc:
|
31
34
|
# Build conditionals
|
32
35
|
@if_condition = options.delete(:if)
|
33
36
|
@unless_condition = options.delete(:unless)
|
@@ -35,9 +38,9 @@ module StateMachines
|
|
35
38
|
# Build event requirement
|
36
39
|
@event_requirement = build_matcher(options, :on, :except_on)
|
37
40
|
|
38
|
-
if (options.keys - [
|
41
|
+
if (options.keys - %i[from to on except_from except_to except_on]).empty?
|
39
42
|
# Explicit from/to requirements specified
|
40
|
-
@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) }]
|
41
44
|
else
|
42
45
|
# Separate out the event requirement
|
43
46
|
options.delete(:on)
|
@@ -47,7 +50,7 @@ module StateMachines
|
|
47
50
|
@state_requirements = options.collect do |from, to|
|
48
51
|
from = WhitelistMatcher.new(from) unless from.is_a?(Matcher)
|
49
52
|
to = WhitelistMatcher.new(to) unless to.is_a?(Matcher)
|
50
|
-
{from: from, to: to}
|
53
|
+
{ from: from, to: to }
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
@@ -55,7 +58,7 @@ module StateMachines
|
|
55
58
|
# on the priority in which tracked states should be added.
|
56
59
|
@known_states = []
|
57
60
|
@state_requirements.each do |state_requirement|
|
58
|
-
[
|
61
|
+
%i[from to].each { |option| @known_states |= state_requirement[option].values }
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
@@ -112,24 +115,24 @@ module StateMachines
|
|
112
115
|
# branch.match(object, :on => :ignite) # => {:to => ..., :from => ..., :on => ...}
|
113
116
|
# branch.match(object, :on => :park) # => nil
|
114
117
|
def match(object, query = {})
|
115
|
-
|
118
|
+
StateMachines::OptionsValidator.assert_valid_keys!(query, :from, :to, :on, :guard)
|
116
119
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
return unless (match = match_query(query)) && matches_conditions?(object, query)
|
121
|
+
|
122
|
+
match
|
120
123
|
end
|
121
124
|
|
122
|
-
def draw(graph, event, valid_states)
|
123
|
-
|
125
|
+
def draw(graph, event, valid_states, io = $stdout)
|
126
|
+
machine.renderer.draw_branch(self, graph, event, valid_states, io)
|
124
127
|
end
|
125
128
|
|
126
|
-
|
129
|
+
protected
|
127
130
|
|
128
131
|
# Builds a matcher strategy to use for the given options. If neither a
|
129
132
|
# whitelist nor a blacklist option is specified, then an AllMatcher is
|
130
133
|
# built.
|
131
134
|
def build_matcher(options, whitelist_option, blacklist_option)
|
132
|
-
|
135
|
+
StateMachines::OptionsValidator.assert_exclusive_keys!(options, whitelist_option, blacklist_option)
|
133
136
|
|
134
137
|
if options.include?(whitelist_option)
|
135
138
|
value = options[whitelist_option]
|
@@ -164,7 +167,7 @@ module StateMachines
|
|
164
167
|
# matching requirement is found, then it is returned.
|
165
168
|
def match_states(query)
|
166
169
|
state_requirements.detect do |state_requirement|
|
167
|
-
[
|
170
|
+
%i[from to].all? { |option| matches_requirement?(query, option, state_requirement[option]) }
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
@@ -178,8 +181,8 @@ module StateMachines
|
|
178
181
|
# given object
|
179
182
|
def matches_conditions?(object, query)
|
180
183
|
query[:guard] == false ||
|
181
|
-
|
182
|
-
|
184
|
+
(Array(if_condition).all? { |condition| evaluate_method(object, condition) } &&
|
185
|
+
!Array(unless_condition).any? { |condition| evaluate_method(object, condition) })
|
183
186
|
end
|
184
187
|
end
|
185
188
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'state_machines/branch'
|
2
4
|
require 'state_machines/eval_helpers'
|
3
5
|
|
@@ -122,7 +124,7 @@ module StateMachines
|
|
122
124
|
# callback can be found in their attribute definitions.
|
123
125
|
def initialize(type, *args, &block)
|
124
126
|
@type = type
|
125
|
-
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)
|
126
128
|
|
127
129
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
128
130
|
@methods = args
|
@@ -130,7 +132,7 @@ module StateMachines
|
|
130
132
|
@methods << block if block_given?
|
131
133
|
raise ArgumentError, 'Method(s) for callback must be specified' unless @methods.any?
|
132
134
|
|
133
|
-
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)
|
134
136
|
|
135
137
|
# Proxy lambda blocks so that they're bound to the object
|
136
138
|
bind_to_object = options.delete(:bind_to_object)
|
@@ -154,16 +156,16 @@ module StateMachines
|
|
154
156
|
#
|
155
157
|
# If a terminator has been configured and it matches the result from the
|
156
158
|
# evaluated method, then the callback chain should be halted.
|
157
|
-
def call(object, context = {},
|
159
|
+
def call(object, context = {}, *, &)
|
158
160
|
if @branch.matches?(object, context)
|
159
|
-
run_methods(object, context, 0,
|
161
|
+
run_methods(object, context, 0, *, &)
|
160
162
|
true
|
161
163
|
else
|
162
164
|
false
|
163
165
|
end
|
164
166
|
end
|
165
167
|
|
166
|
-
|
168
|
+
private
|
167
169
|
|
168
170
|
# Runs all of the methods configured for this callback.
|
169
171
|
#
|
@@ -177,7 +179,7 @@ module StateMachines
|
|
177
179
|
def run_methods(object, context = {}, index = 0, *args, &block)
|
178
180
|
if type == :around
|
179
181
|
current_method = @methods[index]
|
180
|
-
if
|
182
|
+
if current_method
|
181
183
|
yielded = false
|
182
184
|
evaluate_method(object, current_method, *args) do
|
183
185
|
yielded = true
|
@@ -185,8 +187,8 @@ module StateMachines
|
|
185
187
|
end
|
186
188
|
|
187
189
|
throw :halt unless yielded
|
188
|
-
|
189
|
-
yield
|
190
|
+
elsif block_given?
|
191
|
+
yield
|
190
192
|
end
|
191
193
|
else
|
192
194
|
@methods.each do |method|
|
@@ -204,13 +206,12 @@ module StateMachines
|
|
204
206
|
arity += 1 if arity >= 0 # Make sure the object gets passed
|
205
207
|
arity += 1 if arity == 1 && type == :around # Make sure the block gets passed
|
206
208
|
|
207
|
-
method =
|
208
|
-
|
209
|
+
method = ->(object, *args) { object.instance_exec(*args, &block) }
|
209
210
|
|
210
211
|
# Proxy arity to the original block
|
211
212
|
(
|
212
|
-
class << method
|
213
|
-
self
|
213
|
+
class << method
|
214
|
+
self
|
214
215
|
end).class_eval do
|
215
216
|
define_method(:arity) { arity }
|
216
217
|
end
|
data/lib/state_machines/core.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Load all of the core implementation required to use state_machine. This
|
2
4
|
# includes:
|
3
5
|
# * StateMachines::MacroMethods which adds the state_machine DSL to your class
|
4
6
|
# * A set of initializers for setting state_machine defaults based on the current
|
5
7
|
# running environment (such as within Rails)
|
6
|
-
require 'state_machines/assertions'
|
7
8
|
require 'state_machines/error'
|
8
9
|
|
9
10
|
require 'state_machines/extensions'
|
@@ -23,7 +24,6 @@ require 'state_machines/transition_collection'
|
|
23
24
|
require 'state_machines/branch'
|
24
25
|
|
25
26
|
require 'state_machines/helper_module'
|
26
|
-
require 'state_machines/state'
|
27
27
|
require 'state_machines/callback'
|
28
28
|
require 'state_machines/node_collection'
|
29
29
|
|
@@ -40,4 +40,4 @@ require 'state_machines/path_collection'
|
|
40
40
|
require 'state_machines/machine'
|
41
41
|
require 'state_machines/machine_collection'
|
42
42
|
|
43
|
-
require 'state_machines/macro_methods'
|
43
|
+
require 'state_machines/macro_methods'
|
data/lib/state_machines/error.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module StateMachines
|
2
4
|
# An error occurred during a state machine invocation
|
3
5
|
class Error < StandardError
|
4
6
|
# The object that failed
|
5
7
|
attr_reader :object
|
6
8
|
|
7
|
-
def initialize(object, message = nil)
|
9
|
+
def initialize(object, message = nil) # :nodoc:
|
8
10
|
@object = object
|
9
11
|
|
10
12
|
super(message)
|
@@ -47,12 +49,13 @@ module StateMachines
|
|
47
49
|
# The event that was attempted to be run
|
48
50
|
attr_reader :event
|
49
51
|
|
50
|
-
def initialize(object, event_name)
|
52
|
+
def initialize(object, event_name) # :nodoc:
|
51
53
|
@event = event_name
|
52
54
|
|
53
55
|
super(object, "#{event.inspect} is an unknown state machine event")
|
54
56
|
end
|
55
57
|
end
|
58
|
+
|
56
59
|
# An invalid transition was attempted
|
57
60
|
class InvalidTransition < Error
|
58
61
|
# The machine attempting to be transitioned
|
@@ -61,7 +64,7 @@ module StateMachines
|
|
61
64
|
# The current state value for the machine
|
62
65
|
attr_reader :from
|
63
66
|
|
64
|
-
def initialize(object, machine, event)
|
67
|
+
def initialize(object, machine, event) # :nodoc:
|
65
68
|
@machine = machine
|
66
69
|
@from_state = machine.states.match!(object)
|
67
70
|
@from = machine.read(object, :state)
|
@@ -99,7 +102,7 @@ module StateMachines
|
|
99
102
|
# The set of events that failed the transition(s)
|
100
103
|
attr_reader :events
|
101
104
|
|
102
|
-
def initialize(object, events)
|
105
|
+
def initialize(object, events) # :nodoc:
|
103
106
|
@events = events
|
104
107
|
|
105
108
|
super(object, "Cannot run events in parallel: #{events * ', '}")
|