end_state 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -3
- data/lib/end_state/errors.rb +1 -0
- data/lib/end_state/state_machine.rb +31 -2
- data/lib/end_state/version.rb +1 -1
- data/spec/end_state/state_machine_spec.rb +36 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56937726694a33d9ac097f1f224d5602bb9600c3
|
4
|
+
data.tar.gz: 6f3e4fcf53831c33e11e4fdbac2485a1d6fecd3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c2afcefdfaaf9de627121c7e0d13494f4ca632718d79f82c55ee847d935cda7a56ddbf0fefbb4591c88d823d0fee1ba813010ac7631e1fa8b47a48d765ce56c
|
7
|
+
data.tar.gz: 3503000ac420f5746ab0a3389966151d5a1d7a10aefb5e4a438180fd7ab6017cbe5362bc551ce56d6def5c5cd1dccf2cc554c74cfcba3b09c684d46cb315b1bd
|
data/README.md
CHANGED
@@ -62,6 +62,17 @@ machine.go! # => :true
|
|
62
62
|
machine.state # => :b
|
63
63
|
```
|
64
64
|
|
65
|
+
## Initial State
|
66
|
+
|
67
|
+
If you wrap an object that currently has `nil` as the state, the state will be set to `:__nil__`.
|
68
|
+
You can change this using the `set_initial_state` method.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class Machine < EndState::StateMachine
|
72
|
+
set_initial_state :first
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
65
76
|
## Guards
|
66
77
|
|
67
78
|
Guards can be created by subclassing `EndState::Guard`. Your class will be provided access to:
|
@@ -223,7 +234,7 @@ machine.failure_messages # => ['Cannot go!']
|
|
223
234
|
|
224
235
|
You may want to use an attribute other than `state` to track the state of the machine.
|
225
236
|
|
226
|
-
```
|
237
|
+
```ruby
|
227
238
|
class Machine < EndState::StateMachine
|
228
239
|
state_attribute :status
|
229
240
|
end
|
@@ -232,7 +243,7 @@ end
|
|
232
243
|
Depending on how you persist the `state` (if at all) you may want what is stored in `state` to be a string instead
|
233
244
|
of a symbol. You can tell the machine this preference.
|
234
245
|
|
235
|
-
```
|
246
|
+
```ruby
|
236
247
|
class Machine < EndState::StateMachine
|
237
248
|
store_states_as_strings!
|
238
249
|
end
|
@@ -240,13 +251,21 @@ end
|
|
240
251
|
|
241
252
|
## Exceptions for failing Transitions
|
242
253
|
|
243
|
-
By default `transition` will only raise an
|
254
|
+
By default `transition` will only raise an error, `EndState::UnknownState`, if called with a state that doesn't exist.
|
244
255
|
All other failures, such as missing transition, guard failure, or finalizer failure will silently just return `false` and not
|
245
256
|
transition to the new state.
|
246
257
|
|
247
258
|
You also have the option to use `transition!` which will instead raise an error for failures. If your guards and/or finalizers
|
248
259
|
add to the `failure_messages` array then they will be included in the error message.
|
249
260
|
|
261
|
+
Additionally, if you would like to treat all transitions as hard and raise an error you can set that in the machine definition.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
class Machine < EndState::StateMachine
|
265
|
+
treat_all_transitions_as_hard!
|
266
|
+
end
|
267
|
+
```
|
268
|
+
|
250
269
|
## Graphing
|
251
270
|
|
252
271
|
If you install `GraphViz` and the gem `ruby-graphviz` you can create images representing your state machines.
|
data/lib/end_state/errors.rb
CHANGED
@@ -2,6 +2,30 @@ module EndState
|
|
2
2
|
class StateMachine < SimpleDelegator
|
3
3
|
attr_accessor :failure_messages, :success_messages
|
4
4
|
|
5
|
+
def initialize(object)
|
6
|
+
super
|
7
|
+
Action.new(self, self.class.initial_state).call if self.state.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
@initial_state = :__nil__
|
11
|
+
@mode = :soft
|
12
|
+
|
13
|
+
def self.initial_state
|
14
|
+
@initial_state
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.set_initial_state(state)
|
18
|
+
@initial_state = state.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.treat_all_transitions_as_hard!
|
22
|
+
@mode = :hard
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.mode
|
26
|
+
@mode
|
27
|
+
end
|
28
|
+
|
5
29
|
def self.store_states_as_strings!
|
6
30
|
@store_states_as_strings = true
|
7
31
|
end
|
@@ -61,7 +85,7 @@ module EndState
|
|
61
85
|
transition.will_allow? state, params
|
62
86
|
end
|
63
87
|
|
64
|
-
def transition(state, params = {}, mode =
|
88
|
+
def transition(state, params = {}, mode = self.class.mode)
|
65
89
|
@failure_messages = []
|
66
90
|
@success_messages = []
|
67
91
|
previous_state = self.state ? self.state.to_sym : self.state
|
@@ -80,10 +104,10 @@ module EndState
|
|
80
104
|
|
81
105
|
def method_missing(method, *args, &block)
|
82
106
|
check_state = method.to_s[0..-2].to_sym
|
107
|
+
return super unless is_state_or_event?(check_state)
|
83
108
|
return current_state?(check_state) if method.to_s.end_with?('?')
|
84
109
|
check_state = state_for_event(check_state) || check_state
|
85
110
|
return false if check_state == :__invalid_event__
|
86
|
-
return super unless self.class.states.include?(check_state)
|
87
111
|
if method.to_s.end_with?('!')
|
88
112
|
transition check_state, args[0]
|
89
113
|
else
|
@@ -93,6 +117,10 @@ module EndState
|
|
93
117
|
|
94
118
|
private
|
95
119
|
|
120
|
+
def is_state_or_event?(check_state)
|
121
|
+
self.class.states.include?(check_state) or self.class.events[check_state]
|
122
|
+
end
|
123
|
+
|
96
124
|
def current_state?(check_state)
|
97
125
|
state.to_sym == check_state
|
98
126
|
end
|
@@ -105,6 +133,7 @@ module EndState
|
|
105
133
|
end
|
106
134
|
|
107
135
|
def invalid_event(event)
|
136
|
+
fail InvalidEvent, "Transition by event: #{event} is invalid." if self.class.mode == :hard
|
108
137
|
message = self.class.transitions[self.class.events[event].first].blocked_event_message
|
109
138
|
@failure_messages = [message] if message
|
110
139
|
:__invalid_event__
|
data/lib/end_state/version.rb
CHANGED
@@ -9,6 +9,8 @@ module EndState
|
|
9
9
|
StateMachine.instance_variable_set '@transitions'.to_sym, nil
|
10
10
|
StateMachine.instance_variable_set '@events'.to_sym, nil
|
11
11
|
StateMachine.instance_variable_set '@store_states_as_strings'.to_sym, nil
|
12
|
+
StateMachine.instance_variable_set '@initial_state'.to_sym, :__nil__
|
13
|
+
StateMachine.instance_variable_set '@mode'.to_sym, :soft
|
12
14
|
end
|
13
15
|
|
14
16
|
describe '.transition' do
|
@@ -97,7 +99,21 @@ module EndState
|
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
102
|
+
describe '.initial_state' do
|
103
|
+
context 'when set to :first' do
|
104
|
+
before { StateMachine.set_initial_state :first }
|
105
|
+
|
106
|
+
it 'has that initial state' do
|
107
|
+
expect(machine.state).to eq :first
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
100
112
|
describe '#state' do
|
113
|
+
context 'when there is no state set' do
|
114
|
+
specify { expect(machine.state).to eq :__nil__ }
|
115
|
+
end
|
116
|
+
|
101
117
|
context 'when the object has state :a' do
|
102
118
|
let(:object) { OpenStruct.new(state: :a) }
|
103
119
|
|
@@ -140,6 +156,18 @@ module EndState
|
|
140
156
|
specify { expect(machine.stop?).to be_true }
|
141
157
|
end
|
142
158
|
end
|
159
|
+
|
160
|
+
context 'when the requested predicate is not a state or event' do
|
161
|
+
module Predicate
|
162
|
+
def foo? ; :foo ; end
|
163
|
+
end
|
164
|
+
|
165
|
+
before { object.extend(Predicate) }
|
166
|
+
|
167
|
+
it 'call super up to SimpleDelegator, which handles the method' do
|
168
|
+
expect(machine.foo?).to eq(:foo)
|
169
|
+
end
|
170
|
+
end
|
143
171
|
end
|
144
172
|
|
145
173
|
describe '#{state}!' do
|
@@ -178,6 +206,14 @@ module EndState
|
|
178
206
|
machine.go!
|
179
207
|
expect(machine.failure_messages).to eq ['Invalid event!']
|
180
208
|
end
|
209
|
+
|
210
|
+
context 'and all transitions are forced to run in :hard mode' do
|
211
|
+
before { machine.class.treat_all_transitions_as_hard! }
|
212
|
+
|
213
|
+
it 'raises an InvalidEvent error' do
|
214
|
+
expect { machine.go! }.to raise_error(InvalidEvent)
|
215
|
+
end
|
216
|
+
end
|
181
217
|
end
|
182
218
|
end
|
183
219
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: end_state
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alexpeachey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|