finite_machine 0.12.1 → 0.13.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 (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