substation 0.0.10.beta2 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. data/.travis.yml +7 -3
  2. data/Changelog.md +99 -24
  3. data/Gemfile +8 -1
  4. data/Gemfile.devtools +37 -21
  5. data/Guardfile +1 -1
  6. data/README.md +118 -46
  7. data/TODO.md +1 -0
  8. data/config/flay.yml +2 -2
  9. data/config/flog.yml +1 -1
  10. data/config/reek.yml +41 -16
  11. data/config/rubocop.yml +44 -0
  12. data/config/yardstick.yml +1 -1
  13. data/lib/substation.rb +41 -5
  14. data/lib/substation/chain.rb +73 -84
  15. data/lib/substation/chain/definition.rb +147 -0
  16. data/lib/substation/chain/dsl.rb +150 -112
  17. data/lib/substation/chain/dsl/config.rb +55 -0
  18. data/lib/substation/chain/dsl/module_builder.rb +84 -0
  19. data/lib/substation/dispatcher.rb +20 -229
  20. data/lib/substation/dsl/guard.rb +96 -0
  21. data/lib/substation/dsl/registry.rb +181 -0
  22. data/lib/substation/environment.rb +126 -23
  23. data/lib/substation/environment/dsl.rb +31 -12
  24. data/lib/substation/processor.rb +238 -7
  25. data/lib/substation/processor/builder.rb +26 -0
  26. data/lib/substation/processor/config.rb +24 -0
  27. data/lib/substation/processor/evaluator.rb +66 -42
  28. data/lib/substation/processor/evaluator/handler.rb +54 -0
  29. data/lib/substation/processor/evaluator/result.rb +24 -0
  30. data/lib/substation/processor/executor.rb +46 -0
  31. data/lib/substation/processor/nest.rb +40 -0
  32. data/lib/substation/processor/transformer.rb +46 -0
  33. data/lib/substation/processor/wrapper.rb +16 -5
  34. data/lib/substation/request.rb +29 -27
  35. data/lib/substation/response.rb +19 -34
  36. data/lib/substation/response/api.rb +37 -0
  37. data/lib/substation/response/exception.rb +20 -0
  38. data/lib/substation/response/exception/output.rb +59 -0
  39. data/lib/substation/response/failure.rb +11 -0
  40. data/lib/substation/response/success.rb +11 -0
  41. data/lib/substation/version.rb +3 -1
  42. data/spec/demo/core.rb +64 -0
  43. data/spec/demo/core/action.rb +49 -0
  44. data/spec/demo/core/action/create_person.rb +28 -0
  45. data/spec/demo/core/errors.rb +16 -0
  46. data/spec/demo/core/facade.rb +38 -0
  47. data/spec/demo/core/handler.rb +21 -0
  48. data/spec/demo/core/handler/acceptor.rb +47 -0
  49. data/spec/demo/core/handler/authenticator.rb +36 -0
  50. data/spec/demo/core/handler/authorizer.rb +38 -0
  51. data/spec/demo/core/input.rb +15 -0
  52. data/spec/demo/core/observers.rb +10 -0
  53. data/spec/demo/core/validator.rb +21 -0
  54. data/spec/demo/domain/actor.rb +29 -0
  55. data/spec/demo/domain/dto/person.rb +18 -0
  56. data/spec/demo/domain/environment.rb +55 -0
  57. data/spec/demo/domain/storage.rb +49 -0
  58. data/spec/demo/web.rb +26 -0
  59. data/spec/demo/web/errors.rb +9 -0
  60. data/spec/demo/web/facade.rb +60 -0
  61. data/spec/demo/web/handler/deserializer.rb +36 -0
  62. data/spec/demo/web/presenter.rb +38 -0
  63. data/spec/demo/web/presenter/person.rb +19 -0
  64. data/spec/demo/web/renderer.rb +45 -0
  65. data/spec/demo/web/sanitizer.rb +35 -0
  66. data/spec/demo/web/sanitizer/person.rb +20 -0
  67. data/spec/demo/web/views.rb +28 -0
  68. data/spec/integration/demo/core_spec.rb +97 -0
  69. data/spec/integration/demo/web_spec.rb +114 -0
  70. data/spec/shared/context/integration/demo.rb +33 -0
  71. data/spec/shared/context/unit/chain.rb +13 -0
  72. data/spec/shared/context/unit/processor.rb +58 -0
  73. data/spec/shared/context/unit/request.rb +8 -0
  74. data/spec/shared/examples/integration/demo.rb +35 -0
  75. data/spec/shared/examples/unit/processor.rb +72 -0
  76. data/spec/spec_helper.rb +52 -23
  77. data/spec/unit/substation/chain/definition_spec.rb +141 -0
  78. data/spec/unit/substation/chain/dsl/config/dsl_module_spec.rb +13 -0
  79. data/spec/unit/substation/chain/dsl/config/registry_spec.rb +13 -0
  80. data/spec/unit/substation/chain/dsl/config_spec.rb +18 -0
  81. data/spec/unit/substation/chain/dsl/module_builder_spec.rb +77 -0
  82. data/spec/unit/substation/chain/dsl_spec.rb +175 -0
  83. data/spec/unit/substation/chain_spec.rb +303 -0
  84. data/spec/unit/substation/dispatcher_spec.rb +68 -0
  85. data/spec/unit/substation/dsl/guard_spec.rb +72 -0
  86. data/spec/unit/substation/dsl/registry_spec.rb +181 -0
  87. data/spec/unit/substation/environment/dsl_spec.rb +156 -0
  88. data/spec/unit/substation/environment_spec.rb +259 -0
  89. data/spec/unit/substation/processor/builder_spec.rb +21 -0
  90. data/spec/unit/substation/processor/config_spec.rb +40 -0
  91. data/spec/unit/substation/processor/evaluator/handler_spec.rb +20 -0
  92. data/spec/unit/substation/processor/evaluator/pivot_spec.rb +42 -0
  93. data/spec/unit/substation/processor/evaluator/request_spec.rb +11 -0
  94. data/spec/unit/substation/processor/evaluator/result/failure_spec.rb +14 -0
  95. data/spec/unit/substation/processor/evaluator/result/success_spec.rb +14 -0
  96. data/spec/unit/substation/processor/evaluator/result_spec.rb +13 -0
  97. data/spec/unit/substation/processor/evaluator_spec.rb +18 -0
  98. data/spec/unit/substation/processor/executor/null_spec.rb +25 -0
  99. data/spec/unit/substation/processor/executor_spec.rb +32 -0
  100. data/spec/unit/substation/processor/fallible_spec.rb +24 -0
  101. data/spec/unit/substation/processor/incoming_spec.rb +17 -0
  102. data/spec/unit/substation/processor/nest/incoming_spec.rb +56 -0
  103. data/spec/unit/substation/processor/nest_spec.rb +6 -0
  104. data/spec/unit/substation/processor/outgoing_spec.rb +47 -0
  105. data/spec/unit/substation/processor/transformer/incoming_spec.rb +17 -0
  106. data/spec/unit/substation/processor/transformer/outgoing_spec.rb +17 -0
  107. data/spec/unit/substation/processor/wrapper/incoming_spec.rb +15 -0
  108. data/spec/unit/substation/processor/wrapper/outgoing_spec.rb +15 -0
  109. data/spec/unit/substation/processor/wrapper_spec.rb +24 -0
  110. data/spec/unit/substation/processor_spec.rb +68 -0
  111. data/spec/unit/substation/request_spec.rb +70 -0
  112. data/spec/unit/substation/response/api_spec.rb +22 -0
  113. data/spec/unit/substation/response/exception/output_spec.rb +46 -0
  114. data/spec/unit/substation/response/exception_spec.rb +25 -0
  115. data/spec/unit/substation/response/failure_spec.rb +25 -0
  116. data/spec/unit/substation/response/success_spec.rb +24 -0
  117. data/spec/unit/substation/response_spec.rb +73 -0
  118. data/substation.gemspec +7 -6
  119. metadata +157 -67
  120. checksums.yaml +0 -7
  121. data/TODO +0 -0
  122. data/lib/substation/observer.rb +0 -66
  123. data/lib/substation/processor/pivot.rb +0 -25
  124. data/lib/substation/utils.rb +0 -68
  125. data/spec/integration/substation/dispatcher/call_spec.rb +0 -260
  126. data/spec/unit/substation/chain/call_spec.rb +0 -63
  127. data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +0 -19
  128. data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +0 -21
  129. data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +0 -30
  130. data/spec/unit/substation/chain/dsl/chain_spec.rb +0 -15
  131. data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -24
  132. data/spec/unit/substation/chain/dsl/initialize_spec.rb +0 -19
  133. data/spec/unit/substation/chain/dsl/processors_spec.rb +0 -42
  134. data/spec/unit/substation/chain/dsl/use_spec.rb +0 -14
  135. data/spec/unit/substation/chain/each_spec.rb +0 -46
  136. data/spec/unit/substation/chain/incoming/result_spec.rb +0 -21
  137. data/spec/unit/substation/chain/outgoing/call_spec.rb +0 -25
  138. data/spec/unit/substation/chain/outgoing/result_spec.rb +0 -21
  139. data/spec/unit/substation/dispatcher/action/call_spec.rb +0 -23
  140. data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +0 -61
  141. data/spec/unit/substation/dispatcher/action_names_spec.rb +0 -14
  142. data/spec/unit/substation/dispatcher/call_spec.rb +0 -47
  143. data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +0 -20
  144. data/spec/unit/substation/environment/chain_spec.rb +0 -50
  145. data/spec/unit/substation/environment/class_methods/build_spec.rb +0 -11
  146. data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +0 -18
  147. data/spec/unit/substation/environment/dsl/register_spec.rb +0 -14
  148. data/spec/unit/substation/environment/dsl/registry_spec.rb +0 -19
  149. data/spec/unit/substation/observer/chain/call_spec.rb +0 -26
  150. data/spec/unit/substation/observer/class_methods/coerce_spec.rb +0 -33
  151. data/spec/unit/substation/observer/null/call_spec.rb +0 -12
  152. data/spec/unit/substation/processor/evaluator/call_spec.rb +0 -49
  153. data/spec/unit/substation/processor/pivot/call_spec.rb +0 -17
  154. data/spec/unit/substation/processor/wrapper/call_spec.rb +0 -20
  155. data/spec/unit/substation/request/env_spec.rb +0 -14
  156. data/spec/unit/substation/request/error_spec.rb +0 -15
  157. data/spec/unit/substation/request/input_spec.rb +0 -14
  158. data/spec/unit/substation/request/success_spec.rb +0 -15
  159. data/spec/unit/substation/response/env_spec.rb +0 -16
  160. data/spec/unit/substation/response/failure/success_predicate_spec.rb +0 -15
  161. data/spec/unit/substation/response/input_spec.rb +0 -16
  162. data/spec/unit/substation/response/output_spec.rb +0 -16
  163. data/spec/unit/substation/response/request_spec.rb +0 -16
  164. data/spec/unit/substation/response/success/success_predicate_spec.rb +0 -15
  165. data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +0 -46
  166. data/spec/unit/substation/utils/class_methods/const_get_spec.rb +0 -46
  167. data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +0 -20
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+ module Sanitizer
6
+
7
+ NEW_PERSON = Ducktrap.build do
8
+ primitive(Hash)
9
+ hash_transform do
10
+ fetch_key('name') do
11
+ primitive(String)
12
+ dump_key(:name)
13
+ end
14
+ end
15
+ add(ID_TRAP)
16
+ anima_load(Domain::DTO::Person)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+
6
+ module Views
7
+
8
+ module Layout
9
+ def page_title
10
+ 'substation demo'
11
+ end
12
+ end
13
+
14
+ class Person < Presenter
15
+ include Layout
16
+ end
17
+
18
+ class SanitizationError
19
+ include Concord::Public.new(:error)
20
+ include Layout
21
+ end
22
+
23
+ class InternalError
24
+ include Concord::Public.new(:data)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'demo/core'
6
+
7
+ describe 'a typical substation application' do
8
+ subject { object.call(name, input) }
9
+
10
+ let(:object) { Demo::Core::APP }
11
+
12
+ include_context 'demo application'
13
+ include_context 'with registered chains'
14
+
15
+ let(:input) { Demo::Core::Input::Incomplete.new(session_data, person) }
16
+
17
+ context 'with valid input' do
18
+ let(:person_name) { 'John' }
19
+ let(:account_id) { authorized_id }
20
+ let(:processed_input) { accepted_input }
21
+ let(:response) { Substation::Response::Success.new(processed_request, person) }
22
+
23
+ it_behaves_like 'an action invocation' do
24
+ let(:action_response) { response }
25
+ let(:success_status) { true }
26
+ end
27
+ end
28
+
29
+ context 'with an input that produces an application error' do
30
+ let(:person_name) { 'error' }
31
+ let(:account_id) { authorized_id }
32
+ let(:processed_input) { accepted_input }
33
+ let(:error) { Demo::Core::Error::ApplicationError.new(person) }
34
+ let(:response) { error_response }
35
+
36
+ it_behaves_like 'an action invocation' do
37
+ let(:action_response) { Substation::Response::Failure.new(processed_request, person) }
38
+ let(:success_status) { false }
39
+ end
40
+ end
41
+
42
+ context 'with an input that raises an exception while processing' do
43
+ let(:person_name) { 'exception' }
44
+ let(:account_id) { authorized_id }
45
+ let(:processed_input) { accepted_input }
46
+ let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
47
+ let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
48
+ let(:response) { exception_response }
49
+
50
+ it_behaves_like 'no action invocation'
51
+ end
52
+
53
+ context 'with invalid input' do
54
+ let(:person_name) { 'X' }
55
+ let(:account_id) { authorized_id }
56
+ let(:processed_input) { incomplete_input }
57
+ let(:error) { Demo::Core::Error::ValidationError.new(invalid_input) }
58
+ let(:invalid_input) { Demo::Core::Input::Incomplete.new(session_data, validation_error) }
59
+ let(:validation_error) { Demo::Domain::DTO::NEW_PERSON_VALIDATOR.call(person).output }
60
+ let(:response) { error_response }
61
+
62
+ it_behaves_like 'no action invocation'
63
+ end
64
+
65
+ context 'with malformed input' do
66
+ let(:person) { :foo }
67
+ let(:account_id) { authorized_id }
68
+ let(:processed_input) { incomplete_input }
69
+ let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
70
+ let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
71
+ let(:response) { exception_response }
72
+
73
+ pending 'the response is returned is actually correct, but somehow #eql? fails' do
74
+ it_behaves_like 'no action invocation'
75
+ end
76
+ end
77
+
78
+ context 'with input from an unknown user' do
79
+ let(:person_name) { 'unknown' }
80
+ let(:account_id) { unknown_id }
81
+ let(:processed_input) { incomplete_input }
82
+ let(:error) { Demo::Core::Error::AuthenticationError.new(processed_input) }
83
+ let(:response) { error_response }
84
+
85
+ it_behaves_like 'no action invocation'
86
+ end
87
+
88
+ context 'with input from an unauthorized user' do
89
+ let(:person_name) { 'forbidden' }
90
+ let(:account_id) { unauthorized_id }
91
+ let(:processed_input) { incomplete_input }
92
+ let(:error) { Demo::Core::Error::AuthorizationError.new(processed_input) }
93
+ let(:response) { error_response }
94
+
95
+ it_behaves_like 'no action invocation'
96
+ end
97
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'demo/web'
6
+
7
+ describe 'a typical substation application' do
8
+ subject { object.call(name, input) }
9
+
10
+ let(:object) { Demo::Web::HTML::APP }
11
+
12
+ include_context 'demo application'
13
+ include_context 'with registered chains'
14
+
15
+ let(:input) {
16
+ {
17
+ 'session' => MultiJson.dump(session_data),
18
+ 'data' => MultiJson.dump(data)
19
+ }
20
+ }
21
+
22
+ let(:data) { { 'name' => person_name } }
23
+ let(:rendered_error_response) { Substation::Response::Failure.new(processed_request, rendered) }
24
+ let(:rendered_exception_response) { Substation::Response::Exception.new(processed_request, rendered) }
25
+
26
+ context 'with valid input' do
27
+ let(:person_name) { 'John' }
28
+ let(:account_id) { authorized_id }
29
+ let(:processed_input) { accepted_input }
30
+ let(:presenter) { Demo::Web::Presenter::Person.new(person) }
31
+ let(:view) { Demo::Web::Views::Person.new(presenter) }
32
+ let(:response) { Substation::Response::Success.new(processed_request, view) }
33
+
34
+ it_behaves_like 'an action invocation' do
35
+ let(:action_response) { Substation::Response::Success.new(processed_request, person) }
36
+ let(:success_status) { true }
37
+ end
38
+ end
39
+
40
+ context 'with an input that produces an application error' do
41
+ let(:person_name) { 'error' }
42
+ let(:account_id) { authorized_id }
43
+ let(:processed_input) { accepted_input }
44
+ let(:error) { Demo::Core::Error::ApplicationError.new(person) }
45
+ let(:rendered) { Demo::Web::Renderer::ApplicationError.call(error_response) }
46
+ let(:response) { rendered_error_response }
47
+
48
+ it_behaves_like 'an action invocation' do
49
+ let(:action_response) { Substation::Response::Failure.new(processed_request, person) }
50
+ let(:success_status) { false }
51
+ end
52
+ end
53
+
54
+ context 'with an input that raises an exception while processing' do
55
+ let(:person_name) { 'exception' }
56
+ let(:account_id) { authorized_id }
57
+ let(:processed_input) { accepted_input }
58
+ let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
59
+ let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
60
+ let(:view) { Demo::Web::Views::InternalError.new(error) }
61
+ let(:error_data) { view }
62
+ let(:rendered) { Demo::Web::Renderer::InternalError.call(error_response) }
63
+ let(:response) { rendered_exception_response }
64
+
65
+ it_behaves_like 'no action invocation'
66
+ end
67
+
68
+ context 'with invalid input' do
69
+ let(:person_name) { 'X' }
70
+ let(:account_id) { authorized_id }
71
+ let(:processed_input) { incomplete_input }
72
+ let(:error) { Demo::Core::Error::ValidationError.new(person) }
73
+ let(:rendered) { Demo::Web::Renderer::ValidationError.call(error_response) }
74
+ let(:response) { rendered_error_response }
75
+
76
+ it_behaves_like 'no action invocation'
77
+ end
78
+
79
+ context 'with malformed input' do
80
+ let(:data) { { 'malformed' => 'input' } }
81
+ let(:account_id) { authorized_id }
82
+ let(:processed_input) { incomplete_input }
83
+ let(:incomplete_data) { data }
84
+ let(:error) { Demo::Web::Error::SanitizationError.new(processed_input) }
85
+ let(:view) { Demo::Web::Views::SanitizationError.new(error) }
86
+ let(:error_data) { view }
87
+ let(:rendered) { Demo::Web::Renderer::SanitizationError.call(error_response) }
88
+ let(:response) { rendered_error_response }
89
+
90
+ it_behaves_like 'no action invocation'
91
+ end
92
+
93
+ context 'with input from an unknown user' do
94
+ let(:person_name) { 'unknown' }
95
+ let(:account_id) { unknown_id }
96
+ let(:processed_input) { incomplete_input }
97
+ let(:error) { Demo::Core::Error::AuthenticationError.new(processed_input) }
98
+ let(:rendered) { Demo::Web::Renderer::AuthenticationError.call(error_response) }
99
+ let(:response) { rendered_error_response }
100
+
101
+ it_behaves_like 'no action invocation'
102
+ end
103
+
104
+ context 'with input from an unauthorized user' do
105
+ let(:person_name) { 'forbidden' }
106
+ let(:account_id) { unauthorized_id }
107
+ let(:processed_input) { incomplete_input }
108
+ let(:error) { Demo::Core::Error::AuthorizationError.new(processed_input) }
109
+ let(:rendered) { Demo::Web::Renderer::AuthorizationError.call(error_response) }
110
+ let(:response) { rendered_error_response }
111
+
112
+ it_behaves_like 'no action invocation'
113
+ end
114
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ shared_context 'demo application' do
4
+ let(:request) { Substation::Request.new(name, env, input) }
5
+ let(:name) { :create_person }
6
+ let(:env) { Demo::APP_ENV }
7
+
8
+ let(:session_data) { { 'account_id' => account_id } }
9
+
10
+ let(:authorized_id) { 1 }
11
+ let(:unauthorized_id) { 2 }
12
+ let(:unknown_id) { -1 }
13
+
14
+ let(:processed_request) { Substation::Request.new(name, env, processed_input) }
15
+ let(:actor) { Demo::Domain::Actor.coerce(session_data, acting_person) }
16
+ let(:acting_person) { Demo::Domain::DTO::Person.new(:id => account_id, :name => 'Jane') }
17
+
18
+ let(:accepted_input) { Demo::Core::Input::Accepted.new(actor, person) }
19
+ let(:incomplete_input) { Demo::Core::Input::Incomplete.new(session_data, incomplete_data) }
20
+ let(:incomplete_data) { person }
21
+
22
+ let(:person) { Demo::Domain::DTO::Person.new(:id => nil, :name => person_name) }
23
+
24
+ let(:error_response) { Substation::Response::Failure.new(processed_request, error_data) }
25
+ let(:exception_response) { Substation::Response::Exception.new(processed_request, error_data) }
26
+ let(:error_data) { error }
27
+
28
+ end
29
+
30
+ shared_context 'with registered chains' do
31
+ let(:input) { mock }
32
+ let(:account_id) { authorized_id }
33
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ shared_context 'Chain::DSL#initialize' do
4
+ let(:chain_dsl) { Chain::DSL.build(registry, definition) }
5
+ let(:registry) { Environment::DSL.registry(&env_block) }
6
+ let(:env_block) { ->(_) { register(:test, Spec::Processor) } }
7
+ let(:processor_name) { :test }
8
+
9
+ let(:definition) { Chain::Definition.new(chain_name, processors) }
10
+ let(:chain_name) { double('chain_name') }
11
+ let(:processors) { [processor] }
12
+ let(:processor) { double('processor', :name => :processor) }
13
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ shared_context 'Processor::Executor#initialize' do
4
+ let(:executor) { Processor::Executor.new(decomposer, composer) }
5
+ let(:decomposer) { double('decomposer') }
6
+ let(:composer) { double('composer') }
7
+ let(:decomposed) { double('decomposed') }
8
+ let(:composed) { double('composed') }
9
+ let(:handler_result) { double('handler_result') }
10
+ let(:handler_output) { double('handler_output') }
11
+ let(:handler_success) { true }
12
+
13
+ before do
14
+ allow(handler_result).to receive(:success?).with(no_args).and_return(handler_success)
15
+ allow(handler_result).to receive(:output).with(no_args).and_return(handler_output)
16
+ end
17
+ end
18
+
19
+ shared_context 'Processor::Config#initialize' do
20
+ include_context 'Processor::Executor#initialize'
21
+
22
+ let(:processor_config) { Processor::Config.new(executor, failure_chain, observers) }
23
+ let(:failure_chain) { double('failure_chain') }
24
+ let(:observers) { double('observers') }
25
+ let(:observer) { double('observer') }
26
+ end
27
+
28
+ shared_context 'Processor#initialize' do
29
+ include_context 'Processor::Config#initialize'
30
+
31
+ let(:object) { klass.new(processor_name, handler, processor_config) }
32
+ let(:processor_name) { double('name') }
33
+ let(:handler) { double('handler') }
34
+ let(:failure_response) { double('failure_response') }
35
+ end
36
+
37
+ shared_context 'Processor::Call' do
38
+
39
+ include_context 'Request#initialize'
40
+ include_context 'Processor#initialize'
41
+
42
+ let(:response) { Response::Success.new(request, original_data) }
43
+ let(:original_data) { double('original_data') }
44
+
45
+ let(:expected) { Response::Success.new(request, expected_data) }
46
+ let(:expected_data) { composed }
47
+ end
48
+
49
+ shared_context 'Processor::Builder#initialize' do
50
+ include_context 'Processor::Executor#initialize'
51
+
52
+ let(:processor_builder) {
53
+ Processor::Builder.new(processor_name, processor_klass, executor)
54
+ }
55
+
56
+ let(:processor_name) { :test }
57
+ let(:processor_klass) { Spec::Processor }
58
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ shared_context 'Request#initialize' do
4
+ let(:request) { Request.new(name, env, input) }
5
+ let(:name) { double }
6
+ let(:env) { double }
7
+ let(:input) { double }
8
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'all invocations' do
4
+ it { should eql(response) }
5
+
6
+ it 'indicates success via #success?' do
7
+ expect(subject.success?).to be(success_status)
8
+ end
9
+
10
+ it 'provides the processed input in Response#input' do
11
+ expect(subject.input).to eql(processed_input)
12
+ end
13
+ end
14
+
15
+ shared_examples_for 'an action invocation' do
16
+ it_behaves_like 'all invocations'
17
+
18
+ it 'notifies all observers' do
19
+ expect(Demo::Core::Observers::LOG_EVENT).to receive(:call).with(action_response).ordered
20
+ expect(Demo::Core::Observers::SEND_EMAIL).to receive(:call).with(action_response).ordered
21
+ subject
22
+ end
23
+ end
24
+
25
+ shared_examples_for 'no action invocation' do
26
+ let(:success_status) { false }
27
+
28
+ it_behaves_like 'all invocations'
29
+
30
+ it 'does not notify any observers' do
31
+ expect(Demo::Core::Observers::LOG_EVENT).to_not receive(:call)
32
+ expect(Demo::Core::Observers::SEND_EMAIL).to_not receive(:call)
33
+ subject
34
+ end
35
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'Processor::Call::Outgoing#call' do
4
+ subject { object.call(response) }
5
+
6
+ include_context 'Processor::Call'
7
+
8
+ before do
9
+ expect(decomposer).to receive(:call).with(response).and_return(decomposed)
10
+ expect(handler_result).to_not receive(:success?)
11
+ expect(handler_result).to_not receive(:output)
12
+ expect(composer).to receive(:call).with(response, handler_result).and_return(composed)
13
+ end
14
+
15
+ it { should eql(expected) }
16
+ end
17
+
18
+ shared_examples_for 'Processor::Call::Incoming#call' do
19
+ subject { object.call(request) }
20
+
21
+ include_context 'Processor::Call'
22
+
23
+ before do
24
+ expect(decomposer).to receive(:call).with(request).and_return(decomposed)
25
+ expect(handler_result).to_not receive(:success?)
26
+ expect(handler_result).to_not receive(:output)
27
+ expect(composer).to receive(:call).with(request, handler_result).and_return(composed)
28
+ end
29
+
30
+ it { should eql(expected) }
31
+ end
32
+
33
+ shared_examples_for 'Processor::Evaluator#call' do
34
+ subject { object.call(request) }
35
+
36
+ include_context 'Processor::Call'
37
+
38
+ context 'with a successful handler' do
39
+ let(:handler_success) { true }
40
+ let(:response) { Response::Success.new(request, composed) }
41
+
42
+ before do
43
+ expect(decomposer).to receive(:call).with(request).and_return(decomposed)
44
+ expect(handler).to receive(:call).with(decomposed).and_return(handler_result)
45
+ expect(observers).to receive(:each).with(no_args).and_yield(observer)
46
+ expect(observer).to receive(:call).with(handler_result)
47
+ expect(handler_result).to receive(:success?)
48
+ expect(handler_result).to receive(:output)
49
+ expect(composer).to receive(:call).with(request, handler_output).and_return(composed)
50
+ end
51
+
52
+ it { should eql(expected) }
53
+ end
54
+
55
+ context 'with a failing handler' do
56
+ let(:handler_success) { false }
57
+ let(:response) { Response::Failure.new(request, composed) }
58
+
59
+ before do
60
+ expect(decomposer).to receive(:call).with(request).and_return(decomposed)
61
+ expect(handler).to receive(:call).with(decomposed).and_return(handler_result)
62
+ expect(observers).to receive(:each).with(no_args).and_yield(observer)
63
+ expect(observer).to receive(:call).with(handler_result)
64
+ expect(handler_result).to receive(:success?)
65
+ expect(handler_result).to receive(:output)
66
+ expect(composer).to receive(:call).with(request, handler_output).and_return(composed)
67
+ expect(failure_chain).to receive(:call).with(response).and_return(failure_response)
68
+ end
69
+
70
+ it { should eql(failure_response) }
71
+ end
72
+ end