end_state 0.12.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,66 +3,43 @@ require 'ostruct'
3
3
 
4
4
  module EndState
5
5
  describe Transition do
6
- subject(:transition) { Transition.new(state) }
7
- let(:state) { :a }
8
-
9
- describe '#custom_action' do
10
- let(:custom) { double :custom }
11
-
12
- it 'sets the action' do
13
- transition.custom_action custom
14
- expect(transition.action).to eq custom
15
- end
16
- end
17
-
18
- describe '#blocked' do
19
- it 'sets the blocked event message' do
20
- transition.blocked 'This is blocked.'
21
- expect(transition.blocked_event_message).to eq 'This is blocked.'
22
- end
23
- end
24
-
25
- describe '#guard' do
26
- let(:guard) { double :guard }
27
- let(:another_guard) { double :another_guard }
28
-
29
- it 'adds a guard' do
30
- expect { transition.guard guard }.to change(transition.guards, :count).by(1)
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
36
- end
6
+ subject(:transition) { Transition.new(object, previous_state, state, configuration, mode) }
7
+ let(:object) { double :object, failure_messages: [] }
8
+ let(:previous_state) { :a }
9
+ let(:state) { :b }
10
+ let(:configuration) { OpenStruct.new(action: action, concluders: [], guards: [], required_params: []) }
11
+ let(:action) { double :action, new: action_instance }
12
+ let(:action_instance) { double :action_instance, call: action_return_value, rollback: nil }
13
+ let(:action_return_value) { true }
14
+ let(:mode) { :soft }
37
15
 
38
16
  describe '#allowed?' do
39
17
  let(:guard) { double :guard, new: guard_instance }
40
18
  let(:guard_instance) { double :guard_instance, allowed?: nil }
41
- let(:object) { double :object }
42
- before { transition.guards << guard }
19
+ before { configuration.guards << guard }
43
20
 
44
21
  context 'when all guards pass' do
45
22
  before { allow(guard_instance).to receive(:allowed?).and_return(true) }
46
23
 
47
- specify { expect(transition.allowed? object).to be true }
24
+ specify { expect(transition.allowed?).to be true }
48
25
 
49
26
  context 'when params are provided' do
50
27
  it 'creates the guard with the params' do
51
- transition.allowed? object, { foo: 'bar' }
28
+ transition.allowed?({ foo: 'bar' })
52
29
  expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
53
30
  end
54
31
 
55
32
  context 'and some params are required' do
56
- before { transition.require_params :foo, :bar }
33
+ before { configuration.required_params = [:foo, :bar] }
57
34
 
58
35
  context 'and not all required are provided' do
59
36
  it 'throws an exception' do
60
- expect { transition.allowed? object, foo: 'something' }.to raise_error('Missing params: bar')
37
+ expect { transition.allowed? foo: 'something' }.to raise_error('Missing params: bar')
61
38
  end
62
39
  end
63
40
 
64
41
  context 'and all required are provided' do
65
- specify { expect(transition.allowed? object, foo: 1, bar: 2).to be true }
42
+ specify { expect(transition.allowed? foo: 1, bar: 2).to be true }
66
43
  end
67
44
  end
68
45
  end
@@ -71,36 +48,35 @@ module EndState
71
48
  context 'when not all guards pass' do
72
49
  before { allow(guard_instance).to receive(:allowed?).and_return(false) }
73
50
 
74
- specify { expect(transition.allowed? object).to be false }
51
+ specify { expect(transition.allowed?).to be false }
75
52
  end
76
53
  end
77
54
 
78
55
  describe '#will_allow?' do
79
56
  let(:guard) { double :guard, new: guard_instance }
80
57
  let(:guard_instance) { double :guard_instance, will_allow?: nil }
81
- let(:object) { double :object }
82
- before { transition.guards << guard }
58
+ before { configuration.guards << guard }
83
59
 
84
60
  context 'when all guards pass' do
85
61
  before { allow(guard_instance).to receive(:will_allow?).and_return(true) }
86
62
 
87
- specify { expect(transition.will_allow? object).to be true }
63
+ specify { expect(transition.will_allow?).to be true }
88
64
 
89
65
  context 'when params are provided' do
90
66
  it 'creates the guard with the params' do
91
- transition.will_allow? object, { foo: 'bar' }
67
+ transition.will_allow?({ foo: 'bar' })
92
68
  expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
93
69
  end
94
70
 
95
71
  context 'and some params are required' do
96
- before { transition.require_params :foo, :bar }
72
+ before { configuration.required_params = [:foo, :bar] }
97
73
 
98
74
  context 'and not all required are provided' do
99
- specify { expect(transition.will_allow? object).to be false }
75
+ specify { expect(transition.will_allow?).to be false }
100
76
  end
101
77
 
102
78
  context 'and all required are provided' do
103
- specify { expect(transition.will_allow? object, foo: 1, bar: 2).to be true }
79
+ specify { expect(transition.will_allow? foo: 1, bar: 2).to be true }
104
80
  end
105
81
  end
106
82
  end
@@ -109,82 +85,93 @@ module EndState
109
85
  context 'when not all guards pass' do
110
86
  before { allow(guard_instance).to receive(:will_allow?).and_return(false) }
111
87
 
112
- specify { expect(transition.will_allow? object).to be false }
88
+ specify { expect(transition.will_allow?).to be false }
113
89
  end
114
90
 
115
91
  context 'when params are provided' do
116
92
  it 'creates the guard with the params' do
117
- transition.will_allow? object, { foo: 'bar' }
93
+ transition.will_allow?({ foo: 'bar' })
118
94
  expect(guard).to have_received(:new).with(object, state, { foo: 'bar' })
119
95
  end
120
96
  end
121
97
  end
122
98
 
123
- describe '#concluder' do
124
- let(:concluder) { double :concluder }
125
- let(:another_concluder) { double :another_concluder }
99
+ describe '#call' do
100
+ context 'when a guard returns false' do
101
+ let(:guard) { double :guard, new: guard_instance }
102
+ let(:guard_instance) { double :guard_instance, allowed?: false }
103
+ before do
104
+ configuration.guards << guard
105
+ object.failure_messages << 'not ready'
106
+ end
126
107
 
127
- it 'adds a concluder' do
128
- expect { transition.concluder concluder }.to change(transition.concluders, :count).by(1)
129
- end
108
+ context 'soft mode' do
109
+ let(:mode) { :soft }
130
110
 
131
- it 'adds multiple concluders' do
132
- expect { transition.concluder concluder, another_concluder }.to change(transition.concluders, :count).by(2)
133
- end
134
- end
111
+ it 'returns false' do
112
+ expect(transition.call).to be false
113
+ end
114
+ end
135
115
 
136
- describe '#persistence_on' do
137
- it 'adds a Persistence concluder' do
138
- expect { transition.persistence_on }.to change(transition.concluders, :count).by(1)
139
- end
140
- end
116
+ context 'hard mode' do
117
+ let(:mode) { :hard }
141
118
 
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)
119
+ it 'raises GuardFailed' do
120
+ expect{transition.call}.to raise_error(GuardFailed, 'The transition to b was blocked: not ready')
121
+ end
122
+ end
145
123
  end
146
- end
147
124
 
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
125
+ context 'when action returns false' do
126
+ let(:action_return_value) { false }
152
127
 
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)
128
+ it 'returns false' do
129
+ expect(transition.call).to be false
130
+ end
155
131
  end
156
- end
157
132
 
158
- describe '#conclude' do
159
- let(:concluder) { double :concluder, new: concluder_instance }
160
- let(:concluder_instance) { double :concluder_instance, call: nil, rollback: nil }
161
- let(:object) { OpenStruct.new(state: :b) }
162
- before do
163
- allow(object).to receive_message_chain(:class, store_states_as_strings: false)
164
- transition.concluders << concluder
165
- end
133
+ context 'when a concluder returns false' do
134
+ let(:concluder1) { double :concluder, new: concluder1_instance }
135
+ let(:concluder1_instance) { double :concluder_instance, call: true, rollback: nil }
136
+ let(:concluder2) { double :concluder, new: concluder2_instance }
137
+ let(:concluder2_instance) { double :concluder_instance, call: false, rollback: nil }
138
+ let(:concluder3) { double :concluder, new: concluder3_instance }
139
+ let(:concluder3_instance) { double :concluder_instance, call: true, rollback: nil }
140
+ before do
141
+ configuration.concluders = [concluder1, concluder2, concluder3]
142
+ object.failure_messages << 'service failure'
143
+ end
166
144
 
167
- context 'when all concluders succeed' do
168
- before { allow(concluder_instance).to receive(:call).and_return(true) }
145
+ context 'soft mode' do
146
+ let(:mode) { :soft }
169
147
 
170
- specify { expect(transition.conclude object, :a).to be true }
171
- end
148
+ it 'returns false and rolls back the completed concluders and the action' do
149
+ expect(concluder3_instance).to_not receive(:rollback)
150
+ expect(concluder2_instance).to receive(:rollback).ordered
151
+ expect(concluder1_instance).to receive(:rollback).ordered
152
+ expect(action_instance).to receive(:rollback).ordered
153
+
154
+ expect(transition.call).to be false
155
+ end
156
+ end
172
157
 
173
- context 'when not all concluders succeed' do
174
- before { allow(concluder_instance).to receive(:call).and_return(false) }
158
+ context 'hard mode' do
159
+ let(:mode) { :hard }
175
160
 
176
- specify { expect(transition.conclude object, :a).to be false }
161
+ it 'raises ConcluderFailed and rolls back the completed concluders and the action' do
162
+ expect(concluder3_instance).to_not receive(:rollback)
163
+ expect(concluder2_instance).to receive(:rollback).ordered
164
+ expect(concluder1_instance).to receive(:rollback).ordered
165
+ expect(action_instance).to receive(:rollback).ordered
177
166
 
178
- it 'rolls them back' do
179
- transition.conclude object, :a
180
- expect(concluder_instance).to have_received(:rollback)
167
+ expect{transition.call}.to raise_error(ConcluderFailed, 'The transition to b was rolled back: service failure')
168
+ end
181
169
  end
182
170
  end
183
171
 
184
- context 'when params are provided' do
185
- it 'creates a concluder with the params' do
186
- transition.conclude object, :b, { foo: 'bar' }
187
- expect(concluder).to have_received(:new).twice.with(object, :a, { foo: 'bar'} )
172
+ context 'when guards, action, and concluders return true' do
173
+ it 'returns true' do
174
+ expect(transition.call).to be true
188
175
  end
189
176
  end
190
177
  end
@@ -0,0 +1,105 @@
1
+ require "spec_helper"
2
+
3
+ module EndState
4
+ class TestGuard1 < EndState::Guard; end
5
+ class TestGuard2 < EndState::Guard; end
6
+ class TestGuard3 < EndState::Guard; end
7
+
8
+ class TestConcluder1 < EndState::Concluder; end
9
+ class TestConcluder2 < EndState::Concluder; end
10
+ class TestConcluder3 < EndState::Concluder; end
11
+
12
+ class TestMachine < EndState::StateMachine
13
+ transition a: :b, as: :go do |t|
14
+ t.guard TestGuard1, TestGuard2
15
+ t.concluder TestConcluder1, TestConcluder2
16
+ t.require_params :a, :b
17
+ end
18
+ end
19
+
20
+ describe 'matchers' do
21
+ describe 'have_transition' do
22
+ it 'passes when the transition is present' do
23
+ expect(TestMachine).to have_transition(a: :b)
24
+ end
25
+
26
+ it 'fails when the guard is not present' do
27
+ expect {
28
+ expect(TestMachine).to have_transition(b: :c)
29
+ }.to fail_with('expected transition b => c to be defined')
30
+ end
31
+
32
+ describe 'with_event' do
33
+ it 'passes when the guard is present' do
34
+ expect(TestMachine).to have_transition(a: :b).with_event(:go)
35
+ end
36
+
37
+ it 'fails when the guard is not present' do
38
+ expect {
39
+ expect(TestMachine).to have_transition(a: :b).with_event(:reset)
40
+ }.to fail_with('expected transition a => b to have event name: reset')
41
+ end
42
+ end
43
+
44
+ describe 'with_guard' do
45
+ it 'passes when the guard is present' do
46
+ expect(TestMachine).to have_transition(a: :b).with_guard(TestGuard1)
47
+ end
48
+
49
+ it 'fails when the guard is not present' do
50
+ expect {
51
+ expect(TestMachine).to have_transition(a: :b).with_guard(TestGuard3)
52
+ }.to fail_with('expected transition a => b to have guard EndState::TestGuard3')
53
+ end
54
+ end
55
+
56
+ describe 'with_guards' do
57
+ it 'passes when the guards are present' do
58
+ expect(TestMachine).to have_transition(a: :b).with_guards(TestGuard1, TestGuard2)
59
+ end
60
+
61
+ it 'fails when the guards are not present' do
62
+ expect {
63
+ expect(TestMachine).to have_transition(a: :b).with_guards(TestGuard2, TestGuard3)
64
+ }.to fail_with('expected transition a => b to have guard EndState::TestGuard3')
65
+ end
66
+ end
67
+
68
+ describe 'with_concluder' do
69
+ it 'passes when the concluder is present' do
70
+ expect(TestMachine).to have_transition(a: :b).with_concluder(TestConcluder1)
71
+ end
72
+
73
+ it 'fails when the concluder is not present' do
74
+ expect {
75
+ expect(TestMachine).to have_transition(a: :b).with_concluder(TestConcluder3)
76
+ }.to fail_with('expected transition a => b to have concluder EndState::TestConcluder3')
77
+ end
78
+ end
79
+
80
+ describe 'with_concluders' do
81
+ it 'passes when the concluders are present' do
82
+ expect(TestMachine).to have_transition(a: :b).with_concluders(TestConcluder1, TestConcluder2)
83
+ end
84
+
85
+ it 'fails when the concluders are not present' do
86
+ expect {
87
+ expect(TestMachine).to have_transition(a: :b).with_concluders(TestConcluder2, TestConcluder3)
88
+ }.to fail_with('expected transition a => b to have concluder EndState::TestConcluder3')
89
+ end
90
+ end
91
+
92
+ describe 'with_required_params' do
93
+ it 'passes when the required_params match' do
94
+ expect(TestMachine).to have_transition(a: :b).with_required_params(:a, :b)
95
+ end
96
+
97
+ it 'fails when the required_params do not match' do
98
+ expect {
99
+ expect(TestMachine).to have_transition(a: :b).with_required_params(:b, :c)
100
+ }.to fail_with('expected transition a => b to have required param c')
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,19 @@
1
+ if ENV['TRAVIS']
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ else
5
+ require 'simplecov'
6
+ SimpleCov.start
7
+ end
8
+
1
9
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
10
  require 'end_state'
11
+ require 'end_state_matchers'
12
+
13
+ module RSpec
14
+ module Matchers
15
+ def fail_with(message)
16
+ raise_error(RSpec::Expectations::ExpectationNotMetError, message)
17
+ end
18
+ end
19
+ 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.12.0
4
+ version: 1.0.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-09-05 00:00:00.000000000 Z
11
+ date: 2016-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  description: A modular state machine with single responsibilities.
70
84
  email:
71
85
  - alex.peachey@gmail.com
@@ -94,20 +108,26 @@ files:
94
108
  - lib/end_state/graph.rb
95
109
  - lib/end_state/guard.rb
96
110
  - lib/end_state/messages.rb
111
+ - lib/end_state/no_graph.rb
97
112
  - lib/end_state/state_machine.rb
98
- - lib/end_state/state_mapping.rb
113
+ - lib/end_state/state_machine_configuration.rb
99
114
  - lib/end_state/transition.rb
115
+ - lib/end_state/transition_configuration.rb
116
+ - lib/end_state/transition_configuration_set.rb
100
117
  - lib/end_state/version.rb
101
118
  - lib/end_state_matchers.rb
102
119
  - lib/tasks/end_state.rake
103
120
  - spec/end_state/action_spec.rb
104
121
  - spec/end_state/concluder_spec.rb
105
122
  - spec/end_state/concluders/persistence_spec.rb
123
+ - spec/end_state/graph_spec.rb
106
124
  - spec/end_state/guard_spec.rb
125
+ - spec/end_state/state_machine_configuration_spec.rb
107
126
  - spec/end_state/state_machine_spec.rb
108
- - spec/end_state/state_mapping_spec.rb
127
+ - spec/end_state/transition_configuration_set_spec.rb
128
+ - spec/end_state/transition_configuration_spec.rb
109
129
  - spec/end_state/transition_spec.rb
110
- - spec/end_state_spec.rb
130
+ - spec/end_state_matchers_spec.rb
111
131
  - spec/spec_helper.rb
112
132
  homepage: https://github.com/Originate/end_state
113
133
  licenses:
@@ -129,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
149
  version: '0'
130
150
  requirements: []
131
151
  rubyforge_project:
132
- rubygems_version: 2.2.2
152
+ rubygems_version: 2.4.6
133
153
  signing_key:
134
154
  specification_version: 4
135
155
  summary: A State Machine implementation
@@ -137,9 +157,12 @@ test_files:
137
157
  - spec/end_state/action_spec.rb
138
158
  - spec/end_state/concluder_spec.rb
139
159
  - spec/end_state/concluders/persistence_spec.rb
160
+ - spec/end_state/graph_spec.rb
140
161
  - spec/end_state/guard_spec.rb
162
+ - spec/end_state/state_machine_configuration_spec.rb
141
163
  - spec/end_state/state_machine_spec.rb
142
- - spec/end_state/state_mapping_spec.rb
164
+ - spec/end_state/transition_configuration_set_spec.rb
165
+ - spec/end_state/transition_configuration_spec.rb
143
166
  - spec/end_state/transition_spec.rb
144
- - spec/end_state_spec.rb
167
+ - spec/end_state_matchers_spec.rb
145
168
  - spec/spec_helper.rb
@@ -1,25 +0,0 @@
1
- module EndState
2
- class StateMapping < Hash
3
- def start_state
4
- keys.first
5
- end
6
-
7
- def end_state
8
- values.first
9
- end
10
-
11
- def any_start_state?
12
- start_state == :any_state
13
- end
14
-
15
- def matches_start_state?(state)
16
- start_state == state || any_start_state?
17
- end
18
-
19
- def conflicts?(state_mapping)
20
- start_state == state_mapping.start_state ||
21
- any_start_state? ||
22
- state_mapping.any_start_state?
23
- end
24
- end
25
- end
@@ -1,94 +0,0 @@
1
- require 'spec_helper'
2
- require 'ostruct'
3
-
4
- module EndState
5
- describe StateMapping do
6
- describe 'simple example' do
7
- let(:subject) { StateMapping[a: :b] }
8
-
9
- context '#start_state' do
10
- it 'returns the first key' do
11
- expect(subject.start_state).to eq :a
12
- end
13
- end
14
-
15
- context '#end_state' do
16
- it 'returns the first value' do
17
- expect(subject.end_state).to eq :b
18
- end
19
- end
20
-
21
- context '#any_start_state?' do
22
- context 'start_state is :any_state' do
23
- let(:subject) { StateMapping[any_state: :b] }
24
- it 'returns true' do
25
- expect(subject.any_start_state?).to eq true
26
- end
27
- end
28
-
29
- context 'start_state is anything else' do
30
- it 'returns false' do
31
- expect(subject.any_start_state?).to eq false
32
- end
33
- end
34
- end
35
-
36
- context '#matches_start_state?' do
37
- context 'same start_state' do
38
- it 'returns true' do
39
- expect(subject.matches_start_state?(:a)).to eq true
40
- end
41
- end
42
-
43
- context 'different start_state' do
44
- it 'returns false' do
45
- expect(subject.matches_start_state?(:b)).to eq false
46
- end
47
- end
48
-
49
- context 'object has a start_state of :any_state' do
50
- let(:subject) { StateMapping[any_state: :c] }
51
-
52
- it 'returns true' do
53
- expect(subject.matches_start_state?(:b)).to eq true
54
- end
55
- end
56
- end
57
-
58
- context '#conflicts?' do
59
- context 'same start_state' do
60
- let(:other) { StateMapping[a: :c] }
61
-
62
- it 'returns true' do
63
- expect(subject.conflicts?(other)).to eq true
64
- end
65
- end
66
-
67
- context 'different start_state' do
68
- let(:other) { StateMapping[c: :d] }
69
-
70
- it 'returns false' do
71
- expect(subject.conflicts?(other)).to eq false
72
- end
73
- end
74
-
75
- context 'argument has a start_state of :any_state' do
76
- let(:other) { StateMapping[any_state: :c] }
77
-
78
- it 'returns true' do
79
- expect(subject.conflicts?(other)).to eq true
80
- end
81
- end
82
-
83
- context 'object has a start_state of :any_state' do
84
- let(:subject) { StateMapping[any_state: :c] }
85
- let(:other) { StateMapping[a: :c] }
86
-
87
- it 'returns true' do
88
- expect(subject.conflicts?(other)).to eq true
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,4 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe EndState do
4
- end