end_state 0.9.0 → 0.10.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: 51e9a6a8a317af60132c723aabd5a615ec93f4e3
4
- data.tar.gz: 96a3350ec5237b631314c8b5ab574598eb47b219
3
+ metadata.gz: c3fceb0c3ef5dd0efcf27176e899e75ad55f1243
4
+ data.tar.gz: 600cc39bea6aeca957285707a280c29b07092ad1
5
5
  SHA512:
6
- metadata.gz: 29e1d05f5fe9da4df8f33783fde04cb1fd9b0f458ee79cfa9d0c728c003e29d62526259fc01b5e0240613583248f4987f9f3883d154957d39477ee2ed895a1e9
7
- data.tar.gz: 1c82abe3ebcd9475e20865a77559a596d8bbfd718717a011cfa14d8d1938417ebdc57e6faa420a2fc3cb0fa54ad7484cfbad33bc84dfcde47c092bc29f53db5d
6
+ metadata.gz: 05317e5548b4924e211f422a2a9c4eb6f6ddcc872f4fdff91828acf27768cde90ec608f10c35d50b098576e797a6c2ec4ae9a8a010fd9046c09c0e4a01e282f5
7
+ data.tar.gz: d3432693ec760a0ea06ba9790f075a3837571b0b53518b06f781ccaa9654832ff96b875eea229afb5ffe147fd1fbdc25bb9be93b4a6ea250552695c0cf3f7b2f
@@ -16,13 +16,14 @@ end
16
16
 
17
17
  class Machine < EndState::StateMachine
18
18
  transition a: :b do |t|
19
+ t.require_params :some_param
19
20
  t.guard Easy
20
21
  t.concluder NoOp
21
22
  end
22
23
  end
23
24
 
24
25
  describe Machine do
25
- specify { expect(Machine).to have_transition(a: :b).with_guard(Easy).with_concluder(NoOp) }
26
+ specify { expect(Machine).to have_transition(a: :b).with_guard(Easy).with_concluder(NoOp).with_required_params(:some_param) }
26
27
  specify { expect(Machine).to have_transition(a: :b).with_guards(Easy, Easy).with_concluders(NoOp, NoOp) }
27
28
  specify { expect(Machine).not_to have_transition(a: :c) }
28
29
  end
@@ -80,7 +80,7 @@ module EndState
80
80
  def can_transition?(state, params = {})
81
81
  previous_state = self.state.to_sym
82
82
  state = state.to_sym
83
- transition = self.class.transitions[{ previous_state => state }]
83
+ transition = transition_for(previous_state, state)
84
84
  return block_transistion(transition, state, :soft) unless transition
85
85
  transition.will_allow? state, params
86
86
  end
@@ -90,7 +90,7 @@ module EndState
90
90
  @success_messages = []
91
91
  previous_state = self.state ? self.state.to_sym : self.state
92
92
  state = state.to_sym
93
- transition = self.class.transitions[{ previous_state => state }]
93
+ transition = transition_for(previous_state, state)
94
94
  return block_transistion(transition, state, mode) unless transition
95
95
  return guard_failed(state, mode) unless transition.allowed?(self, params)
96
96
  return false unless transition.action.new(self, state).call
@@ -109,7 +109,7 @@ module EndState
109
109
  check_state = state_for_event(check_state) || check_state
110
110
  return false if check_state == :__invalid_event__
111
111
  if method.to_s.end_with?('!')
112
- transition check_state, args[0]
112
+ transition check_state, (args[0] || {})
113
113
  else
114
114
  super
115
115
  end
@@ -132,6 +132,11 @@ module EndState
132
132
  transitions.first.values.first
133
133
  end
134
134
 
135
+ def transition_for(from, to)
136
+ self.class.transitions[{ from => to }] ||
137
+ self.class.transitions[{ any_state: to }]
138
+ end
139
+
135
140
  def invalid_event(event)
136
141
  fail InvalidEvent, "Transition by event: #{event} is invalid." if self.class.mode == :hard
137
142
  message = self.class.transitions[self.class.events[event].first].blocked_event_message
@@ -1,20 +1,24 @@
1
1
  module EndState
2
2
  class Transition
3
3
  attr_reader :state, :blocked_event_message
4
- attr_accessor :action, :guards, :concluders
4
+ attr_accessor :action, :guards, :concluders, :allowed_params, :required_params
5
5
 
6
6
  def initialize(state)
7
7
  @state = state
8
8
  @action = Action
9
9
  @guards = []
10
10
  @concluders = []
11
+ @allowed_params = []
12
+ @required_params = []
11
13
  end
12
14
 
13
15
  def allowed?(object, params={})
16
+ raise "Missing params: #{missing_params(params).join(',')}" unless missing_params(params).empty?
14
17
  guards.all? { |guard| guard.new(object, state, params).allowed? }
15
18
  end
16
19
 
17
20
  def will_allow?(object, params={})
21
+ return false unless missing_params(params).empty?
18
22
  guards.all? { |guard| guard.new(object, state, params).will_allow? }
19
23
  end
20
24
 
@@ -30,18 +34,31 @@ module EndState
30
34
  @action = action
31
35
  end
32
36
 
33
- def guard(guard)
34
- guards << guard
37
+ def guard(*guards)
38
+ Array(guards).flatten.each { |guard| self.guards << guard }
35
39
  end
36
40
 
37
- def concluder(concluder)
38
- concluders << concluder
41
+ def concluder(*concluders)
42
+ Array(concluders).flatten.each { |concluder| self.concluders << concluder }
39
43
  end
40
44
 
41
45
  def persistence_on
42
46
  concluder Concluders::Persistence
43
47
  end
44
48
 
49
+ def allow_params(*params)
50
+ Array(params).flatten.each do |param|
51
+ self.allowed_params << param unless self.allowed_params.include? param
52
+ end
53
+ end
54
+
55
+ def require_params(*params)
56
+ Array(params).flatten.each do |param|
57
+ self.allowed_params << param unless self.allowed_params.include? param
58
+ self.required_params << param unless self.required_params.include? param
59
+ end
60
+ end
61
+
45
62
  def blocked(message)
46
63
  @blocked_event_message = message
47
64
  end
@@ -63,5 +80,9 @@ module EndState
63
80
  def run_concluder(concluder, object, state, params)
64
81
  concluder.new(object, state, params).call
65
82
  end
83
+
84
+ def missing_params(params)
85
+ required_params.select { |key| params[key].nil? }
86
+ end
66
87
  end
67
88
  end
@@ -1,3 +1,3 @@
1
1
  module EndState
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
@@ -4,13 +4,14 @@ module EndStateMatchers
4
4
  end
5
5
 
6
6
  class TransitionMatcher
7
- attr_reader :transition, :machine, :failure_messages, :guards, :concluders
7
+ attr_reader :transition, :machine, :failure_messages, :guards, :concluders, :required_params
8
8
 
9
9
  def initialize(transition)
10
10
  @transition = transition
11
11
  @failure_messages = []
12
12
  @guards = []
13
13
  @concluders = []
14
+ @required_params = []
14
15
  end
15
16
 
16
17
  def matches?(actual)
@@ -32,7 +33,7 @@ module EndStateMatchers
32
33
  end
33
34
 
34
35
  def with_guards(*guards)
35
- @guards += Array(guards)
36
+ @guards += Array(guards).flatten
36
37
  self
37
38
  end
38
39
 
@@ -42,7 +43,12 @@ module EndStateMatchers
42
43
  end
43
44
 
44
45
  def with_concluders(*concluders)
45
- @concluders += Array(concluders)
46
+ @concluders += Array(concluders).flatten
47
+ self
48
+ end
49
+
50
+ def with_required_params(*params)
51
+ @required_params += Array(params).flatten
46
52
  self
47
53
  end
48
54
 
@@ -59,6 +65,7 @@ module EndStateMatchers
59
65
  if machine.transitions.keys.include? transition
60
66
  result = (result && verify_guards) if guards.any?
61
67
  result = (result && verify_concluders) if concluders.any?
68
+ result = (result && verify_required_params) if required_params.any?
62
69
  result
63
70
  else
64
71
  failure_messages << "expected that #{machine.name} would have transition :#{transition.keys.first} => :#{transition.values.first}"
@@ -87,6 +94,17 @@ module EndStateMatchers
87
94
  end
88
95
  result
89
96
  end
97
+
98
+ def verify_required_params
99
+ result = true
100
+ required_params.each do |param|
101
+ unless machine.transitions[transition].required_params.any? { |p| p == param }
102
+ failure_messages << "expected that transition :#{transition.keys.first} => :#{transition.values.first} would have required param #{param}"
103
+ result = false
104
+ end
105
+ end
106
+ result
107
+ end
90
108
  end
91
109
  end
92
110
 
@@ -189,6 +189,12 @@ module EndState
189
189
  expect(machine).to have_received(:transition).with(:b, { foo: 'bar', bar: 'foo' })
190
190
  end
191
191
 
192
+ it 'defaults params to {}' do
193
+ machine.stub(:transition)
194
+ machine.b!
195
+ expect(machine).to have_received(:transition).with(:b, {})
196
+ end
197
+
192
198
  it 'works with an event' do
193
199
  machine.go!
194
200
  expect(machine.state).to eq :b
@@ -231,6 +237,22 @@ module EndState
231
237
  context 'when asking about a disallowed transition' do
232
238
  specify { expect(machine.can_transition? :c).to be_false }
233
239
  end
240
+
241
+ context 'when using :any_state' do
242
+ before { StateMachine.transition any_state: :d }
243
+
244
+ context 'and the initial state is :a' do
245
+ let(:object) { OpenStruct.new(state: :a) }
246
+
247
+ specify { expect(machine.can_transition? :d).to be_true }
248
+ end
249
+
250
+ context 'and the initial state is :b' do
251
+ let(:object) { OpenStruct.new(state: :b) }
252
+
253
+ specify { expect(machine.can_transition? :d).to be_true }
254
+ end
255
+ end
234
256
  end
235
257
 
236
258
  describe '#transition' do
@@ -267,6 +289,28 @@ module EndState
267
289
  expect(object.state).to eq 'b'
268
290
  end
269
291
  end
292
+
293
+ context 'and using :any_state' do
294
+ before { StateMachine.transition any_state: :d }
295
+
296
+ context 'and the initial state is :a' do
297
+ before { object.state = :a }
298
+
299
+ it 'transitions the state' do
300
+ machine.transition :d
301
+ expect(object.state).to eq :d
302
+ end
303
+ end
304
+
305
+ context 'and the initial state is :b' do
306
+ before { object.state = :b }
307
+
308
+ it 'transitions the state' do
309
+ machine.transition :d
310
+ expect(object.state).to eq :d
311
+ end
312
+ end
313
+ end
270
314
  end
271
315
 
272
316
  context 'and a guard is configured' do
@@ -24,10 +24,15 @@ module EndState
24
24
 
25
25
  describe '#guard' do
26
26
  let(:guard) { double :guard }
27
+ let(:another_guard) { double :another_guard }
27
28
 
28
29
  it 'adds a guard' do
29
30
  expect { transition.guard guard }.to change(transition.guards, :count).by(1)
30
31
  end
32
+
33
+ it 'adds multiple guards' do
34
+ expect { transition.guard guard, another_guard }.to change(transition.guards, :count).by(2)
35
+ end
31
36
  end
32
37
 
33
38
  describe '#allowed?' do
@@ -40,6 +45,27 @@ module EndState
40
45
  before { guard_instance.stub(:allowed?).and_return(true) }
41
46
 
42
47
  specify { expect(transition.allowed? object).to be_true }
48
+
49
+ context 'when params are provided' do
50
+ it 'creates the guard with the params' do
51
+ transition.allowed? object, { foo: 'bar' }
52
+ expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
53
+ end
54
+
55
+ context 'and some params are required' do
56
+ before { transition.require_params :foo, :bar }
57
+
58
+ context 'and not all required are provided' do
59
+ it 'throws an exception' do
60
+ expect { transition.allowed? object, foo: 'something' }.to raise_error('Missing params: bar')
61
+ end
62
+ end
63
+
64
+ context 'and all required are provided' do
65
+ specify { expect(transition.allowed? object, foo: 1, bar: 2).to be_true }
66
+ end
67
+ end
68
+ end
43
69
  end
44
70
 
45
71
  context 'when not all guards pass' do
@@ -47,13 +73,6 @@ module EndState
47
73
 
48
74
  specify { expect(transition.allowed? object).to be_false }
49
75
  end
50
-
51
- context 'when params are provided' do
52
- it 'creates the guard with the params' do
53
- transition.allowed? object, { foo: 'bar' }
54
- expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
55
- end
56
- end
57
76
  end
58
77
 
59
78
  describe '#will_allow?' do
@@ -66,6 +85,25 @@ module EndState
66
85
  before { guard_instance.stub(:will_allow?).and_return(true) }
67
86
 
68
87
  specify { expect(transition.will_allow? object).to be_true }
88
+
89
+ context 'when params are provided' do
90
+ it 'creates the guard with the params' do
91
+ transition.will_allow? object, { foo: 'bar' }
92
+ expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
93
+ end
94
+
95
+ context 'and some params are required' do
96
+ before { transition.require_params :foo, :bar }
97
+
98
+ context 'and not all required are provided' do
99
+ specify { expect(transition.will_allow? object).to be_false }
100
+ end
101
+
102
+ context 'and all required are provided' do
103
+ specify { expect(transition.will_allow? object, foo: 1, bar: 2).to be_true }
104
+ end
105
+ end
106
+ end
69
107
  end
70
108
 
71
109
  context 'when not all guards pass' do
@@ -84,10 +122,15 @@ module EndState
84
122
 
85
123
  describe '#concluder' do
86
124
  let(:concluder) { double :concluder }
125
+ let(:another_concluder) { double :another_concluder }
87
126
 
88
127
  it 'adds a concluder' do
89
128
  expect { transition.concluder concluder }.to change(transition.concluders, :count).by(1)
90
129
  end
130
+
131
+ it 'adds multiple concluders' do
132
+ expect { transition.concluder concluder, another_concluder }.to change(transition.concluders, :count).by(2)
133
+ end
91
134
  end
92
135
 
93
136
  describe '#persistence_on' do
@@ -96,6 +139,22 @@ module EndState
96
139
  end
97
140
  end
98
141
 
142
+ describe '#allow_params' do
143
+ it 'adds supplied keys to the allowed_params array' do
144
+ expect { transition.allow_params :foo, :bar }.to change(transition.allowed_params, :count).by(2)
145
+ end
146
+ end
147
+
148
+ describe '#require_params' do
149
+ it 'adds supplied keys to the required_params array' do
150
+ expect { transition.require_params :foo, :bar }.to change(transition.required_params, :count).by(2)
151
+ end
152
+
153
+ it 'adds supplied keys to the allowed_params array' do
154
+ expect { transition.allow_params :foo, :bar }.to change(transition.allowed_params, :count).by(2)
155
+ end
156
+ end
157
+
99
158
  describe '#conclude' do
100
159
  let(:concluder) { double :concluder, new: concluder_instance }
101
160
  let(:concluder_instance) { double :concluder_instance, call: nil, rollback: nil }
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.9.0
4
+ version: 0.10.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-17 00:00:00.000000000 Z
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler