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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c199657701bb9dec215e09e17d5e3e27d1d95d3e
4
- data.tar.gz: 240681ba88cc834317071327063867828e994af0
3
+ metadata.gz: 56937726694a33d9ac097f1f224d5602bb9600c3
4
+ data.tar.gz: 6f3e4fcf53831c33e11e4fdbac2485a1d6fecd3a
5
5
  SHA512:
6
- metadata.gz: 0728b393e7550ad3aa5833854b36c0c84089556c9e798c3ef9c9ac627989d48f4064225c9eedf8770bbdcc818f5991f37646ba3eaa4e784378cb0acebe37dd27
7
- data.tar.gz: 5b4b5f3fd214558f7a1529015f23cea0ca12de44dc5c4c23663013d6cce49582c86c907dd5afda5816251a03430fc6861d78be36daf117e72fa743cca6042dd5
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 exception, `EndState::UnknownState`, if called with a state that doesn't exist.
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.
@@ -2,6 +2,7 @@ module EndState
2
2
  class Error < StandardError; end
3
3
  class UnknownState < Error; end
4
4
  class UnknownTransition < Error; end
5
+ class InvalidEvent < Error; end
5
6
  class GuardFailed < Error; end
6
7
  class FinalizerFailed < Error; end
7
8
  end
@@ -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 = :soft)
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__
@@ -1,3 +1,3 @@
1
1
  module EndState
2
- VERSION = '0.3.2'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -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.3.2
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-03 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler