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,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, 'handlers' do
4
- before(:each) {
5
- stub_const("DummyLogger", Class.new do
6
- attr_reader :result
7
-
8
- def log_error(exception)
9
- @result = "log_error(#{exception})"
10
- end
11
-
12
- def raise_error
13
- raise FiniteMachine::TransitionError
14
- end
15
- end)
16
- }
17
-
18
- it "allows to customise error handling" do
19
- called = []
20
- fsm = FiniteMachine.new do
21
- initial :green
22
-
23
- event :slow, :green => :yellow
24
- event :stop, :yellow => :red
25
-
26
- handle FiniteMachine::InvalidStateError do |exception|
27
- called << 'invalidstate'
28
- end
29
- end
30
-
31
- expect(fsm.current).to eql(:green)
32
- fsm.stop
33
- expect(fsm.current).to eql(:green)
34
- expect(called).to eql([
35
- 'invalidstate'
36
- ])
37
- end
38
-
39
- it 'allows for :with to be symbol' do
40
- logger = DummyLogger.new
41
- fsm = FiniteMachine.new(logger) do
42
- initial :green
43
-
44
- event :slow, :green => :yellow
45
- event :stop, :yellow => :red
46
-
47
- handle FiniteMachine::InvalidStateError, with: :log_error
48
- end
49
-
50
- expect(fsm.current).to eql(:green)
51
- fsm.stop
52
- expect(fsm.current).to eql(:green)
53
- expect(logger.result).to eql('log_error(FiniteMachine::InvalidStateError)')
54
- end
55
-
56
- it 'allows for error type as string' do
57
- logger = DummyLogger.new
58
- called = []
59
- fsm = FiniteMachine.new(target: logger) do
60
- initial :green
61
-
62
- event :slow, :green => :yellow
63
- event :stop, :yellow => :red
64
-
65
- on_enter_yellow do |event|
66
- raise_error
67
- end
68
-
69
- handle 'InvalidStateError' do |exception|
70
- called << 'invalid_state_error'
71
- end
72
- end
73
-
74
- expect(fsm.current).to eql(:green)
75
- fsm.stop
76
- expect(fsm.current).to eql(:green)
77
- expect(called).to eql(['invalid_state_error'])
78
- end
79
-
80
- it 'allows for empty block handler' do
81
- called = []
82
- fsm = FiniteMachine.new do
83
- initial :green
84
-
85
- event :slow, :green => :yellow
86
- event :stop, :yellow => :red
87
-
88
- handle FiniteMachine::InvalidStateError do
89
- called << 'invalidstate'
90
- end
91
- end
92
-
93
- expect(fsm.current).to eql(:green)
94
- fsm.stop
95
- expect(fsm.current).to eql(:green)
96
- expect(called).to eql([
97
- 'invalidstate'
98
- ])
99
- end
100
-
101
- it 'requires error handler' do
102
- expect { FiniteMachine.new do
103
- initial :green
104
-
105
- event :slow, :green => :yellow
106
-
107
- handle 'UnknownErrorType'
108
- end }.to raise_error(ArgumentError, /error handler/)
109
- end
110
-
111
- it 'checks handler class to be Exception' do
112
- expect { FiniteMachine.new do
113
- initial :green
114
-
115
- event :slow, :green => :yellow
116
-
117
- handle Object do end
118
- end }.to raise_error(ArgumentError, /Object isn't an Exception/)
119
- end
120
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::HookEvent, '#any_state_or_event' do
4
- it "infers default name for state" do
5
- hook_event = described_class::Enter
6
- expect(described_class.any_state_or_event(hook_event)).to eq(FiniteMachine::ANY_STATE)
7
- end
8
-
9
- it "infers default name for event" do
10
- hook_event = described_class::Before
11
- expect(described_class.any_state_or_event(hook_event)).to eq(FiniteMachine::ANY_EVENT)
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::HookEvent, '#build' do
4
- it "builds action event" do
5
- hook_event = FiniteMachine::HookEvent::Before.build(:green, :go, :red)
6
- expect(hook_event.name).to eq(:go)
7
- end
8
-
9
- it "builds state event" do
10
- hook_event = FiniteMachine::HookEvent::Enter.build(:green, :go, :red)
11
- expect(hook_event.name).to eq(:green)
12
- end
13
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::HookEvent, 'eql?' do
4
- let(:name) { :green }
5
- let(:event_name) { :go }
6
- let(:object) { described_class }
7
-
8
- subject(:hook) { object.new(name, event_name, name) }
9
-
10
- context 'with the same object' do
11
- let(:other) { hook }
12
-
13
- it "equals" do
14
- expect(hook).to eql(other)
15
- end
16
- end
17
-
18
- context 'with an equivalent object' do
19
- let(:other) { hook.dup }
20
-
21
- it "equals" do
22
- expect(hook).to eql(other)
23
- end
24
- end
25
-
26
- context "with an object having different name" do
27
- let(:other_name) { :red }
28
- let(:other) { object.new(other_name, event_name, other_name) }
29
-
30
- it "doesn't equal" do
31
- expect(hook).not_to eql(other)
32
- end
33
- end
34
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::HookEvent, '#new' do
4
- it "reads event name" do
5
- hook_event = described_class.new(:green, :go, :green)
6
- expect(hook_event.name).to eql(:green)
7
- end
8
-
9
- it "reads event type" do
10
- hook_event = described_class.new(:green, :go, :green)
11
- expect(hook_event.type).to eql(FiniteMachine::HookEvent)
12
- end
13
-
14
- it "reads the from state" do
15
- hook_event = described_class.new(:green, :go, :red)
16
- expect(hook_event.from).to eql(:red)
17
- end
18
-
19
- it "freezes object" do
20
- hook_event = described_class.new(:green, :go, :green)
21
- expect(hook_event.frozen?).to eq(true)
22
- end
23
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::HookEvent, '#notify' do
4
- it "emits event on the subscriber" do
5
- subscriber = spy(:subscriber)
6
- hook_event = described_class.new(:green, :go, :red)
7
-
8
- hook_event.notify(subscriber, 1, 2)
9
-
10
- expect(subscriber).to have_received(:emit).with(hook_event, 1, 2)
11
- end
12
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Hooks, '#clear' do
4
- it "clears all registered hooks" do
5
- hooks = described_class.new
6
-
7
- event_type = FiniteMachine::HookEvent::Before
8
- hook = -> { }
9
- hooks.register(event_type, :foo, hook)
10
- hooks.register(event_type, :bar, hook)
11
-
12
- expect(hooks.empty?).to eq(false)
13
- hooks.clear
14
- expect(hooks.empty?).to eq(true)
15
- end
16
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Hooks, '#[]' do
4
- it "adds and removes a single hook" do
5
- hooks = FiniteMachine::Hooks.new
6
- expect(hooks).to be_empty
7
-
8
- yielded = []
9
- event_type = FiniteMachine::HookEvent::Before
10
- hook = -> { }
11
- hooks.register(event_type, :foo, hook)
12
-
13
- hooks[event_type][:foo].each do |callback|
14
- yielded << callback
15
- end
16
-
17
- expect(yielded).to eq([hook])
18
- end
19
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Hooks, '#inspect' do
4
- it "displays name and transitions" do
5
- hooks = FiniteMachine::Hooks.new
6
- hook = -> { }
7
- event = FiniteMachine::HookEvent::Enter
8
- hooks_map = {event => {yellow: [hook]}}
9
-
10
- hooks.register(event, :yellow, hook)
11
-
12
- expect(hooks.inspect).to eql("<#FiniteMachine::Hooks:0x#{hooks.object_id.to_s(16)} @hooks_map=#{hooks_map}>")
13
- end
14
-
15
- it "displays hooks content" do
16
- hooks = FiniteMachine::Hooks.new
17
- hook = -> { }
18
- event = FiniteMachine::HookEvent::Enter
19
- hooks_map = {event => {yellow: [hook]}}
20
-
21
- hooks.register(event, :yellow, hook)
22
-
23
- expect(hooks.to_s).to eql(hooks_map.to_s)
24
- end
25
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Hooks, '#register' do
4
- it "adds and removes a single hook" do
5
- hooks = FiniteMachine::Hooks.new
6
- expect(hooks).to be_empty
7
-
8
- event_type = FiniteMachine::HookEvent::Before
9
- hook = -> { }
10
-
11
- hooks.register(event_type, :foo, hook)
12
- expect(hooks[event_type][:foo]).to eq([hook])
13
-
14
- hooks.unregister(event_type, :foo, hook)
15
- expect(hooks[event_type][:foo]).to eq([])
16
- end
17
- end
@@ -1,314 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, ':if, :unless' do
4
- before(:each) {
5
- stub_const("Car", Class.new do
6
- attr_accessor :engine_on
7
-
8
- def turn_engine_on
9
- @engine_on = true
10
- end
11
-
12
- def turn_engine_off
13
- @engine_on = false
14
- end
15
-
16
- def engine_on?
17
- !!@engine_on
18
- end
19
- end)
20
-
21
- stub_const("Bug", Class.new do
22
- def pending?
23
- false
24
- end
25
- end)
26
- }
27
-
28
- it "passes context to conditionals" do
29
- called = []
30
- fsm = FiniteMachine.new do
31
- initial :red
32
-
33
- event :go, :red => :green,
34
- if: proc { |context| called << "cond_red_green(#{context})"; true}
35
- event :stop, from: any_state do
36
- choice :red,
37
- if: proc { |context| called << "cond_any_red(#{context})"; true }
38
- end
39
- end
40
-
41
- expect(fsm.current).to eq(:red)
42
-
43
- fsm.go
44
- expect(fsm.current).to eq(:green)
45
- expect(called).to eq(["cond_red_green(#{fsm})"])
46
-
47
- fsm.stop
48
- expect(fsm.current).to eq(:red)
49
- expect(called).to match_array([
50
- "cond_red_green(#{fsm})",
51
- "cond_any_red(#{fsm})"
52
- ])
53
- end
54
-
55
- it "passes context & arguments to conditionals" do
56
- called = []
57
- fsm = FiniteMachine.new do
58
- initial :red
59
-
60
- event :go, :red => :green,
61
- if: proc { |_, a| called << "cond_red_green(#{a})"; true }
62
- event :stop, from: any_state do
63
- choice :red,
64
- if: proc { |_, b| called << "cond_any_red(#{b})"; true }
65
- end
66
- end
67
-
68
- expect(fsm.current).to eq(:red)
69
-
70
- fsm.go(:foo)
71
- expect(fsm.current).to eq(:green)
72
- expect(called).to eq(["cond_red_green(foo)"])
73
-
74
- fsm.stop(:bar)
75
- expect(fsm.current).to eq(:red)
76
- expect(called).to match_array([
77
- "cond_red_green(foo)",
78
- "cond_any_red(bar)"
79
- ])
80
- end
81
-
82
- it "allows to cancel event with :if option" do
83
- called = []
84
-
85
- fsm = FiniteMachine.new do
86
- initial :green
87
-
88
- event :slow, :green => :yellow, if: -> { return false }
89
- event :stop, :yellow => :red
90
-
91
- # generic callbacks
92
- on_enter do |event| called << 'on_enter' end
93
- on_transition do |event| called << 'on_transition' end
94
- on_exit do |event| called << 'on_exit' end
95
-
96
- # state callbacks
97
- on_enter :green do |event| called << 'on_enter_green' end
98
- on_enter :yellow do |event| called << "on_enter_yellow" end
99
-
100
- on_transition :green do |event| called << 'on_transition_green' end
101
- on_transition :yellow do |event| called << "on_transition_yellow" end
102
-
103
- on_exit :green do |event| called << 'on_exit_green' end
104
- on_exit :yellow do |event| called << "on_exit_yellow" end
105
- end
106
-
107
- expect(fsm.current).to eql(:green)
108
- called = []
109
- fsm.slow
110
- expect(fsm.current).to eql(:green)
111
- expect(called).to eql([])
112
- end
113
-
114
- it "allows to cancel event with :unless option" do
115
- called = []
116
-
117
- fsm = FiniteMachine.new do
118
- initial :green
119
-
120
- event :slow, :green => :yellow, unless: -> { true }
121
- event :stop, :yellow => :red
122
-
123
- # generic callbacks
124
- on_enter do |event| called << 'on_enter' end
125
- on_transition do |event| called << 'on_transition' end
126
- on_exit do |event| called << 'on_exit' end
127
-
128
- # state callbacks
129
- on_enter :green do |event| called << 'on_enter_green' end
130
- on_enter :yellow do |event| called << "on_enter_yellow" end
131
-
132
- on_transition :green do |event| called << 'on_transition_green' end
133
- on_transition :yellow do |event| called << "on_transition_yellow" end
134
-
135
- on_exit :green do |event| called << 'on_exit_green' end
136
- on_exit :yellow do |event| called << "on_exit_yellow" end
137
- end
138
-
139
- expect(fsm.current).to eql(:green)
140
- called = []
141
- fsm.slow
142
- expect(fsm.current).to eql(:green)
143
- expect(called).to eql([])
144
- end
145
-
146
- it "allows to combine conditionals" do
147
- conditions = []
148
-
149
- fsm = FiniteMachine.new do
150
- initial :green
151
-
152
- event :slow, :green => :yellow,
153
- if: [ -> { conditions << 'first_if'; return true },
154
- -> { conditions << 'second_if'; return true}],
155
- unless: -> { conditions << 'first_unless'; return true }
156
- event :stop, :yellow => :red
157
- end
158
-
159
- expect(fsm.current).to eql(:green)
160
- fsm.slow
161
- expect(fsm.current).to eql(:green)
162
- expect(conditions).to eql([
163
- 'first_if',
164
- 'second_if',
165
- 'first_unless'
166
- ])
167
- end
168
-
169
- context 'when proc' do
170
- it "specifies :if and :unless" do
171
- car = Car.new
172
-
173
- fsm = FiniteMachine.new(car) do
174
- initial :neutral
175
-
176
- event :start, :neutral => :one, if: proc {|_car| _car.engine_on? }
177
- event :shift, :one => :two
178
- end
179
- car.turn_engine_off
180
- expect(car.engine_on?).to be false
181
- expect(fsm.current).to eql(:neutral)
182
- fsm.start
183
- expect(fsm.current).to eql(:neutral)
184
-
185
- car.turn_engine_on
186
- expect(car.engine_on?).to be true
187
- expect(fsm.current).to eql(:neutral)
188
- fsm.start
189
- expect(fsm.current).to eql(:one)
190
- end
191
-
192
- it "passes arguments to the scope" do
193
- car = Car.new
194
-
195
- fsm = FiniteMachine.new(car) do
196
- initial :neutral
197
-
198
- event :start, :neutral => :one, if: proc { |_car, state|
199
- _car.engine_on = state
200
- _car.engine_on?
201
- }
202
- event :shift, :one => :two
203
- end
204
- fsm.start(false)
205
- expect(fsm.current).to eql(:neutral)
206
- fsm.start(true)
207
- expect(fsm.current).to eql(:one)
208
- end
209
- end
210
-
211
- context 'when symbol' do
212
- it "specifies :if and :unless" do
213
- car = Car.new
214
-
215
- fsm = FiniteMachine.new(car) do
216
- initial :neutral
217
-
218
- event :start, :neutral => :one, if: :engine_on?
219
- event :shift, :one => :two
220
- end
221
- car.turn_engine_off
222
- expect(car.engine_on?).to be false
223
- expect(fsm.current).to eql(:neutral)
224
- fsm.start
225
- expect(fsm.current).to eql(:neutral)
226
-
227
- car.turn_engine_on
228
- expect(car.engine_on?).to be true
229
- expect(fsm.current).to eql(:neutral)
230
- fsm.start
231
- expect(fsm.current).to eql(:one)
232
- end
233
- end
234
-
235
- context 'when string' do
236
- it "specifies :if and :unless" do
237
- car = Car.new
238
-
239
- fsm = FiniteMachine.new(car) do
240
- initial :neutral
241
-
242
- event :start, :neutral => :one, if: "engine_on?"
243
- event :shift, :one => :two
244
- end
245
- car.turn_engine_off
246
- expect(car.engine_on?).to be false
247
- expect(fsm.current).to eql(:neutral)
248
- fsm.start
249
- expect(fsm.current).to eql(:neutral)
250
-
251
- car.turn_engine_on
252
- expect(car.engine_on?).to be true
253
- expect(fsm.current).to eql(:neutral)
254
- fsm.start
255
- expect(fsm.current).to eql(:one)
256
- end
257
- end
258
-
259
- context 'when same event name' do
260
- it "preservers conditions for the same named event" do
261
- bug = Bug.new
262
- fsm = FiniteMachine.new(bug) do
263
- initial :initial
264
-
265
- event :bump, :initial => :low
266
- event :bump, :low => :medium, if: :pending?
267
- event :bump, :medium => :high
268
- end
269
- expect(fsm.current).to eq(:initial)
270
- fsm.bump
271
- expect(fsm.current).to eq(:low)
272
- fsm.bump
273
- expect(fsm.current).to eq(:low)
274
- end
275
-
276
- it "allows for static choice based on branching condition" do
277
- fsm = FiniteMachine.new do
278
- initial :company_form
279
-
280
- event :next, :company_form => :agreement_form, if: -> { false }
281
- event :next, :company_form => :promo_form, if: -> { false }
282
- event :next, :company_form => :official_form, if: -> { true }
283
- end
284
-
285
- expect(fsm.current).to eq(:company_form)
286
- fsm.next
287
- expect(fsm.current).to eq(:official_form)
288
- end
289
-
290
- it "allows for dynamic choice based on branching condition" do
291
- fsm = FiniteMachine.new do
292
- initial :company_form
293
-
294
- event :next, :company_form => :agreement_form, if: proc { |_, a| a < 1 }
295
- event :next, :company_form => :promo_form, if: proc { |_, a| a == 1 }
296
- event :next, :company_form => :official_form, if: proc { |_, a| a > 1 }
297
- end
298
- expect(fsm.current).to eq(:company_form)
299
-
300
- fsm.next(0)
301
- expect(fsm.current).to eq(:agreement_form)
302
- fsm.restore!(:company_form)
303
- expect(fsm.current).to eq(:company_form)
304
-
305
- fsm.next(1)
306
- expect(fsm.current).to eq(:promo_form)
307
- fsm.restore!(:company_form)
308
- expect(fsm.current).to eq(:company_form)
309
-
310
- fsm.next(2)
311
- expect(fsm.current).to eq(:official_form)
312
- end
313
- end
314
- end