finite_machine 0.1.0 → 0.2.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/.travis.yml +6 -2
- data/CHANGELOG.md +12 -0
- data/README.md +345 -105
- data/finite_machine.gemspec +2 -2
- data/lib/finite_machine.rb +12 -0
- data/lib/finite_machine/callable.rb +2 -7
- data/lib/finite_machine/catchable.rb +112 -0
- data/lib/finite_machine/dsl.rb +59 -44
- data/lib/finite_machine/hooks.rb +43 -0
- data/lib/finite_machine/observer.rb +39 -40
- data/lib/finite_machine/state_machine.rb +37 -18
- data/lib/finite_machine/subscribers.rb +1 -1
- data/lib/finite_machine/threadable.rb +8 -2
- data/lib/finite_machine/transition.rb +45 -1
- data/lib/finite_machine/version.rb +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/callable/call_spec.rb +91 -0
- data/spec/unit/callbacks_spec.rb +96 -12
- data/spec/unit/events_spec.rb +1 -1
- data/spec/unit/handlers_spec.rb +99 -0
- data/spec/unit/if_unless_spec.rb +3 -3
- data/spec/unit/initialize_spec.rb +40 -0
- data/spec/unit/target_spec.rb +137 -0
- data/spec/unit/transition/parse_states_spec.rb +16 -5
- metadata +15 -4
data/spec/unit/events_spec.rb
CHANGED
@@ -147,7 +147,7 @@ describe FiniteMachine, 'events' do
|
|
147
147
|
|
148
148
|
expect(fsm.current).to eql(:green)
|
149
149
|
|
150
|
-
expect { fsm.stop }.to raise_error(FiniteMachine::
|
150
|
+
expect { fsm.stop }.to raise_error(FiniteMachine::InvalidStateError, /state 'green'/)
|
151
151
|
end
|
152
152
|
|
153
153
|
context 'when multiple from states' do
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe FiniteMachine, 'handlers' do
|
6
|
+
|
7
|
+
before(:each) {
|
8
|
+
Logger = Class.new do
|
9
|
+
attr_reader :result
|
10
|
+
|
11
|
+
def log_error(exception)
|
12
|
+
@result = "log_error(#{exception})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def raise_error
|
16
|
+
raise FiniteMachine::TransitionError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
it "allows to customise error handling" do
|
22
|
+
called = []
|
23
|
+
fsm = FiniteMachine.define do
|
24
|
+
initial :green
|
25
|
+
|
26
|
+
events {
|
27
|
+
event :slow, :green => :yellow
|
28
|
+
event :stop, :yellow => :red
|
29
|
+
}
|
30
|
+
|
31
|
+
handlers {
|
32
|
+
handle FiniteMachine::InvalidStateError do |exception|
|
33
|
+
called << 'invalidstate'
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
expect(fsm.current).to eql(:green)
|
39
|
+
fsm.stop
|
40
|
+
expect(fsm.current).to eql(:green)
|
41
|
+
expect(called).to eql([
|
42
|
+
'invalidstate'
|
43
|
+
])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'allows for :with to be symbol' do
|
47
|
+
logger = Logger.new
|
48
|
+
fsm = FiniteMachine.define do
|
49
|
+
initial :green
|
50
|
+
|
51
|
+
target logger
|
52
|
+
|
53
|
+
events {
|
54
|
+
event :slow, :green => :yellow
|
55
|
+
event :stop, :yellow => :red
|
56
|
+
}
|
57
|
+
|
58
|
+
handlers {
|
59
|
+
handle FiniteMachine::InvalidStateError, with: :log_error
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
expect(fsm.current).to eql(:green)
|
64
|
+
fsm.stop
|
65
|
+
expect(fsm.current).to eql(:green)
|
66
|
+
expect(logger.result).to eql('log_error(FiniteMachine::InvalidStateError)')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'allows for error type as string' do
|
70
|
+
logger = Logger.new
|
71
|
+
called = []
|
72
|
+
fsm = FiniteMachine.define do
|
73
|
+
initial :green
|
74
|
+
|
75
|
+
target logger
|
76
|
+
|
77
|
+
events {
|
78
|
+
event :slow, :green => :yellow
|
79
|
+
event :stop, :yellow => :red
|
80
|
+
}
|
81
|
+
|
82
|
+
callbacks {
|
83
|
+
on_enter_yellow do |event|
|
84
|
+
raise_error
|
85
|
+
end
|
86
|
+
}
|
87
|
+
handlers {
|
88
|
+
handle 'InvalidStateError' do |exception|
|
89
|
+
called << 'invalid_state_error'
|
90
|
+
end
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
expect(fsm.current).to eql(:green)
|
95
|
+
fsm.stop
|
96
|
+
expect(fsm.current).to eql(:green)
|
97
|
+
expect(called).to eql(['invalid_state_error'])
|
98
|
+
end
|
99
|
+
end
|
data/spec/unit/if_unless_spec.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe FiniteMachine, ':if, :unless' do
|
6
|
-
before(:
|
6
|
+
before(:each) {
|
7
7
|
Car = Class.new do
|
8
8
|
def turn_engine_on
|
9
9
|
@engine_on = true
|
@@ -62,7 +62,7 @@ describe FiniteMachine, ':if, :unless' do
|
|
62
62
|
initial :green
|
63
63
|
|
64
64
|
events {
|
65
|
-
event :slow, :green => :yellow, unless: -> {
|
65
|
+
event :slow, :green => :yellow, unless: -> { true }
|
66
66
|
event :stop, :yellow => :red
|
67
67
|
}
|
68
68
|
|
@@ -125,7 +125,7 @@ describe FiniteMachine, ':if, :unless' do
|
|
125
125
|
target car
|
126
126
|
|
127
127
|
events {
|
128
|
-
event :start, :neutral => :one, if:
|
128
|
+
event :start, :neutral => :one, if: proc {|car| car.engine_on? }
|
129
129
|
event :shift, :one => :two
|
130
130
|
}
|
131
131
|
end
|
@@ -4,6 +4,16 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe FiniteMachine, 'initialize' do
|
6
6
|
|
7
|
+
before(:each) {
|
8
|
+
Logger = Class.new do
|
9
|
+
attr_accessor :level
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@level = :pending
|
13
|
+
end
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
7
17
|
it "defaults initial state to :none" do
|
8
18
|
fsm = FiniteMachine.define do
|
9
19
|
events {
|
@@ -46,6 +56,23 @@ describe FiniteMachine, 'initialize' do
|
|
46
56
|
fsm = FiniteMachine.define do
|
47
57
|
initial state: :green, event: :start
|
48
58
|
|
59
|
+
events {
|
60
|
+
event :slow, :green => :none
|
61
|
+
event :stop, :yellow => :red
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(fsm.current).to eql(:green)
|
66
|
+
fsm.slow
|
67
|
+
expect(fsm.current).to eql(:none)
|
68
|
+
fsm.start
|
69
|
+
expect(fsm.current).to eql(:green)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "allows to specify deferred inital start event" do
|
73
|
+
fsm = FiniteMachine.define do
|
74
|
+
initial state: :green, event: :start, defer: true
|
75
|
+
|
49
76
|
events {
|
50
77
|
event :slow, :green => :yellow
|
51
78
|
event :stop, :yellow => :red
|
@@ -56,4 +83,17 @@ describe FiniteMachine, 'initialize' do
|
|
56
83
|
fsm.start
|
57
84
|
expect(fsm.current).to eql(:green)
|
58
85
|
end
|
86
|
+
|
87
|
+
it "evaluates initial state" do
|
88
|
+
logger = Logger.new
|
89
|
+
fsm = FiniteMachine.define do
|
90
|
+
initial logger.level
|
91
|
+
|
92
|
+
events {
|
93
|
+
event :slow, :green => :none
|
94
|
+
event :stop, :yellow => :red
|
95
|
+
}
|
96
|
+
end
|
97
|
+
expect(fsm.current).to eql(:pending)
|
98
|
+
end
|
59
99
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe FiniteMachine, '#target' do
|
6
|
+
|
7
|
+
it "allows to target external object" do
|
8
|
+
Car = Class.new do
|
9
|
+
attr_accessor :reverse_lights
|
10
|
+
|
11
|
+
def turn_reverse_lights_off
|
12
|
+
@reverse_lights = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def turn_reverse_lights_on
|
16
|
+
@reverse_lights = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def engine
|
20
|
+
context = self
|
21
|
+
@engine ||= FiniteMachine.define do
|
22
|
+
initial :neutral
|
23
|
+
|
24
|
+
target context
|
25
|
+
|
26
|
+
events {
|
27
|
+
event :forward, [:reverse, :neutral] => :one
|
28
|
+
event :shift, :one => :two
|
29
|
+
event :shift, :two => :one
|
30
|
+
event :back, [:neutral, :one] => :reverse
|
31
|
+
}
|
32
|
+
|
33
|
+
callbacks {
|
34
|
+
on_enter :reverse do |event|
|
35
|
+
turn_reverse_lights_on
|
36
|
+
end
|
37
|
+
|
38
|
+
on_exit :reverse do |event|
|
39
|
+
turn_reverse_lights_off
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
car = Car.new
|
46
|
+
expect(car.reverse_lights).to be_false
|
47
|
+
expect(car.engine.current).to eql(:neutral)
|
48
|
+
car.engine.back
|
49
|
+
expect(car.engine.current).to eql(:reverse)
|
50
|
+
expect(car.reverse_lights).to be_true
|
51
|
+
car.engine.forward
|
52
|
+
expect(car.engine.current).to eql(:one)
|
53
|
+
expect(car.reverse_lights).to be_false
|
54
|
+
end
|
55
|
+
|
56
|
+
it "references machine methods inside callback" do
|
57
|
+
called = []
|
58
|
+
fsm = FiniteMachine.define do
|
59
|
+
initial :green
|
60
|
+
|
61
|
+
events {
|
62
|
+
event :slow, :green => :yellow
|
63
|
+
event :stop, :yellow => :red
|
64
|
+
event :ready, :red => :yellow
|
65
|
+
event :go, :yellow => :green
|
66
|
+
}
|
67
|
+
|
68
|
+
callbacks {
|
69
|
+
on_enter_yellow do |event|
|
70
|
+
stop(:now)
|
71
|
+
end
|
72
|
+
|
73
|
+
on_enter_red do |event, param|
|
74
|
+
called << "#{event.from} #{param}"
|
75
|
+
end
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
expect(fsm.current).to eql(:green)
|
80
|
+
fsm.slow
|
81
|
+
expect(fsm.current).to eql(:red)
|
82
|
+
expect(called).to eql(['yellow now'])
|
83
|
+
end
|
84
|
+
|
85
|
+
it "allows context methods take precedence over machine ones" do
|
86
|
+
Car = Class.new do
|
87
|
+
attr_accessor :reverse_lights
|
88
|
+
attr_accessor :called
|
89
|
+
|
90
|
+
def turn_reverse_lights_off
|
91
|
+
@reverse_lights = false
|
92
|
+
end
|
93
|
+
|
94
|
+
def turn_reverse_lights_on
|
95
|
+
@reverse_lights = true
|
96
|
+
end
|
97
|
+
|
98
|
+
def engine
|
99
|
+
self.called ||= []
|
100
|
+
context ||= self
|
101
|
+
@engine ||= FiniteMachine.define do
|
102
|
+
initial :neutral
|
103
|
+
|
104
|
+
target context
|
105
|
+
|
106
|
+
events {
|
107
|
+
event :forward, [:reverse, :neutral] => :one
|
108
|
+
event :shift, :one => :two
|
109
|
+
event :shift, :two => :one
|
110
|
+
event :back, [:neutral, :one] => :reverse
|
111
|
+
}
|
112
|
+
|
113
|
+
callbacks {
|
114
|
+
on_enter :reverse do |event|
|
115
|
+
called << 'on_enter_reverse'
|
116
|
+
turn_reverse_lights_on
|
117
|
+
forward('Piotr!')
|
118
|
+
end
|
119
|
+
on_enter :forward do |event, name|
|
120
|
+
called << "on_enter_forward with #{name}"
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
car = Car.new
|
128
|
+
expect(car.reverse_lights).to be_false
|
129
|
+
expect(car.engine.current).to eql(:neutral)
|
130
|
+
car.engine.back
|
131
|
+
expect(car.engine.current).to eql(:one)
|
132
|
+
expect(car.called).to eql([
|
133
|
+
'on_enter_reverse',
|
134
|
+
'on_enter_forward with Piotr!'
|
135
|
+
])
|
136
|
+
end
|
137
|
+
end
|
@@ -4,15 +4,24 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe FiniteMachine::Transition, 'parse_states' do
|
6
6
|
|
7
|
-
let(:
|
7
|
+
let(:machine) { double }
|
8
8
|
|
9
|
-
subject(:transition) {
|
9
|
+
subject(:transition) { described_class.new(machine, attrs) }
|
10
|
+
|
11
|
+
context 'without transitions' do
|
12
|
+
let(:attrs) { { } }
|
13
|
+
|
14
|
+
it "raises exception" do
|
15
|
+
expect { transition }.to raise_error(FiniteMachine::NotEnoughTransitionsError)
|
16
|
+
end
|
17
|
+
end
|
10
18
|
|
11
19
|
context 'with :from, :to keys' do
|
12
20
|
let(:attrs) { {from: [:green, :yellow], to: :red} }
|
13
21
|
|
14
22
|
it "groups states" do
|
15
|
-
expect(transition).to eql([
|
23
|
+
expect(transition.from).to eql(attrs[:from])
|
24
|
+
expect(transition.to).to eql(attrs[:to])
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
@@ -20,7 +29,8 @@ describe FiniteMachine::Transition, 'parse_states' do
|
|
20
29
|
let(:attrs) { {[:green, :yellow] => :red} }
|
21
30
|
|
22
31
|
it "groups states" do
|
23
|
-
expect(transition).to eql([
|
32
|
+
expect(transition.from).to eql([:green, :yellow])
|
33
|
+
expect(transition.to).to eql(:red)
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
@@ -28,7 +38,8 @@ describe FiniteMachine::Transition, 'parse_states' do
|
|
28
38
|
let(:attrs) { { :green => :red, :yellow => :red} }
|
29
39
|
|
30
40
|
it "groups states" do
|
31
|
-
expect(transition).to eql([
|
41
|
+
expect(transition.from).to eql([:green, :yellow])
|
42
|
+
expect(transition.to).to eql(:red)
|
32
43
|
end
|
33
44
|
end
|
34
45
|
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.2.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-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,9 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description: A minimal finite state machine with a straightforward syntax.
|
55
|
+
description: A minimal finite state machine with a straightforward syntax. You can
|
56
|
+
quickly model states, add callbacks and use object-oriented techniques to integrate
|
57
|
+
with ORMs.
|
56
58
|
email:
|
57
59
|
- ''
|
58
60
|
executables: []
|
@@ -62,6 +64,7 @@ files:
|
|
62
64
|
- .gitignore
|
63
65
|
- .rspec
|
64
66
|
- .travis.yml
|
67
|
+
- CHANGELOG.md
|
65
68
|
- Gemfile
|
66
69
|
- LICENSE.txt
|
67
70
|
- README.md
|
@@ -69,8 +72,10 @@ files:
|
|
69
72
|
- finite_machine.gemspec
|
70
73
|
- lib/finite_machine.rb
|
71
74
|
- lib/finite_machine/callable.rb
|
75
|
+
- lib/finite_machine/catchable.rb
|
72
76
|
- lib/finite_machine/dsl.rb
|
73
77
|
- lib/finite_machine/event.rb
|
78
|
+
- lib/finite_machine/hooks.rb
|
74
79
|
- lib/finite_machine/observer.rb
|
75
80
|
- lib/finite_machine/state_machine.rb
|
76
81
|
- lib/finite_machine/subscribers.rb
|
@@ -78,17 +83,20 @@ files:
|
|
78
83
|
- lib/finite_machine/transition.rb
|
79
84
|
- lib/finite_machine/version.rb
|
80
85
|
- spec/spec_helper.rb
|
86
|
+
- spec/unit/callable/call_spec.rb
|
81
87
|
- spec/unit/callbacks_spec.rb
|
82
88
|
- spec/unit/can_spec.rb
|
83
89
|
- spec/unit/define_spec.rb
|
84
90
|
- spec/unit/events_spec.rb
|
85
91
|
- spec/unit/finished_spec.rb
|
92
|
+
- spec/unit/handlers_spec.rb
|
86
93
|
- spec/unit/if_unless_spec.rb
|
87
94
|
- spec/unit/initialize_spec.rb
|
88
95
|
- spec/unit/is_spec.rb
|
89
96
|
- spec/unit/states_spec.rb
|
97
|
+
- spec/unit/target_spec.rb
|
90
98
|
- spec/unit/transition/parse_states_spec.rb
|
91
|
-
homepage:
|
99
|
+
homepage: https://github.com/peter-murach/finite_machine
|
92
100
|
licenses:
|
93
101
|
- MIT
|
94
102
|
metadata: {}
|
@@ -114,13 +122,16 @@ specification_version: 4
|
|
114
122
|
summary: A minimal finite state machine with a straightforward syntax.
|
115
123
|
test_files:
|
116
124
|
- spec/spec_helper.rb
|
125
|
+
- spec/unit/callable/call_spec.rb
|
117
126
|
- spec/unit/callbacks_spec.rb
|
118
127
|
- spec/unit/can_spec.rb
|
119
128
|
- spec/unit/define_spec.rb
|
120
129
|
- spec/unit/events_spec.rb
|
121
130
|
- spec/unit/finished_spec.rb
|
131
|
+
- spec/unit/handlers_spec.rb
|
122
132
|
- spec/unit/if_unless_spec.rb
|
123
133
|
- spec/unit/initialize_spec.rb
|
124
134
|
- spec/unit/is_spec.rb
|
125
135
|
- spec/unit/states_spec.rb
|
136
|
+
- spec/unit/target_spec.rb
|
126
137
|
- spec/unit/transition/parse_states_spec.rb
|