substation 0.0.9 → 0.0.10.beta2
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.
- checksums.yaml +7 -0
- data/.travis.yml +0 -1
- data/Changelog.md +24 -82
- data/Gemfile.devtools +17 -24
- data/README.md +46 -116
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/mutant.yml +0 -1
- data/config/reek.yml +5 -10
- data/lib/substation.rb +3 -8
- data/lib/substation/chain.rb +64 -108
- data/lib/substation/chain/dsl.rb +30 -57
- data/lib/substation/dispatcher.rb +1 -3
- data/lib/substation/environment.rb +23 -9
- data/lib/substation/environment/dsl.rb +3 -4
- data/lib/substation/observer.rb +2 -4
- data/lib/substation/processor.rb +7 -106
- data/lib/substation/processor/evaluator.rb +42 -83
- data/lib/substation/processor/pivot.rb +25 -0
- data/lib/substation/processor/wrapper.rb +2 -4
- data/lib/substation/request.rb +1 -10
- data/lib/substation/response.rb +0 -11
- data/lib/substation/utils.rb +1 -3
- data/lib/substation/version.rb +1 -3
- data/spec/integration/substation/dispatcher/call_spec.rb +12 -12
- data/spec/spec_helper.rb +21 -30
- data/spec/unit/substation/chain/call_spec.rb +32 -202
- data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +2 -2
- data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +6 -8
- data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +30 -0
- data/spec/unit/substation/chain/dsl/chain_spec.rb +2 -1
- data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +24 -0
- data/spec/unit/substation/chain/dsl/initialize_spec.rb +19 -0
- data/spec/unit/substation/chain/dsl/processors_spec.rb +21 -9
- data/spec/unit/substation/chain/dsl/use_spec.rb +3 -2
- data/spec/unit/substation/chain/each_spec.rb +9 -5
- data/spec/unit/substation/chain/incoming/result_spec.rb +21 -0
- data/spec/unit/substation/chain/outgoing/call_spec.rb +25 -0
- data/spec/unit/substation/{processor → chain/outgoing}/result_spec.rb +5 -6
- data/spec/unit/substation/dispatcher/action/call_spec.rb +6 -7
- data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +5 -7
- data/spec/unit/substation/dispatcher/action_names_spec.rb +1 -1
- data/spec/unit/substation/dispatcher/call_spec.rb +3 -3
- data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +7 -7
- data/spec/unit/substation/environment/chain_spec.rb +32 -22
- data/spec/unit/substation/environment/class_methods/build_spec.rb +4 -11
- data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +3 -5
- data/spec/unit/substation/environment/dsl/register_spec.rb +3 -8
- data/spec/unit/substation/environment/dsl/registry_spec.rb +3 -5
- data/spec/unit/substation/observer/chain/call_spec.rb +3 -5
- data/spec/unit/substation/observer/class_methods/coerce_spec.rb +2 -4
- data/spec/unit/substation/observer/null/call_spec.rb +1 -3
- data/spec/unit/substation/processor/evaluator/call_spec.rb +35 -21
- data/spec/unit/substation/processor/pivot/call_spec.rb +17 -0
- data/spec/unit/substation/processor/wrapper/call_spec.rb +7 -8
- data/spec/unit/substation/request/env_spec.rb +4 -5
- data/spec/unit/substation/request/error_spec.rb +4 -5
- data/spec/unit/substation/request/input_spec.rb +4 -5
- data/spec/unit/substation/request/success_spec.rb +4 -5
- data/spec/unit/substation/response/env_spec.rb +5 -6
- data/spec/unit/substation/response/failure/success_predicate_spec.rb +4 -5
- data/spec/unit/substation/response/input_spec.rb +5 -6
- data/spec/unit/substation/response/output_spec.rb +4 -5
- data/spec/unit/substation/response/request_spec.rb +5 -6
- data/spec/unit/substation/response/success/success_predicate_spec.rb +4 -5
- data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +13 -15
- 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
- data/substation.gemspec +1 -1
- metadata +18 -45
- data/config/rubocop.yml +0 -35
- data/lib/substation/processor/transformer.rb +0 -26
- data/spec/unit/substation/chain/class_methods/failure_response_spec.rb +0 -16
- data/spec/unit/substation/chain/dsl/class_methods/build_spec.rb +0 -24
- data/spec/unit/substation/chain/dsl/failure_chain_spec.rb +0 -35
- data/spec/unit/substation/chain/failure_data/equalizer_spec.rb +0 -46
- data/spec/unit/substation/chain/failure_data/hash_spec.rb +0 -13
- data/spec/unit/substation/environment/equalizer_spec.rb +0 -25
- data/spec/unit/substation/processor/evaluator/class_methods/new_spec.rb +0 -9
- data/spec/unit/substation/processor/evaluator/data/call_spec.rb +0 -34
- data/spec/unit/substation/processor/evaluator/pivot/call_spec.rb +0 -34
- data/spec/unit/substation/processor/evaluator/request/call_spec.rb +0 -34
- data/spec/unit/substation/processor/fallible/name_spec.rb +0 -15
- data/spec/unit/substation/processor/fallible/with_failure_chain_spec.rb +0 -18
- data/spec/unit/substation/processor/incoming/result_spec.rb +0 -25
- data/spec/unit/substation/processor/outgoing/call_spec.rb +0 -28
- data/spec/unit/substation/processor/outgoing/name_spec.rb +0 -14
- data/spec/unit/substation/processor/outgoing/success_predicate_spec.rb +0 -15
- data/spec/unit/substation/processor/success_predicate_spec.rb +0 -22
- data/spec/unit/substation/processor/transformer/call_spec.rb +0 -21
- data/spec/unit/substation/request/name_spec.rb +0 -15
- data/spec/unit/substation/response/to_request_spec.rb +0 -19
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 674a347348ff4f36dbca255925b70c9ea794c95d
|
|
4
|
+
data.tar.gz: a28efb6f4038ee99e8f820f2dcaee74506e4182b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5a9dcabb1d4a225cee6a4579587387c3829747fcaf77319ae2a0c2a2863eae6558c4d825e6c48f59ecc7d2a860831ae8dcadcda053d947f431b9b73bbaa0c593
|
|
7
|
+
data.tar.gz: bbd1fca2660e54697b3d1d8f5a69e4a527cf5a129a0995e589cf99966d7c54f9a10b30313e3f51b91a60b4ea38443dcb0a5f3e68253e028ffcda4804def5d107
|
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
|
@@ -1,98 +1,40 @@
|
|
|
1
|
-
# v0.0.
|
|
1
|
+
# v0.0.9 (not yet released)
|
|
2
2
|
|
|
3
|
-
*
|
|
3
|
+
* [feature] Support definining failure chains
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 }
|
|
5
|
+
env = Substation::Environment.build do
|
|
6
|
+
register :evaluate, Substation::Processor::Evaluator
|
|
7
|
+
register :wrap, Substation::Processor::Wrapper
|
|
8
|
+
end
|
|
42
9
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
10
|
+
class Error
|
|
11
|
+
attr_reader :data
|
|
12
|
+
def initialize(data)
|
|
13
|
+
@data = data
|
|
47
14
|
end
|
|
48
15
|
|
|
49
|
-
|
|
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
|
|
16
|
+
class ValidationError < self
|
|
67
17
|
end
|
|
68
18
|
end
|
|
69
19
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
module App
|
|
75
|
-
INTERNAL_ERROR = Demo::ENV.chain { wrap Error::InternalError }
|
|
20
|
+
chain = env.chain do
|
|
21
|
+
evaluate Vanguard::Validator do
|
|
22
|
+
wrap Errors::ValidationError
|
|
76
23
|
end
|
|
24
|
+
call Some::Action
|
|
25
|
+
wrap Some::Presenter
|
|
26
|
+
end
|
|
77
27
|
|
|
78
|
-
|
|
28
|
+
env = Object.new
|
|
29
|
+
invalid_request = Substation::Requet.new(env, :invalid)
|
|
30
|
+
response = chain.call(invalid_request)
|
|
79
31
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
end
|
|
32
|
+
response.data.instance_of?(Errors::ValidationError)
|
|
33
|
+
# => true
|
|
83
34
|
|
|
84
|
-
|
|
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
|
|
35
|
+
response.data.data # => the actual vanguard violation set
|
|
94
36
|
|
|
95
|
-
[Compare v0.0.8..
|
|
37
|
+
[Compare v0.0.8..master](https://github.com/snusnu/substation/compare/v0.0.8...master)
|
|
96
38
|
|
|
97
39
|
# v0.0.8 2013-06-19
|
|
98
40
|
|
data/Gemfile.devtools
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
group :development do
|
|
4
4
|
gem 'rake', '~> 10.1.0'
|
|
5
|
-
gem 'rspec', '~> 2.
|
|
6
|
-
gem 'yard', '~> 0.8.
|
|
5
|
+
gem 'rspec', '~> 2.14.1'
|
|
6
|
+
gem 'yard', '~> 0.8.7'
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
group :yard do
|
|
10
|
-
gem 'kramdown', '~> 1.
|
|
10
|
+
gem 'kramdown', '~> 1.2.0'
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
group :guard do
|
|
@@ -15,40 +15,33 @@ group :guard do
|
|
|
15
15
|
gem 'guard-bundler', '~> 1.0.0'
|
|
16
16
|
gem 'guard-rspec', '~> 3.0.2'
|
|
17
17
|
gem 'guard-rubocop', '~> 0.2.0'
|
|
18
|
+
gem 'guard-mutant', '~> 0.0.1'
|
|
18
19
|
|
|
19
20
|
# file system change event handling
|
|
20
|
-
gem 'listen', '~> 1.
|
|
21
|
-
gem 'rb-fchange', '~> 0.0.6', :
|
|
22
|
-
gem 'rb-fsevent', '~> 0.9.3', :
|
|
23
|
-
gem 'rb-inotify', '~> 0.9.0', :
|
|
21
|
+
gem 'listen', '~> 1.3.0'
|
|
22
|
+
gem 'rb-fchange', '~> 0.0.6', require: false
|
|
23
|
+
gem 'rb-fsevent', '~> 0.9.3', require: false
|
|
24
|
+
gem 'rb-inotify', '~> 0.9.0', require: false
|
|
24
25
|
|
|
25
26
|
# notification handling
|
|
26
|
-
gem 'libnotify', '~> 0.8.0', :
|
|
27
|
-
gem 'rb-notifu', '~> 0.0.4', :
|
|
28
|
-
gem 'terminal-notifier-guard', '~> 1.5.3', :
|
|
27
|
+
gem 'libnotify', '~> 0.8.0', require: false
|
|
28
|
+
gem 'rb-notifu', '~> 0.0.4', require: false
|
|
29
|
+
gem 'terminal-notifier-guard', '~> 1.5.3', require: false
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
group :metrics do
|
|
32
33
|
gem 'coveralls', '~> 0.6.7'
|
|
33
|
-
gem 'flay', '~> 2.
|
|
34
|
-
gem 'flog', '~> 4.1.
|
|
35
|
-
gem 'reek', '~> 1.3.
|
|
36
|
-
gem 'rubocop', '~> 0.
|
|
34
|
+
gem 'flay', '~> 2.4.0'
|
|
35
|
+
gem 'flog', '~> 4.1.1'
|
|
36
|
+
gem 'reek', '~> 1.3.2'
|
|
37
|
+
gem 'rubocop', '~> 0.13.0'
|
|
37
38
|
gem 'simplecov', '~> 0.7.1'
|
|
38
|
-
gem 'yardstick', '~> 0.9.
|
|
39
|
+
gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
|
|
39
40
|
|
|
40
41
|
platforms :ruby_19, :ruby_20 do
|
|
41
|
-
gem 'mutant',
|
|
42
|
+
gem 'mutant', git: 'https://github.com/mbj/mutant.git'
|
|
42
43
|
gem 'yard-spellcheck', '~> 0.1.5'
|
|
43
44
|
end
|
|
44
|
-
|
|
45
|
-
platforms :ruby_19 do
|
|
46
|
-
gem 'json', '~> 1.8.0'
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
platforms :rbx do
|
|
50
|
-
gem 'pelusa', '~> 0.2.2'
|
|
51
|
-
end
|
|
52
45
|
end
|
|
53
46
|
|
|
54
47
|
group :benchmarks do
|
data/README.md
CHANGED
|
@@ -22,8 +22,7 @@ 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.
|
|
26
|
-
name the `Substation::Dispatcher` used when dispatching to an action.
|
|
25
|
+
abstraction object.
|
|
27
26
|
|
|
28
27
|
The contract further specifies that every action must return an instance
|
|
29
28
|
of either `Substation::Response::Success` or
|
|
@@ -510,9 +509,9 @@ In a typical application scenario, a few things need to happen before an
|
|
|
510
509
|
actual use case (an action) can be invoked. These things will often
|
|
511
510
|
include the following steps (probably in that order).
|
|
512
511
|
|
|
513
|
-
* Input data sanitization
|
|
514
512
|
* Authentication
|
|
515
513
|
* Authorization
|
|
514
|
+
* Input data sanitization
|
|
516
515
|
* Input data validation
|
|
517
516
|
|
|
518
517
|
We only want to invoke our action if all those steps succeed. If any of
|
|
@@ -542,198 +541,129 @@ b) If you need to return JSON, you might just
|
|
|
542
541
|
* Pass the response data to some serializer object and dump it to JSON
|
|
543
542
|
|
|
544
543
|
To allow chaining all those steps in a declarative way, substation
|
|
545
|
-
provides an object called `Substation::Chain`.
|
|
546
|
-
|
|
547
|
-
|
|
544
|
+
provides an object called `Substation::Chain`. Its contract is dead
|
|
545
|
+
simple:
|
|
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:
|
|
548
560
|
|
|
549
561
|
1. `#call(<Substation::Request, Substation::Response>) => Substation::Response`
|
|
550
562
|
2. `#result(Substation::Response) => <Substation::Request, Substation::Response>`
|
|
551
|
-
3. `#success?(Substation::Response) => Boolean`
|
|
552
563
|
|
|
553
|
-
### Incoming
|
|
564
|
+
### Incoming handlers
|
|
554
565
|
|
|
555
566
|
All steps required *before* processing the action will potentially
|
|
556
567
|
produce a new, altered, `Substation::Request`. Therefore, the object
|
|
557
568
|
passed to `#call` must be an instance of `Substation::Request`.
|
|
558
569
|
|
|
559
570
|
Since `#call` must return a `Substation::Response` (because the chain
|
|
560
|
-
would halt and return that response in case calling
|
|
571
|
+
would halt and return that response in case calling its `#success?`
|
|
561
572
|
method would return `false`), we also need to implement `#result`
|
|
562
573
|
and have it return a `Substation::Request` instance that can be passed
|
|
563
574
|
on to the next handler.
|
|
564
575
|
|
|
565
|
-
The contract for incoming
|
|
576
|
+
The contract for incoming handlers therefore is:
|
|
566
577
|
|
|
567
578
|
1. `#call(Substation::Request) => Substation::Response`
|
|
568
579
|
2. `#result(Substation::Response) => Substation::Request`
|
|
569
|
-
3. `#success?(Substation::Response) => Boolean`
|
|
570
580
|
|
|
571
|
-
By including the `Substation::
|
|
572
|
-
|
|
581
|
+
By including the `Substation::Chain::Incoming` module into your handler
|
|
582
|
+
class, you'll get the following for free:
|
|
573
583
|
|
|
574
584
|
```ruby
|
|
575
|
-
def initialize(name, handler, failure_chain)
|
|
576
|
-
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
577
|
-
end
|
|
578
|
-
|
|
579
585
|
def result(response)
|
|
580
|
-
response.
|
|
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)
|
|
586
|
+
Request.new(response.env, response.output)
|
|
589
587
|
end
|
|
590
588
|
```
|
|
591
589
|
|
|
592
|
-
This shows that an incoming
|
|
590
|
+
This shows that an incoming handler can alter the incoming request in any
|
|
593
591
|
way that it wants to, as long as it returns the new request input data in
|
|
594
592
|
`Substation::Response#output` returned from `#call`.
|
|
595
593
|
|
|
596
|
-
|
|
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
|
|
594
|
+
### The pivot handler
|
|
603
595
|
|
|
604
596
|
Pivot is just another fancy name for the action in the context of a
|
|
605
|
-
chain. It's also the point where all subsequent
|
|
597
|
+
chain. It's also the point where all subsequent handlers have to further
|
|
606
598
|
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.
|
|
609
599
|
|
|
610
|
-
The contract for the pivot
|
|
600
|
+
The contract for the pivot handler therefore is:
|
|
611
601
|
|
|
612
602
|
1. `#call(Substation::Request) => Substation::Response`
|
|
613
603
|
2. `#result(Substation::Response) => Substation::Response`
|
|
614
|
-
3. `#success?(Substation::Response) => Boolean`
|
|
615
604
|
|
|
616
|
-
By including the `Substation::
|
|
605
|
+
By including the `Substation::Chain::Pivot` module into your handler
|
|
617
606
|
class, you'll get the following for free:
|
|
618
607
|
|
|
619
608
|
```ruby
|
|
620
|
-
def initialize(name, handler, failure_chain)
|
|
621
|
-
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
622
|
-
end
|
|
623
|
-
|
|
624
609
|
def result(response)
|
|
625
610
|
response
|
|
626
611
|
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
|
|
635
612
|
```
|
|
636
613
|
|
|
637
|
-
This reflects the fact that a pivot
|
|
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`.
|
|
614
|
+
This reflects the fact that a pivot handler (since it's the one actually
|
|
615
|
+
producing the "raw" response, returns it unaltered.
|
|
642
616
|
|
|
643
|
-
### Outgoing
|
|
617
|
+
### Outgoing handlers
|
|
644
618
|
|
|
645
619
|
All steps required *after* processing the action will potentially
|
|
646
620
|
produce a new, altered, `Substation::Response` instance to be returned.
|
|
647
621
|
Therefore the object passed to `#call` must be an instance of
|
|
648
|
-
`Substation::Response`. Since subsequent outgoing
|
|
622
|
+
`Substation::Response`. Since subsequent outgoing handlers might further
|
|
649
623
|
process the response, `#result` must be implemented so that it returns a
|
|
650
|
-
`Substation::Response` object that can be passed on to the next
|
|
651
|
-
processor.
|
|
624
|
+
`Substation::Response` object that can be passed on to the next handler.
|
|
652
625
|
|
|
653
|
-
The contract for outgoing
|
|
626
|
+
The contract for outgoing handlers therefore is:
|
|
654
627
|
|
|
655
628
|
1. `#call(Substation::Response) => Substation::Response`
|
|
656
629
|
2. `#result(Substation::Response) => Substation::Response`
|
|
657
|
-
3. `#success?(Substation::Response) => true`
|
|
658
630
|
|
|
659
|
-
By including the `Substation::
|
|
660
|
-
|
|
631
|
+
By including the `Substation::Chain::Outgoing` module into your handler
|
|
632
|
+
class, you'll get the following for free:
|
|
661
633
|
|
|
662
634
|
```ruby
|
|
663
|
-
def initialize(name, handler)
|
|
664
|
-
@name, @handler = name, handler
|
|
665
|
-
end
|
|
666
|
-
|
|
667
635
|
def result(response)
|
|
668
636
|
response
|
|
669
637
|
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
|
|
680
638
|
```
|
|
681
639
|
|
|
682
|
-
This shows that an outgoing
|
|
640
|
+
This shows that an outgoing handler's `#call` can do anything with
|
|
683
641
|
the `Substation::Response#output` it received, as long as it makes
|
|
684
642
|
sure to return a new response with the new output properly set.
|
|
685
643
|
|
|
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
|
-
|
|
720
644
|
### Example
|
|
721
645
|
|
|
722
646
|
[substation-demo](https://github.com/snusnu/substation-demo) implements a
|
|
723
647
|
simple web application using `Substation::Chain`.
|
|
724
648
|
|
|
725
|
-
The demo
|
|
649
|
+
The demo implements a few of the above mentioned *incoming handlers*
|
|
726
650
|
for
|
|
727
651
|
|
|
728
652
|
* [Sanitization](https://github.com/snusnu/substation-demo/blob/master/demo/web/sanitizers.rb) using [ducktrap](https://github.com/mbj/ducktrap)
|
|
729
653
|
* [Validation](https://github.com/snusnu/substation-demo/blob/master/demo/validators.rb) using [vanguard](https://github.com/mbj/vanguard)
|
|
730
654
|
|
|
731
|
-
and some simple *outgoing
|
|
655
|
+
and some simple *outgoing handlers* for
|
|
732
656
|
|
|
733
657
|
* Wrapping response output in a
|
|
734
658
|
[presenter](https://github.com/snusnu/substation-demo/blob/master/demo/web/presenters.rb)
|
|
735
659
|
* [Serializing](https://github.com/snusnu/substation-demo/blob/master/demo/web/serializers.rb) response output to JSON
|
|
736
660
|
|
|
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
|
+
|
|
737
667
|
Have a look at the base
|
|
738
668
|
[actions](https://github.com/snusnu/substation-demo/blob/master/demo/web/actions.rb)
|
|
739
669
|
that are then used to either produce
|