end_state 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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