end_state 0.3.2 → 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/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
|