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,98 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, 'can?' do
6
- before(:each) {
7
- stub_const("Bug", Class.new do
8
- def pending?
9
- false
10
- end
11
- end)
12
- }
13
-
14
- it "allows to check if event can be fired" do
15
- fsm = FiniteMachine.define do
16
- initial :green
17
-
18
- events {
19
- event :slow, :green => :yellow
20
- event :stop, :yellow => :red
21
- event :ready, :red => :yellow
22
- event :go, :yellow => :green
23
- }
24
- end
25
-
26
- expect(fsm.current).to eql(:green)
27
-
28
- expect(fsm.can?(:slow)).to be(true)
29
- expect(fsm.cannot?(:stop)).to be(true)
30
- expect(fsm.can?(:ready)).to be(false)
31
- expect(fsm.can?(:go)).to be(false)
32
-
33
- fsm.slow
34
- expect(fsm.current).to eql(:yellow)
35
-
36
- expect(fsm.can?(:slow)).to be(false)
37
- expect(fsm.can?(:stop)).to be(true)
38
- expect(fsm.can?(:ready)).to be(false)
39
- expect(fsm.can?(:go)).to be(true)
40
-
41
- fsm.stop
42
- expect(fsm.current).to eql(:red)
43
-
44
- expect(fsm.can?(:slow)).to be(false)
45
- expect(fsm.can?(:stop)).to be(false)
46
- expect(fsm.can?(:ready)).to be(true)
47
- expect(fsm.can?(:go)).to be(false)
48
-
49
- fsm.ready
50
- expect(fsm.current).to eql(:yellow)
51
-
52
- expect(fsm.can?(:slow)).to be(false)
53
- expect(fsm.can?(:stop)).to be(true)
54
- expect(fsm.can?(:ready)).to be(false)
55
- expect(fsm.can?(:go)).to be(true)
56
- end
57
-
58
- context 'with conditionl transition' do
59
- it "evalutes condition with parameters" do
60
- fsm = FiniteMachine.define do
61
- initial :green
62
-
63
- events {
64
- event :slow, :green => :yellow
65
- event :stop, :yellow => :red, if: proc { |_, state| state }
66
- }
67
- end
68
- expect(fsm.current).to eq(:green)
69
- expect(fsm.can?(:slow)).to be(true)
70
- expect(fsm.can?(:stop)).to be(false)
71
-
72
- fsm.slow
73
- expect(fsm.current).to eq(:yellow)
74
- expect(fsm.can?(:stop, false)).to be(false)
75
- expect(fsm.can?(:stop, true)).to be(true)
76
- end
77
-
78
- it "checks against target and grouped events" do
79
- bug = Bug.new
80
- fsm = FiniteMachine.define do
81
- initial :initial
82
-
83
- target bug
84
-
85
- events {
86
- event :bump, :initial => :low
87
- event :bump, :low => :medium, if: :pending?
88
- event :bump, :medium => :high
89
- }
90
- end
91
- expect(fsm.current).to eq(:initial)
92
-
93
- expect(fsm.can?(:bump)).to be(true)
94
- fsm.bump
95
- expect(fsm.can?(:bump)).to be(false)
96
- end
97
- end
98
- end
@@ -1,331 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, '#choice' do
6
- before(:each) {
7
- stub_const("User", Class.new do
8
- def promo?(token = false)
9
- token == :yes
10
- end
11
- end)
12
- }
13
-
14
- it "allows for static choice based on conditional branching" do
15
- called = []
16
- fsm = FiniteMachine.define do
17
- initial :company_form
18
-
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
- }
31
- end
32
- expect(fsm.current).to eq(:company_form)
33
- fsm.next
34
- expect(fsm.current).to eq(:official_form)
35
- expect(called).to eq([
36
- 'on_exit_company_form',
37
- 'on_enter_official_form'
38
- ])
39
- end
40
-
41
- it "allows for dynamic choice based on conditional branching" do
42
- fsm = FiniteMachine.define do
43
- initial :company_form
44
-
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
- }
52
- end
53
- expect(fsm.current).to eq(:company_form)
54
- fsm.next(0)
55
- expect(fsm.current).to eq(:agreement_form)
56
-
57
- fsm.restore!(:company_form)
58
- fsm.next(1)
59
- expect(fsm.current).to eq(:promo_form)
60
-
61
- fsm.restore!(:company_form)
62
- fsm.next(2)
63
- expect(fsm.current).to eq(:official_form)
64
- end
65
-
66
- it "allows for dynamic choice based on conditional branching and target" do
67
- user = User.new
68
- fsm = FiniteMachine.define do
69
- initial :company_form
70
-
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
- }
79
- end
80
- expect(fsm.current).to eq(:company_form)
81
- fsm.next(:no)
82
- expect(fsm.current).to eq(:promo_form)
83
- fsm.restore!(:company_form)
84
- fsm.next(:yes)
85
- expect(fsm.current).to eq(:agreement_form)
86
- end
87
-
88
- it "chooses state when skipped if/unless" do
89
- fsm = FiniteMachine.define do
90
- initial :company_form
91
-
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
- }
99
- end
100
- expect(fsm.current).to eq(:company_form)
101
- fsm.next
102
- expect(fsm.current).to eq(:promo_form)
103
- end
104
-
105
- it "chooses default state when branching conditions don't match" do
106
- fsm = FiniteMachine.define do
107
- initial :company_form
108
-
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
- }
116
- end
117
- expect(fsm.current).to eq(:company_form)
118
- fsm.next
119
- expect(fsm.current).to eq(:official_form)
120
- end
121
-
122
- it "fails to transition when no condition matches without default state" do
123
- fsm = FiniteMachine.define do
124
- initial :company_form
125
-
126
- events {
127
- event :next, from: :company_form do
128
- choice :agreement_form, if: -> { false }
129
- choice :promo_form, if: -> { false }
130
- end
131
- }
132
- end
133
- expect(fsm.current).to eq(:company_form)
134
- fsm.next
135
- expect(fsm.current).to eq(:company_form)
136
- end
137
-
138
- it "allows to transition from multiple states to choice pseudostate" do
139
- fsm = FiniteMachine.define do
140
- initial :red
141
-
142
- event :go, from: [:yellow, :red] do
143
- choice :pink, if: -> { false }
144
- choice :green
145
- end
146
- end
147
- expect(fsm.current).to eq(:red)
148
- fsm.go
149
- expect(fsm.current).to eq(:green)
150
- fsm.restore!(:yellow)
151
- expect(fsm.current).to eq(:yellow)
152
- fsm.go
153
- expect(fsm.current).to eq(:green)
154
- end
155
-
156
- it "allows to transition from any state to choice pseudo state" do
157
- fsm = FiniteMachine.define do
158
- initial :red
159
-
160
- event :go, from: :any do
161
- choice :pink, if: -> { false }
162
- choice :green
163
- end
164
- end
165
- expect(fsm.current).to eq(:red)
166
- fsm.go
167
- expect(fsm.current).to eq(:green)
168
- end
169
-
170
- it "groups correctly events under the same name" do
171
- fsm = FiniteMachine.define do
172
- initial :red
173
-
174
- event :next, from: :yellow, to: :green
175
-
176
- event :next, from: :red do
177
- choice :pink, if: -> { false }
178
- choice :yellow
179
- end
180
- end
181
- expect(fsm.current).to eq(:red)
182
- fsm.next
183
- expect(fsm.current).to eq(:yellow)
184
- fsm.next
185
- expect(fsm.current).to eq(:green)
186
- end
187
-
188
- it "performs matching transitions for multiple event definitions with the same name" do
189
- ticket = double(:ticket, :pending? => true, :finished? => true)
190
- fsm = FiniteMachine.define do
191
- initial :inactive
192
-
193
- target ticket
194
-
195
- events {
196
- event :advance, from: [:inactive, :paused, :fulfilled] do
197
- choice :active, if: proc { |_ticket| !_ticket.pending? }
198
- end
199
-
200
- event :advance, from: [:inactive, :active, :fulfilled] do
201
- choice :paused, if: proc { |_ticket| _ticket.pending? }
202
- end
203
-
204
- event :advance, from: [:inactive, :active, :paused] do
205
- choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
206
- end
207
- }
208
- end
209
- expect(fsm.current).to eq(:inactive)
210
- fsm.advance
211
- expect(fsm.current).to eq(:paused)
212
- fsm.advance
213
- expect(fsm.current).to eq(:fulfilled)
214
- end
215
-
216
- it "does not transition when no matching choice for multiple event definitions" do
217
- ticket = double(:ticket, :pending? => true, :finished? => false)
218
- called = []
219
- fsm = FiniteMachine.define do
220
- initial :inactive
221
-
222
- target ticket
223
-
224
- events {
225
- event :advance, from: [:inactive, :paused, :fulfilled] do
226
- choice :active, if: proc { |_ticket| !_ticket.pending? }
227
- end
228
-
229
- event :advance, from: [:inactive, :active, :fulfilled] do
230
- choice :paused, if: proc { |_ticket| _ticket.pending? }
231
- end
232
-
233
- event :advance, from: [:inactive, :active, :paused] do
234
- choice :fulfilled, if: proc { |_ticket| _ticket.finished? }
235
- end
236
- }
237
-
238
- callbacks {
239
- on_before(:advance) { called << 'on_before_advance' }
240
- on_after(:advance) { called << 'on_after_advance' }
241
- }
242
- end
243
- expect(fsm.current).to eq(:inactive)
244
- fsm.advance
245
- expect(fsm.current).to eq(:paused)
246
- fsm.advance
247
- expect(fsm.current).to eq(:paused)
248
- expect(called).to eq([
249
- 'on_before_advance',
250
- 'on_after_advance',
251
- 'on_before_advance',
252
- 'on_after_advance'
253
- ])
254
- end
255
-
256
- it "sets callback properties correctly" do
257
- expected = {name: :init, from: :none, to: :red, a: nil, b: nil, c: nil }
258
-
259
- callback = Proc.new { |event, a, b, c|
260
- target.expect(event.from).to target.eql(expected[:from])
261
- target.expect(event.to).to target.eql(expected[:to])
262
- target.expect(event.name).to target.eql(expected[:name])
263
- target.expect(a).to target.eql(expected[:a])
264
- target.expect(b).to target.eql(expected[:b])
265
- target.expect(c).to target.eql(expected[:c])
266
- }
267
-
268
- context = self
269
-
270
- fsm = FiniteMachine.define do
271
- initial :red
272
-
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
- }
319
- end
320
- expect(fsm.current).to eq(:red)
321
-
322
- expected = {name: :next, from: :red, to: :yellow, a: 1, b: 2, c: 3}
323
- fsm.next(1, 2, 3)
324
-
325
- expected = {name: :next, from: :yellow, to: :green, a: 4, b: 5, c: 6}
326
- fsm.next(4, 5, 6)
327
-
328
- expected = {name: :finish, from: :green, to: :red, a: 7, b: 8, c: 9}
329
- fsm.finish(7, 8, 9)
330
- end
331
- end
@@ -1,55 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine, 'define' do
6
-
7
- context 'with block' do
8
- it "creates system state machine" do
9
- fsm = FiniteMachine.define do
10
- initial :green
11
-
12
- events {
13
- event :slow, :green => :yellow
14
- event :stop, :yellow => :red
15
- event :ready, :red => :yellow
16
- event :go, :yellow => :green
17
- }
18
- end
19
-
20
- expect(fsm.current).to eql(:green)
21
-
22
- fsm.slow
23
- expect(fsm.current).to eql(:yellow)
24
- fsm.stop
25
- expect(fsm.current).to eql(:red)
26
- fsm.ready
27
- expect(fsm.current).to eql(:yellow)
28
- fsm.go
29
- expect(fsm.current).to eql(:green)
30
- end
31
- end
32
-
33
- context 'without block' do
34
- it "creates state machine" do
35
- called = []
36
- fsm = FiniteMachine.define
37
- fsm.initial(:green)
38
- fsm.event(:slow, :green => :yellow)
39
- fsm.event(:stop, :yellow => :red)
40
- fsm.event(:ready,:red => :yellow)
41
- fsm.event(:go, :yellow => :green)
42
- fsm.on_enter(:yellow) { |event| called << 'on_enter_yellow' }
43
- fsm.handle(FiniteMachine::InvalidStateError) { |exception|
44
- called << 'error_handler'
45
- }
46
- fsm.init
47
- expect(fsm.current).to eql(:green)
48
- fsm.slow
49
- expect(fsm.current).to eql(:yellow)
50
- fsm.ready
51
- expect(fsm.current).to eql(:yellow)
52
- expect(called).to match_array(['on_enter_yellow', 'error_handler'])
53
- end
54
- end
55
- end