finite_machine 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +85 -14
- data/examples/atm.rb +45 -0
- data/examples/bug_system.rb +145 -0
- data/lib/finite_machine.rb +16 -4
- data/lib/finite_machine/async_call.rb +10 -1
- data/lib/finite_machine/dsl.rb +27 -8
- data/lib/finite_machine/event_queue.rb +12 -10
- data/lib/finite_machine/logger.rb +23 -0
- data/lib/finite_machine/observer.rb +11 -3
- data/lib/finite_machine/state_machine.rb +2 -4
- data/lib/finite_machine/subscribers.rb +36 -2
- data/lib/finite_machine/thread_context.rb +3 -1
- data/lib/finite_machine/transition.rb +32 -6
- data/lib/finite_machine/version.rb +1 -1
- data/spec/unit/async_events_spec.rb +4 -4
- data/spec/unit/callbacks_spec.rb +48 -6
- data/spec/unit/events_spec.rb +15 -0
- data/spec/unit/if_unless_spec.rb +90 -60
- data/spec/unit/initialize_spec.rb +48 -1
- data/spec/unit/inspect_spec.rb +25 -0
- data/spec/unit/logger_spec.rb +33 -0
- data/spec/unit/subscribers_spec.rb +31 -0
- metadata +11 -2
data/spec/unit/events_spec.rb
CHANGED
@@ -150,6 +150,21 @@ describe FiniteMachine, 'events' do
|
|
150
150
|
expect { fsm.stop }.to raise_error(FiniteMachine::InvalidStateError, /state 'green'/)
|
151
151
|
end
|
152
152
|
|
153
|
+
it "allows to transition to any state" do
|
154
|
+
fsm = FiniteMachine.define do
|
155
|
+
initial :green
|
156
|
+
|
157
|
+
events {
|
158
|
+
event :slow, from: :green, to: :yellow
|
159
|
+
event :stop, from: :yellow, to: :red
|
160
|
+
}
|
161
|
+
end
|
162
|
+
expect(fsm.current).to eql(:green)
|
163
|
+
expect(fsm.can?(:stop)).to be_false
|
164
|
+
fsm.stop!
|
165
|
+
expect(fsm.current).to eql(:red)
|
166
|
+
end
|
167
|
+
|
153
168
|
context 'when multiple from states' do
|
154
169
|
it "allows for array from key" do
|
155
170
|
fsm = FiniteMachine.define do
|
data/spec/unit/if_unless_spec.rb
CHANGED
@@ -5,6 +5,8 @@ require 'spec_helper'
|
|
5
5
|
describe FiniteMachine, ':if, :unless' do
|
6
6
|
before(:each) {
|
7
7
|
Car = Class.new do
|
8
|
+
attr_accessor :engine_on
|
9
|
+
|
8
10
|
def turn_engine_on
|
9
11
|
@engine_on = true
|
10
12
|
end
|
@@ -116,81 +118,109 @@ describe FiniteMachine, ':if, :unless' do
|
|
116
118
|
])
|
117
119
|
end
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
+
context 'when proc' do
|
122
|
+
it "specifies :if and :unless" do
|
123
|
+
car = Car.new
|
121
124
|
|
122
|
-
|
123
|
-
|
125
|
+
fsm = FiniteMachine.define do
|
126
|
+
initial :neutral
|
124
127
|
|
125
|
-
|
128
|
+
target car
|
126
129
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
130
|
+
events {
|
131
|
+
event :start, :neutral => :one, if: proc {|_car| _car.engine_on? }
|
132
|
+
event :shift, :one => :two
|
133
|
+
}
|
134
|
+
end
|
135
|
+
car.turn_engine_off
|
136
|
+
expect(car.engine_on?).to be_false
|
137
|
+
expect(fsm.current).to eql(:neutral)
|
138
|
+
fsm.start
|
139
|
+
expect(fsm.current).to eql(:neutral)
|
140
|
+
|
141
|
+
car.turn_engine_on
|
142
|
+
expect(car.engine_on?).to be_true
|
143
|
+
expect(fsm.current).to eql(:neutral)
|
144
|
+
fsm.start
|
145
|
+
expect(fsm.current).to eql(:one)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "passes arguments to the scope" do
|
149
|
+
car = Car.new
|
150
|
+
|
151
|
+
fsm = FiniteMachine.define do
|
152
|
+
initial :neutral
|
153
|
+
|
154
|
+
target car
|
155
|
+
|
156
|
+
events {
|
157
|
+
event :start, :neutral => :one, if: proc { |_car, state|
|
158
|
+
_car.engine_on = state
|
159
|
+
_car.engine_on?
|
160
|
+
}
|
161
|
+
event :shift, :one => :two
|
162
|
+
}
|
163
|
+
end
|
164
|
+
fsm.start(false)
|
165
|
+
expect(fsm.current).to eql(:neutral)
|
166
|
+
fsm.start(true)
|
167
|
+
expect(fsm.current).to eql(:one)
|
131
168
|
end
|
132
|
-
car.turn_engine_off
|
133
|
-
expect(car.engine_on?).to be_false
|
134
|
-
expect(fsm.current).to eql(:neutral)
|
135
|
-
fsm.start
|
136
|
-
expect(fsm.current).to eql(:neutral)
|
137
|
-
|
138
|
-
car.turn_engine_on
|
139
|
-
expect(car.engine_on?).to be_true
|
140
|
-
expect(fsm.current).to eql(:neutral)
|
141
|
-
fsm.start
|
142
|
-
expect(fsm.current).to eql(:one)
|
143
169
|
end
|
144
170
|
|
145
|
-
|
146
|
-
|
171
|
+
context 'when symbol' do
|
172
|
+
it "specifies :if and :unless" do
|
173
|
+
car = Car.new
|
147
174
|
|
148
|
-
|
149
|
-
|
175
|
+
fsm = FiniteMachine.define do
|
176
|
+
initial :neutral
|
150
177
|
|
151
|
-
|
178
|
+
target car
|
152
179
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
180
|
+
events {
|
181
|
+
event :start, :neutral => :one, if: :engine_on?
|
182
|
+
event :shift, :one => :two
|
183
|
+
}
|
184
|
+
end
|
185
|
+
car.turn_engine_off
|
186
|
+
expect(car.engine_on?).to be_false
|
187
|
+
expect(fsm.current).to eql(:neutral)
|
188
|
+
fsm.start
|
189
|
+
expect(fsm.current).to eql(:neutral)
|
190
|
+
|
191
|
+
car.turn_engine_on
|
192
|
+
expect(car.engine_on?).to be_true
|
193
|
+
expect(fsm.current).to eql(:neutral)
|
194
|
+
fsm.start
|
195
|
+
expect(fsm.current).to eql(:one)
|
157
196
|
end
|
158
|
-
car.turn_engine_off
|
159
|
-
expect(car.engine_on?).to be_false
|
160
|
-
expect(fsm.current).to eql(:neutral)
|
161
|
-
fsm.start
|
162
|
-
expect(fsm.current).to eql(:neutral)
|
163
|
-
|
164
|
-
car.turn_engine_on
|
165
|
-
expect(car.engine_on?).to be_true
|
166
|
-
expect(fsm.current).to eql(:neutral)
|
167
|
-
fsm.start
|
168
|
-
expect(fsm.current).to eql(:one)
|
169
197
|
end
|
170
198
|
|
171
|
-
|
172
|
-
|
199
|
+
context 'when string' do
|
200
|
+
it "specifies :if and :unless" do
|
201
|
+
car = Car.new
|
173
202
|
|
174
|
-
|
175
|
-
|
203
|
+
fsm = FiniteMachine.define do
|
204
|
+
initial :neutral
|
176
205
|
|
177
|
-
|
206
|
+
target car
|
178
207
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
208
|
+
events {
|
209
|
+
event :start, :neutral => :one, if: "engine_on?"
|
210
|
+
event :shift, :one => :two
|
211
|
+
}
|
212
|
+
end
|
213
|
+
car.turn_engine_off
|
214
|
+
expect(car.engine_on?).to be_false
|
215
|
+
expect(fsm.current).to eql(:neutral)
|
216
|
+
fsm.start
|
217
|
+
expect(fsm.current).to eql(:neutral)
|
218
|
+
|
219
|
+
car.turn_engine_on
|
220
|
+
expect(car.engine_on?).to be_true
|
221
|
+
expect(fsm.current).to eql(:neutral)
|
222
|
+
fsm.start
|
223
|
+
expect(fsm.current).to eql(:one)
|
183
224
|
end
|
184
|
-
car.turn_engine_off
|
185
|
-
expect(car.engine_on?).to be_false
|
186
|
-
expect(fsm.current).to eql(:neutral)
|
187
|
-
fsm.start
|
188
|
-
expect(fsm.current).to eql(:neutral)
|
189
|
-
|
190
|
-
car.turn_engine_on
|
191
|
-
expect(car.engine_on?).to be_true
|
192
|
-
expect(fsm.current).to eql(:neutral)
|
193
|
-
fsm.start
|
194
|
-
expect(fsm.current).to eql(:one)
|
195
225
|
end
|
196
226
|
end
|
@@ -25,7 +25,22 @@ describe FiniteMachine, 'initialize' do
|
|
25
25
|
expect(fsm.current).to eql(:none)
|
26
26
|
end
|
27
27
|
|
28
|
+
it "requires initial state transition from :none" do
|
29
|
+
fsm = FiniteMachine.define do
|
30
|
+
events {
|
31
|
+
event :init, :none => :green
|
32
|
+
event :slow, :green => :yellow
|
33
|
+
event :stop, :yellow => :red
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
expect(fsm.current).to eql(:none)
|
38
|
+
fsm.init
|
39
|
+
expect(fsm.current).to eql(:green)
|
40
|
+
end
|
41
|
+
|
28
42
|
it "allows to specify inital state" do
|
43
|
+
called = []
|
29
44
|
fsm = FiniteMachine.define do
|
30
45
|
initial :green
|
31
46
|
|
@@ -33,9 +48,12 @@ describe FiniteMachine, 'initialize' do
|
|
33
48
|
event :slow, :green => :yellow
|
34
49
|
event :stop, :yellow => :red
|
35
50
|
}
|
51
|
+
callbacks {
|
52
|
+
on_enter :green do |event| called << 'on_enter_green' end
|
53
|
+
}
|
36
54
|
end
|
37
|
-
|
38
55
|
expect(fsm.current).to eql(:green)
|
56
|
+
expect(called).to be_empty
|
39
57
|
end
|
40
58
|
|
41
59
|
it "allows to specify deferred inital state" do
|
@@ -48,10 +66,24 @@ describe FiniteMachine, 'initialize' do
|
|
48
66
|
}
|
49
67
|
end
|
50
68
|
|
69
|
+
expect(fsm.current).to eql(:none)
|
51
70
|
fsm.init
|
52
71
|
expect(fsm.current).to eql(:green)
|
53
72
|
end
|
54
73
|
|
74
|
+
it "raises error when specyfying initial without state name" do
|
75
|
+
expect {
|
76
|
+
FiniteMachine.define do
|
77
|
+
initial defer: true
|
78
|
+
|
79
|
+
events {
|
80
|
+
event :slow, :green => :yellow
|
81
|
+
event :stop, :yellow => :red
|
82
|
+
}
|
83
|
+
end
|
84
|
+
}.to raise_error(FiniteMachine::MissingInitialStateError)
|
85
|
+
end
|
86
|
+
|
55
87
|
it "allows to specify inital start event" do
|
56
88
|
fsm = FiniteMachine.define do
|
57
89
|
initial state: :green, event: :start
|
@@ -96,4 +128,19 @@ describe FiniteMachine, 'initialize' do
|
|
96
128
|
end
|
97
129
|
expect(fsm.current).to eql(:pending)
|
98
130
|
end
|
131
|
+
|
132
|
+
it "doesn't care about state type" do
|
133
|
+
fsm = FiniteMachine.define do
|
134
|
+
initial 1
|
135
|
+
events {
|
136
|
+
event :a, 1 => 2
|
137
|
+
event :b, 2 => 3
|
138
|
+
}
|
139
|
+
end
|
140
|
+
expect(fsm.current).to eql(1)
|
141
|
+
fsm.a
|
142
|
+
expect(fsm.current).to eql(2)
|
143
|
+
fsm.b
|
144
|
+
expect(fsm.current).to eql(3)
|
145
|
+
end
|
99
146
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe FiniteMachine::Transition, 'inspect' do
|
6
|
+
let(:machine) { double }
|
7
|
+
|
8
|
+
subject(:transition) { described_class.new(machine, attrs) }
|
9
|
+
|
10
|
+
context 'when inspecting' do
|
11
|
+
let(:attrs) { {name: :start, :foo => :bar } }
|
12
|
+
|
13
|
+
it "displays name and transitions" do
|
14
|
+
expect(transition.inspect).to eql("<FiniteMachine::Transition name: start, transitions: [:foo] => bar, when: []>")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when converting to string' do
|
19
|
+
let(:attrs) { {name: :start, :foo => :bar } }
|
20
|
+
|
21
|
+
it "displays name and transitions" do
|
22
|
+
expect(transition.to_s).to eql("start")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
describe FiniteMachine::Logger do
|
7
|
+
let(:message) { 'error' }
|
8
|
+
let(:log) { double }
|
9
|
+
|
10
|
+
subject(:logger) { described_class }
|
11
|
+
|
12
|
+
before { FiniteMachine.stub(:logger) { log } }
|
13
|
+
|
14
|
+
it "debugs message call" do
|
15
|
+
expect(log).to receive(:debug).with(message)
|
16
|
+
logger.debug(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "informs message call" do
|
20
|
+
expect(log).to receive(:info).with(message)
|
21
|
+
logger.info(message)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "warns message call" do
|
25
|
+
expect(log).to receive(:warn).with(message)
|
26
|
+
logger.warn(message)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "errors message call" do
|
30
|
+
expect(log).to receive(:error).with(message)
|
31
|
+
logger.error(message)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe FiniteMachine::Subscribers do
|
6
|
+
let(:machine) { double }
|
7
|
+
let(:event) { double }
|
8
|
+
let(:listener) { double }
|
9
|
+
|
10
|
+
subject(:subscribers) { described_class.new(machine) }
|
11
|
+
|
12
|
+
before { subscribers.subscribe(listener) }
|
13
|
+
|
14
|
+
it "checks if any subscribers exist" do
|
15
|
+
expect(subscribers.empty?).to be_false
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns index for the subscriber" do
|
19
|
+
expect(subscribers.index(listener)).to eql(0)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "visits all subscribed listeners for the event" do
|
23
|
+
expect(event).to receive(:notify).with(listener)
|
24
|
+
subscribers.visit(event)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "resets the subscribers" do
|
28
|
+
subscribers.reset
|
29
|
+
expect(subscribers.empty?).to be_true
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finite_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -43,6 +43,8 @@ files:
|
|
43
43
|
- LICENSE.txt
|
44
44
|
- README.md
|
45
45
|
- Rakefile
|
46
|
+
- examples/atm.rb
|
47
|
+
- examples/bug_system.rb
|
46
48
|
- finite_machine.gemspec
|
47
49
|
- lib/finite_machine.rb
|
48
50
|
- lib/finite_machine/async_call.rb
|
@@ -53,6 +55,7 @@ files:
|
|
53
55
|
- lib/finite_machine/event.rb
|
54
56
|
- lib/finite_machine/event_queue.rb
|
55
57
|
- lib/finite_machine/hooks.rb
|
58
|
+
- lib/finite_machine/logger.rb
|
56
59
|
- lib/finite_machine/observer.rb
|
57
60
|
- lib/finite_machine/state_machine.rb
|
58
61
|
- lib/finite_machine/subscribers.rb
|
@@ -71,8 +74,11 @@ files:
|
|
71
74
|
- spec/unit/handlers_spec.rb
|
72
75
|
- spec/unit/if_unless_spec.rb
|
73
76
|
- spec/unit/initialize_spec.rb
|
77
|
+
- spec/unit/inspect_spec.rb
|
74
78
|
- spec/unit/is_spec.rb
|
79
|
+
- spec/unit/logger_spec.rb
|
75
80
|
- spec/unit/states_spec.rb
|
81
|
+
- spec/unit/subscribers_spec.rb
|
76
82
|
- spec/unit/target_spec.rb
|
77
83
|
- spec/unit/transition/parse_states_spec.rb
|
78
84
|
- tasks/console.rake
|
@@ -114,8 +120,11 @@ test_files:
|
|
114
120
|
- spec/unit/handlers_spec.rb
|
115
121
|
- spec/unit/if_unless_spec.rb
|
116
122
|
- spec/unit/initialize_spec.rb
|
123
|
+
- spec/unit/inspect_spec.rb
|
117
124
|
- spec/unit/is_spec.rb
|
125
|
+
- spec/unit/logger_spec.rb
|
118
126
|
- spec/unit/states_spec.rb
|
127
|
+
- spec/unit/subscribers_spec.rb
|
119
128
|
- spec/unit/target_spec.rb
|
120
129
|
- spec/unit/transition/parse_states_spec.rb
|
121
130
|
has_rdoc:
|