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
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
|
@@ -1,8 +1,98 @@
|
|
|
1
|
-
# v0.0.
|
|
1
|
+
# v0.0.10 (not yet released)
|
|
2
2
|
|
|
3
3
|
*
|
|
4
4
|
|
|
5
|
-
[Compare v0.0.
|
|
5
|
+
[Compare v0.0.9..master](https://github.com/snusnu/substation/compare/v0.0.9...master)
|
|
6
|
+
|
|
7
|
+
# v0.0.9 2013-07-10
|
|
8
|
+
|
|
9
|
+
* [BREAKING CHANGE] Refactor `Substation::Processor` classes.
|
|
10
|
+
|
|
11
|
+
* Renamed `Substation::Processor::Evaluator` to `Substation::Processor::Evaluator::Data`.
|
|
12
|
+
* Renamed `Substation::Processor::Pivot` to `Substation::Processor::Evaluator::Pivot`.
|
|
13
|
+
* Added `Substation::Processor::Evaluator::Request` which passes the complete `Request` instance on to the handler.
|
|
14
|
+
* Added `Substation::Processor::Transformer` to transform `Response#output` into any other object.
|
|
15
|
+
|
|
16
|
+
* [feature] Make the dispatched name available in `Request#name`.
|
|
17
|
+
|
|
18
|
+
* [feature] Support (re)definining failure chains for `Substation::Processor::Fallible` processors.
|
|
19
|
+
|
|
20
|
+
module Demo
|
|
21
|
+
ENV = Substation::Environment.build do
|
|
22
|
+
register :validate, Substation::Processor::Evaluator::Data
|
|
23
|
+
register :call, Substation::Processor::Evaluator::Pivot
|
|
24
|
+
register :wrap, Substation::Processor::Wrapper
|
|
25
|
+
register :render, Substation::Processor::Transformer
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Error
|
|
29
|
+
attr_reader :data
|
|
30
|
+
def initialize(data)
|
|
31
|
+
@data = data
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ValidationError = Class.new(self)
|
|
35
|
+
ApplicationError = Class.new(self)
|
|
36
|
+
InternalError = Class.new(self)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module App
|
|
40
|
+
VALIDATION_ERROR = Demo::ENV.chain { wrap Error::ValidationError }
|
|
41
|
+
APPLICATION_ERROR = Demo::ENV.chain { wrap Error::ApplicationError }
|
|
42
|
+
|
|
43
|
+
SOME_ACTION = Demo::ENV.chain do
|
|
44
|
+
validate Vanguard::Validator, VALIDATION_ERROR
|
|
45
|
+
call Some::Action, APPLICATION_ERROR
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module Web
|
|
50
|
+
VALIDATION_ERROR = Demo::ENV.chain(App::VALIDATION_ERROR) do
|
|
51
|
+
render Renderer::ValidationError
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
APPLICATION_ERROR = Demo::ENV.chain(App::APPLICATION_ERROR) do
|
|
55
|
+
render Renderer::ApplicationError
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# in case of success, returns an instance of Views::Person
|
|
59
|
+
# in case of validation failure, renders using Renderer::ValidationError
|
|
60
|
+
# in case of internal error, renders using Renderer::InternalError
|
|
61
|
+
SOME_ACTION = Demo::ENV.chain(App::SOME_ACTION) do
|
|
62
|
+
failure_chain :validate, VALIDATION_ERROR
|
|
63
|
+
failure_chain :call, INTERNAL_ERROR
|
|
64
|
+
wrap Presenters::Person
|
|
65
|
+
wrap Views::ShowPerson
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
* [feature] Support (re)defining chain specific failure chains in case of uncaught exceptions.
|
|
71
|
+
|
|
72
|
+
module Demo
|
|
73
|
+
|
|
74
|
+
module App
|
|
75
|
+
INTERNAL_ERROR = Demo::ENV.chain { wrap Error::InternalError }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
module Web
|
|
79
|
+
|
|
80
|
+
INTERNAL_ERROR = Demo::ENV.chain(App::INTERNAL_ERROR) do
|
|
81
|
+
render Renderer::InternalError
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The INTERNAL_ERROR chain will be called if an exception
|
|
85
|
+
# isn't rescued by the responsible handler
|
|
86
|
+
SOME_ACTION = Demo::ENV.chain(App::SOME_ACTION, INTERNAL_ERROR) do
|
|
87
|
+
failure_chain :validate, VALIDATION_ERROR
|
|
88
|
+
failure_chain :call, INTERNAL_ERROR
|
|
89
|
+
wrap Presenters::Person
|
|
90
|
+
wrap Views::ShowPerson
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
[Compare v0.0.8..v0.0.9](https://github.com/snusnu/substation/compare/v0.0.8...v0.0.9)
|
|
6
96
|
|
|
7
97
|
# v0.0.8 2013-06-19
|
|
8
98
|
|
data/Gemfile.devtools
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
group :development do
|
|
4
|
-
gem 'rake', '~> 10.0
|
|
4
|
+
gem 'rake', '~> 10.1.0'
|
|
5
5
|
gem 'rspec', '~> 2.13.0'
|
|
6
|
-
gem 'yard', '~> 0.8.6.
|
|
6
|
+
gem 'yard', '~> 0.8.6.2'
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
group :yard do
|
|
10
|
-
gem 'kramdown', '~> 1.0
|
|
10
|
+
gem 'kramdown', '~> 1.1.0'
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
group :guard do
|
|
14
|
-
gem 'guard', '~> 1.8.
|
|
14
|
+
gem 'guard', '~> 1.8.1'
|
|
15
15
|
gem 'guard-bundler', '~> 1.0.0'
|
|
16
|
-
gem 'guard-rspec', '~>
|
|
16
|
+
gem 'guard-rspec', '~> 3.0.2'
|
|
17
|
+
gem 'guard-rubocop', '~> 0.2.0'
|
|
17
18
|
|
|
18
19
|
# file system change event handling
|
|
19
|
-
gem 'listen', '~> 1.
|
|
20
|
+
gem 'listen', '~> 1.2.2'
|
|
20
21
|
gem 'rb-fchange', '~> 0.0.6', :require => false
|
|
21
22
|
gem 'rb-fsevent', '~> 0.9.3', :require => false
|
|
22
23
|
gem 'rb-inotify', '~> 0.9.0', :require => false
|
|
@@ -28,20 +29,21 @@ group :guard do
|
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
group :metrics do
|
|
31
|
-
gem '
|
|
32
|
-
gem '
|
|
33
|
-
gem '
|
|
34
|
-
gem '
|
|
35
|
-
gem '
|
|
36
|
-
gem 'simplecov',
|
|
37
|
-
gem 'yardstick',
|
|
38
|
-
|
|
39
|
-
platforms :ruby_19 do
|
|
32
|
+
gem 'coveralls', '~> 0.6.7'
|
|
33
|
+
gem 'flay', '~> 2.3.0'
|
|
34
|
+
gem 'flog', '~> 4.1.0'
|
|
35
|
+
gem 'reek', '~> 1.3.1', :git => 'https://github.com/troessner/reek.git'
|
|
36
|
+
gem 'rubocop', '~> 0.9.1'
|
|
37
|
+
gem 'simplecov', '~> 0.7.1'
|
|
38
|
+
gem 'yardstick', '~> 0.9.6'
|
|
39
|
+
|
|
40
|
+
platforms :ruby_19, :ruby_20 do
|
|
41
|
+
gem 'mutant', '~> 0.3.0.beta13'
|
|
40
42
|
gem 'yard-spellcheck', '~> 0.1.5'
|
|
41
43
|
end
|
|
42
44
|
|
|
43
|
-
platforms :
|
|
44
|
-
gem '
|
|
45
|
+
platforms :ruby_19 do
|
|
46
|
+
gem 'json', '~> 1.8.0'
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
platforms :rbx do
|
data/README.md
CHANGED
|
@@ -22,7 +22,8 @@ receive arbitrary input data which will be available in `request.input`.
|
|
|
22
22
|
Additionally, `request.env` contains an arbitrary object that
|
|
23
23
|
represents your application environment and will typically provide access
|
|
24
24
|
to useful things like a logger and probably some sort of storage engine
|
|
25
|
-
abstraction object.
|
|
25
|
+
abstraction object. Furthermore, `request.name` will contain the action
|
|
26
|
+
name the `Substation::Dispatcher` used when dispatching to an action.
|
|
26
27
|
|
|
27
28
|
The contract further specifies that every action must return an instance
|
|
28
29
|
of either `Substation::Response::Success` or
|
|
@@ -509,9 +510,9 @@ In a typical application scenario, a few things need to happen before an
|
|
|
509
510
|
actual use case (an action) can be invoked. These things will often
|
|
510
511
|
include the following steps (probably in that order).
|
|
511
512
|
|
|
513
|
+
* Input data sanitization
|
|
512
514
|
* Authentication
|
|
513
515
|
* Authorization
|
|
514
|
-
* Input data sanitization
|
|
515
516
|
* Input data validation
|
|
516
517
|
|
|
517
518
|
We only want to invoke our action if all those steps succeed. If any of
|
|
@@ -541,129 +542,198 @@ b) If you need to return JSON, you might just
|
|
|
541
542
|
* Pass the response data to some serializer object and dump it to JSON
|
|
542
543
|
|
|
543
544
|
To allow chaining all those steps in a declarative way, substation
|
|
544
|
-
provides an object called `Substation::Chain`.
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
1. `#call(Substation::Request) => Substation::Response`
|
|
548
|
-
2. `#result(Substation::Response) => Substation::Response`
|
|
549
|
-
|
|
550
|
-
You typically won't be calling `Substation::Chain#result` yourself, but
|
|
551
|
-
having it around, allows us to use chains in *incoming handlers*,
|
|
552
|
-
essentially nesting chains. This makes it possible to construct one
|
|
553
|
-
chain up until the pivot handler, and then reuse that same chain in one
|
|
554
|
-
usecase that takes the response and renders HTML, and in another that
|
|
555
|
-
renders JSON.
|
|
556
|
-
|
|
557
|
-
To construct a chain, you need to pass an enumerable of so called
|
|
558
|
-
handler objects to `Substation::Chain.new`. Handlers must support two
|
|
559
|
-
methods:
|
|
545
|
+
provides an object called `Substation::Chain`. To construct a chain, you
|
|
546
|
+
need to pass an enumerable of processors to `Substation::Chain#initialize`.
|
|
547
|
+
Processors must support three methods:
|
|
560
548
|
|
|
561
549
|
1. `#call(<Substation::Request, Substation::Response>) => Substation::Response`
|
|
562
550
|
2. `#result(Substation::Response) => <Substation::Request, Substation::Response>`
|
|
551
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
563
552
|
|
|
564
|
-
### Incoming
|
|
553
|
+
### Incoming processors
|
|
565
554
|
|
|
566
555
|
All steps required *before* processing the action will potentially
|
|
567
556
|
produce a new, altered, `Substation::Request`. Therefore, the object
|
|
568
557
|
passed to `#call` must be an instance of `Substation::Request`.
|
|
569
558
|
|
|
570
559
|
Since `#call` must return a `Substation::Response` (because the chain
|
|
571
|
-
would halt and return that response in case calling
|
|
560
|
+
would halt and return that response in case calling `Processor#success?`
|
|
572
561
|
method would return `false`), we also need to implement `#result`
|
|
573
562
|
and have it return a `Substation::Request` instance that can be passed
|
|
574
563
|
on to the next handler.
|
|
575
564
|
|
|
576
|
-
The contract for incoming
|
|
565
|
+
The contract for incoming processors therefore is:
|
|
577
566
|
|
|
578
567
|
1. `#call(Substation::Request) => Substation::Response`
|
|
579
568
|
2. `#result(Substation::Response) => Substation::Request`
|
|
569
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
580
570
|
|
|
581
|
-
By including the `Substation::
|
|
582
|
-
class, you'll get the following for free:
|
|
571
|
+
By including the `Substation::Processor::Incoming` module into your
|
|
572
|
+
processor class, you'll get the following for free:
|
|
583
573
|
|
|
584
574
|
```ruby
|
|
575
|
+
def initialize(name, handler, failure_chain)
|
|
576
|
+
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
577
|
+
end
|
|
578
|
+
|
|
585
579
|
def result(response)
|
|
586
|
-
|
|
580
|
+
response.to_request
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def success?(response)
|
|
584
|
+
response.success?
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def with_failure_chain(chain)
|
|
588
|
+
self.class.new(name, handler, chain)
|
|
587
589
|
end
|
|
588
590
|
```
|
|
589
591
|
|
|
590
|
-
This shows that an incoming
|
|
592
|
+
This shows that an incoming processor can alter the incoming request in any
|
|
591
593
|
way that it wants to, as long as it returns the new request input data in
|
|
592
594
|
`Substation::Response#output` returned from `#call`.
|
|
593
595
|
|
|
594
|
-
|
|
596
|
+
Currently, `substation` provides the following incoming processors out
|
|
597
|
+
of the box:
|
|
598
|
+
|
|
599
|
+
* `Substation::Processor::Evaluator::Request` passes `request` to the handler
|
|
600
|
+
* `Substation::Processor::Evaluator::Data` passes `request.input` to the handler
|
|
601
|
+
|
|
602
|
+
### The pivot processor
|
|
595
603
|
|
|
596
604
|
Pivot is just another fancy name for the action in the context of a
|
|
597
|
-
chain. It's also the point where all subsequent
|
|
605
|
+
chain. It's also the point where all subsequent processors have to further
|
|
598
606
|
process the `Substation::Response` returned from invoking the action.
|
|
607
|
+
Therefore, the pivot processor is the last processor that expects a
|
|
608
|
+
`Substation::Request` as parameter to its `#call` method.
|
|
599
609
|
|
|
600
|
-
The contract for the pivot
|
|
610
|
+
The contract for the pivot processor therefore is:
|
|
601
611
|
|
|
602
612
|
1. `#call(Substation::Request) => Substation::Response`
|
|
603
613
|
2. `#result(Substation::Response) => Substation::Response`
|
|
614
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
604
615
|
|
|
605
|
-
By including the `Substation::
|
|
616
|
+
By including the `Substation::Processor::Pivot` module into your handler
|
|
606
617
|
class, you'll get the following for free:
|
|
607
618
|
|
|
608
619
|
```ruby
|
|
620
|
+
def initialize(name, handler, failure_chain)
|
|
621
|
+
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
622
|
+
end
|
|
623
|
+
|
|
609
624
|
def result(response)
|
|
610
625
|
response
|
|
611
626
|
end
|
|
627
|
+
|
|
628
|
+
def success?(response)
|
|
629
|
+
response.success?
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def with_failure_chain(chain)
|
|
633
|
+
self.class.new(name, handler, chain)
|
|
634
|
+
end
|
|
612
635
|
```
|
|
613
636
|
|
|
614
|
-
This reflects the fact that a pivot
|
|
615
|
-
producing the "raw" response, returns it unaltered.
|
|
637
|
+
This reflects the fact that a pivot processor (since it's the one actually
|
|
638
|
+
producing the "raw" response, returns it unaltered).
|
|
639
|
+
|
|
640
|
+
The pivot processor is shipped with `substation` and is implemented by
|
|
641
|
+
`Substation::Processor::Evaluator::Pivot`.
|
|
616
642
|
|
|
617
|
-
### Outgoing
|
|
643
|
+
### Outgoing processors
|
|
618
644
|
|
|
619
645
|
All steps required *after* processing the action will potentially
|
|
620
646
|
produce a new, altered, `Substation::Response` instance to be returned.
|
|
621
647
|
Therefore the object passed to `#call` must be an instance of
|
|
622
|
-
`Substation::Response`. Since subsequent outgoing
|
|
648
|
+
`Substation::Response`. Since subsequent outgoing processors might further
|
|
623
649
|
process the response, `#result` must be implemented so that it returns a
|
|
624
|
-
`Substation::Response` object that can be passed on to the next
|
|
650
|
+
`Substation::Response` object that can be passed on to the next
|
|
651
|
+
processor.
|
|
625
652
|
|
|
626
|
-
The contract for outgoing
|
|
653
|
+
The contract for outgoing processors therefore is:
|
|
627
654
|
|
|
628
655
|
1. `#call(Substation::Response) => Substation::Response`
|
|
629
656
|
2. `#result(Substation::Response) => Substation::Response`
|
|
657
|
+
3. `#success?(Substation::Response) => true`
|
|
630
658
|
|
|
631
|
-
By including the `Substation::
|
|
632
|
-
class, you'll get the following for free:
|
|
659
|
+
By including the `Substation::Processor::Outgoing` module into your
|
|
660
|
+
processor class, you'll get the following for free:
|
|
633
661
|
|
|
634
662
|
```ruby
|
|
663
|
+
def initialize(name, handler)
|
|
664
|
+
@name, @handler = name, handler
|
|
665
|
+
end
|
|
666
|
+
|
|
635
667
|
def result(response)
|
|
636
668
|
response
|
|
637
669
|
end
|
|
670
|
+
|
|
671
|
+
def success?(response)
|
|
672
|
+
true
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
private
|
|
676
|
+
|
|
677
|
+
def respond_with(response, output)
|
|
678
|
+
response.class.new(response.request, output)
|
|
679
|
+
end
|
|
638
680
|
```
|
|
639
681
|
|
|
640
|
-
This shows that an outgoing
|
|
682
|
+
This shows that an outgoing processor's `#call` can do anything with
|
|
641
683
|
the `Substation::Response#output` it received, as long as it makes
|
|
642
684
|
sure to return a new response with the new output properly set.
|
|
643
685
|
|
|
686
|
+
Currently, `substation` provides the following outgoing processors out
|
|
687
|
+
of the box:
|
|
688
|
+
|
|
689
|
+
* `Substation::Processor::Wrapper` wraps `response.output` in a new handler instance
|
|
690
|
+
* `Substation::Processor::Transformer` transforms `response.output` using a new handler instance
|
|
691
|
+
|
|
692
|
+
### Handlers
|
|
693
|
+
|
|
694
|
+
You might have noticed the `handler` param passed to any processor's
|
|
695
|
+
`#initialize` method. Handlers are the actual objects performing your
|
|
696
|
+
application logic. Processors use these handlers to produce the data
|
|
697
|
+
they're supposed to "pipe through the chain".
|
|
698
|
+
|
|
699
|
+
The interface your handlers must implement should be familiar by now.
|
|
700
|
+
|
|
701
|
+
All handlers to be used with incoming processors must accept an instance
|
|
702
|
+
of `Substation::Request` as parameter to `#call`. Handlers to be used
|
|
703
|
+
with `Substation::Processor::Evaluator` subclasses must furthermore
|
|
704
|
+
return an object that responds to `#success?` and `#output`.
|
|
705
|
+
|
|
706
|
+
Note how the interface required for evaluator handler return values
|
|
707
|
+
matches the interface a `Substation::Response` exposes. This means that
|
|
708
|
+
the pivot processor can be (and is) implemented using the builtin
|
|
709
|
+
`Substation::Processor::Evaluator::Request` processor. The handler you
|
|
710
|
+
pass to the pivot processor is the object that actually implements your
|
|
711
|
+
application usecase, the action, and it's response gets evaluated.
|
|
712
|
+
|
|
713
|
+
All handlers to be used with outgoing processors must accept an instance
|
|
714
|
+
of `Substation::Response` as parameter to `#call`. They can do whatever
|
|
715
|
+
they want with the passed in response, but they must make sure to return
|
|
716
|
+
another instance of `Substation::Response`. To help with this, outgoing
|
|
717
|
+
processors provide the `#respond_with(response, data)` method that
|
|
718
|
+
you'll typically call to return the response value for `#call`.
|
|
719
|
+
|
|
644
720
|
### Example
|
|
645
721
|
|
|
646
722
|
[substation-demo](https://github.com/snusnu/substation-demo) implements a
|
|
647
723
|
simple web application using `Substation::Chain`.
|
|
648
724
|
|
|
649
|
-
The demo
|
|
725
|
+
The demo uses a few of the above mentioned *incoming processors*
|
|
650
726
|
for
|
|
651
727
|
|
|
652
728
|
* [Sanitization](https://github.com/snusnu/substation-demo/blob/master/demo/web/sanitizers.rb) using [ducktrap](https://github.com/mbj/ducktrap)
|
|
653
729
|
* [Validation](https://github.com/snusnu/substation-demo/blob/master/demo/validators.rb) using [vanguard](https://github.com/mbj/vanguard)
|
|
654
730
|
|
|
655
|
-
and some simple *outgoing
|
|
731
|
+
and some simple *outgoing processors* for
|
|
656
732
|
|
|
657
733
|
* Wrapping response output in a
|
|
658
734
|
[presenter](https://github.com/snusnu/substation-demo/blob/master/demo/web/presenters.rb)
|
|
659
735
|
* [Serializing](https://github.com/snusnu/substation-demo/blob/master/demo/web/serializers.rb) response output to JSON
|
|
660
736
|
|
|
661
|
-
The
|
|
662
|
-
[handlers](https://github.com/snusnu/substation-demo/blob/master/demo/web/processors.rb)
|
|
663
|
-
are called *processors* in that app, and encapsulate the actual handler
|
|
664
|
-
performing the job. That's a common pattern, because you typically will
|
|
665
|
-
have to adapt to the interface your actual handlers provide.
|
|
666
|
-
|
|
667
737
|
Have a look at the base
|
|
668
738
|
[actions](https://github.com/snusnu/substation-demo/blob/master/demo/web/actions.rb)
|
|
669
739
|
that are then used to either produce
|
data/config/flay.yml
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
---
|
|
2
|
-
threshold:
|
|
3
|
-
total_score:
|
|
2
|
+
threshold: 9
|
|
3
|
+
total_score: 134
|
data/config/mutant.yml
CHANGED
data/config/reek.yml
CHANGED
|
@@ -25,9 +25,10 @@ FeatureEnvy:
|
|
|
25
25
|
enabled: true
|
|
26
26
|
exclude:
|
|
27
27
|
- Substation::Chain#call # loops over instance state
|
|
28
|
-
- Substation::
|
|
29
|
-
- Substation::Chain::Outgoing#respond_with #defined in a module
|
|
28
|
+
- Substation::Processor::Outgoing#respond_with #defined in a module
|
|
30
29
|
- Substation::Processor::Evaluator#call # method object
|
|
30
|
+
- Substation::Processor::Evaluator#on_success # method object
|
|
31
|
+
- Substation::Processor#success? # in a module to be included in a method object
|
|
31
32
|
IrresponsibleModule:
|
|
32
33
|
enabled: true
|
|
33
34
|
exclude: []
|
|
@@ -36,6 +37,8 @@ LongParameterList:
|
|
|
36
37
|
exclude:
|
|
37
38
|
- Substation::Dispatcher#call
|
|
38
39
|
- Substation::Chain::DSL::Builder#define_dsl_method
|
|
40
|
+
- Substation::Chain#self.failure_response
|
|
41
|
+
- Substation::Chain#on_failure
|
|
39
42
|
max_params: 2
|
|
40
43
|
LongYieldList:
|
|
41
44
|
enabled: true
|
|
@@ -58,10 +61,12 @@ TooManyInstanceVariables:
|
|
|
58
61
|
enabled: true
|
|
59
62
|
exclude:
|
|
60
63
|
- Substation::Response
|
|
64
|
+
- Substation::Processor::Evaluator
|
|
61
65
|
max_instance_variables: 3
|
|
62
66
|
TooManyMethods:
|
|
63
67
|
enabled: true
|
|
64
|
-
exclude:
|
|
68
|
+
exclude:
|
|
69
|
+
- Substation::Chain::DSL
|
|
65
70
|
max_methods: 4
|
|
66
71
|
TooManyStatements:
|
|
67
72
|
enabled: true
|
|
@@ -110,6 +115,7 @@ UnusedParameters:
|
|
|
110
115
|
UtilityFunction:
|
|
111
116
|
enabled: true
|
|
112
117
|
exclude:
|
|
113
|
-
- Substation::
|
|
114
|
-
- Substation::
|
|
118
|
+
- Substation::Processor::Outgoing#respond_with # defined in a module
|
|
119
|
+
- Substation::Processor::Evaluator#on_success # inside a method object
|
|
120
|
+
- Substation::Processor#success? # in a module to be included in a method object
|
|
115
121
|
max_helper_calls: 0
|