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,54 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+ class Evaluator
6
+
7
+ # Helps returning an api compatible result from custom Evaluator handlers
8
+ module Handler
9
+
10
+ # Return a successful result
11
+ #
12
+ # @param [Object] output
13
+ # the data associated with the result
14
+ #
15
+ # @return [Result::Success]
16
+ #
17
+ # @api private
18
+ def success(output)
19
+ respond_with(Result::Success, output)
20
+ end
21
+
22
+ # Return an errorneous result
23
+ #
24
+ # @param [Object] output
25
+ # the data associated with the result
26
+ #
27
+ # @return [Result::Failure]
28
+ #
29
+ # @api private
30
+ def error(output)
31
+ respond_with(Result::Failure, output)
32
+ end
33
+
34
+ private
35
+
36
+ # Return a new result subclass instance
37
+ #
38
+ # @param [Result::Success, Result::Failure] klass
39
+ # the result class
40
+ #
41
+ # @param [Object] output
42
+ # the data associated with the result
43
+ #
44
+ # @return [Response]
45
+ #
46
+ # @api private
47
+ def respond_with(klass, output)
48
+ klass.new(output)
49
+ end
50
+
51
+ end # module Handler
52
+ end # class Evaluator
53
+ end # module Processor
54
+ end # module Substation
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+ class Evaluator
6
+
7
+ # A result object compatible with the {Evaluator} api contract
8
+ class Result
9
+ include AbstractType
10
+ include Concord::Public.new(:output)
11
+
12
+ # A successful evaluation result
13
+ class Success < self
14
+ include Response::API::Success
15
+ end # class Success
16
+
17
+ # An errorneous evaluation result
18
+ class Failure < self
19
+ include Response::API::Failure
20
+ end # class Failure
21
+ end # class Result
22
+ end # class Evaluator
23
+ end # module Processor
24
+ end # module Substation
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # Supports executing new {Processor} handler instances
7
+ class Executor
8
+
9
+ include Concord.new(:decomposer, :composer)
10
+ include Adamantium::Flat
11
+
12
+ decompose = ->(input) { input }
13
+ compose = ->(input, output) { output }
14
+
15
+ NULL = new(decompose, compose)
16
+
17
+ # Decompose input
18
+ #
19
+ # @param [Object] input
20
+ # the input to decompose
21
+ #
22
+ # @return [Object]
23
+ #
24
+ # @api private
25
+ def decompose(input)
26
+ decomposer.call(input)
27
+ end
28
+
29
+ # Compose input and output
30
+ #
31
+ # @param [Object] input
32
+ # the input to compose from
33
+ #
34
+ # @param [Object] output
35
+ # the output to compose with
36
+ #
37
+ # @return [Object]
38
+ #
39
+ # @api private
40
+ def compose(input, output)
41
+ composer.call(input, output)
42
+ end
43
+
44
+ end # class Executor
45
+ end # module Processor
46
+ end # module Substation
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # Namespace for processor that call a nested chain
7
+ module Nest
8
+
9
+ # An incoming processor that calls a nested chain
10
+ class Incoming
11
+
12
+ include Processor::Incoming
13
+
14
+ # Call the processor
15
+ #
16
+ # @param [Request] request
17
+ # the request to process
18
+ #
19
+ # @return [Response::Success]
20
+ #
21
+ # @api private
22
+ def call(request)
23
+ state = invoke(decompose(request))
24
+
25
+ case state
26
+ when Request
27
+ request.success(compose(request, request.success(state.data)))
28
+ when Response::Success
29
+ request.success(compose(request, state))
30
+ when Response::Failure
31
+ request.error(compose(request, state))
32
+ else
33
+ raise 'Illegal state returned from the invoked handler'
34
+ end
35
+ end
36
+
37
+ end # class Incoming
38
+ end # module Nest
39
+ end # module Processor
40
+ end # module Substation
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # A processor that transforms output data into something else
7
+ module Transformer
8
+
9
+ # A transformer used to transform an incoming request
10
+ class Incoming
11
+ include Processor::Incoming
12
+
13
+ # Call the processor
14
+ #
15
+ # @param [Request] request
16
+ # the request to process
17
+ #
18
+ # @return [Response::Success]
19
+ #
20
+ # @api private
21
+ def call(request)
22
+ request.success(execute(request))
23
+ end
24
+ end
25
+
26
+ # A transformer used to transform an outgoing response
27
+ class Outgoing
28
+ include Processor::Outgoing
29
+
30
+ # Call the processor
31
+ #
32
+ # @param [Response] response
33
+ # the response to process
34
+ #
35
+ # @return [Response]
36
+ # a new instance of the same class as +response+
37
+ #
38
+ # @api private
39
+ def call(response)
40
+ respond_with(response, execute(response))
41
+ end
42
+ end
43
+
44
+ end # class Transformer
45
+ end # module Processor
46
+ end # module Substation
@@ -1,11 +1,22 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
  module Processor
3
5
 
4
6
  # A processor that wraps output data in a new handler instance
5
- class Wrapper
7
+ module Wrapper
8
+
9
+ # A wrapper used to wrap incoming request data
10
+ class Incoming < Transformer::Incoming
11
+ include Wrapper
12
+ end
13
+
14
+ # A wrapper used to wrap outgoing response data
15
+ class Outgoing < Transformer::Outgoing
16
+ include Wrapper
17
+ end
6
18
 
7
- include Outgoing
8
- include Concord.new(:env, :handler)
19
+ private
9
20
 
10
21
  # Wrap response data in an instance of {#handler}
11
22
  #
@@ -15,8 +26,8 @@ module Substation
15
26
  # @return [Response]
16
27
  #
17
28
  # @api private
18
- def call(response)
19
- respond_with(response, handler.new(response.data))
29
+ def invoke(state)
30
+ handler.new(state)
20
31
  end
21
32
 
22
33
  end # class Wrapper
@@ -1,41 +1,27 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Encapsulates the application environment and an input model instance
4
6
  class Request
5
7
 
6
- include Concord.new(:env, :input)
8
+ include Concord::Public.new(:name, :env, :input)
7
9
  include Adamantium::Flat
8
10
 
9
- # The application environment
10
- #
11
- # @example
12
- #
13
- # class SomeUseCase
14
- # def self.call(request)
15
- # request.env
16
- # end
17
- # end
18
- #
19
- # @return [Object]
20
- #
21
- # @api public
22
- attr_reader :env
11
+ # @!attribute [r] name
12
+ # @return [Symbol] the name of the request
23
13
 
24
- # The input passed to an action
25
- #
26
- # @example
27
- #
28
- # class SomeUseCase
29
- # def self.call(request)
30
- # request.input
31
- # end
32
- # end
14
+ # @!attribute [r] env
15
+ # @return [Object] the application environment
16
+
17
+ # @!attribute [r] input
18
+ # @return [Object] the input data
19
+
20
+ # An alias for {#input}
33
21
  #
34
22
  # @return [Object]
35
23
  #
36
- # @api public
37
- attr_reader :input
38
-
24
+ # @api private
39
25
  alias_method :data, :input
40
26
 
41
27
  # Create a new successful response
@@ -80,6 +66,22 @@ module Substation
80
66
  respond_with(Response::Failure, output)
81
67
  end
82
68
 
69
+ # Return self or a new instance with +input+
70
+ #
71
+ # @param [Object] input
72
+ # the input for the new instance
73
+ #
74
+ # @return [self]
75
+ # if +input+ is {Undefined}
76
+ #
77
+ # @return [Request]
78
+ # a new instance with +input+
79
+ #
80
+ # @api private
81
+ def to_request(new_input = Undefined)
82
+ new_input.equal?(Undefined) ? self : self.class.new(name, env, new_input)
83
+ end
84
+
83
85
  private
84
86
 
85
87
  # Instantiate an instance of +klass+ and pass +output+
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Base class for action responses
@@ -129,40 +131,23 @@ module Substation
129
131
  # @api public
130
132
  abstract_method :success?
131
133
 
132
- # An errorneous {Response}
133
- class Failure < self
134
-
135
- # Tests wether this response was successful
136
- #
137
- # @example
138
- #
139
- # response = dispatcher.call(:failing_action, :some_input)
140
- # response.success? # => false
141
- #
142
- # @return [false]
143
- #
144
- # @api public
145
- def success?
146
- false
147
- end
134
+ # Test wether processing raised an exception
135
+ #
136
+ # @return [true]
137
+ #
138
+ # @api private
139
+ def exception?
140
+ false
148
141
  end
149
142
 
150
- # A successful {Response}
151
- class Success < self
152
-
153
- # Tests wether this response was successful
154
- #
155
- # @example
156
- #
157
- # response = dispatcher.call(:successful_action, :some_input)
158
- # response.success? # => true
159
- #
160
- # @return [true]
161
- #
162
- # @api public
163
- def success?
164
- true
165
- end
143
+ # Return a {Request} instance built upon this response
144
+ #
145
+ # @return [Request]
146
+ #
147
+ # @api private
148
+ def to_request(new_input = output)
149
+ Request.new(request.name, env, new_input)
166
150
  end
167
- end
168
- end
151
+
152
+ end # class Response
153
+ end # module Substation
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ class Response
5
+
6
+ # Namespace for modules that help with processor result compatibility
7
+ module API
8
+
9
+ # Indicate successful processing
10
+ module Success
11
+
12
+ # Test wether processing was successful
13
+ #
14
+ # @return [true]
15
+ #
16
+ # @api private
17
+ def success?
18
+ true
19
+ end
20
+ end
21
+
22
+ # Indicate errorneous processing
23
+ module Failure
24
+
25
+ # Test wether processing was successful
26
+ #
27
+ # @return [false]
28
+ #
29
+ # @api private
30
+ def success?
31
+ false
32
+ end
33
+ end
34
+
35
+ end # module API
36
+ end # class Response
37
+ end # module Substation
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ class Response
5
+
6
+ # An exception {Response}
7
+ class Exception < self
8
+ include API::Failure
9
+
10
+ # Test wether processing raised an exception
11
+ #
12
+ # @return [true]
13
+ #
14
+ # @api private
15
+ def exception?
16
+ true
17
+ end
18
+ end # class Exception
19
+ end # class Response
20
+ end # module Substation