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,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::StateParser, '#parse' do
4
- let(:object) { described_class }
5
-
6
- context 'when no attributes' do
7
- let(:attrs) { { } }
8
-
9
- it "raises error for no transitions" do
10
- expect {
11
- object.parse(attrs)
12
- }.to raise_error(FiniteMachine::NotEnoughTransitionsError,
13
- /please provide state transitions/)
14
- end
15
- end
16
-
17
- context 'when :from and :to keys' do
18
- let(:attrs) { { from: :green, to: :yellow }}
19
-
20
- it "removes :from and :to keys" do
21
- expect(object.parse(attrs)).to eq({green: :yellow})
22
- end
23
- end
24
-
25
- context 'when only :from key' do
26
- let(:attrs) { { from: :green }}
27
-
28
- it "adds to state as copy of from" do
29
- expect(object.parse(attrs)).to eq({green: :green})
30
- end
31
- end
32
-
33
- context 'when only :to key' do
34
- let(:attrs) { { to: :green }}
35
-
36
- it "inserts :any from state" do
37
- expect(object.parse(attrs)).to eq({FiniteMachine::ANY_STATE => :green})
38
- end
39
- end
40
-
41
- context 'when attribuets as hash' do
42
- let(:attrs) { { green: :yellow } }
43
-
44
- it "copies attributes over" do
45
- expect(object.parse(attrs)).to eq({green: :yellow})
46
- end
47
- end
48
-
49
- context 'when array of from states' do
50
- let(:attrs) { { [:green, :red] => :yellow } }
51
-
52
- it "extracts states" do
53
- expect(object.parse(attrs)).to include({red: :yellow, green: :yellow})
54
- end
55
- end
56
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, '#states' do
4
- it "retrieves all available states" do
5
- fsm = FiniteMachine.new 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
- end
13
-
14
- expect(fsm.states).to match_array([:none, :green, :yellow, :red])
15
- end
16
-
17
- it "retrieves all unique states for choice transition" do
18
- fsm = FiniteMachine.new do
19
- initial :green
20
-
21
- event :next, from: :green do
22
- choice :yellow, if: -> { false }
23
- choice :red, if: -> { true }
24
- end
25
- end
26
- expect(fsm.states).to match_array([:none, :green, :yellow, :red])
27
- end
28
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Subscribers do
4
- let(:listener) { double }
5
-
6
- it "checks if any subscribers exist" do
7
- subscribers = described_class.new
8
- expect(subscribers.empty?).to eq(true)
9
- subscribers.subscribe(listener)
10
- expect(subscribers.empty?).to eq(false)
11
- end
12
-
13
- it "allows to subscribe multiple listeners" do
14
- subscribers = described_class.new
15
- subscribers.subscribe(listener, listener)
16
- expect(subscribers.size).to eq(2)
17
- end
18
-
19
- it "returns index for the subscriber" do
20
- subscribers = described_class.new
21
- subscribers.subscribe(listener)
22
- expect(subscribers.index(listener)).to eql(0)
23
- end
24
-
25
- it "visits all subscribed listeners for the event" do
26
- subscribers = described_class.new
27
- subscribers.subscribe(listener)
28
- event = spy(:event)
29
- subscribers.visit(event)
30
- expect(event).to have_received(:notify).with(listener)
31
- end
32
-
33
- it "resets the subscribers" do
34
- subscribers = described_class.new
35
- subscribers.subscribe(listener)
36
- expect(subscribers.empty?).to eq(false)
37
- subscribers.reset
38
- expect(subscribers.empty?).to eq(true)
39
- end
40
- end
@@ -1,225 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, '#target' do
4
- it "allows to target external object" do
5
- stub_const("Car", Class.new do
6
- attr_accessor :reverse_lights
7
-
8
- def turn_reverse_lights_off
9
- @reverse_lights = false
10
- end
11
-
12
- def turn_reverse_lights_on
13
- @reverse_lights = true
14
- end
15
-
16
- def reverse_lights?
17
- @reverse_lights ||= false
18
- end
19
-
20
- def engine
21
- @engine ||= FiniteMachine.new(self) do
22
- initial :neutral
23
-
24
- event :forward, [:reverse, :neutral] => :one
25
- event :shift, :one => :two
26
- event :shift, :two => :one
27
- event :back, [:neutral, :one] => :reverse
28
-
29
- on_enter :reverse do |event|
30
- target.turn_reverse_lights_on
31
- end
32
-
33
- on_exit :reverse do |event|
34
- target.turn_reverse_lights_off
35
- end
36
- end
37
- end
38
- end)
39
- car = Car.new
40
- expect(car.reverse_lights?).to be(false)
41
- expect(car.engine.current).to eql(:neutral)
42
- car.engine.back
43
- expect(car.engine.current).to eql(:reverse)
44
- expect(car.reverse_lights?).to be(true)
45
- car.engine.forward
46
- expect(car.engine.current).to eql(:one)
47
- expect(car.reverse_lights?).to be(false)
48
- end
49
-
50
- it "propagates method call" do
51
- fsm = FiniteMachine.new do
52
- initial :green
53
-
54
- event :slow, :green => :yellow
55
-
56
- on_enter_yellow do |event|
57
- uknown_method
58
- end
59
- end
60
- expect(fsm.current).to eql(:green)
61
- expect { fsm.slow }.to raise_error(StandardError)
62
- end
63
-
64
- it "references machine methods inside callback" do
65
- called = []
66
- fsm = FiniteMachine.new do
67
- initial :green
68
-
69
- event :slow, :green => :yellow
70
- event :stop, :yellow => :red
71
- event :ready, :red => :yellow
72
- event :go, :yellow => :green
73
-
74
- on_enter_yellow do |event|
75
- stop(:now)
76
- end
77
-
78
- on_enter_red do |event, param|
79
- called << "#{event.from} #{param}"
80
- end
81
- end
82
-
83
- expect(fsm.current).to eql(:green)
84
- fsm.slow
85
- expect(fsm.current).to eql(:red)
86
- expect(called).to eql(['yellow now'])
87
- end
88
-
89
- it "allows context methods take precedence over machine ones" do
90
- stub_const("Car", Class.new do
91
- attr_accessor :reverse_lights
92
- attr_accessor :called
93
-
94
- def turn_reverse_lights_off
95
- @reverse_lights = false
96
- end
97
-
98
- def turn_reverse_lights_on
99
- @reverse_lights = true
100
- end
101
-
102
- def reverse_lights?
103
- @reverse_lights ||= false
104
- end
105
-
106
- def engine
107
- self.called ||= []
108
-
109
- @engine ||= FiniteMachine.new(self) do
110
- initial :neutral
111
-
112
- event :forward, [:reverse, :neutral] => :one
113
- event :shift, :one => :two
114
- event :shift, :two => :one
115
- event :back, [:neutral, :one] => :reverse
116
-
117
- on_enter :reverse do |event|
118
- target.called << 'on_enter_reverse'
119
- target.turn_reverse_lights_on
120
- forward('Piotr!')
121
- end
122
- on_before :forward do |event, name|
123
- target.called << "on_enter_forward with #{name}"
124
- end
125
- end
126
- end
127
- end)
128
-
129
- car = Car.new
130
- expect(car.reverse_lights?).to be(false)
131
- expect(car.engine.current).to eql(:neutral)
132
- car.engine.back
133
- expect(car.engine.current).to eql(:one)
134
- expect(car.called).to eql([
135
- 'on_enter_reverse',
136
- 'on_enter_forward with Piotr!'
137
- ])
138
- end
139
-
140
- it "allows to access target inside the callback" do
141
- context = double(:context)
142
- called = nil
143
- fsm = FiniteMachine.new(context) do
144
- initial :green
145
-
146
- event :slow, :green => :yellow
147
- event :stop, :yellow => :red
148
-
149
- on_enter_yellow do |event|
150
- called = target
151
- end
152
- end
153
- expect(fsm.current).to eql(:green)
154
- fsm.slow
155
- expect(called).to eq(context)
156
- end
157
-
158
- it "allows to differentiate between same named methods" do
159
- called = []
160
- stub_const("Car", Class.new do
161
- def initialize(called)
162
- @called = called
163
- end
164
- def save
165
- @called << 'car save called'
166
- end
167
- end)
168
-
169
- car = Car.new(called)
170
- fsm = FiniteMachine.new(car) do
171
- initial :unsaved
172
-
173
- event :validate, :unsaved => :valid
174
- event :save, :valid => :saved
175
-
176
- on_enter :valid do |event|
177
- target.save
178
- save
179
- end
180
- on_after :save do |event|
181
- called << 'event save called'
182
- end
183
- end
184
- expect(fsm.current).to eql(:unsaved)
185
- fsm.validate
186
- expect(fsm.current).to eql(:saved)
187
- expect(called).to eq([
188
- 'car save called',
189
- 'event save called'
190
- ])
191
- end
192
-
193
- it "handles targets responding to :to_hash message" do
194
- stub_const("Serializer", Class.new do
195
- def initialize(data)
196
- @data = data
197
- end
198
-
199
- def write(new_data)
200
- @data.merge!(new_data)
201
- end
202
-
203
- def to_hash
204
- @data
205
- end
206
- alias to_h to_hash
207
- end)
208
-
209
- model = Serializer.new({a: 1, b: 2})
210
-
211
- fsm = FiniteMachine.new(model) do
212
- initial :a
213
-
214
- event :serialize, :a => :b
215
-
216
- on_after :serialize do |event|
217
- target.write(c: 3)
218
- end
219
- end
220
-
221
- fsm.serialize
222
-
223
- expect(model.to_h).to include({a: 1, b: 2, c: 3})
224
- end
225
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, '#terminated?' do
4
-
5
- it "allows to specify terminal state" do
6
- fsm = FiniteMachine.new do
7
- initial :green
8
- terminal :red
9
-
10
- event :slow, :green => :yellow
11
- event :stop, :yellow => :red
12
- end
13
-
14
- expect(fsm.current).to eql(:green)
15
- expect(fsm.terminated?).to be(false)
16
-
17
- fsm.slow
18
- expect(fsm.current).to eql(:yellow)
19
- expect(fsm.terminated?).to be(false)
20
-
21
- fsm.stop
22
- expect(fsm.current).to eql(:red)
23
- expect(fsm.terminated?).to be(true)
24
- end
25
-
26
- it "allows to specify terminal state as parameter" do
27
- fsm = FiniteMachine.new terminal: :red do
28
- initial :green
29
-
30
- event :slow, :green => :yellow
31
- event :stop, :yellow => :red
32
- end
33
- fsm.slow
34
- fsm.stop
35
- expect(fsm.terminated?).to be(true)
36
- end
37
-
38
- it "checks without terminal state" do
39
- fsm = FiniteMachine.new do
40
- initial :green
41
-
42
- event :slow, :green => :yellow
43
- event :stop, :yellow => :red
44
- end
45
-
46
- expect(fsm.current).to eql(:green)
47
- expect(fsm.terminated?).to be(false)
48
-
49
- fsm.slow
50
- expect(fsm.current).to eql(:yellow)
51
- expect(fsm.terminated?).to be(false)
52
-
53
- fsm.stop
54
- expect(fsm.current).to eql(:red)
55
- expect(fsm.terminated?).to be(false)
56
- end
57
-
58
- it "allows for multiple terminal states" do
59
- fsm = FiniteMachine.new do
60
- initial :open
61
-
62
- terminal :close, :canceled, :faulty
63
-
64
- event :resolve, :open => :close
65
- event :decline, :open => :canceled
66
- event :error, :open => :faulty
67
- end
68
- expect(fsm.current).to eql(:open)
69
- expect(fsm.terminated?).to be(false)
70
-
71
- fsm.resolve
72
- expect(fsm.current).to eql(:close)
73
- expect(fsm.terminated?).to be(true)
74
-
75
- fsm.restore!(:open)
76
- fsm.decline
77
- expect(fsm.current).to eql(:canceled)
78
- expect(fsm.terminated?).to be(true)
79
-
80
- fsm.restore!(:open)
81
- fsm.error
82
- expect(fsm.current).to eql(:faulty)
83
- expect(fsm.terminated?).to be(true)
84
- end
85
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Transition, '#check_conditions' do
4
- it "verifies all conditions pass" do
5
- context = double(:context)
6
- exec_conditions = 0
7
- ok_condition = -> { exec_conditions += 1; return true }
8
- fail_condition = -> { exec_conditions += 1; return false }
9
-
10
- transition = described_class.new(context, :event_name,
11
- if: [ok_condition, fail_condition])
12
-
13
- expect(transition.check_conditions).to eql(false)
14
- expect(exec_conditions).to eq(2)
15
- end
16
-
17
- it "verifies 'if' and 'unless' conditions" do
18
- context = double(:context)
19
- exec_conditions = 0
20
- ok_condition = -> { exec_conditions += 1; return true }
21
- fail_condition = -> { exec_conditions += 1; return false }
22
-
23
- transition = described_class.new(context, :event_name,
24
- if: [ok_condition],
25
- unless: [fail_condition])
26
-
27
- expect(transition.check_conditions).to eql(true)
28
- expect(exec_conditions).to eq(2)
29
- end
30
-
31
- it "verifies condition with arguments" do
32
- context = double(:context)
33
- condition = -> (_, arg) { arg == 1 }
34
-
35
- transition = described_class.new(context, :event_name,
36
- if: [condition])
37
-
38
- expect(transition.check_conditions(2)).to eql(false)
39
- expect(transition.check_conditions(1)).to eql(true)
40
- end
41
-
42
- it "verifies condition on target" do
43
- stub_const("Car", Class.new do
44
- def engine_on?
45
- true
46
- end
47
- end)
48
- context = Car.new
49
- condition = -> (car) { car.engine_on? }
50
-
51
- transition = described_class.new(context, :event_name, if: condition)
52
-
53
- expect(transition.check_conditions).to eql(true)
54
- end
55
- end