finite_machine 0.11.3 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +564 -569
  4. data/Rakefile +5 -1
  5. data/benchmarks/memory_profile.rb +11 -0
  6. data/benchmarks/memory_usage.rb +16 -9
  7. data/finite_machine.gemspec +10 -3
  8. data/lib/finite_machine.rb +34 -46
  9. data/lib/finite_machine/async_call.rb +5 -21
  10. data/lib/finite_machine/callable.rb +4 -4
  11. data/lib/finite_machine/catchable.rb +4 -2
  12. data/lib/finite_machine/choice_merger.rb +19 -19
  13. data/lib/finite_machine/const.rb +16 -0
  14. data/lib/finite_machine/definition.rb +2 -2
  15. data/lib/finite_machine/dsl.rb +66 -149
  16. data/lib/finite_machine/env.rb +4 -2
  17. data/lib/finite_machine/event_definition.rb +7 -15
  18. data/lib/finite_machine/{events_chain.rb → events_map.rb} +39 -51
  19. data/lib/finite_machine/hook_event.rb +60 -61
  20. data/lib/finite_machine/hooks.rb +44 -36
  21. data/lib/finite_machine/listener.rb +2 -2
  22. data/lib/finite_machine/logger.rb +5 -4
  23. data/lib/finite_machine/message_queue.rb +39 -30
  24. data/lib/finite_machine/observer.rb +55 -37
  25. data/lib/finite_machine/safety.rb +12 -10
  26. data/lib/finite_machine/state_definition.rb +3 -5
  27. data/lib/finite_machine/state_machine.rb +83 -64
  28. data/lib/finite_machine/state_parser.rb +51 -79
  29. data/lib/finite_machine/subscribers.rb +1 -1
  30. data/lib/finite_machine/threadable.rb +3 -1
  31. data/lib/finite_machine/transition.rb +30 -31
  32. data/lib/finite_machine/transition_builder.rb +23 -32
  33. data/lib/finite_machine/transition_event.rb +12 -11
  34. data/lib/finite_machine/two_phase_lock.rb +3 -1
  35. data/lib/finite_machine/undefined_transition.rb +5 -6
  36. data/lib/finite_machine/version.rb +2 -2
  37. data/spec/integration/system_spec.rb +36 -38
  38. data/spec/performance/benchmark_spec.rb +13 -21
  39. data/spec/unit/alias_target_spec.rb +22 -41
  40. data/spec/unit/async_callbacks_spec.rb +8 -13
  41. data/spec/unit/auto_methods_spec.rb +44 -0
  42. data/spec/unit/callable/call_spec.rb +1 -3
  43. data/spec/unit/callbacks_spec.rb +372 -463
  44. data/spec/unit/can_spec.rb +13 -23
  45. data/spec/unit/cancel_callbacks_spec.rb +46 -0
  46. data/spec/unit/choice_spec.rb +105 -141
  47. data/spec/unit/define_spec.rb +31 -31
  48. data/spec/unit/definition_spec.rb +24 -41
  49. data/spec/unit/event_names_spec.rb +6 -10
  50. data/spec/unit/events_map/add_spec.rb +23 -0
  51. data/spec/unit/events_map/choice_transition_spec.rb +25 -0
  52. data/spec/unit/events_map/clear_spec.rb +13 -0
  53. data/spec/unit/events_map/events_spec.rb +16 -0
  54. data/spec/unit/events_map/inspect_spec.rb +22 -0
  55. data/spec/unit/{events_chain → events_map}/match_transition_spec.rb +12 -14
  56. data/spec/unit/{events_chain → events_map}/move_to_spec.rb +14 -17
  57. data/spec/unit/events_map/states_for_spec.rb +17 -0
  58. data/spec/unit/events_spec.rb +91 -160
  59. data/spec/unit/handlers_spec.rb +34 -66
  60. data/spec/unit/hook_event/any_state_or_event_spec.rb +13 -0
  61. data/spec/unit/hook_event/build_spec.rb +1 -3
  62. data/spec/unit/hook_event/eql_spec.rb +1 -3
  63. data/spec/unit/hook_event/initialize_spec.rb +2 -4
  64. data/spec/unit/hook_event/notify_spec.rb +2 -4
  65. data/spec/unit/hooks/clear_spec.rb +1 -1
  66. data/spec/unit/hooks/{call_spec.rb → find_spec.rb} +4 -9
  67. data/spec/unit/hooks/inspect_spec.rb +16 -8
  68. data/spec/unit/hooks/register_spec.rb +4 -9
  69. data/spec/unit/if_unless_spec.rb +76 -115
  70. data/spec/unit/initial_spec.rb +50 -82
  71. data/spec/unit/inspect_spec.rb +14 -9
  72. data/spec/unit/is_spec.rb +12 -18
  73. data/spec/unit/log_transitions_spec.rb +4 -10
  74. data/spec/unit/logger_spec.rb +1 -3
  75. data/spec/unit/{event_queue_spec.rb → message_queue_spec.rb} +15 -8
  76. data/spec/unit/new_spec.rb +50 -0
  77. data/spec/unit/respond_to_spec.rb +2 -6
  78. data/spec/unit/state_parser/parse_spec.rb +9 -12
  79. data/spec/unit/states_spec.rb +12 -18
  80. data/spec/unit/subscribers_spec.rb +1 -3
  81. data/spec/unit/target_spec.rb +60 -93
  82. data/spec/unit/terminated_spec.rb +15 -25
  83. data/spec/unit/transition/check_conditions_spec.rb +16 -15
  84. data/spec/unit/transition/inspect_spec.rb +6 -6
  85. data/spec/unit/transition/matches_spec.rb +5 -7
  86. data/spec/unit/transition/states_spec.rb +5 -7
  87. data/spec/unit/transition/to_state_spec.rb +5 -13
  88. data/spec/unit/trigger_spec.rb +5 -9
  89. data/spec/unit/undefined_transition/eql_spec.rb +1 -3
  90. metadata +86 -49
  91. data/.gitignore +0 -18
  92. data/.rspec +0 -5
  93. data/.travis.yml +0 -27
  94. data/Gemfile +0 -16
  95. data/assets/finite_machine_logo.png +0 -0
  96. data/lib/finite_machine/async_proxy.rb +0 -55
  97. data/spec/unit/async_events_spec.rb +0 -107
  98. data/spec/unit/events_chain/add_spec.rb +0 -25
  99. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  100. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  101. data/spec/unit/events_chain/clear_spec.rb +0 -15
  102. data/spec/unit/events_chain/events_spec.rb +0 -18
  103. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  104. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  105. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  106. data/spec/unit/state_parser/inspect_spec.rb +0 -25
@@ -1,6 +1,4 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
1
+ # frozen_string_literal: true
4
2
 
5
3
  RSpec.describe FiniteMachine, 'can?' do
6
4
  before(:each) {
@@ -12,15 +10,13 @@ RSpec.describe FiniteMachine, 'can?' do
12
10
  }
13
11
 
14
12
  it "allows to check if event can be fired" do
15
- fsm = FiniteMachine.define do
13
+ fsm = FiniteMachine.new do
16
14
  initial :green
17
15
 
18
- events {
19
- event :slow, :green => :yellow
20
- event :stop, :yellow => :red
21
- event :ready, :red => :yellow
22
- event :go, :yellow => :green
23
- }
16
+ event :slow, :green => :yellow
17
+ event :stop, :yellow => :red
18
+ event :ready, :red => :yellow
19
+ event :go, :yellow => :green
24
20
  end
25
21
 
26
22
  expect(fsm.current).to eql(:green)
@@ -57,13 +53,11 @@ RSpec.describe FiniteMachine, 'can?' do
57
53
 
58
54
  context 'with conditionl transition' do
59
55
  it "evalutes condition with parameters" do
60
- fsm = FiniteMachine.define do
56
+ fsm = FiniteMachine.new do
61
57
  initial :green
62
58
 
63
- events {
64
- event :slow, :green => :yellow
65
- event :stop, :yellow => :red, if: proc { |_, state| state }
66
- }
59
+ event :slow, :green => :yellow
60
+ event :stop, :yellow => :red, if: proc { |_, state| state }
67
61
  end
68
62
  expect(fsm.current).to eq(:green)
69
63
  expect(fsm.can?(:slow)).to be(true)
@@ -77,16 +71,12 @@ RSpec.describe FiniteMachine, 'can?' do
77
71
 
78
72
  it "checks against target and grouped events" do
79
73
  bug = Bug.new
80
- fsm = FiniteMachine.define do
74
+ fsm = FiniteMachine.new(bug) do
81
75
  initial :initial
82
76
 
83
- target bug
84
-
85
- events {
86
- event :bump, :initial => :low
87
- event :bump, :low => :medium, if: :pending?
88
- event :bump, :medium => :high
89
- }
77
+ event :bump, :initial => :low
78
+ event :bump, :low => :medium, if: :pending?
79
+ event :bump, :medium => :high
90
80
  end
91
81
  expect(fsm.current).to eq(:initial)
92
82
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe FiniteMachine, '#cancel_event' do
4
+ it "cancels transition on event callback" do
5
+ fsm = FiniteMachine.new do
6
+ initial :green
7
+
8
+ event :slow, :green => :yellow
9
+ event :go, :yellow => :green
10
+
11
+ on_exit :green do |event|
12
+ cancel_event(event)
13
+ end
14
+ end
15
+
16
+ expect(fsm.current).to eql(:green)
17
+ fsm.slow
18
+ expect(fsm.current).to eql(:green)
19
+ end
20
+
21
+ it "stops executing callbacks when cancelled" do
22
+ called = []
23
+
24
+ fsm = FiniteMachine.new do
25
+ initial :initial
26
+
27
+ event :bump, initial: :low
28
+
29
+ on_before do |event|
30
+ called << "enter_#{event.name}_#{event.from}_#{event.to}"
31
+
32
+ cancel_event(event)
33
+ end
34
+
35
+ on_exit :initial do |event| called << "exit_initial" end
36
+ on_exit do |event| called << "exit_any" end
37
+ on_enter :low do |event| called << "enter_low" end
38
+ on_after :bump do |event| called << "after_#{event.name}" end
39
+ on_after do |event| called << "after_any" end
40
+ end
41
+
42
+ fsm.bump
43
+
44
+ expect(called).to eq(['enter_bump_initial_low'])
45
+ end
46
+ end
@@ -1,6 +1,4 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
1
+ # frozen_string_literal: true
4
2
 
5
3
  RSpec.describe FiniteMachine, '#choice' do
6
4
  before(:each) {
@@ -13,21 +11,17 @@ RSpec.describe FiniteMachine, '#choice' do
13
11
 
14
12
  it "allows for static choice based on conditional branching" do
15
13
  called = []
16
- fsm = FiniteMachine.define do
14
+ fsm = FiniteMachine.new do
17
15
  initial :company_form
18
16
 
19
- events {
20
- event :next, from: :company_form do
21
- choice :agreement_form, if: -> { false }
22
- choice :promo_form, if: -> { false }
23
- choice :official_form, if: -> { true }
24
- end
25
- }
26
-
27
- callbacks {
28
- on_exit do |event| called << "on_exit_#{event.from}" end
29
- on_enter do |event| called << "on_enter_#{event.to}" end
30
- }
17
+ event :next, from: :company_form do
18
+ choice :agreement_form, if: -> { false }
19
+ choice :promo_form, if: -> { false }
20
+ choice :official_form, if: -> { true }
21
+ end
22
+
23
+ on_exit do |event| called << "on_exit_#{event.from}" end
24
+ on_enter do |event| called << "on_enter_#{event.to}" end
31
25
  end
32
26
  expect(fsm.current).to eq(:company_form)
33
27
  fsm.next
@@ -39,16 +33,14 @@ RSpec.describe FiniteMachine, '#choice' do
39
33
  end
40
34
 
41
35
  it "allows for dynamic choice based on conditional branching" do
42
- fsm = FiniteMachine.define do
36
+ fsm = FiniteMachine.new do
43
37
  initial :company_form
44
38
 
45
- events {
46
- event :next, from: :company_form do
47
- choice :agreement_form, if: proc { |_, a| a < 1 }
48
- choice :promo_form, if: proc { |_, a| a == 1 }
49
- choice :official_form, if: proc { |_, a| a > 1 }
50
- end
51
- }
39
+ event :next, from: :company_form do
40
+ choice :agreement_form, if: proc { |_, a| a < 1 }
41
+ choice :promo_form, if: proc { |_, a| a == 1 }
42
+ choice :official_form, if: proc { |_, a| a > 1 }
43
+ end
52
44
  end
53
45
  expect(fsm.current).to eq(:company_form)
54
46
  fsm.next(0)
@@ -65,17 +57,13 @@ RSpec.describe FiniteMachine, '#choice' do
65
57
 
66
58
  it "allows for dynamic choice based on conditional branching and target" do
67
59
  user = User.new
68
- fsm = FiniteMachine.define do
60
+ fsm = FiniteMachine.new(user) do
69
61
  initial :company_form
70
62
 
71
- target user
72
-
73
- events {
74
- event :next, from: :company_form do
75
- choice :agreement_form, if: proc { |_user, token| _user.promo?(token) }
76
- choice :promo_form, unless: proc { |_user, token| _user.promo?(token) }
77
- end
78
- }
63
+ event :next, from: :company_form do
64
+ choice :agreement_form, if: proc { |_user, token| _user.promo?(token) }
65
+ choice :promo_form, unless: proc { |_user, token| _user.promo?(token) }
66
+ end
79
67
  end
80
68
  expect(fsm.current).to eq(:company_form)
81
69
  fsm.next(:no)
@@ -86,16 +74,14 @@ RSpec.describe FiniteMachine, '#choice' do
86
74
  end
87
75
 
88
76
  it "chooses state when skipped if/unless" do
89
- fsm = FiniteMachine.define do
77
+ fsm = FiniteMachine.new do
90
78
  initial :company_form
91
79
 
92
- events {
93
- event :next, from: :company_form do
94
- choice :agreement_form, if: -> { false }
95
- choice :promo_form
96
- choice :official_form, if: -> { true }
97
- end
98
- }
80
+ event :next, from: :company_form do
81
+ choice :agreement_form, if: -> { false }
82
+ choice :promo_form
83
+ choice :official_form, if: -> { true }
84
+ end
99
85
  end
100
86
  expect(fsm.current).to eq(:company_form)
101
87
  fsm.next
@@ -103,16 +89,14 @@ RSpec.describe FiniteMachine, '#choice' do
103
89
  end
104
90
 
105
91
  it "chooses default state when branching conditions don't match" do
106
- fsm = FiniteMachine.define do
92
+ fsm = FiniteMachine.new do
107
93
  initial :company_form
108
94
 
109
- events {
110
- event :next, from: :company_form do
111
- choice :agreement_form, if: -> { false }
112
- choice :promo_form, if: -> { false }
113
- default :official_form
114
- end
115
- }
95
+ event :next, from: :company_form do
96
+ choice :agreement_form, if: -> { false }
97
+ choice :promo_form, if: -> { false }
98
+ default :official_form
99
+ end
116
100
  end
117
101
  expect(fsm.current).to eq(:company_form)
118
102
  fsm.next
@@ -120,15 +104,13 @@ RSpec.describe FiniteMachine, '#choice' do
120
104
  end
121
105
 
122
106
  it "fails to transition when no condition matches without default state" do
123
- fsm = FiniteMachine.define do
107
+ fsm = FiniteMachine.new do
124
108
  initial :company_form
125
109
 
126
- events {
127
- event :next, from: :company_form do
128
- choice :agreement_form, if: -> { false }
129
- choice :promo_form, if: -> { false }
130
- end
131
- }
110
+ event :next, from: :company_form do
111
+ choice :agreement_form, if: -> { false }
112
+ choice :promo_form, if: -> { false }
113
+ end
132
114
  end
133
115
  expect(fsm.current).to eq(:company_form)
134
116
  fsm.next
@@ -136,7 +118,7 @@ RSpec.describe FiniteMachine, '#choice' do
136
118
  end
137
119
 
138
120
  it "allows to transition from multiple states to choice pseudostate" do
139
- fsm = FiniteMachine.define do
121
+ fsm = FiniteMachine.new do
140
122
  initial :red
141
123
 
142
124
  event :go, from: [:yellow, :red] do
@@ -154,10 +136,10 @@ RSpec.describe FiniteMachine, '#choice' do
154
136
  end
155
137
 
156
138
  it "allows to transition from any state to choice pseudo state" do
157
- fsm = FiniteMachine.define do
139
+ fsm = FiniteMachine.new do
158
140
  initial :red
159
141
 
160
- event :go, from: :any do
142
+ event :go, from: any_state do
161
143
  choice :pink, if: -> { false }
162
144
  choice :green
163
145
  end
@@ -168,7 +150,7 @@ RSpec.describe FiniteMachine, '#choice' do
168
150
  end
169
151
 
170
152
  it "groups correctly events under the same name" do
171
- fsm = FiniteMachine.define do
153
+ fsm = FiniteMachine.new do
172
154
  initial :red
173
155
 
174
156
  event :next, from: :yellow, to: :green
@@ -187,24 +169,20 @@ RSpec.describe FiniteMachine, '#choice' do
187
169
 
188
170
  it "performs matching transitions for multiple event definitions with the same name" do
189
171
  ticket = double(:ticket, :pending? => true, :finished? => true)
190
- fsm = FiniteMachine.define do
172
+ fsm = FiniteMachine.new(ticket) do
191
173
  initial :inactive
192
174
 
193
- target ticket
194
-
195
- events {
196
- event :advance, from: [:inactive, :paused, :fulfilled] do
197
- choice :active, if: proc { |_ticket| !_ticket.pending? }
198
- end
175
+ event :advance, from: [:inactive, :paused, :fulfilled] do
176
+ choice :active, if: proc { |_ticket| !_ticket.pending? }
177
+ end
199
178
 
200
- event :advance, from: [:inactive, :active, :fulfilled] do
201
- choice :paused, if: proc { |_ticket| _ticket.pending? }
202
- end
179
+ event :advance, from: [:inactive, :active, :fulfilled] do
180
+ choice :paused, if: proc { |_ticket| _ticket.pending? }
181
+ end
203
182
 
204
- event :advance, from: [:inactive, :active, :paused] do
205
- choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
206
- end
207
- }
183
+ event :advance, from: [:inactive, :active, :paused] do
184
+ choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
185
+ end
208
186
  end
209
187
  expect(fsm.current).to eq(:inactive)
210
188
  fsm.advance
@@ -216,29 +194,23 @@ RSpec.describe FiniteMachine, '#choice' do
216
194
  it "does not transition when no matching choice for multiple event definitions" do
217
195
  ticket = double(:ticket, :pending? => true, :finished? => false)
218
196
  called = []
219
- fsm = FiniteMachine.define do
197
+ fsm = FiniteMachine.new(ticket) do
220
198
  initial :inactive
221
199
 
222
- target ticket
223
-
224
- events {
225
- event :advance, from: [:inactive, :paused, :fulfilled] do
226
- choice :active, if: proc { |_ticket| !_ticket.pending? }
227
- end
200
+ event :advance, from: [:inactive, :paused, :fulfilled] do
201
+ choice :active, if: proc { |_ticket| !_ticket.pending? }
202
+ end
228
203
 
229
- event :advance, from: [:inactive, :active, :fulfilled] do
230
- choice :paused, if: proc { |_ticket| _ticket.pending? }
231
- end
204
+ event :advance, from: [:inactive, :active, :fulfilled] do
205
+ choice :paused, if: proc { |_ticket| _ticket.pending? }
206
+ end
232
207
 
233
- event :advance, from: [:inactive, :active, :paused] do
234
- choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
235
- end
236
- }
208
+ event :advance, from: [:inactive, :active, :paused] do
209
+ choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
210
+ end
237
211
 
238
- callbacks {
239
- on_before(:advance) { called << 'on_before_advance' }
240
- on_after(:advance) { called << 'on_after_advance' }
241
- }
212
+ on_before(:advance) { called << 'on_before_advance' }
213
+ on_after(:advance) { called << 'on_after_advance' }
242
214
  end
243
215
  expect(fsm.current).to eq(:inactive)
244
216
  fsm.advance
@@ -265,57 +237,49 @@ RSpec.describe FiniteMachine, '#choice' do
265
237
  target.expect(c).to target.eql(expected[:c])
266
238
  }
267
239
 
268
- context = self
269
-
270
- fsm = FiniteMachine.define do
240
+ fsm = FiniteMachine.new(self) do
271
241
  initial :red
272
242
 
273
- target context
274
-
275
- events {
276
- event :next, from: :red do
277
- choice :green, if: -> { false }
278
- choice :yellow
279
- end
280
-
281
- event :next, from: :yellow do
282
- choice :green, if: -> { true }
283
- choice :yellow
284
- end
285
-
286
- event :finish, from: :any do
287
- choice :green, if: -> { false }
288
- choice :red
289
- end
290
- }
291
-
292
- callbacks {
293
- # generic state callbacks
294
- on_enter(&callback)
295
- on_transition(&callback)
296
- on_exit(&callback)
297
-
298
- # generic event callbacks
299
- on_before(&callback)
300
- on_after(&callback)
301
-
302
- # state callbacks
303
- on_enter :green, &callback
304
- on_enter :yellow, &callback
305
- on_enter :red, &callback
306
-
307
- on_transition :green, &callback
308
- on_transition :yellow, &callback
309
- on_transition :red, &callback
310
-
311
- on_exit :green, &callback
312
- on_exit :yellow, &callback
313
- on_exit :red, &callback
314
-
315
- # event callbacks
316
- on_before :next, &callback
317
- on_after :next, &callback
318
- }
243
+ event :next, from: :red do
244
+ choice :green, if: -> { false }
245
+ choice :yellow
246
+ end
247
+
248
+ event :next, from: :yellow do
249
+ choice :green, if: -> { true }
250
+ choice :yellow
251
+ end
252
+
253
+ event :finish, from: any_state do
254
+ choice :green, if: -> { false }
255
+ choice :red
256
+ end
257
+
258
+ # generic state callbacks
259
+ on_enter(&callback)
260
+ on_transition(&callback)
261
+ on_exit(&callback)
262
+
263
+ # generic event callbacks
264
+ on_before(&callback)
265
+ on_after(&callback)
266
+
267
+ # state callbacks
268
+ on_enter :green, &callback
269
+ on_enter :yellow, &callback
270
+ on_enter :red, &callback
271
+
272
+ on_transition :green, &callback
273
+ on_transition :yellow, &callback
274
+ on_transition :red, &callback
275
+
276
+ on_exit :green, &callback
277
+ on_exit :yellow, &callback
278
+ on_exit :red, &callback
279
+
280
+ # event callbacks
281
+ on_before :next, &callback
282
+ on_after :next, &callback
319
283
  end
320
284
  expect(fsm.current).to eq(:red)
321
285