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,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