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
@@ -1,23 +1,254 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Namespace for chain processors
4
6
  module Processor
5
7
 
6
- include AbstractType
7
- include Adamantium::Flat
8
+ include Equalizer.new(:name, :handler, :config)
9
+
10
+ # FIXME: include this once the bug with deep freezing
11
+ # is resolved (currently this deep freezes and thus
12
+ # tries to incorrectly freeze observers too.
13
+ #
14
+ # include Adamantium::Flat
15
+
16
+ # Initialize a new instance
17
+ #
18
+ # @param [Symbol] name
19
+ # the processor's name
20
+ #
21
+ # @param [Config] config
22
+ # the processor's configuration
23
+ #
24
+ # @return [undefined]
25
+ #
26
+ # @api private
27
+ def initialize(name, handler, config)
28
+ @name = name
29
+ @handler = handler
30
+ @config = config
31
+ @executor = @config.executor
32
+ @observers = @config.observers
33
+ @failure_chain = @config.failure_chain
34
+ end
35
+
36
+ # This processor's name
37
+ #
38
+ # @return [Symbol]
39
+ #
40
+ # @api private
41
+ attr_reader :name
42
+
43
+ # This processor's config
44
+ #
45
+ # @return [Builder::Config]
46
+ #
47
+ # @api private
48
+ attr_reader :config
49
+ protected :config
50
+
51
+ # Return handler
52
+ #
53
+ # @return [#call]
54
+ #
55
+ # @api private
56
+ attr_reader :handler
57
+ protected :handler
58
+
59
+ # Return failure chain
60
+ #
61
+ # @return [Chain]
62
+ #
63
+ # @api private
64
+ attr_reader :failure_chain
65
+ private :failure_chain
66
+
67
+ # Return executor
68
+ #
69
+ # @return [Executor]
70
+ #
71
+ # @api private
72
+ attr_reader :executor
73
+ private :executor
74
+
75
+ # Return the observers
76
+ #
77
+ # @return [Enumerable<#call>]
78
+ #
79
+ # @api private
80
+ attr_reader :observers
81
+ private :observers
82
+
83
+ # Test wether chain processing should continue
84
+ #
85
+ # @param [Response] response
86
+ # the response returned from invoking the processor
87
+ #
88
+ # @return [true] for a successful response
89
+ # @return [false] otherwise
90
+ #
91
+ # @api private
92
+ def success?(response)
93
+ response.success?
94
+ end
95
+
96
+ # The response passed on to the next processor in a {Chain}
97
+ #
98
+ # @param [Response] response
99
+ # the response returned from invoking the processor
100
+ #
101
+ # @return [Response]
102
+ # the response passed on to the next processor in a {Chain}
103
+ #
104
+ # @api private
105
+ def result(response)
106
+ response
107
+ end
108
+
109
+ private
110
+
111
+ # Execute processor on state
112
+ #
113
+ # @param [Object] state
114
+ # the state to execute with
115
+ #
116
+ # @return [Object]
117
+ #
118
+ # @api private
119
+ def execute(state)
120
+ compose(state, invoke(decompose(state)))
121
+ end
122
+
123
+ # Transform response data into something else
124
+ #
125
+ # @param [Response] response
126
+ # the response to process
127
+ #
128
+ # @return [Response]
129
+ #
130
+ # @api private
131
+ def invoke(state)
132
+ notify_observers(handler.call(state))
133
+ end
134
+
135
+ # Decompose +input+ before processing
136
+ #
137
+ # @param [Request, Response] input
138
+ # the object to decompose
139
+ #
140
+ # @return [Object]
141
+ # the decomposed object
142
+ #
143
+ # @api private
144
+ def decompose(input)
145
+ executor.decompose(input)
146
+ end
147
+
148
+ # Compose a new object based on +input+ and +output+
149
+ #
150
+ # @param [Request, Response] input
151
+ # the input as it was before calling {#decompose}
152
+ #
153
+ # @param [Object] output
154
+ # the data used to compose a new object
155
+ #
156
+ # @return [Object]
157
+ # the composed object
158
+ #
159
+ # @api private
160
+ def compose(input, output)
161
+ executor.compose(input, output)
162
+ end
8
163
 
9
- abstract_method :call
10
- abstract_method :result
164
+ # Notify all observers
165
+ #
166
+ # @param [Response] response
167
+ # the response returned from {handler#call}
168
+ #
169
+ # @return [Response]
170
+ #
171
+ # @api private
172
+ def notify_observers(response)
173
+ observers.each { |observer| observer.call(response) }
174
+ response
175
+ end
176
+
177
+ # Supports {Processor} instances with a defined failure {Chain}
178
+ module Fallible
179
+
180
+ # Return a new processor with +chain+ as failure_chain
181
+ #
182
+ # @param [#call] chain
183
+ # the failure chain to use for the new processor
184
+ #
185
+ # @return [#call]
186
+ #
187
+ # @api private
188
+ def with_failure_chain(chain)
189
+ self.class.new(name, handler, config.with_failure_chain(chain))
190
+ end
191
+ end
11
192
 
193
+ # Supports incoming {Processor} instances
12
194
  module Incoming
13
195
  include Processor
14
- include Chain::Incoming
196
+ include Fallible
197
+
198
+ # The request passed on to the next processor in a {Chain}
199
+ #
200
+ # @param [Response] _response
201
+ # the response returned from invoking this processor
202
+ #
203
+ # @return [Request]
204
+ # the request passed on to the next processor in a {Chain}
205
+ #
206
+ # @api private
207
+ def result(_response)
208
+ super.to_request
209
+ end
15
210
  end
16
211
 
17
- module Outgoing
212
+ # Supports pivot {Processor} instances
213
+ module Pivot
18
214
  include Processor
19
- include Chain::Outgoing
215
+ include Fallible
20
216
  end
21
217
 
218
+ # Supports outgoing {Processor} instances
219
+ module Outgoing
220
+ include Processor
221
+
222
+ # Test wether chain processing should continue
223
+ #
224
+ # @param [Response] _response
225
+ # the response returned from invoking the processor
226
+ #
227
+ # @return [true]
228
+ #
229
+ # @api private
230
+ def success?(_response)
231
+ true
232
+ end
233
+
234
+ private
235
+
236
+ # Build a new {Response} based on +response+ and +output+
237
+ #
238
+ # @param [Response] response
239
+ # the original response
240
+ #
241
+ # @param [Object] output
242
+ # the data to be wrapped within the new {Response}
243
+ #
244
+ # @return [Response]
245
+ #
246
+ # @api private
247
+ def respond_with(response, output)
248
+ response.class.new(response.request, output)
249
+ end
250
+
251
+ end # module Outgoing
252
+
22
253
  end # module Processor
23
254
  end # module Substation
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # Supports building new {Processor} instances
7
+ class Builder
8
+
9
+ include Concord.new(:name, :klass, :executor)
10
+
11
+ # Build processor
12
+ #
13
+ # @param [#call] handler
14
+ # @param [Chain] failure_chain
15
+ #
16
+ # @return [Processor]
17
+ #
18
+ # @api private
19
+ def call(handler, failure_chain, observers)
20
+ klass.new(name, handler, Config.new(executor, failure_chain, observers))
21
+ end
22
+
23
+ end # class Builder
24
+
25
+ end # module Processor
26
+ end # module Substation
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # Wraps {Processor} configuration
7
+ class Config
8
+ include Concord::Public.new(:executor, :failure_chain, :observers)
9
+
10
+ # Add failure chain
11
+ #
12
+ # @param [Chain] chain
13
+ #
14
+ # @return [Processor]
15
+ #
16
+ # @api private
17
+ def with_failure_chain(chain)
18
+ self.class.new(executor, chain, observers)
19
+ end
20
+
21
+ end # class Config
22
+
23
+ end # module Processor
24
+ end # module Substation
@@ -1,30 +1,40 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
  module Processor
3
5
 
4
- # A processor to evaluate a chain's request input data
6
+ # Abstract processor to evaluate a request coming into a chain
5
7
  class Evaluator
6
8
 
7
- include Incoming
9
+ # Processor to evaluate an incoming request
10
+ class Request < self
11
+ include Processor::Incoming
12
+ end
8
13
 
9
- # Initialize a new instance
10
- #
11
- # @param [Environment] env
12
- # the substation environment used to build chains
13
- #
14
- # @param [#call] handler
15
- # the handler to perform evaluation
16
- #
17
- # @param [Proc] block
18
- # a block to construct a failure chain
19
- #
20
- # @return [undefined]
21
- #
22
- # @api private
23
- def initialize(env, handler, &block)
24
- @env, @handler = env, handler
25
- @failure_chain = block ? @env.chain(&block) : Undefined
14
+ # Processor to evaluate a pivot chain handler
15
+ class Pivot < self
16
+ include Processor::Pivot
17
+
18
+ private
19
+
20
+ # Return a successful response
21
+ #
22
+ # @param [Request] _request
23
+ # the evaluated request
24
+ #
25
+ # @param [#output] result
26
+ # the evaluation result
27
+ #
28
+ # @return [Response::Success]
29
+ #
30
+ # @api private
31
+ def on_success(_request, response)
32
+ response
33
+ end
26
34
  end
27
35
 
36
+ include AbstractType
37
+
28
38
  # Evaluate a chain's request input data
29
39
  #
30
40
  # @param [Request] request
@@ -34,47 +44,61 @@ module Substation
34
44
  #
35
45
  # @api private
36
46
  def call(request)
37
- result = handler.call(request.input)
38
- output = result.output
47
+ result = invoke(decompose(request))
39
48
  if result.success?
40
- request.success(output)
49
+ on_success(request, result)
41
50
  else
42
- response = request.error(output)
43
- if fail_safe?
44
- failure_chain.call(response)
45
- else
46
- response
47
- end
51
+ on_failure(request, result)
48
52
  end
49
53
  end
50
54
 
51
- protected
55
+ private
52
56
 
53
- # The handler used to perform evaluation
57
+ # Return a successful response
54
58
  #
55
- # @return [#call]
59
+ # @param [Request] request
60
+ # the evaluated request
61
+ #
62
+ # @param [#output] result
63
+ # the evaluation result
64
+ #
65
+ # @return [Response::Success]
56
66
  #
57
67
  # @api private
58
- attr_reader :handler
59
-
60
- private
68
+ def on_success(request, result)
69
+ request.success(output(request, result))
70
+ end
61
71
 
62
- # The chain to invoke if evaluation returned an error
72
+ # Return a failure response by invoking a failure chain
73
+ #
74
+ # @param [Request] request
75
+ # the evaluated request
63
76
  #
64
- # @return [Chain]
77
+ # @param [#output] result
78
+ # the evaluation result
79
+ #
80
+ # @return [Response::Failure]
65
81
  #
66
82
  # @api private
67
- attr_reader :failure_chain
83
+ def on_failure(request, result)
84
+ failure_chain.call(request.error(output(request, result)))
85
+ end
68
86
 
69
- # Test wether this evaluator has a failure chain
87
+ # Return a new composed output
88
+ #
89
+ # @param [Request] request
90
+ # the evaluated request
91
+ #
92
+ # @param [Response] response
93
+ # the evaluation result
70
94
  #
71
- # @return [TrueClass] if a failure chain is registered
72
- # @return [FalseClass] otherwise
95
+ # @return [Object]
73
96
  #
74
97
  # @api private
75
- def fail_safe?
76
- !@failure_chain.equal?(Undefined)
98
+ def output(request, result)
99
+ compose(request, result.output)
77
100
  end
101
+
78
102
  end # class Evaluator
79
103
  end # module Processor
80
104
  end # module Substation