finite_machine 0.11.2 → 0.14.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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +80 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +679 -624
  5. data/lib/finite_machine.rb +35 -45
  6. data/lib/finite_machine/async_call.rb +5 -21
  7. data/lib/finite_machine/callable.rb +4 -4
  8. data/lib/finite_machine/catchable.rb +24 -14
  9. data/lib/finite_machine/choice_merger.rb +20 -20
  10. data/lib/finite_machine/const.rb +16 -0
  11. data/lib/finite_machine/definition.rb +3 -3
  12. data/lib/finite_machine/dsl.rb +98 -151
  13. data/lib/finite_machine/env.rb +4 -2
  14. data/lib/finite_machine/event_definition.rb +7 -15
  15. data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
  16. data/lib/finite_machine/hook_event.rb +60 -61
  17. data/lib/finite_machine/hooks.rb +44 -36
  18. data/lib/finite_machine/listener.rb +2 -2
  19. data/lib/finite_machine/logger.rb +5 -4
  20. data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
  21. data/lib/finite_machine/observer.rb +71 -34
  22. data/lib/finite_machine/safety.rb +16 -14
  23. data/lib/finite_machine/state_definition.rb +3 -5
  24. data/lib/finite_machine/state_machine.rb +93 -76
  25. data/lib/finite_machine/state_parser.rb +55 -83
  26. data/lib/finite_machine/subscribers.rb +2 -2
  27. data/lib/finite_machine/threadable.rb +3 -1
  28. data/lib/finite_machine/transition.rb +34 -34
  29. data/lib/finite_machine/transition_builder.rb +23 -32
  30. data/lib/finite_machine/transition_event.rb +12 -11
  31. data/lib/finite_machine/two_phase_lock.rb +8 -6
  32. data/lib/finite_machine/undefined_transition.rb +5 -6
  33. data/lib/finite_machine/version.rb +2 -2
  34. metadata +58 -142
  35. data/.gitignore +0 -18
  36. data/.rspec +0 -5
  37. data/.ruby-gemset +0 -1
  38. data/.ruby-version +0 -1
  39. data/.travis.yml +0 -26
  40. data/Gemfile +0 -15
  41. data/Rakefile +0 -8
  42. data/assets/finite_machine_logo.png +0 -0
  43. data/examples/atm.rb +0 -45
  44. data/examples/bug_system.rb +0 -145
  45. data/finite_machine.gemspec +0 -23
  46. data/lib/finite_machine/async_proxy.rb +0 -30
  47. data/spec/integration/system_spec.rb +0 -95
  48. data/spec/spec_helper.rb +0 -33
  49. data/spec/unit/alias_target_spec.rb +0 -108
  50. data/spec/unit/async_events_spec.rb +0 -138
  51. data/spec/unit/callable/call_spec.rb +0 -113
  52. data/spec/unit/callbacks_spec.rb +0 -942
  53. data/spec/unit/can_spec.rb +0 -98
  54. data/spec/unit/choice_spec.rb +0 -331
  55. data/spec/unit/define_spec.rb +0 -55
  56. data/spec/unit/definition_spec.rb +0 -115
  57. data/spec/unit/event_names_spec.rb +0 -19
  58. data/spec/unit/event_queue_spec.rb +0 -52
  59. data/spec/unit/events_chain/add_spec.rb +0 -25
  60. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  61. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  62. data/spec/unit/events_chain/clear_spec.rb +0 -15
  63. data/spec/unit/events_chain/events_spec.rb +0 -18
  64. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  65. data/spec/unit/events_chain/match_transition_spec.rb +0 -37
  66. data/spec/unit/events_chain/move_to_spec.rb +0 -48
  67. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  68. data/spec/unit/events_spec.rb +0 -459
  69. data/spec/unit/handlers_spec.rb +0 -152
  70. data/spec/unit/hook_event/build_spec.rb +0 -15
  71. data/spec/unit/hook_event/eql_spec.rb +0 -36
  72. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  73. data/spec/unit/hook_event/initialize_spec.rb +0 -25
  74. data/spec/unit/hook_event/notify_spec.rb +0 -14
  75. data/spec/unit/hooks/call_spec.rb +0 -24
  76. data/spec/unit/hooks/clear_spec.rb +0 -16
  77. data/spec/unit/hooks/inspect_spec.rb +0 -17
  78. data/spec/unit/hooks/register_spec.rb +0 -22
  79. data/spec/unit/if_unless_spec.rb +0 -353
  80. data/spec/unit/initial_spec.rb +0 -222
  81. data/spec/unit/inspect_spec.rb +0 -17
  82. data/spec/unit/is_spec.rb +0 -55
  83. data/spec/unit/log_transitions_spec.rb +0 -30
  84. data/spec/unit/logger_spec.rb +0 -38
  85. data/spec/unit/respond_to_spec.rb +0 -38
  86. data/spec/unit/state_parser/inspect_spec.rb +0 -25
  87. data/spec/unit/state_parser/parse_spec.rb +0 -59
  88. data/spec/unit/states_spec.rb +0 -34
  89. data/spec/unit/subscribers_spec.rb +0 -42
  90. data/spec/unit/target_spec.rb +0 -225
  91. data/spec/unit/terminated_spec.rb +0 -95
  92. data/spec/unit/transition/check_conditions_spec.rb +0 -54
  93. data/spec/unit/transition/inspect_spec.rb +0 -25
  94. data/spec/unit/transition/matches_spec.rb +0 -23
  95. data/spec/unit/transition/states_spec.rb +0 -31
  96. data/spec/unit/transition/to_state_spec.rb +0 -27
  97. data/spec/unit/trigger_spec.rb +0 -22
  98. data/spec/unit/undefined_transition/eql_spec.rb +0 -17
  99. data/tasks/console.rake +0 -11
  100. data/tasks/coverage.rake +0 -11
  101. data/tasks/spec.rake +0 -29
@@ -1,34 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, 'states' do
6
- it "retrieves all available states" do
7
- fsm = FiniteMachine.define do
8
- initial :green
9
-
10
- events {
11
- event :slow, :green => :yellow
12
- event :stop, :yellow => :red
13
- event :ready, :red => :yellow
14
- event :go, :yellow => :green
15
- }
16
- end
17
-
18
- expect(fsm.states).to match_array([:none, :green, :yellow, :red])
19
- end
20
-
21
- it "retrieves all unique states for choice transition" do
22
- fsm = FiniteMachine.define do
23
- initial :green
24
-
25
- events {
26
- event :next, from: :green do
27
- choice :yellow, if: -> { false }
28
- choice :red, if: -> { true }
29
- end
30
- }
31
- end
32
- expect(fsm.states).to match_array([:none, :green, :yellow, :red])
33
- end
34
- end
@@ -1,42 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::Subscribers do
6
- let(:listener) { double }
7
-
8
- it "checks if any subscribers exist" do
9
- subscribers = described_class.new
10
- expect(subscribers.empty?).to eq(true)
11
- subscribers.subscribe(listener)
12
- expect(subscribers.empty?).to eq(false)
13
- end
14
-
15
- it "allows to subscribe multiple listeners" do
16
- subscribers = described_class.new
17
- subscribers.subscribe(listener, listener)
18
- expect(subscribers.size).to eq(2)
19
- end
20
-
21
- it "returns index for the subscriber" do
22
- subscribers = described_class.new
23
- subscribers.subscribe(listener)
24
- expect(subscribers.index(listener)).to eql(0)
25
- end
26
-
27
- it "visits all subscribed listeners for the event" do
28
- subscribers = described_class.new
29
- subscribers.subscribe(listener)
30
- event = spy(:event)
31
- subscribers.visit(event)
32
- expect(event).to have_received(:notify).with(listener)
33
- end
34
-
35
- it "resets the subscribers" do
36
- subscribers = described_class.new
37
- subscribers.subscribe(listener)
38
- expect(subscribers.empty?).to eq(false)
39
- subscribers.reset
40
- expect(subscribers.empty?).to eq(true)
41
- end
42
- end
@@ -1,225 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, '#target' do
6
- it "allows to target external object" do
7
- stub_const("Car", Class.new do
8
- attr_accessor :reverse_lights
9
-
10
- def turn_reverse_lights_off
11
- @reverse_lights = false
12
- end
13
-
14
- def turn_reverse_lights_on
15
- @reverse_lights = true
16
- end
17
-
18
- def reverse_lights?
19
- @reverse_lights ||= false
20
- end
21
-
22
- def engine
23
- context = self
24
- @engine ||= FiniteMachine.define do
25
- initial :neutral
26
-
27
- target context
28
-
29
- events {
30
- event :forward, [:reverse, :neutral] => :one
31
- event :shift, :one => :two
32
- event :shift, :two => :one
33
- event :back, [:neutral, :one] => :reverse
34
- }
35
-
36
- callbacks {
37
- on_enter :reverse do |event|
38
- target.turn_reverse_lights_on
39
- end
40
-
41
- on_exit :reverse do |event|
42
- target.turn_reverse_lights_off
43
- end
44
- }
45
- end
46
- end
47
- end)
48
- car = Car.new
49
- expect(car.reverse_lights?).to be(false)
50
- expect(car.engine.current).to eql(:neutral)
51
- car.engine.back
52
- expect(car.engine.current).to eql(:reverse)
53
- expect(car.reverse_lights?).to be(true)
54
- car.engine.forward
55
- expect(car.engine.current).to eql(:one)
56
- expect(car.reverse_lights?).to be(false)
57
- end
58
-
59
- it "propagates method call" do
60
- fsm = FiniteMachine.define do
61
- initial :green
62
- events {
63
- event :slow, :green => :yellow
64
- }
65
-
66
- callbacks {
67
- on_enter_yellow do |event|
68
- uknown_method
69
- end
70
- }
71
- end
72
- expect(fsm.current).to eql(:green)
73
- expect { fsm.slow }.to raise_error(StandardError)
74
- end
75
-
76
- it "references machine methods inside callback" do
77
- called = []
78
- fsm = FiniteMachine.define do
79
- initial :green
80
-
81
- events {
82
- event :slow, :green => :yellow
83
- event :stop, :yellow => :red
84
- event :ready, :red => :yellow
85
- event :go, :yellow => :green
86
- }
87
-
88
- callbacks {
89
- on_enter_yellow do |event|
90
- stop(:now)
91
- end
92
-
93
- on_enter_red do |event, param|
94
- called << "#{event.from} #{param}"
95
- end
96
- }
97
- end
98
-
99
- expect(fsm.current).to eql(:green)
100
- fsm.slow
101
- expect(fsm.current).to eql(:red)
102
- expect(called).to eql(['yellow now'])
103
- end
104
-
105
- it "allows context methods take precedence over machine ones" do
106
- stub_const("Car", Class.new do
107
- attr_accessor :reverse_lights
108
- attr_accessor :called
109
-
110
- def turn_reverse_lights_off
111
- @reverse_lights = false
112
- end
113
-
114
- def turn_reverse_lights_on
115
- @reverse_lights = true
116
- end
117
-
118
- def reverse_lights?
119
- @reverse_lights ||= false
120
- end
121
-
122
- def engine
123
- self.called ||= []
124
- context ||= self
125
- @engine ||= FiniteMachine.define do
126
- initial :neutral
127
-
128
- target context
129
-
130
- events {
131
- event :forward, [:reverse, :neutral] => :one
132
- event :shift, :one => :two
133
- event :shift, :two => :one
134
- event :back, [:neutral, :one] => :reverse
135
- }
136
-
137
- callbacks {
138
- on_enter :reverse do |event|
139
- target.called << 'on_enter_reverse'
140
- target.turn_reverse_lights_on
141
- forward('Piotr!')
142
- end
143
- on_before :forward do |event, name|
144
- target.called << "on_enter_forward with #{name}"
145
- end
146
- }
147
- end
148
- end
149
- end)
150
-
151
- car = Car.new
152
- expect(car.reverse_lights?).to be(false)
153
- expect(car.engine.current).to eql(:neutral)
154
- car.engine.back
155
- expect(car.engine.current).to eql(:one)
156
- expect(car.called).to eql([
157
- 'on_enter_reverse',
158
- 'on_enter_forward with Piotr!'
159
- ])
160
- end
161
-
162
- it "allows to access target inside the callback" do
163
- context = double(:context)
164
- called = nil
165
- fsm = FiniteMachine.define do
166
- initial :green
167
-
168
- target context
169
-
170
- events {
171
- event :slow, :green => :yellow
172
- event :stop, :yellow => :red
173
- }
174
- callbacks {
175
- on_enter_yellow do |event|
176
- called = target
177
- end
178
- }
179
- end
180
- expect(fsm.current).to eql(:green)
181
- fsm.slow
182
- expect(called).to eq(context)
183
- end
184
-
185
- it "allows to differentiate between same named methods" do
186
- called = []
187
- stub_const("Car", Class.new do
188
- def initialize(called)
189
- @called = called
190
- end
191
- def save
192
- @called << 'car save called'
193
- end
194
- end)
195
-
196
- car = Car.new(called)
197
- fsm = FiniteMachine.define do
198
- initial :unsaved
199
-
200
- target car
201
-
202
- events {
203
- event :validate, :unsaved => :valid
204
- event :save, :valid => :saved
205
- }
206
-
207
- callbacks {
208
- on_enter :valid do |event|
209
- target.save
210
- save
211
- end
212
- on_after :save do |event|
213
- called << 'event save called'
214
- end
215
- }
216
- end
217
- expect(fsm.current).to eql(:unsaved)
218
- fsm.validate
219
- expect(fsm.current).to eql(:saved)
220
- expect(called).to eq([
221
- 'car save called',
222
- 'event save called'
223
- ])
224
- end
225
- end
@@ -1,95 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, 'terminated?' do
6
-
7
- it "allows to specify terminal state" do
8
- fsm = FiniteMachine.define do
9
- initial :green
10
- terminal :red
11
-
12
- events {
13
- event :slow, :green => :yellow
14
- event :stop, :yellow => :red
15
- }
16
- end
17
-
18
- expect(fsm.current).to eql(:green)
19
- expect(fsm.terminated?).to be(false)
20
-
21
- fsm.slow
22
- expect(fsm.current).to eql(:yellow)
23
- expect(fsm.terminated?).to be(false)
24
-
25
- fsm.stop
26
- expect(fsm.current).to eql(:red)
27
- expect(fsm.terminated?).to be(true)
28
- end
29
-
30
- it "allows to specify terminal state as parameter" do
31
- fsm = FiniteMachine.define terminal: :red do
32
- initial :green
33
-
34
- events {
35
- event :slow, :green => :yellow
36
- event :stop, :yellow => :red
37
- }
38
- end
39
- fsm.slow
40
- fsm.stop
41
- expect(fsm.terminated?).to be(true)
42
- end
43
-
44
- it "checks without terminal state" do
45
- fsm = FiniteMachine.define do
46
- initial :green
47
-
48
- events {
49
- event :slow, :green => :yellow
50
- event :stop, :yellow => :red
51
- }
52
- end
53
-
54
- expect(fsm.current).to eql(:green)
55
- expect(fsm.terminated?).to be(false)
56
-
57
- fsm.slow
58
- expect(fsm.current).to eql(:yellow)
59
- expect(fsm.terminated?).to be(false)
60
-
61
- fsm.stop
62
- expect(fsm.current).to eql(:red)
63
- expect(fsm.terminated?).to be(false)
64
- end
65
-
66
- it "allows for multiple terminal states" do
67
- fsm = FiniteMachine.define do
68
- initial :open
69
-
70
- terminal :close, :canceled, :faulty
71
-
72
- events {
73
- event :resolve, :open => :close
74
- event :decline, :open => :canceled
75
- event :error, :open => :faulty
76
- }
77
- end
78
- expect(fsm.current).to eql(:open)
79
- expect(fsm.terminated?).to be(false)
80
-
81
- fsm.resolve
82
- expect(fsm.current).to eql(:close)
83
- expect(fsm.terminated?).to be(true)
84
-
85
- fsm.restore!(:open)
86
- fsm.decline
87
- expect(fsm.current).to eql(:canceled)
88
- expect(fsm.terminated?).to be(true)
89
-
90
- fsm.restore!(:open)
91
- fsm.error
92
- expect(fsm.current).to eql(:faulty)
93
- expect(fsm.terminated?).to be(true)
94
- end
95
- end
@@ -1,54 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::Transition, '.check_conditions' do
6
- it "verifies all conditions pass" do
7
- exec_conditions = 0
8
- ok_condition = -> { exec_conditions += 1; return true }
9
- fail_condition = -> { exec_conditions += 1; return false }
10
- machine = double(:machine, target: Object.new)
11
-
12
- transition = described_class.new(machine, if: [ok_condition, fail_condition])
13
-
14
- expect(transition.check_conditions).to eql(false)
15
- expect(exec_conditions).to eq(2)
16
- end
17
-
18
- it "verifies 'if' and 'unless' conditions" do
19
- machine = double(:machine, target: Object.new)
20
- exec_conditions = 0
21
- ok_condition = -> { exec_conditions += 1; return true }
22
- fail_condition = -> { exec_conditions += 1; return false }
23
-
24
- transition = described_class.new(machine, if: [ok_condition], unless: [fail_condition])
25
-
26
- expect(transition.check_conditions).to eql(true)
27
- expect(exec_conditions).to eq(2)
28
- end
29
-
30
- it "verifies condition with arguments" do
31
- machine = double(:machine, target: Object.new)
32
- condition = lambda { |target, arg| arg == 1 }
33
-
34
- transition = described_class.new(machine, if: [condition])
35
-
36
- expect(transition.check_conditions(2)).to eql(false)
37
- expect(transition.check_conditions(1)).to eql(true)
38
- end
39
-
40
- it "verifies condition on target" do
41
- stub_const("Car", Class.new do
42
- def engine_on?
43
- true
44
- end
45
- end)
46
- target = Car.new
47
- machine = double(:machine, target: target)
48
- condition = lambda { |car| car.engine_on? }
49
-
50
- transition = described_class.new(machine, if: condition)
51
-
52
- expect(transition.check_conditions).to eql(true)
53
- end
54
- end