finite_machine 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/README.md +1 -1
  4. data/lib/finite_machine.rb +3 -1
  5. data/lib/finite_machine/choice_merger.rb +2 -2
  6. data/lib/finite_machine/dsl.rb +4 -4
  7. data/lib/finite_machine/message_queue.rb +0 -1
  8. data/lib/finite_machine/state_machine.rb +3 -3
  9. data/lib/finite_machine/two_phase_lock.rb +6 -6
  10. data/lib/finite_machine/version.rb +1 -1
  11. metadata +20 -146
  12. data/Rakefile +0 -12
  13. data/benchmarks/memory_profile.rb +0 -11
  14. data/benchmarks/memory_usage.rb +0 -28
  15. data/examples/atm.rb +0 -45
  16. data/examples/bug_system.rb +0 -145
  17. data/finite_machine.gemspec +0 -30
  18. data/spec/integration/system_spec.rb +0 -93
  19. data/spec/performance/benchmark_spec.rb +0 -54
  20. data/spec/spec_helper.rb +0 -34
  21. data/spec/unit/alias_target_spec.rb +0 -89
  22. data/spec/unit/async_callbacks_spec.rb +0 -28
  23. data/spec/unit/auto_methods_spec.rb +0 -44
  24. data/spec/unit/callable/call_spec.rb +0 -111
  25. data/spec/unit/callbacks_spec.rb +0 -851
  26. data/spec/unit/can_spec.rb +0 -88
  27. data/spec/unit/cancel_callbacks_spec.rb +0 -46
  28. data/spec/unit/choice_spec.rb +0 -295
  29. data/spec/unit/define_spec.rb +0 -55
  30. data/spec/unit/definition_spec.rb +0 -98
  31. data/spec/unit/event_names_spec.rb +0 -15
  32. data/spec/unit/events_map/add_spec.rb +0 -23
  33. data/spec/unit/events_map/choice_transition_spec.rb +0 -25
  34. data/spec/unit/events_map/clear_spec.rb +0 -13
  35. data/spec/unit/events_map/events_spec.rb +0 -16
  36. data/spec/unit/events_map/inspect_spec.rb +0 -22
  37. data/spec/unit/events_map/match_transition_spec.rb +0 -35
  38. data/spec/unit/events_map/move_to_spec.rb +0 -45
  39. data/spec/unit/events_map/states_for_spec.rb +0 -17
  40. data/spec/unit/events_spec.rb +0 -390
  41. data/spec/unit/handlers_spec.rb +0 -120
  42. data/spec/unit/hook_event/any_state_or_event_spec.rb +0 -13
  43. data/spec/unit/hook_event/build_spec.rb +0 -13
  44. data/spec/unit/hook_event/eql_spec.rb +0 -34
  45. data/spec/unit/hook_event/initialize_spec.rb +0 -23
  46. data/spec/unit/hook_event/notify_spec.rb +0 -12
  47. data/spec/unit/hooks/clear_spec.rb +0 -16
  48. data/spec/unit/hooks/find_spec.rb +0 -19
  49. data/spec/unit/hooks/inspect_spec.rb +0 -25
  50. data/spec/unit/hooks/register_spec.rb +0 -17
  51. data/spec/unit/if_unless_spec.rb +0 -314
  52. data/spec/unit/initial_spec.rb +0 -190
  53. data/spec/unit/inspect_spec.rb +0 -22
  54. data/spec/unit/is_spec.rb +0 -49
  55. data/spec/unit/log_transitions_spec.rb +0 -24
  56. data/spec/unit/logger_spec.rb +0 -36
  57. data/spec/unit/message_queue_spec.rb +0 -62
  58. data/spec/unit/new_spec.rb +0 -50
  59. data/spec/unit/respond_to_spec.rb +0 -34
  60. data/spec/unit/state_parser/parse_spec.rb +0 -56
  61. data/spec/unit/states_spec.rb +0 -28
  62. data/spec/unit/subscribers_spec.rb +0 -40
  63. data/spec/unit/target_spec.rb +0 -225
  64. data/spec/unit/terminated_spec.rb +0 -85
  65. data/spec/unit/transition/check_conditions_spec.rb +0 -55
  66. data/spec/unit/transition/inspect_spec.rb +0 -25
  67. data/spec/unit/transition/matches_spec.rb +0 -21
  68. data/spec/unit/transition/states_spec.rb +0 -29
  69. data/spec/unit/transition/to_state_spec.rb +0 -19
  70. data/spec/unit/trigger_spec.rb +0 -18
  71. data/spec/unit/undefined_transition/eql_spec.rb +0 -15
  72. data/tasks/console.rake +0 -11
  73. data/tasks/coverage.rake +0 -11
  74. data/tasks/spec.rake +0 -34
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, 'async callbacks' do
4
- it "permits async callback" do
5
- called = []
6
- fsm = FiniteMachine.new do
7
- initial :green, silent: false
8
-
9
- event :slow, :green => :yellow
10
- event :go, :yellow => :green
11
-
12
- on_enter :green, :async do |event| called << 'on_enter_green' end
13
- on_before :slow, :async do |event| called << 'on_before_slow' end
14
- on_exit :yellow, :async do |event| called << 'on_exit_yellow' end
15
- on_after :go, :async do |event| called << 'on_after_go' end
16
- end
17
- fsm.slow
18
- fsm.go
19
- sleep 0.1
20
- expect(called).to match_array([
21
- 'on_enter_green',
22
- 'on_before_slow',
23
- 'on_exit_yellow',
24
- 'on_enter_green',
25
- 'on_after_go'
26
- ])
27
- end
28
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, ':auto_methods' do
4
- it "allows turning off automatic methods generation" do
5
- fsm = FiniteMachine.new(auto_methods: false) do
6
- initial :green
7
-
8
- event :slow, :green => :yellow
9
- event :stop, :yellow => :red
10
- event :ready, :red => :yellow
11
- event :go, :yellow => :green
12
-
13
- # allows for fluid callback names
14
- once_on_enter_yellow do |event| 'once_on_enter_yellow' end
15
- end
16
-
17
- expect(fsm.respond_to?(:slow)).to eq(false)
18
- expect { fsm.slow }.to raise_error(NoMethodError)
19
- expect(fsm.current).to eq(:green)
20
-
21
- fsm.trigger(:slow)
22
- expect(fsm.current).to eq(:yellow)
23
- end
24
-
25
- it "allows to use any method name without auto method generation" do
26
- fsm = FiniteMachine.new(auto_methods: false) do
27
- initial :green
28
-
29
- event :fail, :green => :red
30
- end
31
-
32
- fsm.trigger(:fail)
33
- expect(fsm.current).to eq(:red)
34
- end
35
-
36
- it "detects dangerous event names" do
37
- expect {
38
- FiniteMachine.new do
39
- event :trigger, :a => :b
40
- end
41
- }.to raise_error(FiniteMachine::AlreadyDefinedError)
42
- end
43
- end
44
-
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Callable, '#call' do
4
-
5
- before(:each) {
6
- stub_const("Car", Class.new do
7
- attr_reader :result
8
-
9
- def initialize
10
- @engine_on = false
11
- end
12
-
13
- def turn_engine_on
14
- @result = 'turn_engine_on'
15
- @engine_on = true
16
- end
17
-
18
- def set_engine(value = :on)
19
- @result = "set_engine(#{value})"
20
- @engine = value.to_sym == :on
21
- end
22
-
23
- def turn_engine_off
24
- @result = 'turn_engine_off'
25
- @engine_on = false
26
- end
27
-
28
- def engine_on?
29
- @result = 'engine_on'
30
- !!@engine_on
31
- end
32
- end)
33
- }
34
-
35
- let(:called) { [] }
36
-
37
- let(:target) { Car.new }
38
-
39
- let(:instance) { described_class.new(object) }
40
-
41
- context 'when string' do
42
- let(:object) { 'engine_on?' }
43
-
44
- it 'executes method on target' do
45
- instance.call(target)
46
- expect(target.result).to eql('engine_on')
47
- end
48
- end
49
-
50
- context 'when string' do
51
- let(:object) { 'set_engine(:on)' }
52
-
53
- it 'executes method with arguments' do
54
- instance.call(target)
55
- expect(target.result).to eql('set_engine(on)')
56
- end
57
- end
58
-
59
- context 'when string with arguments' do
60
- let(:object) { 'set_engine' }
61
-
62
- it 'executes method with arguments' do
63
- instance.call(target, :off)
64
- expect(target.result).to eql('set_engine(off)')
65
- end
66
- end
67
-
68
- context 'when symbol' do
69
- let(:object) { :set_engine }
70
-
71
- it 'executes method on target' do
72
- instance.call(target)
73
- expect(target.result).to eql('set_engine(on)')
74
- end
75
- end
76
-
77
- context 'when symbol with arguments' do
78
- let(:object) { :set_engine }
79
-
80
- it 'executes method on target' do
81
- instance.call(target, :off)
82
- expect(target.result).to eql('set_engine(off)')
83
- end
84
- end
85
-
86
- context 'when proc without args' do
87
- let(:object) { proc { |a| called << "block_with(#{a})" } }
88
-
89
- it 'passes arguments' do
90
- instance.call(target)
91
- expect(called).to eql(["block_with(#{target})"])
92
- end
93
- end
94
-
95
- context 'when proc with args' do
96
- let(:object) { proc { |a,b| called << "block_with(#{a},#{b})" } }
97
-
98
- it 'passes arguments' do
99
- instance.call(target, :red)
100
- expect(called).to eql(["block_with(#{target},red)"])
101
- end
102
- end
103
-
104
- context 'when unknown' do
105
- let(:object) { Object.new }
106
-
107
- it 'raises error' do
108
- expect { instance.call(target) }.to raise_error(ArgumentError)
109
- end
110
- end
111
- end
@@ -1,851 +0,0 @@
1
- # frozen_string_literal
2
-
3
- RSpec.describe FiniteMachine, 'callbacks' do
4
- it "triggers default init event" do
5
- called = []
6
- fsm = FiniteMachine.new do
7
- initial :green, defer: true, silent: false
8
-
9
- # generic state callbacks
10
- on_enter do |event| called << 'on_enter' end
11
- on_transition do |event| called << 'on_transition' end
12
- on_exit do |event| called << 'on_exit' end
13
-
14
- # generic event callbacks
15
- on_before any_event do |event| called << 'on_before' end
16
- on_after do |event| called << 'on_after' end
17
-
18
- # state callbacks
19
- on_enter :none do |event| called << 'on_enter_none' end
20
- on_enter :green do |event| called << 'on_enter_green' end
21
-
22
- on_transition :none do |event| called << 'on_transition_none' end
23
- on_transition :green do |event| called << 'on_transition_green' end
24
-
25
- on_exit :none do |event| called << 'on_exit_none' end
26
- on_exit :green do |event| called << 'on_exit_green' end
27
-
28
- # event callbacks
29
- on_before :init do |event| called << 'on_before_init' end
30
- on_after :init do |event| called << 'on_after_init' end
31
- end
32
-
33
- expect(fsm.current).to eql(:none)
34
- fsm.init
35
- expect(called).to eql([
36
- 'on_before',
37
- 'on_before_init',
38
- 'on_exit',
39
- 'on_exit_none',
40
- 'on_transition',
41
- 'on_transition_green',
42
- 'on_enter',
43
- 'on_enter_green',
44
- 'on_after',
45
- 'on_after_init'
46
- ])
47
- end
48
-
49
- it "executes callbacks in order" do
50
- called = []
51
- fsm = FiniteMachine.new do
52
- initial :green, silent: false
53
-
54
- event :slow, :green => :yellow
55
- event :stop, :yellow => :red
56
- event :ready, :red => :yellow
57
- event :go, :yellow => :green
58
-
59
- # generic callbacks
60
- on_enter do |event| called << 'on_enter' end
61
- on_transition do |event| called << 'on_transition' end
62
- on_exit do |event| called << 'on_exit' end
63
-
64
- on_before do |event| called << 'on_before' end
65
- on_after do |event| called << 'on_after' end
66
-
67
- # state callbacks
68
- on_enter :green do |event| called << 'on_enter_green' end
69
- on_enter :yellow do |event| called << "on_enter_yellow" end
70
- on_enter :red do |event| called << "on_enter_red" end
71
-
72
- on_transition :green do |event| called << 'on_transition_green' end
73
- on_transition :yellow do |event| called << "on_transition_yellow" end
74
- on_transition :red do |event| called << "on_transition_red" end
75
-
76
- on_exit :green do |event| called << 'on_exit_green' end
77
- on_exit :yellow do |event| called << "on_exit_yellow" end
78
- on_exit :red do |event| called << "on_exit_red" end
79
-
80
- # event callbacks
81
- on_before :slow do |event| called << 'on_before_slow' end
82
- on_before :stop do |event| called << "on_before_stop" end
83
- on_before :ready do |event| called << "on_before_ready" end
84
- on_before :go do |event| called << "on_before_go" end
85
-
86
- on_after :slow do |event| called << 'on_after_slow' end
87
- on_after :stop do |event| called << "on_after_stop" end
88
- on_after :ready do |event| called << "on_after_ready" end
89
- on_after :go do |event| called << "on_after_go" end
90
- end
91
-
92
- expect(fsm.current).to eq(:green)
93
- expect(called).to eq([
94
- 'on_before',
95
- 'on_exit',
96
- 'on_transition',
97
- 'on_transition_green',
98
- 'on_enter',
99
- 'on_enter_green',
100
- 'on_after'
101
- ])
102
-
103
- called = []
104
- fsm.slow
105
- expect(called).to eql([
106
- 'on_before',
107
- 'on_before_slow',
108
- 'on_exit',
109
- 'on_exit_green',
110
- 'on_transition',
111
- 'on_transition_yellow',
112
- 'on_enter',
113
- 'on_enter_yellow',
114
- 'on_after',
115
- 'on_after_slow'
116
- ])
117
-
118
- called = []
119
- fsm.stop
120
- expect(called).to eql([
121
- 'on_before',
122
- 'on_before_stop',
123
- 'on_exit',
124
- 'on_exit_yellow',
125
- 'on_transition',
126
- 'on_transition_red',
127
- 'on_enter',
128
- 'on_enter_red',
129
- 'on_after',
130
- 'on_after_stop'
131
- ])
132
-
133
- called = []
134
- fsm.ready
135
- expect(called).to eql([
136
- 'on_before',
137
- 'on_before_ready',
138
- 'on_exit',
139
- 'on_exit_red',
140
- 'on_transition',
141
- 'on_transition_yellow',
142
- 'on_enter',
143
- 'on_enter_yellow',
144
- 'on_after',
145
- 'on_after_ready'
146
- ])
147
-
148
- called = []
149
- fsm.go
150
- expect(called).to eql([
151
- 'on_before',
152
- 'on_before_go',
153
- 'on_exit',
154
- 'on_exit_yellow',
155
- 'on_transition',
156
- 'on_transition_green',
157
- 'on_enter',
158
- 'on_enter_green',
159
- 'on_after',
160
- 'on_after_go'
161
- ])
162
- end
163
-
164
- it "maintains transition execution sequence from UML statechart" do
165
- called = []
166
- fsm = FiniteMachine.new do
167
- initial :previous, silent: false
168
-
169
- event :go, :previous => :next, if: -> { called << 'guard'; true}
170
-
171
- on_exit { |event| called << "exit_#{event.from}" }
172
- on_before { |event| called << "before_#{event.name}" }
173
- on_transition { |event| called << "transition_#{event.from}_#{event.to}"}
174
- on_enter { |event| called << "enter_#{event.to}"}
175
- on_after { |event| called << "after_#{event.name}" }
176
- end
177
-
178
- expect(fsm.current).to eq(:previous)
179
- fsm.go
180
- expect(called).to eq([
181
- 'before_init',
182
- 'exit_none',
183
- 'transition_none_previous',
184
- 'enter_previous',
185
- 'after_init',
186
- 'before_go',
187
- 'guard',
188
- 'exit_previous',
189
- 'transition_previous_next',
190
- 'enter_next',
191
- 'after_go'
192
- ])
193
- end
194
-
195
- it "allows multiple callbacks for the same state" do
196
- called = []
197
- fsm = FiniteMachine.new do
198
- initial :green, silent: false
199
-
200
- event :slow, :green => :yellow
201
- event :stop, :yellow => :red
202
- event :ready, :red => :yellow
203
- event :go, :yellow => :green
204
-
205
- # generic state callbacks
206
- on_enter do |event| called << 'on_enter' end
207
- on_transition do |event| called << 'on_transition' end
208
- on_exit do |event| called << 'on_exit' end
209
-
210
- # generic event callbacks
211
- on_before do |event| called << 'on_before' end
212
- on_after do |event| called << 'on_after' end
213
-
214
- # state callbacks
215
- on_exit :green do |event| called << 'on_exit_green_1' end
216
- on_exit :green do |event| called << 'on_exit_green_2' end
217
- on_enter :yellow do |event| called << 'on_enter_yellow_1' end
218
- on_enter :yellow do |event| called << 'on_enter_yellow_2' end
219
- on_transition :yellow do |event| called << 'on_transition_yellow_1' end
220
- on_transition :yellow do |event| called << 'on_transition_yellow_2' end
221
-
222
- # event callbacks
223
- on_before :slow do |event| called << 'on_before_slow_1' end
224
- on_before :slow do |event| called << 'on_before_slow_2' end
225
- on_after :slow do |event| called << 'on_after_slow_1' end
226
- on_after :slow do |event| called << 'on_after_slow_2' end
227
- end
228
-
229
- expect(fsm.current).to eql(:green)
230
- expect(called).to eql([
231
- 'on_before',
232
- 'on_exit',
233
- 'on_transition',
234
- 'on_enter',
235
- 'on_after'
236
- ])
237
- called = []
238
- fsm.slow
239
- expect(fsm.current).to eql(:yellow)
240
- expect(called).to eql([
241
- 'on_before',
242
- 'on_before_slow_1',
243
- 'on_before_slow_2',
244
- 'on_exit',
245
- 'on_exit_green_1',
246
- 'on_exit_green_2',
247
- 'on_transition',
248
- 'on_transition_yellow_1',
249
- 'on_transition_yellow_2',
250
- 'on_enter',
251
- 'on_enter_yellow_1',
252
- 'on_enter_yellow_2',
253
- 'on_after',
254
- 'on_after_slow_1',
255
- 'on_after_slow_2'
256
- ])
257
- end
258
-
259
- it "allows for fluid callback definition" do
260
- called = []
261
- fsm = FiniteMachine.new do
262
- initial :green
263
-
264
- event :slow, :green => :yellow
265
- event :stop, :yellow => :red
266
- event :ready, :red => :yellow
267
- event :go, :yellow => :green
268
-
269
- # state callbacks
270
- on_exit_green do |event| called << 'on_exit_green' end
271
- on_enter_yellow do |event| called << 'on_enter_yellow' end
272
- on_transition_yellow do |event| called << 'on_transition_yellow' end
273
-
274
- # event callbacks
275
- on_before_slow do |event| called << 'on_before_slow' end
276
- on_after_slow do |event| called << 'on_after_slow' end
277
- end
278
-
279
- called = []
280
- fsm.slow
281
- expect(fsm.current).to eql(:yellow)
282
- expect(called).to eql([
283
- 'on_before_slow',
284
- 'on_exit_green',
285
- 'on_transition_yellow',
286
- 'on_enter_yellow',
287
- 'on_after_slow'
288
- ])
289
- end
290
-
291
- it "passes event object to callback" do
292
- evt = nil
293
- fsm = FiniteMachine.new do
294
- initial :green
295
-
296
- event :slow, :green => :yellow
297
-
298
- on_enter(:yellow) { |e| evt = e }
299
- end
300
-
301
- expect(fsm.current).to eql(:green)
302
- fsm.slow
303
- expect(fsm.current).to eql(:yellow)
304
-
305
- expect(evt.from).to eql(:green)
306
- expect(evt.to).to eql(:yellow)
307
- expect(evt.name).to eql(:slow)
308
- end
309
-
310
- it "identifies the from state for callback event parameter" do
311
- evt = nil
312
- fsm = FiniteMachine.new do
313
- initial :green
314
-
315
- event :slow, [:red, :blue, :green] => :yellow
316
- event :fast, :red => :purple
317
-
318
- on_enter(:yellow) { |e| evt = e }
319
- end
320
-
321
- expect(fsm.current).to eql(:green)
322
- fsm.slow
323
- expect(fsm.current).to eql(:yellow)
324
-
325
- expect(evt.from).to eql(:green)
326
- expect(evt.to).to eql(:yellow)
327
- expect(evt.name).to eql(:slow)
328
- end
329
-
330
- it "passes extra parameters to callbacks" do
331
- expected = {name: :init, from: :none, to: :green, a: nil, b: nil, c: nil }
332
-
333
- callback = Proc.new { |event, a, b, c|
334
- target.expect(event.from).to target.eql(expected[:from])
335
- target.expect(event.to).to target.eql(expected[:to])
336
- target.expect(event.name).to target.eql(expected[:name])
337
- target.expect(a).to target.eql(expected[:a])
338
- target.expect(b).to target.eql(expected[:b])
339
- target.expect(c).to target.eql(expected[:c])
340
- }
341
-
342
- fsm = FiniteMachine.new(self) do
343
- initial :green
344
-
345
- event :slow, :green => :yellow
346
- event :stop, :yellow => :red
347
- event :ready, :red => :yellow
348
- event :go, :yellow => :green
349
-
350
- # generic state callbacks
351
- on_enter(&callback)
352
- on_transition(&callback)
353
- on_exit(&callback)
354
-
355
- # generic event callbacks
356
- on_before(&callback)
357
- on_after(&callback)
358
-
359
- # state callbacks
360
- on_enter :green, &callback
361
- on_enter :yellow, &callback
362
- on_enter :red, &callback
363
-
364
- on_transition :green , &callback
365
- on_transition :yellow, &callback
366
- on_transition :red , &callback
367
-
368
- on_exit :green , &callback
369
- on_exit :yellow, &callback
370
- on_exit :red , &callback
371
-
372
- # event callbacks
373
- on_before :slow , &callback
374
- on_before :stop , &callback
375
- on_before :ready, &callback
376
- on_before :go , &callback
377
-
378
- on_after :slow , &callback
379
- on_after :stop , &callback
380
- on_after :ready, &callback
381
- on_after :go , &callback
382
- end
383
-
384
- expected = {name: :slow, from: :green, to: :yellow, a: 1, b: 2, c: 3}
385
- fsm.slow(1, 2, 3)
386
-
387
- expected = {name: :stop, from: :yellow, to: :red, a: 'foo', b: 'bar'}
388
- fsm.stop('foo', 'bar')
389
-
390
- expected = {name: :ready, from: :red, to: :yellow, a: :foo, b: :bar}
391
- fsm.ready(:foo, :bar)
392
-
393
- expected = {name: :go, from: :yellow, to: :green, a: nil, b: nil}
394
- fsm.go(nil, nil)
395
- end
396
-
397
- it "sets callback parameters correctly for transition from :any state" do
398
- expected = {name: :init, from: :none, to: :green, a: nil, b: nil, c: nil }
399
-
400
- callback = Proc.new { |event, a, b, c|
401
- target.expect(event.from).to target.eql(expected[:from])
402
- target.expect(event.to).to target.eql(expected[:to])
403
- target.expect(event.name).to target.eql(expected[:name])
404
- target.expect(a).to target.eql(expected[:a])
405
- target.expect(b).to target.eql(expected[:b])
406
- target.expect(c).to target.eql(expected[:c])
407
- }
408
-
409
- fsm = FiniteMachine.new(self) do
410
- initial :red
411
-
412
- event :power_on, :off => :red
413
- event :power_off, any_state => :off
414
- event :go, :red => :green
415
- event :slow, :green => :yellow
416
- event :stop, :yellow => :red
417
-
418
- # generic state callbacks
419
- on_enter(&callback)
420
- on_transition(&callback)
421
- on_exit(&callback)
422
-
423
- # generic event callbacks
424
- on_before(&callback)
425
- on_after(&callback)
426
-
427
- # state callbacks
428
- on_enter :green, &callback
429
- on_enter :yellow, &callback
430
- on_enter :red, &callback
431
- on_enter :off, &callback
432
- on_enter :off, &callback
433
-
434
- on_transition :green, &callback
435
- on_transition :yellow, &callback
436
- on_transition :red, &callback
437
- on_transition :off, &callback
438
- on_transition :off, &callback
439
-
440
- on_exit :green, &callback
441
- on_exit :yellow, &callback
442
- on_exit :red, &callback
443
- on_exit :off, &callback
444
- on_exit :off, &callback
445
-
446
- # event callbacks
447
- on_before :power_on, &callback
448
- on_before :power_off, &callback
449
- on_before :go, &callback
450
- on_before :slow, &callback
451
- on_before :stop, &callback
452
-
453
- on_after :power_on, &callback
454
- on_after :power_off, &callback
455
- on_after :go, &callback
456
- on_after :slow, &callback
457
- on_after :stop, &callback
458
- end
459
-
460
- expect(fsm.current).to eq(:red)
461
-
462
- expected = {name: :go, from: :red, to: :green, a: 1, b: 2, c: 3 }
463
- fsm.go(1, 2, 3)
464
-
465
- expected = {name: :slow, from: :green, to: :yellow, a: 4, b: 5, c: 6}
466
- fsm.slow(4, 5, 6)
467
-
468
- expected = {name: :stop, from: :yellow, to: :red, a: 7, b: 8, c: 9}
469
- fsm.stop(7, 8, 9)
470
-
471
- expected = {name: :power_off, from: :red, to: :off, a: 10, b: 11, c: 12}
472
- fsm.power_off(10, 11, 12)
473
- end
474
-
475
- it "raises an error with invalid callback name" do
476
- expect {
477
- FiniteMachine.new do
478
- initial :green
479
-
480
- event :slow, :green => :yellow
481
-
482
- on_enter(:magic) { |event| called << 'on_enter'}
483
- end
484
- }.to raise_error(FiniteMachine::InvalidCallbackNameError, /\"magic\" is not a valid callback name/)
485
- end
486
-
487
- it "doesn't allow to mix state callback with event name" do
488
- expect {
489
- FiniteMachine.new do
490
- event :slow, :green => :yellow
491
-
492
- on_enter_slow do |event| end
493
- end
494
- }.to raise_error(FiniteMachine::InvalidCallbackNameError, "\"on_enter\" callback is a state listener and cannot be used with \"slow\" event name. Please use on_before or on_after instead.")
495
- end
496
-
497
- it "doesn't allow to mix event callback with state name" do
498
- expect {
499
- FiniteMachine.new do
500
- event :slow, :green => :yellow
501
-
502
- on_before_green do |event| end
503
- end
504
- }.to raise_error(FiniteMachine::InvalidCallbackNameError, '"on_before" callback is an event listener and cannot be used with "green" state name. Please use on_enter, on_transition or on_exit instead.')
505
- end
506
-
507
- it "propagates exceptions raised inside callback" do
508
- fsm = FiniteMachine.new do
509
- initial :green
510
-
511
- event :slow, :green => :yellow
512
-
513
- on_enter(:yellow) { raise RuntimeError }
514
- end
515
-
516
- expect(fsm.current).to eql(:green)
517
- expect { fsm.slow }.to raise_error(RuntimeError)
518
- end
519
-
520
- it "executes callbacks with multiple 'from' transitions" do
521
- called = []
522
- fsm = FiniteMachine.new do
523
- initial :green
524
-
525
- event :stop, :green => :yellow
526
- event :stop, :yellow => :red
527
-
528
- on_before_stop do |event|
529
- called << 'on_before_stop'
530
- end
531
- end
532
- expect(fsm.current).to eql(:green)
533
- fsm.stop
534
- expect(fsm.current).to eql(:yellow)
535
- fsm.stop
536
- expect(fsm.current).to eql(:red)
537
- expect(called).to eql([
538
- 'on_before_stop',
539
- 'on_before_stop'
540
- ])
541
- end
542
-
543
- it "allows to define callbacks on machine instance" do
544
- called = []
545
- fsm = FiniteMachine.new do
546
- initial :green
547
-
548
- event :slow, :green => :yellow
549
- event :stop, :yellow => :red
550
- event :ready, :red => :yellow
551
- event :go, :yellow => :green
552
- end
553
-
554
- fsm.on_enter_yellow do |event|
555
- called << 'on_enter_yellow'
556
- end
557
-
558
- expect(fsm.current).to eql(:green)
559
- fsm.slow
560
- expect(called).to eql([
561
- 'on_enter_yellow'
562
- ])
563
- end
564
-
565
- it "raises error for unknown callback" do
566
- expect {
567
- FiniteMachine.new do
568
- initial :green
569
-
570
- event :slow, :green => :yellow
571
- event :stop, :yellow => :red
572
- event :ready, :red => :yellow
573
- event :go, :yellow => :green
574
-
575
- on_enter_unknown do |event| end
576
- end
577
- }.to raise_error(NameError, /`on_enter_unknown'/)
578
- end
579
-
580
- it "triggers callbacks only once" do
581
- called = []
582
- fsm = FiniteMachine.new do
583
- initial :green, silent: false
584
-
585
- event :slow, :green => :yellow
586
- event :go, :yellow => :green
587
-
588
- # state callbacks
589
- once_on_enter_green do |event| called << 'once_on_enter_green' end
590
- once_on_enter_yellow do |event| called << 'once_on_enter_yellow' end
591
-
592
- once_on_transition_green do |event| called << 'once_on_transition_green' end
593
- once_on_transition_yellow do |event| called << 'once_on_transition_yellow' end
594
- once_on_exit_none do |event| called << 'once_on_exit_none' end
595
- once_on_exit_green do |event| called << 'once_on_exit_green' end
596
- once_on_exit_yellow do |event| called << 'once_on_exit_yellow' end
597
-
598
- # event callbacks
599
- once_on_before_init do |event| called << 'once_on_before_init' end
600
- once_on_before_slow do |event| called << 'once_on_before_slow' end
601
- once_on_before_go do |event| called << 'once_on_before_go' end
602
-
603
- once_on_after_init do |event| called << 'once_on_after_init' end
604
- once_on_after_slow do |event| called << 'once_on_after_slow' end
605
- once_on_after_go do |event| called << 'once_on_after_go' end
606
- end
607
- expect(fsm.current).to eql(:green)
608
- fsm.slow
609
- expect(fsm.current).to eql(:yellow)
610
- fsm.go
611
- expect(fsm.current).to eql(:green)
612
- fsm.slow
613
- expect(fsm.current).to eql(:yellow)
614
- expect(called).to eql([
615
- 'once_on_before_init',
616
- 'once_on_exit_none',
617
- 'once_on_transition_green',
618
- 'once_on_enter_green',
619
- 'once_on_after_init',
620
- 'once_on_before_slow',
621
- 'once_on_exit_green',
622
- 'once_on_transition_yellow',
623
- 'once_on_enter_yellow',
624
- 'once_on_after_slow',
625
- 'once_on_before_go',
626
- 'once_on_exit_yellow',
627
- 'once_on_after_go'
628
- ])
629
- end
630
-
631
- xit "groups callbacks"
632
-
633
- it "groups states from separate events with the same name" do
634
- callbacks = []
635
- fsm = FiniteMachine.new do
636
- initial :initial, silent: false
637
-
638
- event :bump, :initial => :low
639
- event :bump, :low => :medium
640
- event :bump, :medium => :high
641
-
642
- on_enter do |event|
643
- callbacks << "enter_#{event.name}_#{event.from}_#{event.to}"
644
- end
645
- on_exit do |event|
646
- callbacks << "exit_#{event.name}_#{event.from}_#{event.to}"
647
- end
648
- on_before do |event|
649
- callbacks << "before_#{event.name}_#{event.from}_#{event.to}"
650
- end
651
- on_after do |event|
652
- callbacks << "after_#{event.name}_#{event.from}_#{event.to}"
653
- end
654
- end
655
-
656
- expect(fsm.current).to eq(:initial)
657
- fsm.bump
658
- expect(callbacks).to eq([
659
- 'before_init_none_initial',
660
- 'exit_init_none_initial',
661
- 'enter_init_none_initial',
662
- 'after_init_none_initial',
663
- 'before_bump_initial_low',
664
- 'exit_bump_initial_low',
665
- 'enter_bump_initial_low',
666
- 'after_bump_initial_low'
667
- ])
668
- fsm.bump
669
- expect(callbacks).to eq([
670
- 'before_init_none_initial',
671
- 'exit_init_none_initial',
672
- 'enter_init_none_initial',
673
- 'after_init_none_initial',
674
- 'before_bump_initial_low',
675
- 'exit_bump_initial_low',
676
- 'enter_bump_initial_low',
677
- 'after_bump_initial_low',
678
- 'before_bump_low_medium',
679
- 'exit_bump_low_medium',
680
- 'enter_bump_low_medium',
681
- 'after_bump_low_medium'
682
- ])
683
- fsm.bump
684
- expect(callbacks).to eq([
685
- 'before_init_none_initial',
686
- 'exit_init_none_initial',
687
- 'enter_init_none_initial',
688
- 'after_init_none_initial',
689
- 'before_bump_initial_low',
690
- 'exit_bump_initial_low',
691
- 'enter_bump_initial_low',
692
- 'after_bump_initial_low',
693
- 'before_bump_low_medium',
694
- 'exit_bump_low_medium',
695
- 'enter_bump_low_medium',
696
- 'after_bump_low_medium',
697
- 'before_bump_medium_high',
698
- 'exit_bump_medium_high',
699
- 'enter_bump_medium_high',
700
- 'after_bump_medium_high'
701
- ])
702
- end
703
-
704
- it "groups states under event name" do
705
- callbacks = []
706
- fsm = FiniteMachine.new do
707
- initial :initial, silent: false
708
-
709
- event :bump, :initial => :low,
710
- :low => :medium,
711
- :medium => :high
712
-
713
- on_enter do |event|
714
- callbacks << "enter_#{event.name}_#{event.from}_#{event.to}"
715
- end
716
- on_before do |event|
717
- callbacks << "before_#{event.name}_#{event.from}_#{event.to}"
718
- end
719
- end
720
-
721
- expect(fsm.current).to eq(:initial)
722
- fsm.bump
723
- expect(callbacks).to eq([
724
- 'before_init_none_initial',
725
- 'enter_init_none_initial',
726
- 'before_bump_initial_low',
727
- 'enter_bump_initial_low'
728
- ])
729
- fsm.bump
730
- expect(callbacks).to eq([
731
- 'before_init_none_initial',
732
- 'enter_init_none_initial',
733
- 'before_bump_initial_low',
734
- 'enter_bump_initial_low',
735
- 'before_bump_low_medium',
736
- 'enter_bump_low_medium'
737
- ])
738
- fsm.bump
739
- expect(callbacks).to eq([
740
- 'before_init_none_initial',
741
- 'enter_init_none_initial',
742
- 'before_bump_initial_low',
743
- 'enter_bump_initial_low',
744
- 'before_bump_low_medium',
745
- 'enter_bump_low_medium',
746
- 'before_bump_medium_high',
747
- 'enter_bump_medium_high'
748
- ])
749
- end
750
-
751
- it "permits state and event with the same name" do
752
- called = []
753
- fsm = FiniteMachine.new do
754
- initial :on_hook, silent: false
755
-
756
- event :off_hook, :on_hook => :off_hook
757
- event :on_hook, :off_hook => :on_hook
758
-
759
- on_before(:on_hook) { |event| called << "on_before_#{event.name}"}
760
- on_enter(:on_hook) { |event| called << "on_enter_#{event.to}"}
761
- end
762
- expect(fsm.current).to eq(:on_hook)
763
- expect(called).to eq([
764
- 'on_enter_on_hook'
765
- ])
766
- fsm.off_hook
767
- expect(fsm.current).to eq(:off_hook)
768
- fsm.on_hook
769
- expect(called).to eq([
770
- 'on_enter_on_hook',
771
- 'on_before_on_hook',
772
- 'on_enter_on_hook'
773
- ]);
774
- end
775
-
776
- it "allows to selectively silence events" do
777
- called = []
778
- fsm = FiniteMachine.new do
779
- initial :yellow
780
-
781
- event :go, :yellow => :green, silent: true
782
- event :stop, :green => :red
783
-
784
- on_enter :green do |event| called << 'on_enter_yellow' end
785
- on_enter :red do |event| called << 'on_enter_red' end
786
- end
787
- expect(fsm.current).to eq(:yellow)
788
- fsm.go
789
- fsm.stop
790
- expect(called).to eq(['on_enter_red'])
791
- end
792
-
793
- it "executes event-based callbacks even when state does not change" do
794
- called = []
795
- fsm = FiniteMachine.new do
796
- initial :active
797
-
798
- event :advance, :active => :inactive, if: -> { false }
799
- event :advance, :inactive => :active, if: -> { false }
800
-
801
- on_before do |event|
802
- called << "before_#{event.name}_#{event.from}_#{event.to}"
803
- end
804
- on_after do |event|
805
- called << "after_#{event.name}_#{event.from}_#{event.to}"
806
- end
807
- end
808
- expect(fsm.current).to eq(:active)
809
- fsm.advance
810
- expect(fsm.current).to eq(:active)
811
- expect(called).to eq([
812
- 'before_advance_active_inactive',
813
- 'after_advance_active_inactive'
814
- ])
815
- end
816
-
817
- it "doesn't transition if error raised in callback" do
818
- fsm = FiniteMachine.new do
819
- initial :green
820
-
821
- event :slow, :green => :yellow
822
-
823
- on_enter { raise RuntimeError }
824
- end
825
-
826
- expect {
827
- fsm.slow
828
- }.to raise_error(RuntimeError)
829
- expect(fsm.current).to eq(:green)
830
- end
831
-
832
- xit "narrows down on_transition callback to state transition" do
833
- called = []
834
- fsm = FiniteMachine.new do
835
- initial :red
836
-
837
- event :ready, :red => :yellow
838
- event :go, :yellow => :green
839
- event :stop, :green => :red
840
-
841
- on_transition :yellow => :green do
842
- called << 'on_transition_yellow_green'
843
- end
844
- end
845
-
846
- fsm.ready
847
- fsm.go
848
-
849
- expect(called).to eq(['on_transition_yellow_green'])
850
- end
851
- end