end_state 0.0.2 → 0.1.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: 809272ea728fbfd921f3cf13974883104447861e
4
- data.tar.gz: 9660e13251dccad1d37fa22c5e5bfdf7cbe917ad
3
+ metadata.gz: b1d64ebcbb974d3190a824a6cf1c77742c009339
4
+ data.tar.gz: 6acbeb81100f52bf97956a2bcbe7c5680bf02cba
5
5
  SHA512:
6
- metadata.gz: 6f452a250ae2e681e53b79902312903ab8a98ad19c2cf37d72748af0208f6405170cb8b1470c96b6ce7e0e6ef84bfd04f424af9d5bea60633a789758c0c132a0
7
- data.tar.gz: 3103761a9adc01570e0d4788a961dc594d72b706eacc34f99a69d15e1a5df52c4a3aa64ba3035140840a63a60de3dd42941990669152f667ab80a8409790fba9
6
+ metadata.gz: 16a4bc9c85e10fce41a81659aa9bb70b182a3378030e2531f4b5077c2749cda53de5dec0b49e94862d6d71aaf698c047c5b5532dffd0079b5fc7dd459a4caf56
7
+ data.tar.gz: 953922dc24b1bccd281564ee4769d515e45ef4fa4cd30d28c945368e2bba89defb92c6da22464ff8f83ecbaa96d5b1ec0b0bd689c32c8acd8f18d4e229d01f95
data/README.md CHANGED
@@ -29,7 +29,7 @@ Create a state machine by subclassing `EndState::StateMachine`.
29
29
 
30
30
  ```ruby
31
31
  class Machine < EndState::StateMachine
32
- transition a: :b
32
+ transition a: :b, as: :go
33
33
  transition b: :c
34
34
  transition [:b, :c] => :a
35
35
  end
@@ -58,6 +58,8 @@ machine.can_transition? :a # => true
58
58
  machine.b! # => false
59
59
  machine.a! # => true
60
60
  machine.state # => :a
61
+ machine.go! # => :true
62
+ machine.state # => :b
61
63
  ```
62
64
 
63
65
  ## Guards
@@ -66,7 +68,7 @@ Guards can be created by subclassing `EndState::Guard`. Your class will be provi
66
68
 
67
69
  * `object` - The wrapped object.
68
70
  * `state` - The desired state.
69
- * `params` - A hash of params as set in the transition definition.
71
+ * `params` - A hash of params passed when calling transition on the machine.
70
72
 
71
73
  Your class should implement the `will_allow?` method which must return true or false.
72
74
 
@@ -95,7 +97,7 @@ A guard can be added to the transition definition:
95
97
  class Machine < EndState::StateMachine
96
98
  transition a: :b do |t|
97
99
  t.guard EasyGuard
98
- t.guard SomeOtherGuard, option1: 'Some Option', option2: 'Some Other Option'
100
+ t.guard SomeOtherGuard
99
101
  end
100
102
  end
101
103
  ```
@@ -106,7 +108,7 @@ Finalizers can be created by subclassing `EndState::Finalizer`. Your class will
106
108
 
107
109
  * `object` - The wrapped object that has been transitioned.
108
110
  * `state` - The previous state.
109
- * `params` - A hash of params as set in the transition definition.
111
+ * `params` - A hash of params passed when calling transition on the machine.
110
112
 
111
113
  Your class should implement the `call` method which should return true or false as to whether it was successful or not.
112
114
 
@@ -116,7 +118,7 @@ set up a little differently and you have access to:
116
118
 
117
119
  * `object` - The wrapped object that has been rolled back.
118
120
  * `state` - The attempted desired state.
119
- * `params` - A hash of params as set in the transition definition.
121
+ * `params` - A hash of params passed when calling transition on the machine.
120
122
 
121
123
  The wrapped object has an array `failure_messages` available for tracking reasons for invalid transitions. You may shovel
122
124
  a reason (string) into this if you want to provide information on why your finalizer failed.
@@ -139,7 +141,7 @@ A finalizer can be added to the transition definition:
139
141
  ```ruby
140
142
  class Machine < EndState::StateMachine
141
143
  transition a: :b do |t|
142
- t.finalizer WrapUp, option1: 'Some Option', option2: 'Some Other Option'
144
+ t.finalizer WrapUp
143
145
  end
144
146
  end
145
147
  ```
@@ -2,19 +2,28 @@ module EndState
2
2
  class StateMachine < SimpleDelegator
3
3
  attr_accessor :failure_messages
4
4
 
5
- def self.transition(state_map, &block)
5
+ def self.transition(state_map)
6
+ initial_states = Array(state_map.keys.first)
6
7
  final_state = state_map.values.first
8
+ transition_alias = state_map[:as] if state_map.keys.length > 1
7
9
  transition = Transition.new(final_state)
8
- Array(state_map.keys.first).each do |state|
10
+ initial_states.each do |state|
9
11
  transitions[{ state => final_state }] = transition
10
12
  end
11
- yield transition if block
13
+ unless transition_alias.nil?
14
+ aliases[transition_alias] = final_state
15
+ end
16
+ yield transition if block_given?
12
17
  end
13
18
 
14
19
  def self.transitions
15
20
  @transitions ||= {}
16
21
  end
17
22
 
23
+ def self.aliases
24
+ @aliases ||= {}
25
+ end
26
+
18
27
  def self.state_attribute(attribute)
19
28
  define_method(:state) { send(attribute.to_sym) }
20
29
  define_method(:state=) { |val| send("#{attribute}=".to_sym, val) }
@@ -32,6 +41,11 @@ module EndState
32
41
  transitions.keys.map { |state_map| state_map.values.first }.uniq
33
42
  end
34
43
 
44
+ def self.transition_state_for(check_state)
45
+ return check_state if states.include? check_state
46
+ return aliases[check_state] if aliases.keys.include? check_state
47
+ end
48
+
35
49
  def object
36
50
  __getobj__
37
51
  end
@@ -43,28 +57,29 @@ module EndState
43
57
  transition.will_allow? state
44
58
  end
45
59
 
46
- def transition(state, mode = :soft)
60
+ def transition(state, params = {}, mode = :soft)
47
61
  @failure_messages = []
48
62
  previous_state = self.state
49
63
  transition = self.class.transitions[{ previous_state => state }]
50
64
  return block_transistion(transition, state, mode) unless transition
51
- return guard_failed(state, mode) unless transition.allowed?(self)
65
+ return guard_failed(state, mode) unless transition.allowed?(self, params)
52
66
  return false unless transition.action.new(self, state).call
53
- return finalize_failed(state, mode) unless transition.finalize(self, previous_state)
67
+ return finalize_failed(state, mode) unless transition.finalize(self, previous_state, params)
54
68
  true
55
69
  end
56
70
 
57
- def transition!(state)
58
- transition state, :hard
71
+ def transition!(state, params = {})
72
+ transition state, params, :hard
59
73
  end
60
74
 
61
75
  def method_missing(method, *args, &block)
62
76
  check_state = method.to_s[0..-2].to_sym
63
- return super unless self.class.states.include? check_state
77
+ check_state = self.class.transition_state_for(check_state)
78
+ return super if check_state.nil?
64
79
  if method.to_s.end_with?('?')
65
80
  state == check_state
66
81
  elsif method.to_s.end_with?('!')
67
- transition check_state
82
+ transition check_state, args[0]
68
83
  else
69
84
  super
70
85
  end
@@ -10,18 +10,18 @@ module EndState
10
10
  @finalizers = []
11
11
  end
12
12
 
13
- def allowed?(object)
14
- guards.all? { |guard| guard[:guard].new(object, state, guard[:params]).allowed? }
13
+ def allowed?(object, params={})
14
+ guards.all? { |guard| guard.new(object, state, params).allowed? }
15
15
  end
16
16
 
17
- def will_allow?(object)
18
- guards.all? { |guard| guard[:guard].new(object, state, guard[:params]).will_allow? }
17
+ def will_allow?(object, params={})
18
+ guards.all? { |guard| guard.new(object, state, params).will_allow? }
19
19
  end
20
20
 
21
- def finalize(object, previous_state)
21
+ def finalize(object, previous_state, params={})
22
22
  finalizers.each_with_object([]) do |finalizer, finalized|
23
23
  finalized << finalizer
24
- return rollback(finalized, object, previous_state) unless run_finalizer(finalizer, object, state)
24
+ return rollback(finalized, object, previous_state, params) unless run_finalizer(finalizer, object, state, params)
25
25
  end
26
26
  true
27
27
  end
@@ -30,12 +30,12 @@ module EndState
30
30
  @action = action
31
31
  end
32
32
 
33
- def guard(guard, params = {})
34
- guards << { guard: guard, params: params }
33
+ def guard(guard)
34
+ guards << guard
35
35
  end
36
36
 
37
- def finalizer(finalizer, params = {})
38
- finalizers << { finalizer: finalizer, params: params }
37
+ def finalizer(finalizer)
38
+ finalizers << finalizer
39
39
  end
40
40
 
41
41
  def persistence_on
@@ -44,14 +44,14 @@ module EndState
44
44
 
45
45
  private
46
46
 
47
- def rollback(finalized, object, previous_state)
47
+ def rollback(finalized, object, previous_state, params)
48
48
  action.new(object, previous_state).rollback
49
- finalized.reverse.each { |f| f[:finalizer].new(object, state, f[:params]).rollback }
49
+ finalized.reverse.each { |finalizer| finalizer.new(object, state, params).rollback }
50
50
  false
51
51
  end
52
52
 
53
- def run_finalizer(finalizer, object, state)
54
- finalizer[:finalizer].new(object, state, finalizer[:params]).call
53
+ def run_finalizer(finalizer, object, state, params)
54
+ finalizer.new(object, state, params).call
55
55
  end
56
56
  end
57
57
  end
@@ -1,3 +1,3 @@
1
1
  module EndState
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -63,7 +63,7 @@ module EndStateMatchers
63
63
  def verify_guards
64
64
  result = true
65
65
  guards.each do |guard|
66
- unless machine.transitions[transition].guards.any? { |g| g[:guard] == guard }
66
+ unless machine.transitions[transition].guards.any? { |g| g == guard }
67
67
  failure_messages << "expected that transition :#{transition.keys.first} => :#{transition.values.first} would have guard #{guard.name}"
68
68
  result = false
69
69
  end
@@ -74,7 +74,7 @@ module EndStateMatchers
74
74
  def verify_finalizers
75
75
  result = true
76
76
  finalizers.each do |finalizer|
77
- unless machine.transitions[transition].finalizers.any? { |f| f[:finalizer] == finalizer }
77
+ unless machine.transitions[transition].finalizers.any? { |f| f == finalizer }
78
78
  failure_messages << "expected that transition :#{transition.keys.first} => :#{transition.values.first} would have finalizer #{finalizer.name}"
79
79
  result = false
80
80
  end
@@ -5,7 +5,10 @@ module EndState
5
5
  describe StateMachine do
6
6
  subject(:machine) { StateMachine.new(object) }
7
7
  let(:object) { OpenStruct.new(state: nil) }
8
- before { StateMachine.instance_variable_set '@transitions'.to_sym, nil }
8
+ before do
9
+ StateMachine.instance_variable_set '@transitions'.to_sym, nil
10
+ StateMachine.instance_variable_set '@aliases'.to_sym, nil
11
+ end
9
12
 
10
13
  describe '.transition' do
11
14
  let(:state_map) { { a: :b } }
@@ -23,6 +26,13 @@ module EndState
23
26
  it 'adds the transition to the state machine' do
24
27
  expect(StateMachine.transitions[state_map]).to eq yielded.transition
25
28
  end
29
+
30
+ context 'when the :as option is used' do
31
+ it 'creates an alias' do
32
+ StateMachine.transition(state_map.merge(as: :go))
33
+ expect(StateMachine.aliases[:go]).to eq :b
34
+ end
35
+ end
26
36
  end
27
37
 
28
38
  describe '.state_attribute' do
@@ -111,13 +121,24 @@ module EndState
111
121
  describe '#{state}!' do
112
122
  let(:object) { OpenStruct.new(state: :a) }
113
123
  before do
114
- StateMachine.transition a: :b
124
+ StateMachine.transition a: :b, as: :go
115
125
  end
116
126
 
117
127
  it 'transitions the state' do
118
128
  machine.b!
119
129
  expect(machine.state).to eq :b
120
130
  end
131
+
132
+ it 'accepts params' do
133
+ machine.stub(:transition)
134
+ machine.b! foo: 'bar', bar: 'foo'
135
+ expect(machine).to have_received(:transition).with(:b, { foo: 'bar', bar: 'foo' })
136
+ end
137
+
138
+ it 'works with an alias' do
139
+ machine.go!
140
+ expect(machine.state).to eq :b
141
+ end
121
142
  end
122
143
 
123
144
  describe '#can_transition?' do
@@ -167,8 +188,9 @@ module EndState
167
188
  let(:guard) { double :guard, new: guard_instance }
168
189
  let(:guard_instance) { double :guard_instance, allowed?: nil }
169
190
  before do
170
- StateMachine.transition a: :b
171
- StateMachine.transitions[{ a: :b }].guards << { guard: guard, params: {} }
191
+ StateMachine.transition a: :b do |transition|
192
+ transition.guard guard
193
+ end
172
194
  end
173
195
 
174
196
  context 'and the object satisfies the guard' do
@@ -194,19 +216,28 @@ module EndState
194
216
  expect(object.state).to eq :a
195
217
  end
196
218
  end
219
+
220
+ context 'and params are passed in' do
221
+ let(:params) { { foo: 'bar' } }
222
+ it 'sends the guard the params' do
223
+ machine.transition :b, params, :soft
224
+ expect(guard).to have_received(:new).with(machine, :b, params)
225
+ end
226
+ end
197
227
  end
198
228
 
199
229
  context 'and a finalizer is configured' do
230
+ let(:finalizer) { double :finalizer, new: finalizer_instance }
231
+ let(:finalizer_instance) { double :finalizer_instance, call: nil, rollback: nil }
200
232
  before do
201
233
  StateMachine.transition a: :b do |transition|
202
- transition.persistence_on
234
+ transition.finalizer finalizer
203
235
  end
204
236
  end
205
237
 
206
238
  context 'and the finalizer is successful' do
207
239
  before do
208
- object.state = :a
209
- object.stub(:save).and_return(true)
240
+ finalizer_instance.stub(:call).and_return(true)
210
241
  end
211
242
 
212
243
  it 'transitions the state' do
@@ -217,8 +248,7 @@ module EndState
217
248
 
218
249
  context 'and the finalizer fails' do
219
250
  before do
220
- object.state = :a
221
- object.stub(:save).and_return(false)
251
+ finalizer_instance.stub(:call).and_return(false)
222
252
  end
223
253
 
224
254
  it 'does not transition the state' do
@@ -262,7 +292,7 @@ module EndState
262
292
  let(:guard_instance) { double :guard_instance, allowed?: nil }
263
293
  before do
264
294
  StateMachine.transition a: :b
265
- StateMachine.transitions[{ a: :b }].guards << { guard: guard, params: {} }
295
+ StateMachine.transitions[{ a: :b }].guards << guard
266
296
  end
267
297
 
268
298
  context 'and the object satisfies the guard' do
@@ -291,16 +321,17 @@ module EndState
291
321
  end
292
322
 
293
323
  context 'and a finalizer is configured' do
324
+ let(:finalizer) { double :finalizer, new: finalizer_instance }
325
+ let(:finalizer_instance) { double :finalizer_instance, call: nil, rollback: nil }
294
326
  before do
295
327
  StateMachine.transition a: :b do |transition|
296
- transition.persistence_on
328
+ transition.finalizer finalizer
297
329
  end
298
330
  end
299
331
 
300
332
  context 'and the finalizer is successful' do
301
333
  before do
302
- object.state = :a
303
- object.stub(:save).and_return(true)
334
+ finalizer_instance.stub(:call).and_return(true)
304
335
  end
305
336
 
306
337
  it 'transitions the state' do
@@ -311,8 +342,7 @@ module EndState
311
342
 
312
343
  context 'and the finalizer fails' do
313
344
  before do
314
- object.state = :a
315
- object.stub(:save).and_return(false)
345
+ finalizer_instance.stub(:call).and_return(false)
316
346
  end
317
347
 
318
348
  it 'does not transition the state' do
@@ -21,54 +21,58 @@ module EndState
21
21
  it 'adds a guard' do
22
22
  expect { transition.guard guard }.to change(transition.guards, :count).by(1)
23
23
  end
24
-
25
- context 'when params are provided' do
26
- let(:params) { {} }
27
-
28
- it 'adds a guard' do
29
- expect { transition.guard guard, params }.to change(transition.guards, :count).by(1)
30
- end
31
- end
32
24
  end
33
25
 
34
26
  describe '#allowed?' do
35
27
  let(:guard) { double :guard, new: guard_instance }
36
28
  let(:guard_instance) { double :guard_instance, allowed?: nil }
37
- before { transition.guards << { guard: guard, params: {} } }
29
+ let(:object) { double :object }
30
+ before { transition.guards << guard }
38
31
 
39
32
  context 'when all guards pass' do
40
- let(:object) { double :object }
41
33
  before { guard_instance.stub(:allowed?).and_return(true) }
42
34
 
43
35
  specify { expect(transition.allowed? object).to be_true }
44
36
  end
45
37
 
46
38
  context 'when not all guards pass' do
47
- let(:object) { double :object }
48
39
  before { guard_instance.stub(:allowed?).and_return(false) }
49
40
 
50
41
  specify { expect(transition.allowed? object).to be_false }
51
42
  end
43
+
44
+ context 'when params are provided' do
45
+ it 'creates the guard with the params' do
46
+ transition.allowed? object, { foo: 'bar' }
47
+ expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
48
+ end
49
+ end
52
50
  end
53
51
 
54
52
  describe '#will_allow?' do
55
53
  let(:guard) { double :guard, new: guard_instance }
56
54
  let(:guard_instance) { double :guard_instance, will_allow?: nil }
57
- before { transition.guards << { guard: guard, params: {} } }
55
+ let(:object) { double :object }
56
+ before { transition.guards << guard }
58
57
 
59
58
  context 'when all guards pass' do
60
- let(:object) { double :object }
61
59
  before { guard_instance.stub(:will_allow?).and_return(true) }
62
60
 
63
61
  specify { expect(transition.will_allow? object).to be_true }
64
62
  end
65
63
 
66
64
  context 'when not all guards pass' do
67
- let(:object) { double :object }
68
65
  before { guard_instance.stub(:will_allow?).and_return(false) }
69
66
 
70
67
  specify { expect(transition.will_allow? object).to be_false }
71
68
  end
69
+
70
+ context 'when params are provided' do
71
+ it 'creates the guard with the params' do
72
+ transition.will_allow? object, { foo: 'bar' }
73
+ expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
74
+ end
75
+ end
72
76
  end
73
77
 
74
78
  describe '#finalizer' do
@@ -77,14 +81,6 @@ module EndState
77
81
  it 'adds a finalizer' do
78
82
  expect { transition.finalizer finalizer }.to change(transition.finalizers, :count).by(1)
79
83
  end
80
-
81
- context 'when params are provided' do
82
- let(:params) { {} }
83
-
84
- it 'adds a finalizer' do
85
- expect { transition.finalizer finalizer, params }.to change(transition.finalizers, :count).by(1)
86
- end
87
- end
88
84
  end
89
85
 
90
86
  describe '#persistence_on' do
@@ -97,7 +93,7 @@ module EndState
97
93
  let(:finalizer) { double :finalizer, new: finalizer_instance }
98
94
  let(:finalizer_instance) { double :finalizer_instance, call: nil, rollback: nil }
99
95
  let(:object) { OpenStruct.new(state: :b) }
100
- before { transition.finalizers << { finalizer: finalizer, params: {} } }
96
+ before { transition.finalizers << finalizer }
101
97
 
102
98
  context 'when all finalizers succeed' do
103
99
  before { finalizer_instance.stub(:call).and_return(true) }
@@ -115,6 +111,13 @@ module EndState
115
111
  expect(finalizer_instance).to have_received(:rollback)
116
112
  end
117
113
  end
114
+
115
+ context 'when params are provided' do
116
+ it 'creates a finalizer with the params' do
117
+ transition.finalize object, :b, { foo: 'bar' }
118
+ expect(finalizer).to have_received(:new).twice.with(object, :a, { foo: 'bar'} )
119
+ end
120
+ end
118
121
  end
119
122
  end
120
123
  end
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.0.2
4
+ version: 0.1.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-04-17 00:00:00.000000000 Z
11
+ date: 2014-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler