end_state 0.12.0 → 1.0.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.
@@ -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