finite_machine 0.11.2 → 0.14.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +80 -0
- data/LICENSE.txt +1 -1
- data/README.md +679 -624
- data/lib/finite_machine.rb +35 -45
- data/lib/finite_machine/async_call.rb +5 -21
- data/lib/finite_machine/callable.rb +4 -4
- data/lib/finite_machine/catchable.rb +24 -14
- data/lib/finite_machine/choice_merger.rb +20 -20
- data/lib/finite_machine/const.rb +16 -0
- data/lib/finite_machine/definition.rb +3 -3
- data/lib/finite_machine/dsl.rb +98 -151
- data/lib/finite_machine/env.rb +4 -2
- data/lib/finite_machine/event_definition.rb +7 -15
- data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
- data/lib/finite_machine/hook_event.rb +60 -61
- data/lib/finite_machine/hooks.rb +44 -36
- data/lib/finite_machine/listener.rb +2 -2
- data/lib/finite_machine/logger.rb +5 -4
- data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
- data/lib/finite_machine/observer.rb +71 -34
- data/lib/finite_machine/safety.rb +16 -14
- data/lib/finite_machine/state_definition.rb +3 -5
- data/lib/finite_machine/state_machine.rb +93 -76
- data/lib/finite_machine/state_parser.rb +55 -83
- data/lib/finite_machine/subscribers.rb +2 -2
- data/lib/finite_machine/threadable.rb +3 -1
- data/lib/finite_machine/transition.rb +34 -34
- data/lib/finite_machine/transition_builder.rb +23 -32
- data/lib/finite_machine/transition_event.rb +12 -11
- data/lib/finite_machine/two_phase_lock.rb +8 -6
- data/lib/finite_machine/undefined_transition.rb +5 -6
- data/lib/finite_machine/version.rb +2 -2
- metadata +58 -142
- data/.gitignore +0 -18
- data/.rspec +0 -5
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -26
- data/Gemfile +0 -15
- data/Rakefile +0 -8
- data/assets/finite_machine_logo.png +0 -0
- data/examples/atm.rb +0 -45
- data/examples/bug_system.rb +0 -145
- data/finite_machine.gemspec +0 -23
- data/lib/finite_machine/async_proxy.rb +0 -30
- data/spec/integration/system_spec.rb +0 -95
- data/spec/spec_helper.rb +0 -33
- data/spec/unit/alias_target_spec.rb +0 -108
- data/spec/unit/async_events_spec.rb +0 -138
- data/spec/unit/callable/call_spec.rb +0 -113
- data/spec/unit/callbacks_spec.rb +0 -942
- data/spec/unit/can_spec.rb +0 -98
- data/spec/unit/choice_spec.rb +0 -331
- data/spec/unit/define_spec.rb +0 -55
- data/spec/unit/definition_spec.rb +0 -115
- data/spec/unit/event_names_spec.rb +0 -19
- data/spec/unit/event_queue_spec.rb +0 -52
- data/spec/unit/events_chain/add_spec.rb +0 -25
- data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
- data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
- data/spec/unit/events_chain/clear_spec.rb +0 -15
- data/spec/unit/events_chain/events_spec.rb +0 -18
- data/spec/unit/events_chain/inspect_spec.rb +0 -24
- data/spec/unit/events_chain/match_transition_spec.rb +0 -37
- data/spec/unit/events_chain/move_to_spec.rb +0 -48
- data/spec/unit/events_chain/states_for_spec.rb +0 -17
- data/spec/unit/events_spec.rb +0 -459
- data/spec/unit/handlers_spec.rb +0 -152
- data/spec/unit/hook_event/build_spec.rb +0 -15
- data/spec/unit/hook_event/eql_spec.rb +0 -36
- data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
- data/spec/unit/hook_event/initialize_spec.rb +0 -25
- data/spec/unit/hook_event/notify_spec.rb +0 -14
- data/spec/unit/hooks/call_spec.rb +0 -24
- data/spec/unit/hooks/clear_spec.rb +0 -16
- data/spec/unit/hooks/inspect_spec.rb +0 -17
- data/spec/unit/hooks/register_spec.rb +0 -22
- data/spec/unit/if_unless_spec.rb +0 -353
- data/spec/unit/initial_spec.rb +0 -222
- data/spec/unit/inspect_spec.rb +0 -17
- data/spec/unit/is_spec.rb +0 -55
- data/spec/unit/log_transitions_spec.rb +0 -30
- data/spec/unit/logger_spec.rb +0 -38
- data/spec/unit/respond_to_spec.rb +0 -38
- data/spec/unit/state_parser/inspect_spec.rb +0 -25
- data/spec/unit/state_parser/parse_spec.rb +0 -59
- data/spec/unit/states_spec.rb +0 -34
- data/spec/unit/subscribers_spec.rb +0 -42
- data/spec/unit/target_spec.rb +0 -225
- data/spec/unit/terminated_spec.rb +0 -95
- data/spec/unit/transition/check_conditions_spec.rb +0 -54
- data/spec/unit/transition/inspect_spec.rb +0 -25
- data/spec/unit/transition/matches_spec.rb +0 -23
- data/spec/unit/transition/states_spec.rb +0 -31
- data/spec/unit/transition/to_state_spec.rb +0 -27
- data/spec/unit/trigger_spec.rb +0 -22
- data/spec/unit/undefined_transition/eql_spec.rb +0 -17
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
data/spec/unit/states_spec.rb
DELETED
@@ -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
|
data/spec/unit/target_spec.rb
DELETED
@@ -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
|