end_state 0.9.0 → 0.10.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: 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