finite_machine 0.11.2 → 0.14.0

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