substation 0.0.8 → 0.0.9

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.
Files changed (85) hide show
  1. data/.travis.yml +4 -0
  2. data/Changelog.md +92 -2
  3. data/Gemfile.devtools +19 -17
  4. data/README.md +116 -46
  5. data/config/flay.yml +2 -2
  6. data/config/mutant.yml +1 -0
  7. data/config/reek.yml +11 -5
  8. data/config/rubocop.yml +35 -0
  9. data/lib/substation.rb +10 -1
  10. data/lib/substation/chain.rb +108 -64
  11. data/lib/substation/chain/dsl.rb +62 -37
  12. data/lib/substation/dispatcher.rb +3 -1
  13. data/lib/substation/environment.rb +9 -6
  14. data/lib/substation/environment/dsl.rb +4 -3
  15. data/lib/substation/observer.rb +2 -0
  16. data/lib/substation/processor.rb +106 -7
  17. data/lib/substation/processor/evaluator.rb +98 -7
  18. data/lib/substation/processor/transformer.rb +26 -0
  19. data/lib/substation/processor/wrapper.rb +5 -3
  20. data/lib/substation/request.rb +12 -1
  21. data/lib/substation/response.rb +13 -0
  22. data/lib/substation/utils.rb +3 -1
  23. data/lib/substation/version.rb +3 -1
  24. data/spec/integration/substation/dispatcher/call_spec.rb +12 -12
  25. data/spec/spec_helper.rb +39 -32
  26. data/spec/unit/substation/chain/call_spec.rb +205 -29
  27. data/spec/unit/substation/chain/class_methods/failure_response_spec.rb +16 -0
  28. data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +7 -4
  29. data/spec/unit/substation/chain/dsl/class_methods/build_spec.rb +24 -0
  30. data/spec/unit/substation/chain/dsl/failure_chain_spec.rb +35 -0
  31. data/spec/unit/substation/chain/dsl/processors_spec.rb +8 -6
  32. data/spec/unit/substation/chain/dsl/use_spec.rb +1 -1
  33. data/spec/unit/substation/chain/each_spec.rb +5 -9
  34. data/spec/unit/substation/chain/failure_data/equalizer_spec.rb +46 -0
  35. data/spec/unit/substation/chain/failure_data/hash_spec.rb +13 -0
  36. data/spec/unit/substation/dispatcher/action/call_spec.rb +2 -1
  37. data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +7 -5
  38. data/spec/unit/substation/dispatcher/call_spec.rb +2 -2
  39. data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +6 -6
  40. data/spec/unit/substation/environment/chain_spec.rb +22 -27
  41. data/spec/unit/substation/environment/class_methods/build_spec.rb +11 -4
  42. data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +5 -3
  43. data/spec/unit/substation/environment/dsl/register_spec.rb +8 -3
  44. data/spec/unit/substation/environment/dsl/registry_spec.rb +5 -3
  45. data/spec/unit/substation/environment/equalizer_spec.rb +25 -0
  46. data/spec/unit/substation/observer/chain/call_spec.rb +2 -0
  47. data/spec/unit/substation/observer/class_methods/coerce_spec.rb +2 -0
  48. data/spec/unit/substation/observer/null/call_spec.rb +2 -0
  49. data/spec/unit/substation/processor/evaluator/call_spec.rb +20 -10
  50. data/spec/unit/substation/processor/evaluator/class_methods/new_spec.rb +9 -0
  51. data/spec/unit/substation/processor/evaluator/data/call_spec.rb +34 -0
  52. data/spec/unit/substation/processor/evaluator/pivot/call_spec.rb +34 -0
  53. data/spec/unit/substation/processor/evaluator/request/call_spec.rb +34 -0
  54. data/spec/unit/substation/processor/fallible/name_spec.rb +15 -0
  55. data/spec/unit/substation/processor/fallible/with_failure_chain_spec.rb +18 -0
  56. data/spec/unit/substation/processor/incoming/result_spec.rb +25 -0
  57. data/spec/unit/substation/processor/outgoing/call_spec.rb +28 -0
  58. data/spec/unit/substation/processor/outgoing/name_spec.rb +14 -0
  59. data/spec/unit/substation/processor/outgoing/success_predicate_spec.rb +15 -0
  60. data/spec/unit/substation/{chain/outgoing → processor}/result_spec.rb +4 -3
  61. data/spec/unit/substation/processor/success_predicate_spec.rb +22 -0
  62. data/spec/unit/substation/processor/transformer/call_spec.rb +21 -0
  63. data/spec/unit/substation/processor/wrapper/call_spec.rb +9 -7
  64. data/spec/unit/substation/request/env_spec.rb +3 -2
  65. data/spec/unit/substation/request/error_spec.rb +2 -1
  66. data/spec/unit/substation/request/input_spec.rb +3 -2
  67. data/spec/unit/substation/request/name_spec.rb +15 -0
  68. data/spec/unit/substation/request/success_spec.rb +2 -1
  69. data/spec/unit/substation/response/env_spec.rb +3 -2
  70. data/spec/unit/substation/response/failure/success_predicate_spec.rb +2 -1
  71. data/spec/unit/substation/response/input_spec.rb +3 -2
  72. data/spec/unit/substation/response/output_spec.rb +2 -1
  73. data/spec/unit/substation/response/request_spec.rb +3 -2
  74. data/spec/unit/substation/response/success/success_predicate_spec.rb +2 -1
  75. data/spec/unit/substation/response/to_request_spec.rb +19 -0
  76. data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +14 -12
  77. data/spec/unit/substation/utils/class_methods/const_get_spec.rb +6 -6
  78. data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +4 -4
  79. metadata +25 -9
  80. data/lib/substation/processor/pivot.rb +0 -25
  81. data/spec/unit/substation/chain/class_methods/build_spec.rb +0 -31
  82. data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -23
  83. data/spec/unit/substation/chain/incoming/result_spec.rb +0 -21
  84. data/spec/unit/substation/chain/outgoing/call_spec.rb +0 -25
  85. data/spec/unit/substation/processor/pivot/call_spec.rb +0 -16
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Encapsulates all registered actions and their observers
@@ -249,7 +251,7 @@ module Substation
249
251
  #
250
252
  # @api public
251
253
  def call(name, input)
252
- fetch(name).call(Request.new(env, input))
254
+ fetch(name).call(Request.new(name, env, input))
253
255
  end
254
256
 
255
257
  # The names of all registered actions
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # The environment holding all registered {Chain} processors
@@ -15,7 +17,9 @@ module Substation
15
17
  #
16
18
  # @api private
17
19
  def self.build(&block)
18
- new(DSL.registry(&block))
20
+ registry = DSL.registry(&block)
21
+ chain_dsl = Chain::DSL::Builder.call(registry)
22
+ new(registry, chain_dsl)
19
23
  end
20
24
 
21
25
  # Initialize a new instance
@@ -26,9 +30,8 @@ module Substation
26
30
  # @return [undefined]
27
31
  #
28
32
  # @api private
29
- def initialize(registry)
30
- @registry = registry
31
- @chain_dsl = Chain::DSL::Builder.call(@registry)
33
+ def initialize(registry, chain_dsl)
34
+ @registry, @chain_dsl = registry, chain_dsl
32
35
  end
33
36
 
34
37
  # Build a new {Chain} instance
@@ -42,8 +45,8 @@ module Substation
42
45
  # @return [Chain]
43
46
  #
44
47
  # @api private
45
- def chain(other = Chain::EMPTY, &block)
46
- Chain.build(@chain_dsl, other, &block)
48
+ def chain(other = Chain::EMPTY, failure_chain = Chain::EMPTY, &block)
49
+ @chain_dsl.build(other, failure_chain, &block)
47
50
  end
48
51
 
49
52
  protected
@@ -1,7 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
  class Environment
3
5
 
4
- # The DSL class used to define register processors
6
+ # The DSL class used to register processors
5
7
  class DSL
6
8
 
7
9
  # The registry of processors
@@ -11,7 +13,6 @@ module Substation
11
13
  # @api private
12
14
  attr_reader :registry
13
15
 
14
-
15
16
  # The registry of processors
16
17
  #
17
18
  # @param [Proc] block
@@ -49,7 +50,7 @@ module Substation
49
50
  #
50
51
  # @api private
51
52
  def register(name, processor)
52
- @registry[name] = processor
53
+ @registry[name.to_sym] = processor
53
54
  self
54
55
  end
55
56
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Abstract observer base class
@@ -1,23 +1,122 @@
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
+ # Test wether chain processing should continue
9
+ #
10
+ # @param [Response] response
11
+ # the response returned from invoking the processor
12
+ #
13
+ # @return [true] for a successful response
14
+ # @return [false] otherwise
15
+ #
16
+ # @api private
17
+ def success?(response)
18
+ response.success?
19
+ end
20
+
21
+ # The response passed on to the next processor in a {Chain}
22
+ #
23
+ # @param [Response] response
24
+ # the response returned from invoking the processor
25
+ #
26
+ # @return [Response]
27
+ # the response passed on to the next processor in a {Chain}
28
+ #
29
+ # @api private
30
+ def result(response)
31
+ response
32
+ end
33
+
34
+ module Fallible
35
+ include Concord.new(:name, :handler, :failure_chain)
36
+
37
+ # This processor's name
38
+ #
39
+ # @return [Symbol]
40
+ #
41
+ # @api private
42
+ attr_reader :name
8
43
 
9
- abstract_method :call
10
- abstract_method :result
44
+ # Return a new processor with +chain+ as failure_chain
45
+ #
46
+ # @param [#call] chain
47
+ # the failure chain to use for the new processor
48
+ #
49
+ # @return [#call]
50
+ #
51
+ # @api private
52
+ def with_failure_chain(chain)
53
+ self.class.new(name, handler, chain)
54
+ end
55
+ end
11
56
 
12
57
  module Incoming
13
58
  include Processor
14
- include Chain::Incoming
59
+ include Fallible
60
+
61
+ # The request passed on to the next processor in a {Chain}
62
+ #
63
+ # @param [Response] _response
64
+ # the response returned from invoking this processor
65
+ #
66
+ # @return [Request]
67
+ # the request passed on to the next processor in a {Chain}
68
+ #
69
+ # @api private
70
+ def result(_response)
71
+ super.to_request
72
+ end
73
+ end
74
+
75
+ module Pivot
76
+ include Processor
77
+ include Fallible
15
78
  end
16
79
 
17
80
  module Outgoing
81
+ include Concord.new(:name, :handler)
18
82
  include Processor
19
- include Chain::Outgoing
20
- end
21
83
 
84
+ # This processor's name
85
+ #
86
+ # @return [Symbol]
87
+ #
88
+ # @api private
89
+ attr_reader :name
90
+
91
+ # Test wether chain processing should continue
92
+ #
93
+ # @param [Response] _response
94
+ # the response returned from invoking the processor
95
+ #
96
+ # @return [false]
97
+ #
98
+ # @api private
99
+ def success?(_response)
100
+ true
101
+ end
102
+
103
+ private
104
+
105
+ # Build a new {Response} based on +response+ and +output+
106
+ #
107
+ # @param [Response] response
108
+ # the original response
109
+ #
110
+ # @param [Object] output
111
+ # the data to be wrapped within the new {Response}
112
+ #
113
+ # @return [Response]
114
+ #
115
+ # @api private
116
+ def respond_with(response, output)
117
+ response.class.new(response.request, output)
118
+ end
119
+
120
+ end # module Outgoing
22
121
  end # module Processor
23
122
  end # module Substation
@@ -1,11 +1,60 @@
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
8
- include Concord.new(:handler)
9
+ # Processor to evaluate request input data
10
+ class Data < self
11
+ include Incoming
12
+
13
+ private
14
+
15
+ # Invoke the handler
16
+ #
17
+ # @param [Request] request
18
+ # the request to evaluate
19
+ #
20
+ # @return [Response]
21
+ #
22
+ # @api private
23
+ def invoke(request)
24
+ handler.call(request.input)
25
+ end
26
+
27
+ end
28
+
29
+ # Processor to evaluate an incoming request
30
+ class Request < self
31
+ include Incoming
32
+ end
33
+
34
+ # Processor to evaluate a pivot chain handler
35
+ class Pivot < self
36
+ include Processor::Pivot
37
+
38
+ private
39
+
40
+ # Return a successful response
41
+ #
42
+ # @param [Request] _request
43
+ # the evaluated request
44
+ #
45
+ # @param [#output] result
46
+ # the evaluation result
47
+ #
48
+ # @return [Response::Success]
49
+ #
50
+ # @api private
51
+ def on_success(_request, response)
52
+ response
53
+ end
54
+ end
55
+
56
+ include Adamantium::Flat
57
+ include AbstractType
9
58
 
10
59
  # Evaluate a chain's request input data
11
60
  #
@@ -16,15 +65,57 @@ module Substation
16
65
  #
17
66
  # @api private
18
67
  def call(request)
19
- result = handler.call(request.input)
20
- output = result.output
68
+ result = invoke(request)
21
69
  if result.success?
22
- request.success(output)
70
+ on_success(request, result)
23
71
  else
24
- request.error(output)
72
+ on_failure(request, result)
25
73
  end
26
74
  end
27
75
 
76
+ private
77
+
78
+ # Invoke the handler
79
+ #
80
+ # @param [Request] request
81
+ # the request to evaluate
82
+ #
83
+ # @return [Response]
84
+ #
85
+ # @api private
86
+ def invoke(request)
87
+ handler.call(request)
88
+ end
89
+
90
+ # Return a successful response
91
+ #
92
+ # @param [Request] request
93
+ # the evaluated request
94
+ #
95
+ # @param [#output] result
96
+ # the evaluation result
97
+ #
98
+ # @return [Response::Success]
99
+ #
100
+ # @api private
101
+ def on_success(request, result)
102
+ request.success(result.output)
103
+ end
104
+
105
+ # Return a failure response by invoking a failure chain
106
+ #
107
+ # @param [Request] request
108
+ # the evaluated request
109
+ #
110
+ # @param [#output] result
111
+ # the evaluation result
112
+ #
113
+ # @return [Response::Failure]
114
+ #
115
+ # @api private
116
+ def on_failure(request, result)
117
+ failure_chain.call(request.error(result.output))
118
+ end
28
119
  end # class Evaluator
29
120
  end # module Processor
30
121
  end # module Substation
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Substation
4
+ module Processor
5
+
6
+ # A processor that transforms output data into something else
7
+ class Transformer
8
+
9
+ include Processor::Outgoing
10
+ include Adamantium::Flat
11
+
12
+ # Transform response data into something else
13
+ #
14
+ # @param [Response] response
15
+ # the response to process
16
+ #
17
+ # @return [Response]
18
+ #
19
+ # @api private
20
+ def call(response)
21
+ respond_with(response, handler.call(response))
22
+ end
23
+
24
+ end # class Wrapper
25
+ end # module Processor
26
+ end # module Substation
@@ -1,11 +1,13 @@
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
7
  class Wrapper
6
8
 
7
- include Outgoing
8
- include Concord.new(:handler)
9
+ include Processor::Outgoing
10
+ include Adamantium::Flat
9
11
 
10
12
  # Wrap response data in an instance of {#handler}
11
13
  #
@@ -16,7 +18,7 @@ module Substation
16
18
  #
17
19
  # @api private
18
20
  def call(response)
19
- respond_with(response, handler.new(response.output))
21
+ respond_with(response, handler.new(response.data))
20
22
  end
21
23
 
22
24
  end # class Wrapper
@@ -1,11 +1,20 @@
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.new(:name, :env, :input)
7
9
  include Adamantium::Flat
8
10
 
11
+ # The name of the request
12
+ #
13
+ # @return [Symbol]
14
+ #
15
+ # @api private
16
+ attr_reader :name
17
+
9
18
  # The application environment
10
19
  #
11
20
  # @example
@@ -36,6 +45,8 @@ module Substation
36
45
  # @api public
37
46
  attr_reader :input
38
47
 
48
+ alias_method :data, :input
49
+
39
50
  # Create a new successful response
40
51
  #
41
52
  # @example
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Substation
2
4
 
3
5
  # Base class for action responses
@@ -91,6 +93,8 @@ module Substation
91
93
  # @api public
92
94
  attr_reader :output
93
95
 
96
+ alias_method :data, :output
97
+
94
98
  # Initialize a new instance
95
99
  #
96
100
  # @param [Request] request
@@ -127,6 +131,15 @@ module Substation
127
131
  # @api public
128
132
  abstract_method :success?
129
133
 
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
+
130
143
  # An errorneous {Response}
131
144
  class Failure < self
132
145