substation 0.0.9 → 0.0.10.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +0 -1
  3. data/Changelog.md +24 -82
  4. data/Gemfile.devtools +17 -24
  5. data/README.md +46 -116
  6. data/config/flay.yml +2 -2
  7. data/config/flog.yml +1 -1
  8. data/config/mutant.yml +0 -1
  9. data/config/reek.yml +5 -10
  10. data/lib/substation.rb +3 -8
  11. data/lib/substation/chain.rb +64 -108
  12. data/lib/substation/chain/dsl.rb +30 -57
  13. data/lib/substation/dispatcher.rb +1 -3
  14. data/lib/substation/environment.rb +23 -9
  15. data/lib/substation/environment/dsl.rb +3 -4
  16. data/lib/substation/observer.rb +2 -4
  17. data/lib/substation/processor.rb +7 -106
  18. data/lib/substation/processor/evaluator.rb +42 -83
  19. data/lib/substation/processor/pivot.rb +25 -0
  20. data/lib/substation/processor/wrapper.rb +2 -4
  21. data/lib/substation/request.rb +1 -10
  22. data/lib/substation/response.rb +0 -11
  23. data/lib/substation/utils.rb +1 -3
  24. data/lib/substation/version.rb +1 -3
  25. data/spec/integration/substation/dispatcher/call_spec.rb +12 -12
  26. data/spec/spec_helper.rb +21 -30
  27. data/spec/unit/substation/chain/call_spec.rb +32 -202
  28. data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +2 -2
  29. data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +6 -8
  30. data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +30 -0
  31. data/spec/unit/substation/chain/dsl/chain_spec.rb +2 -1
  32. data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +24 -0
  33. data/spec/unit/substation/chain/dsl/initialize_spec.rb +19 -0
  34. data/spec/unit/substation/chain/dsl/processors_spec.rb +21 -9
  35. data/spec/unit/substation/chain/dsl/use_spec.rb +3 -2
  36. data/spec/unit/substation/chain/each_spec.rb +9 -5
  37. data/spec/unit/substation/chain/incoming/result_spec.rb +21 -0
  38. data/spec/unit/substation/chain/outgoing/call_spec.rb +25 -0
  39. data/spec/unit/substation/{processor → chain/outgoing}/result_spec.rb +5 -6
  40. data/spec/unit/substation/dispatcher/action/call_spec.rb +6 -7
  41. data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +5 -7
  42. data/spec/unit/substation/dispatcher/action_names_spec.rb +1 -1
  43. data/spec/unit/substation/dispatcher/call_spec.rb +3 -3
  44. data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +7 -7
  45. data/spec/unit/substation/environment/chain_spec.rb +32 -22
  46. data/spec/unit/substation/environment/class_methods/build_spec.rb +4 -11
  47. data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +3 -5
  48. data/spec/unit/substation/environment/dsl/register_spec.rb +3 -8
  49. data/spec/unit/substation/environment/dsl/registry_spec.rb +3 -5
  50. data/spec/unit/substation/observer/chain/call_spec.rb +3 -5
  51. data/spec/unit/substation/observer/class_methods/coerce_spec.rb +2 -4
  52. data/spec/unit/substation/observer/null/call_spec.rb +1 -3
  53. data/spec/unit/substation/processor/evaluator/call_spec.rb +35 -21
  54. data/spec/unit/substation/processor/pivot/call_spec.rb +17 -0
  55. data/spec/unit/substation/processor/wrapper/call_spec.rb +7 -8
  56. data/spec/unit/substation/request/env_spec.rb +4 -5
  57. data/spec/unit/substation/request/error_spec.rb +4 -5
  58. data/spec/unit/substation/request/input_spec.rb +4 -5
  59. data/spec/unit/substation/request/success_spec.rb +4 -5
  60. data/spec/unit/substation/response/env_spec.rb +5 -6
  61. data/spec/unit/substation/response/failure/success_predicate_spec.rb +4 -5
  62. data/spec/unit/substation/response/input_spec.rb +5 -6
  63. data/spec/unit/substation/response/output_spec.rb +4 -5
  64. data/spec/unit/substation/response/request_spec.rb +5 -6
  65. data/spec/unit/substation/response/success/success_predicate_spec.rb +4 -5
  66. data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +13 -15
  67. data/spec/unit/substation/utils/class_methods/const_get_spec.rb +6 -6
  68. data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +4 -4
  69. data/substation.gemspec +1 -1
  70. metadata +18 -45
  71. data/config/rubocop.yml +0 -35
  72. data/lib/substation/processor/transformer.rb +0 -26
  73. data/spec/unit/substation/chain/class_methods/failure_response_spec.rb +0 -16
  74. data/spec/unit/substation/chain/dsl/class_methods/build_spec.rb +0 -24
  75. data/spec/unit/substation/chain/dsl/failure_chain_spec.rb +0 -35
  76. data/spec/unit/substation/chain/failure_data/equalizer_spec.rb +0 -46
  77. data/spec/unit/substation/chain/failure_data/hash_spec.rb +0 -13
  78. data/spec/unit/substation/environment/equalizer_spec.rb +0 -25
  79. data/spec/unit/substation/processor/evaluator/class_methods/new_spec.rb +0 -9
  80. data/spec/unit/substation/processor/evaluator/data/call_spec.rb +0 -34
  81. data/spec/unit/substation/processor/evaluator/pivot/call_spec.rb +0 -34
  82. data/spec/unit/substation/processor/evaluator/request/call_spec.rb +0 -34
  83. data/spec/unit/substation/processor/fallible/name_spec.rb +0 -15
  84. data/spec/unit/substation/processor/fallible/with_failure_chain_spec.rb +0 -18
  85. data/spec/unit/substation/processor/incoming/result_spec.rb +0 -25
  86. data/spec/unit/substation/processor/outgoing/call_spec.rb +0 -28
  87. data/spec/unit/substation/processor/outgoing/name_spec.rb +0 -14
  88. data/spec/unit/substation/processor/outgoing/success_predicate_spec.rb +0 -15
  89. data/spec/unit/substation/processor/success_predicate_spec.rb +0 -22
  90. data/spec/unit/substation/processor/transformer/call_spec.rb +0 -21
  91. data/spec/unit/substation/request/name_spec.rb +0 -15
  92. data/spec/unit/substation/response/to_request_spec.rb +0 -19
@@ -1,20 +1,11 @@
1
- # encoding: utf-8
2
-
3
1
  module Substation
4
2
 
5
3
  # Encapsulates the application environment and an input model instance
6
4
  class Request
7
5
 
8
- include Concord.new(:name, :env, :input)
6
+ include Concord.new(:env, :input)
9
7
  include Adamantium::Flat
10
8
 
11
- # The name of the request
12
- #
13
- # @return [Symbol]
14
- #
15
- # @api private
16
- attr_reader :name
17
-
18
9
  # The application environment
19
10
  #
20
11
  # @example
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Substation
4
2
 
5
3
  # Base class for action responses
@@ -131,15 +129,6 @@ module Substation
131
129
  # @api public
132
130
  abstract_method :success?
133
131
 
134
- # Return a {Request} instance built upon this response
135
- #
136
- # @return [Request]
137
- #
138
- # @api private
139
- def to_request
140
- Request.new(request.name, env, output)
141
- end
142
-
143
132
  # An errorneous {Response}
144
133
  class Failure < self
145
134
 
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Substation
4
2
 
5
3
  # A collection of utility methods
@@ -14,7 +12,7 @@ module Substation
14
12
  #
15
13
  # @api private
16
14
  def self.const_get(name)
17
- list = name.to_s.split('::')
15
+ list = name.to_s.split("::")
18
16
  list.shift if list.first.empty?
19
17
  obj = Object
20
18
  list.each do |const|
@@ -1,6 +1,4 @@
1
- # encoding: utf-8
2
-
3
1
  module Substation
4
2
  # Gem version
5
- VERSION = '0.0.9'.freeze
3
+ VERSION = '0.0.10.beta2'.freeze
6
4
  end
@@ -181,8 +181,8 @@ module App
181
181
  end # module Actions
182
182
 
183
183
  module Observers
184
- LOG_EVENT = ->(response) { response }
185
- SEND_EMAIL = ->(response) { response }
184
+ LogEvent = Proc.new { |response| response }
185
+ SendEmail = Proc.new { |response| response }
186
186
  end
187
187
 
188
188
  DB = Database.new({
@@ -198,8 +198,8 @@ module App
198
198
  :create_person => {
199
199
  :action => Actions::CreatePerson,
200
200
  :observer => [
201
- Observers::LOG_EVENT,
202
- Observers::SEND_EMAIL
201
+ Observers::LogEvent,
202
+ Observers::SendEmail
203
203
  ]
204
204
  }
205
205
  }
@@ -213,11 +213,11 @@ end
213
213
 
214
214
  describe App::APP, '#call' do
215
215
 
216
- context 'when dispatching an action' do
216
+ context "when dispatching an action" do
217
217
  subject { object.call(action, input) }
218
218
 
219
219
  let(:object) { described_class }
220
- let(:request) { Substation::Request.new(action, env, input) }
220
+ let(:request) { Substation::Request.new(env, input) }
221
221
  let(:env) { App::Environment.new(storage) }
222
222
  let(:storage) { App::Storage.new(App::DB) }
223
223
  let(:response) { Substation::Response::Success.new(request, output) }
@@ -225,7 +225,7 @@ describe App::APP, '#call' do
225
225
  let(:john) { App::Models::Person.new(:id => 1, :name => 'John') }
226
226
  let(:jane) { App::Models::Person.new(:id => 2, :name => 'Jane') }
227
227
 
228
- context 'with no input data' do
228
+ context "with no input data" do
229
229
  let(:action) { :list_people }
230
230
  let(:input) { nil }
231
231
  let(:output) { [ john ] }
@@ -233,8 +233,8 @@ describe App::APP, '#call' do
233
233
  it { should eql(response) }
234
234
  end
235
235
 
236
- context 'with input data' do
237
- context 'and no observer' do
236
+ context "with input data" do
237
+ context "and no observer" do
238
238
  let(:action) { :load_person }
239
239
  let(:input) { 1 }
240
240
  let(:output) { john }
@@ -242,14 +242,14 @@ describe App::APP, '#call' do
242
242
  it { should eq(response) }
243
243
  end
244
244
 
245
- context 'and observers' do
245
+ context "and observers" do
246
246
  let(:action) { :create_person }
247
247
  let(:input) { jane }
248
248
  let(:output) { [ john, jane ] }
249
249
 
250
250
  before do
251
- App::Observers::LOG_EVENT.should_receive(:call).with(response).ordered
252
- App::Observers::SEND_EMAIL.should_receive(:call).with(response).ordered
251
+ App::Observers::LogEvent.should_receive(:call).with(response).ordered
252
+ App::Observers::SendEmail.should_receive(:call).with(response).ordered
253
253
  end
254
254
 
255
255
  it { should eql(response) }
data/spec/spec_helper.rb CHANGED
@@ -1,28 +1,7 @@
1
- # encoding: utf-8
2
-
3
1
  require 'devtools/spec_helper'
4
2
 
5
3
  require 'concord' # makes spec setup easier
6
4
 
7
- if ENV['COVERAGE'] == 'true'
8
- require 'simplecov'
9
- require 'coveralls'
10
-
11
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
12
- SimpleCov::Formatter::HTMLFormatter,
13
- Coveralls::SimpleCov::Formatter
14
- ]
15
-
16
- SimpleCov.start do
17
- command_name 'spec:unit'
18
- add_filter 'config'
19
- add_filter 'spec'
20
- minimum_coverage 100
21
- end
22
- end
23
-
24
- require 'substation'
25
-
26
5
  module Spec
27
6
 
28
7
  def self.response_data
@@ -49,20 +28,13 @@ module Spec
49
28
  end
50
29
 
51
30
  class Processor
52
- include Substation::Processor::Fallible
53
- attr_reader :name
31
+ include Concord::Public.new(:env, :handler)
54
32
  end
55
33
 
56
34
  class Presenter
57
35
  include Concord.new(:data)
58
36
  end
59
37
 
60
- class Transformer
61
- def self.call(response)
62
- :transformed
63
- end
64
- end
65
-
66
38
  module Handler
67
39
 
68
40
  class Evaluator
@@ -106,10 +78,29 @@ module Spec
106
78
 
107
79
  FAKE_HANDLER = Object.new
108
80
  FAKE_ENV = Object.new
109
- FAKE_PROCESSOR = Processor.new(:test, FAKE_HANDLER, [])
81
+ FAKE_PROCESSOR = Processor.new(FAKE_ENV, FAKE_HANDLER)
110
82
 
111
83
  end
112
84
 
85
+ if ENV['COVERAGE'] == 'true'
86
+ require 'simplecov'
87
+ require 'coveralls'
88
+
89
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
90
+ SimpleCov::Formatter::HTMLFormatter,
91
+ Coveralls::SimpleCov::Formatter
92
+ ]
93
+
94
+ SimpleCov.start do
95
+ command_name 'spec:unit'
96
+ add_filter 'config'
97
+ add_filter 'spec'
98
+ minimum_coverage 100
99
+ end
100
+ end
101
+
102
+ require 'substation'
103
+
113
104
  include Substation
114
105
 
115
106
  RSpec.configure do |config|
@@ -6,228 +6,58 @@ describe Chain, '#call' do
6
6
 
7
7
  subject { object.call(request) }
8
8
 
9
- let(:object) { described_class.new(processors, exception_chain) }
10
- let(:exception_chain) { mock(:call => response) }
11
- let(:processors) { [ processor_1, processor_2, processor_3 ] }
12
- let(:request) { Request.new(name, env, input) }
13
- let(:name) { mock }
14
- let(:env) { mock }
15
- let(:input) { mock }
16
-
17
- let(:failure_chain) { mock }
18
- let(:handler) { mock }
19
-
20
- let(:processor_1_name) { mock }
21
- let(:processor_2_name) { mock }
22
- let(:processor_3_name) { mock }
23
-
24
- context 'when all processors are successful' do
25
- let(:processor_1) {
26
- Class.new {
27
- include Substation::Processor::Incoming
28
- def call(request)
29
- request.success(:success_1)
30
- end
31
- }.new(processor_1_name, handler, failure_chain)
32
- }
33
-
34
- let(:processor_2) {
35
- Class.new {
36
- include Substation::Processor::Pivot
37
- def call(request)
38
- request.success(:success_2)
39
- end
40
- }.new(processor_2_name, handler, failure_chain)
41
- }
42
-
43
- let(:processor_3) {
44
- Class.new {
45
- include Substation::Processor::Outgoing
46
- def call(response)
47
- respond_with(response, :success_3)
48
- end
49
- }.new(processor_3_name, handler)
50
- }
51
-
52
- let(:response) { Response::Success.new(current_request, :success_3) }
53
- let(:current_request) { Request.new(name, env, :success_1) }
54
-
55
- it { should eql(response) }
56
- end
57
-
58
- context 'when an incoming processor is not successful' do
59
-
60
- let(:processor_2) {
61
- Class.new {
62
- include Substation::Processor::Pivot
63
- def call(request)
64
- request.success(:success_1)
65
- end
66
- }.new(processor_2_name, handler, failure_chain)
67
- }
68
-
69
- let(:processor_3) {
70
- Class.new {
71
- include Substation::Processor::Outgoing
72
- def call(response)
73
- respond_with(response, :success_3)
74
- end
75
- }.new(processor_3_name, handler)
76
- }
77
-
78
- let(:response_class) { Response::Failure }
79
-
80
- context 'because it returned a failure response' do
81
- let(:processor_1) {
82
- Class.new {
83
- include Substation::Processor::Incoming
84
- def call(request)
85
- request.error(:error_1)
86
- end
87
- }.new(processor_1_name, handler, failure_chain)
88
- }
89
-
90
- let(:response) { Response::Failure.new(request, :error_1) }
91
-
92
- it { should eql(response) }
93
- end
94
-
95
- context 'because it raised an uncaught exception' do
96
- let(:processor_1) {
97
- Class.new {
98
- include Substation::Processor::Incoming
99
- def call(request)
100
- raise RuntimeError, 'exception_1'
101
- end
102
- }.new(processor_1_name, handler, failure_chain)
103
- }
104
-
105
- let(:response) { Response::Failure.new(request, data) }
106
- let(:data) { Chain::FailureData.new(request, RuntimeError.new('exception_1')) }
107
-
108
- it { should eql(response) }
109
-
110
- it 'wraps the original exception instance' do
111
- expect(subject.output.exception.message).to eql('exception_1')
9
+ let(:object) { described_class.new(handlers) }
10
+ let(:handlers) { [ handler_1, handler_2 ] }
11
+ let(:request) { Request.new(env, input) }
12
+ let(:env) { double }
13
+ let(:input) { double }
14
+
15
+ let(:handler_2) {
16
+ Class.new {
17
+ include Substation::Chain::Outgoing
18
+ def call(request)
19
+ request.success(request.input)
112
20
  end
21
+ }.new
22
+ }
113
23
 
114
- it 'calls the failure chain' do
115
- exception_chain.should_receive(:call).with(response)
116
- subject
117
- end
118
- end
119
- end
24
+ let(:response) { response_class.new(request, request.input) }
120
25
 
121
- context 'when the pivot processor is not successful' do
122
- let(:processor_1) {
26
+ context "when all handlers are successful" do
27
+ let(:handler_1) {
123
28
  Class.new {
124
- include Substation::Processor::Incoming
29
+ include Substation::Chain::Incoming
125
30
  def call(request)
126
- request.success(:success_1)
127
- end
128
- }.new(processor_1_name, handler, failure_chain)
129
- }
130
-
131
- let(:processor_3) {
132
- Class.new {
133
- include Substation::Processor::Outgoing
134
- def call(response)
135
- response
31
+ request.success(request.input)
136
32
  end
137
- }.new(processor_3_name, handler)
33
+ }.new
138
34
  }
139
35
 
140
- let(:response_class) { Response::Failure }
141
-
142
- context 'because it returned a failure response' do
143
- let(:processor_2) {
144
- Class.new {
145
- include Substation::Processor::Pivot
146
- def call(request)
147
- request.error(:error_2)
148
- end
149
- }.new(processor_2_name, handler, failure_chain)
150
- }
36
+ let(:response_class) { Response::Success }
151
37
 
152
- let(:response) { Response::Failure.new(current_request, :error_2) }
153
- let(:current_request) { Request.new(name, env, :success_1) }
154
-
155
- it { should eql(response) }
38
+ before do
39
+ handler_2.should_receive(:call).with(request).and_return(response)
156
40
  end
157
41
 
158
- context 'because it raised an uncaught exception' do
159
- let(:processor_2) {
160
- Class.new {
161
- include Substation::Processor::Pivot
162
- def call(request)
163
- raise RuntimeError, 'exception_2'
164
- end
165
- }.new(processor_2_name, handler, failure_chain)
166
- }
167
-
168
- let(:response) { Response::Failure.new(request, data) }
169
- let(:data) { Chain::FailureData.new(current_request, RuntimeError.new('exception_2')) }
170
- let(:current_request) { Request.new(name, env, :success_1) }
171
-
172
- it { should eql(response) }
173
-
174
- it 'wraps the original exception instance' do
175
- expect(subject.output.exception.message).to eql('exception_2')
176
- end
177
-
178
- it 'calls the failure chain' do
179
- exception_chain.should_receive(:call).with(response)
180
- subject
181
- end
182
- end
42
+ it { should eql(response) }
183
43
  end
184
44
 
185
- context 'when an outgoing processor is not successful' do
186
- let(:processor_1) {
45
+ context "when an intermediate handler is not successful" do
46
+ let(:handler_1) {
187
47
  Class.new {
188
- include Substation::Processor::Incoming
48
+ include Substation::Chain::Incoming
189
49
  def call(request)
190
- request.success(:success_1)
191
- end
192
- }.new(processor_1_name, handler, failure_chain)
193
- }
194
-
195
- let(:processor_2) {
196
- Class.new {
197
- include Substation::Processor::Pivot
198
- def call(response)
199
- response.success(:success_2)
50
+ request.error(request.input)
200
51
  end
201
- }.new(processor_2_name, handler, failure_chain)
52
+ }.new
202
53
  }
203
54
 
204
55
  let(:response_class) { Response::Failure }
205
56
 
206
- context 'because it raised an uncaught exception' do
207
- let(:processor_3) {
208
- Class.new {
209
- include Substation::Processor::Outgoing
210
- def call(response)
211
- raise RuntimeError, 'exception_3'
212
- end
213
- }.new(processor_3_name, handler)
214
- }
215
-
216
- let(:response) { Response::Failure.new(request, data) }
217
- let(:data) { Chain::FailureData.new(current_response, RuntimeError.new('exception_3')) }
218
- let(:current_request) { Request.new(name, env, :success_1) }
219
- let(:current_response) { Response::Success.new(current_request, :success_2) }
220
-
221
- it { should eql(response) }
222
-
223
- it 'wraps the original exception instance' do
224
- expect(subject.output.exception.message).to eql('exception_3')
225
- end
226
-
227
- it 'calls the failure chain' do
228
- exception_chain.should_receive(:call).with(response)
229
- subject
230
- end
57
+ before do
58
+ handler_2.should_not_receive(:call)
231
59
  end
60
+
61
+ it { should eql(response) }
232
62
  end
233
63
  end