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.
- data/.travis.yml +4 -0
- data/Changelog.md +92 -2
- data/Gemfile.devtools +19 -17
- data/README.md +116 -46
- data/config/flay.yml +2 -2
- data/config/mutant.yml +1 -0
- data/config/reek.yml +11 -5
- data/config/rubocop.yml +35 -0
- data/lib/substation.rb +10 -1
- data/lib/substation/chain.rb +108 -64
- data/lib/substation/chain/dsl.rb +62 -37
- data/lib/substation/dispatcher.rb +3 -1
- data/lib/substation/environment.rb +9 -6
- data/lib/substation/environment/dsl.rb +4 -3
- data/lib/substation/observer.rb +2 -0
- data/lib/substation/processor.rb +106 -7
- data/lib/substation/processor/evaluator.rb +98 -7
- data/lib/substation/processor/transformer.rb +26 -0
- data/lib/substation/processor/wrapper.rb +5 -3
- data/lib/substation/request.rb +12 -1
- data/lib/substation/response.rb +13 -0
- data/lib/substation/utils.rb +3 -1
- data/lib/substation/version.rb +3 -1
- data/spec/integration/substation/dispatcher/call_spec.rb +12 -12
- data/spec/spec_helper.rb +39 -32
- data/spec/unit/substation/chain/call_spec.rb +205 -29
- data/spec/unit/substation/chain/class_methods/failure_response_spec.rb +16 -0
- data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +7 -4
- data/spec/unit/substation/chain/dsl/class_methods/build_spec.rb +24 -0
- data/spec/unit/substation/chain/dsl/failure_chain_spec.rb +35 -0
- data/spec/unit/substation/chain/dsl/processors_spec.rb +8 -6
- data/spec/unit/substation/chain/dsl/use_spec.rb +1 -1
- data/spec/unit/substation/chain/each_spec.rb +5 -9
- data/spec/unit/substation/chain/failure_data/equalizer_spec.rb +46 -0
- data/spec/unit/substation/chain/failure_data/hash_spec.rb +13 -0
- data/spec/unit/substation/dispatcher/action/call_spec.rb +2 -1
- data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +7 -5
- data/spec/unit/substation/dispatcher/call_spec.rb +2 -2
- data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +6 -6
- data/spec/unit/substation/environment/chain_spec.rb +22 -27
- data/spec/unit/substation/environment/class_methods/build_spec.rb +11 -4
- data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +5 -3
- data/spec/unit/substation/environment/dsl/register_spec.rb +8 -3
- data/spec/unit/substation/environment/dsl/registry_spec.rb +5 -3
- data/spec/unit/substation/environment/equalizer_spec.rb +25 -0
- data/spec/unit/substation/observer/chain/call_spec.rb +2 -0
- data/spec/unit/substation/observer/class_methods/coerce_spec.rb +2 -0
- data/spec/unit/substation/observer/null/call_spec.rb +2 -0
- data/spec/unit/substation/processor/evaluator/call_spec.rb +20 -10
- data/spec/unit/substation/processor/evaluator/class_methods/new_spec.rb +9 -0
- data/spec/unit/substation/processor/evaluator/data/call_spec.rb +34 -0
- data/spec/unit/substation/processor/evaluator/pivot/call_spec.rb +34 -0
- data/spec/unit/substation/processor/evaluator/request/call_spec.rb +34 -0
- data/spec/unit/substation/processor/fallible/name_spec.rb +15 -0
- data/spec/unit/substation/processor/fallible/with_failure_chain_spec.rb +18 -0
- data/spec/unit/substation/processor/incoming/result_spec.rb +25 -0
- data/spec/unit/substation/processor/outgoing/call_spec.rb +28 -0
- data/spec/unit/substation/processor/outgoing/name_spec.rb +14 -0
- data/spec/unit/substation/processor/outgoing/success_predicate_spec.rb +15 -0
- data/spec/unit/substation/{chain/outgoing → processor}/result_spec.rb +4 -3
- data/spec/unit/substation/processor/success_predicate_spec.rb +22 -0
- data/spec/unit/substation/processor/transformer/call_spec.rb +21 -0
- data/spec/unit/substation/processor/wrapper/call_spec.rb +9 -7
- data/spec/unit/substation/request/env_spec.rb +3 -2
- data/spec/unit/substation/request/error_spec.rb +2 -1
- data/spec/unit/substation/request/input_spec.rb +3 -2
- data/spec/unit/substation/request/name_spec.rb +15 -0
- data/spec/unit/substation/request/success_spec.rb +2 -1
- data/spec/unit/substation/response/env_spec.rb +3 -2
- data/spec/unit/substation/response/failure/success_predicate_spec.rb +2 -1
- data/spec/unit/substation/response/input_spec.rb +3 -2
- data/spec/unit/substation/response/output_spec.rb +2 -1
- data/spec/unit/substation/response/request_spec.rb +3 -2
- data/spec/unit/substation/response/success/success_predicate_spec.rb +2 -1
- data/spec/unit/substation/response/to_request_spec.rb +19 -0
- data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +14 -12
- data/spec/unit/substation/utils/class_methods/const_get_spec.rb +6 -6
- data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +4 -4
- metadata +25 -9
- data/lib/substation/processor/pivot.rb +0 -25
- data/spec/unit/substation/chain/class_methods/build_spec.rb +0 -31
- data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -23
- data/spec/unit/substation/chain/incoming/result_spec.rb +0 -21
- data/spec/unit/substation/chain/outgoing/call_spec.rb +0 -25
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
data/lib/substation/observer.rb
CHANGED
data/lib/substation/processor.rb
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
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
|
-
#
|
|
6
|
+
# Abstract processor to evaluate a request coming into a chain
|
|
5
7
|
class Evaluator
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
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 =
|
|
20
|
-
output = result.output
|
|
68
|
+
result = invoke(request)
|
|
21
69
|
if result.success?
|
|
22
|
-
request
|
|
70
|
+
on_success(request, result)
|
|
23
71
|
else
|
|
24
|
-
request
|
|
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
|
|
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.
|
|
21
|
+
respond_with(response, handler.new(response.data))
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
end # class Wrapper
|
data/lib/substation/request.rb
CHANGED
|
@@ -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
|
data/lib/substation/response.rb
CHANGED
|
@@ -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
|
|