substation 0.0.10.beta2 → 0.0.10
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 +7 -3
- data/Changelog.md +99 -24
- data/Gemfile +8 -1
- data/Gemfile.devtools +37 -21
- data/Guardfile +1 -1
- data/README.md +118 -46
- data/TODO.md +1 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +41 -16
- data/config/rubocop.yml +44 -0
- data/config/yardstick.yml +1 -1
- data/lib/substation.rb +41 -5
- data/lib/substation/chain.rb +73 -84
- data/lib/substation/chain/definition.rb +147 -0
- data/lib/substation/chain/dsl.rb +150 -112
- data/lib/substation/chain/dsl/config.rb +55 -0
- data/lib/substation/chain/dsl/module_builder.rb +84 -0
- data/lib/substation/dispatcher.rb +20 -229
- data/lib/substation/dsl/guard.rb +96 -0
- data/lib/substation/dsl/registry.rb +181 -0
- data/lib/substation/environment.rb +126 -23
- data/lib/substation/environment/dsl.rb +31 -12
- data/lib/substation/processor.rb +238 -7
- data/lib/substation/processor/builder.rb +26 -0
- data/lib/substation/processor/config.rb +24 -0
- data/lib/substation/processor/evaluator.rb +66 -42
- data/lib/substation/processor/evaluator/handler.rb +54 -0
- data/lib/substation/processor/evaluator/result.rb +24 -0
- data/lib/substation/processor/executor.rb +46 -0
- data/lib/substation/processor/nest.rb +40 -0
- data/lib/substation/processor/transformer.rb +46 -0
- data/lib/substation/processor/wrapper.rb +16 -5
- data/lib/substation/request.rb +29 -27
- data/lib/substation/response.rb +19 -34
- data/lib/substation/response/api.rb +37 -0
- data/lib/substation/response/exception.rb +20 -0
- data/lib/substation/response/exception/output.rb +59 -0
- data/lib/substation/response/failure.rb +11 -0
- data/lib/substation/response/success.rb +11 -0
- data/lib/substation/version.rb +3 -1
- data/spec/demo/core.rb +64 -0
- data/spec/demo/core/action.rb +49 -0
- data/spec/demo/core/action/create_person.rb +28 -0
- data/spec/demo/core/errors.rb +16 -0
- data/spec/demo/core/facade.rb +38 -0
- data/spec/demo/core/handler.rb +21 -0
- data/spec/demo/core/handler/acceptor.rb +47 -0
- data/spec/demo/core/handler/authenticator.rb +36 -0
- data/spec/demo/core/handler/authorizer.rb +38 -0
- data/spec/demo/core/input.rb +15 -0
- data/spec/demo/core/observers.rb +10 -0
- data/spec/demo/core/validator.rb +21 -0
- data/spec/demo/domain/actor.rb +29 -0
- data/spec/demo/domain/dto/person.rb +18 -0
- data/spec/demo/domain/environment.rb +55 -0
- data/spec/demo/domain/storage.rb +49 -0
- data/spec/demo/web.rb +26 -0
- data/spec/demo/web/errors.rb +9 -0
- data/spec/demo/web/facade.rb +60 -0
- data/spec/demo/web/handler/deserializer.rb +36 -0
- data/spec/demo/web/presenter.rb +38 -0
- data/spec/demo/web/presenter/person.rb +19 -0
- data/spec/demo/web/renderer.rb +45 -0
- data/spec/demo/web/sanitizer.rb +35 -0
- data/spec/demo/web/sanitizer/person.rb +20 -0
- data/spec/demo/web/views.rb +28 -0
- data/spec/integration/demo/core_spec.rb +97 -0
- data/spec/integration/demo/web_spec.rb +114 -0
- data/spec/shared/context/integration/demo.rb +33 -0
- data/spec/shared/context/unit/chain.rb +13 -0
- data/spec/shared/context/unit/processor.rb +58 -0
- data/spec/shared/context/unit/request.rb +8 -0
- data/spec/shared/examples/integration/demo.rb +35 -0
- data/spec/shared/examples/unit/processor.rb +72 -0
- data/spec/spec_helper.rb +52 -23
- data/spec/unit/substation/chain/definition_spec.rb +141 -0
- data/spec/unit/substation/chain/dsl/config/dsl_module_spec.rb +13 -0
- data/spec/unit/substation/chain/dsl/config/registry_spec.rb +13 -0
- data/spec/unit/substation/chain/dsl/config_spec.rb +18 -0
- data/spec/unit/substation/chain/dsl/module_builder_spec.rb +77 -0
- data/spec/unit/substation/chain/dsl_spec.rb +175 -0
- data/spec/unit/substation/chain_spec.rb +303 -0
- data/spec/unit/substation/dispatcher_spec.rb +68 -0
- data/spec/unit/substation/dsl/guard_spec.rb +72 -0
- data/spec/unit/substation/dsl/registry_spec.rb +181 -0
- data/spec/unit/substation/environment/dsl_spec.rb +156 -0
- data/spec/unit/substation/environment_spec.rb +259 -0
- data/spec/unit/substation/processor/builder_spec.rb +21 -0
- data/spec/unit/substation/processor/config_spec.rb +40 -0
- data/spec/unit/substation/processor/evaluator/handler_spec.rb +20 -0
- data/spec/unit/substation/processor/evaluator/pivot_spec.rb +42 -0
- data/spec/unit/substation/processor/evaluator/request_spec.rb +11 -0
- data/spec/unit/substation/processor/evaluator/result/failure_spec.rb +14 -0
- data/spec/unit/substation/processor/evaluator/result/success_spec.rb +14 -0
- data/spec/unit/substation/processor/evaluator/result_spec.rb +13 -0
- data/spec/unit/substation/processor/evaluator_spec.rb +18 -0
- data/spec/unit/substation/processor/executor/null_spec.rb +25 -0
- data/spec/unit/substation/processor/executor_spec.rb +32 -0
- data/spec/unit/substation/processor/fallible_spec.rb +24 -0
- data/spec/unit/substation/processor/incoming_spec.rb +17 -0
- data/spec/unit/substation/processor/nest/incoming_spec.rb +56 -0
- data/spec/unit/substation/processor/nest_spec.rb +6 -0
- data/spec/unit/substation/processor/outgoing_spec.rb +47 -0
- data/spec/unit/substation/processor/transformer/incoming_spec.rb +17 -0
- data/spec/unit/substation/processor/transformer/outgoing_spec.rb +17 -0
- data/spec/unit/substation/processor/wrapper/incoming_spec.rb +15 -0
- data/spec/unit/substation/processor/wrapper/outgoing_spec.rb +15 -0
- data/spec/unit/substation/processor/wrapper_spec.rb +24 -0
- data/spec/unit/substation/processor_spec.rb +68 -0
- data/spec/unit/substation/request_spec.rb +70 -0
- data/spec/unit/substation/response/api_spec.rb +22 -0
- data/spec/unit/substation/response/exception/output_spec.rb +46 -0
- data/spec/unit/substation/response/exception_spec.rb +25 -0
- data/spec/unit/substation/response/failure_spec.rb +25 -0
- data/spec/unit/substation/response/success_spec.rb +24 -0
- data/spec/unit/substation/response_spec.rb +73 -0
- data/substation.gemspec +7 -6
- metadata +157 -67
- checksums.yaml +0 -7
- data/TODO +0 -0
- data/lib/substation/observer.rb +0 -66
- data/lib/substation/processor/pivot.rb +0 -25
- data/lib/substation/utils.rb +0 -68
- data/spec/integration/substation/dispatcher/call_spec.rb +0 -260
- data/spec/unit/substation/chain/call_spec.rb +0 -63
- data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +0 -19
- data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +0 -21
- data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +0 -30
- data/spec/unit/substation/chain/dsl/chain_spec.rb +0 -15
- data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -24
- data/spec/unit/substation/chain/dsl/initialize_spec.rb +0 -19
- data/spec/unit/substation/chain/dsl/processors_spec.rb +0 -42
- data/spec/unit/substation/chain/dsl/use_spec.rb +0 -14
- data/spec/unit/substation/chain/each_spec.rb +0 -46
- 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/chain/outgoing/result_spec.rb +0 -21
- data/spec/unit/substation/dispatcher/action/call_spec.rb +0 -23
- data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +0 -61
- data/spec/unit/substation/dispatcher/action_names_spec.rb +0 -14
- data/spec/unit/substation/dispatcher/call_spec.rb +0 -47
- data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +0 -20
- data/spec/unit/substation/environment/chain_spec.rb +0 -50
- data/spec/unit/substation/environment/class_methods/build_spec.rb +0 -11
- data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +0 -18
- data/spec/unit/substation/environment/dsl/register_spec.rb +0 -14
- data/spec/unit/substation/environment/dsl/registry_spec.rb +0 -19
- data/spec/unit/substation/observer/chain/call_spec.rb +0 -26
- data/spec/unit/substation/observer/class_methods/coerce_spec.rb +0 -33
- data/spec/unit/substation/observer/null/call_spec.rb +0 -12
- data/spec/unit/substation/processor/evaluator/call_spec.rb +0 -49
- data/spec/unit/substation/processor/pivot/call_spec.rb +0 -17
- data/spec/unit/substation/processor/wrapper/call_spec.rb +0 -20
- data/spec/unit/substation/request/env_spec.rb +0 -14
- data/spec/unit/substation/request/error_spec.rb +0 -15
- data/spec/unit/substation/request/input_spec.rb +0 -14
- data/spec/unit/substation/request/success_spec.rb +0 -15
- data/spec/unit/substation/response/env_spec.rb +0 -16
- data/spec/unit/substation/response/failure/success_predicate_spec.rb +0 -15
- data/spec/unit/substation/response/input_spec.rb +0 -16
- data/spec/unit/substation/response/output_spec.rb +0 -16
- data/spec/unit/substation/response/request_spec.rb +0 -16
- data/spec/unit/substation/response/success/success_predicate_spec.rb +0 -15
- data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +0 -46
- data/spec/unit/substation/utils/class_methods/const_get_spec.rb +0 -46
- data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +0 -20
data/.travis.yml
CHANGED
|
@@ -6,9 +6,13 @@ rvm:
|
|
|
6
6
|
- 1.9.3
|
|
7
7
|
- 2.0.0
|
|
8
8
|
- ruby-head
|
|
9
|
-
-
|
|
10
|
-
- jruby-head
|
|
11
|
-
- rbx-19mode
|
|
9
|
+
- rbx
|
|
12
10
|
matrix:
|
|
11
|
+
include:
|
|
12
|
+
- rvm: jruby-19mode
|
|
13
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug"
|
|
14
|
+
- rvm: jruby-head
|
|
15
|
+
env: JRUBY_OPTS="$JRUBY_OPTS --debug"
|
|
13
16
|
allow_failures:
|
|
14
17
|
- rvm: ruby-head
|
|
18
|
+
- rvm: jruby-head
|
data/Changelog.md
CHANGED
|
@@ -1,40 +1,115 @@
|
|
|
1
|
-
# v0.0.
|
|
1
|
+
# v0.0.10 2014-08-11
|
|
2
2
|
|
|
3
|
-
*
|
|
3
|
+
* This release is full of breaking changes and new features. The README is still out of
|
|
4
|
+
date too, but I need to get this out of the door. For a complete list of changes, have
|
|
5
|
+
a look at the diffs linked at the end of this section.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
* Add the possibility to nest chains inside others
|
|
8
|
+
* Support decomposing/composing state before/after it is sent to handlers
|
|
9
|
+
* Make all handlers observable
|
|
10
|
+
* Support building on top of existing environments
|
|
11
|
+
* Rename Chain#{failure_chain => exception_chain}
|
|
12
|
+
* Store a chain's name in its definition
|
|
13
|
+
* Expose Dispatcher#include?(name)
|
|
14
|
+
* Make Chain::DSL accept an optional block
|
|
15
|
+
|
|
16
|
+
This allows for easy and syntactically nice
|
|
17
|
+
failure chain replacement when its desired
|
|
18
|
+
to replace/augment any of the failure chains
|
|
19
|
+
registered for processors within the chain to
|
|
20
|
+
merge.
|
|
21
|
+
|
|
22
|
+
[Compare v0.0.9..v0.0.10](https://github.com/snusnu/substation/compare/v0.0.9...v0.0.10)
|
|
23
|
+
|
|
24
|
+
# v0.0.9 2013-07-10
|
|
25
|
+
|
|
26
|
+
* [BREAKING CHANGE] Refactor `Substation::Processor` classes.
|
|
27
|
+
|
|
28
|
+
* Renamed `Substation::Processor::Evaluator` to `Substation::Processor::Evaluator::Data`.
|
|
29
|
+
* Renamed `Substation::Processor::Pivot` to `Substation::Processor::Evaluator::Pivot`.
|
|
30
|
+
* Added `Substation::Processor::Evaluator::Request` which passes the complete `Request` instance on to the handler.
|
|
31
|
+
* Added `Substation::Processor::Transformer` to transform `Response#output` into any other object.
|
|
32
|
+
|
|
33
|
+
* [feature] Make the dispatched name available in `Request#name`.
|
|
34
|
+
|
|
35
|
+
* [feature] Support (re)definining failure chains for `Substation::Processor::Fallible` processors.
|
|
36
|
+
|
|
37
|
+
module Demo
|
|
38
|
+
ENV = Substation::Environment.build do
|
|
39
|
+
register :validate, Substation::Processor::Evaluator::Data
|
|
40
|
+
register :call, Substation::Processor::Evaluator::Pivot
|
|
41
|
+
register :wrap, Substation::Processor::Wrapper
|
|
42
|
+
register :render, Substation::Processor::Transformer
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Error
|
|
46
|
+
attr_reader :data
|
|
47
|
+
def initialize(data)
|
|
48
|
+
@data = data
|
|
49
|
+
end
|
|
9
50
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@data = data
|
|
51
|
+
ValidationError = Class.new(self)
|
|
52
|
+
ApplicationError = Class.new(self)
|
|
53
|
+
InternalError = Class.new(self)
|
|
14
54
|
end
|
|
15
55
|
|
|
16
|
-
|
|
56
|
+
module App
|
|
57
|
+
VALIDATION_ERROR = Demo::ENV.chain { wrap Error::ValidationError }
|
|
58
|
+
APPLICATION_ERROR = Demo::ENV.chain { wrap Error::ApplicationError }
|
|
59
|
+
|
|
60
|
+
SOME_ACTION = Demo::ENV.chain do
|
|
61
|
+
validate Vanguard::Validator, VALIDATION_ERROR
|
|
62
|
+
call Some::Action, APPLICATION_ERROR
|
|
63
|
+
end
|
|
17
64
|
end
|
|
18
|
-
end
|
|
19
65
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
66
|
+
module Web
|
|
67
|
+
VALIDATION_ERROR = Demo::ENV.chain(App::VALIDATION_ERROR) do
|
|
68
|
+
render Renderer::ValidationError
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
APPLICATION_ERROR = Demo::ENV.chain(App::APPLICATION_ERROR) do
|
|
72
|
+
render Renderer::ApplicationError
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# in case of success, returns an instance of Views::Person
|
|
76
|
+
# in case of validation failure, renders using Renderer::ValidationError
|
|
77
|
+
# in case of internal error, renders using Renderer::InternalError
|
|
78
|
+
SOME_ACTION = Demo::ENV.chain(App::SOME_ACTION) do
|
|
79
|
+
failure_chain :validate, VALIDATION_ERROR
|
|
80
|
+
failure_chain :call, INTERNAL_ERROR
|
|
81
|
+
wrap Presenters::Person
|
|
82
|
+
wrap Views::ShowPerson
|
|
83
|
+
end
|
|
23
84
|
end
|
|
24
|
-
call Some::Action
|
|
25
|
-
wrap Some::Presenter
|
|
26
85
|
end
|
|
27
86
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
87
|
+
* [feature] Support (re)defining chain specific failure chains in case of uncaught exceptions.
|
|
88
|
+
|
|
89
|
+
module Demo
|
|
31
90
|
|
|
32
|
-
|
|
33
|
-
|
|
91
|
+
module App
|
|
92
|
+
INTERNAL_ERROR = Demo::ENV.chain { wrap Error::InternalError }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
module Web
|
|
96
|
+
|
|
97
|
+
INTERNAL_ERROR = Demo::ENV.chain(App::INTERNAL_ERROR) do
|
|
98
|
+
render Renderer::InternalError
|
|
99
|
+
end
|
|
34
100
|
|
|
35
|
-
|
|
101
|
+
# The INTERNAL_ERROR chain will be called if an exception
|
|
102
|
+
# isn't rescued by the responsible handler
|
|
103
|
+
SOME_ACTION = Demo::ENV.chain(App::SOME_ACTION, INTERNAL_ERROR) do
|
|
104
|
+
failure_chain :validate, VALIDATION_ERROR
|
|
105
|
+
failure_chain :call, INTERNAL_ERROR
|
|
106
|
+
wrap Presenters::Person
|
|
107
|
+
wrap Views::ShowPerson
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
36
111
|
|
|
37
|
-
[Compare v0.0.8..
|
|
112
|
+
[Compare v0.0.8..v0.0.9](https://github.com/snusnu/substation/compare/v0.0.8...v0.0.9)
|
|
38
113
|
|
|
39
114
|
# v0.0.8 2013-06-19
|
|
40
115
|
|
data/Gemfile
CHANGED
|
@@ -2,7 +2,14 @@ source 'http://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
+
group :test do
|
|
6
|
+
gem 'multi_json', '~> 1.8.0'
|
|
7
|
+
gem 'ducktrap', '~> 0.0.2', :git => 'https://github.com/mbj/ducktrap', :branch => 'master'
|
|
8
|
+
gem 'vanguard', '~> 0.0.4', :git => 'https://github.com/mbj/vanguard', :branch => 'master'
|
|
9
|
+
gem 'anima', '~> 0.2.0'
|
|
10
|
+
end
|
|
11
|
+
|
|
5
12
|
group :development do
|
|
6
|
-
gem 'devtools', :git => 'https://github.com/rom-rb/devtools.git'
|
|
13
|
+
gem 'devtools', :git => 'https://github.com/rom-rb/devtools.git', :branch => 'master'
|
|
7
14
|
eval File.read('Gemfile.devtools')
|
|
8
15
|
end
|
data/Gemfile.devtools
CHANGED
|
@@ -1,47 +1,63 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
group :development do
|
|
4
|
-
gem 'rake',
|
|
5
|
-
gem 'rspec',
|
|
6
|
-
gem '
|
|
4
|
+
gem 'rake', '~> 10.3.2'
|
|
5
|
+
gem 'rspec', '~> 3.0.0'
|
|
6
|
+
gem 'rspec-its', '~> 1.0.1'
|
|
7
|
+
gem 'yard', '~> 0.8.7.4'
|
|
8
|
+
|
|
9
|
+
platform :rbx do
|
|
10
|
+
gem 'rubysl-singleton', '~> 2.0.0'
|
|
11
|
+
end
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
group :yard do
|
|
10
|
-
gem 'kramdown', '~> 1.
|
|
15
|
+
gem 'kramdown', '~> 1.3.3'
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
group :guard do
|
|
14
|
-
gem 'guard', '~>
|
|
15
|
-
gem 'guard-bundler', '~>
|
|
16
|
-
gem 'guard-rspec', '~>
|
|
17
|
-
gem 'guard-rubocop', '~>
|
|
18
|
-
gem 'guard-mutant', '~> 0.0.1'
|
|
19
|
+
gem 'guard', '~> 2.6.1'
|
|
20
|
+
gem 'guard-bundler', '~> 2.0.0'
|
|
21
|
+
gem 'guard-rspec', '~> 4.2.9'
|
|
22
|
+
gem 'guard-rubocop', '~> 1.1.0'
|
|
19
23
|
|
|
20
24
|
# file system change event handling
|
|
21
|
-
gem 'listen', '~>
|
|
25
|
+
gem 'listen', '~> 2.7.7'
|
|
22
26
|
gem 'rb-fchange', '~> 0.0.6', require: false
|
|
23
|
-
gem 'rb-fsevent', '~> 0.9.
|
|
24
|
-
gem 'rb-inotify', '~> 0.9.
|
|
27
|
+
gem 'rb-fsevent', '~> 0.9.4', require: false
|
|
28
|
+
gem 'rb-inotify', '~> 0.9.5', require: false
|
|
25
29
|
|
|
26
30
|
# notification handling
|
|
27
|
-
gem 'libnotify', '~> 0.8.
|
|
31
|
+
gem 'libnotify', '~> 0.8.3', require: false
|
|
28
32
|
gem 'rb-notifu', '~> 0.0.4', require: false
|
|
29
33
|
gem 'terminal-notifier-guard', '~> 1.5.3', require: false
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
group :metrics do
|
|
33
|
-
gem 'coveralls', '~> 0.
|
|
34
|
-
gem 'flay', '~> 2.
|
|
35
|
-
gem 'flog', '~> 4.
|
|
36
|
-
gem 'reek', '~> 1.3.
|
|
37
|
-
gem 'rubocop', '~> 0.
|
|
37
|
+
gem 'coveralls', '~> 0.7.0'
|
|
38
|
+
gem 'flay', '~> 2.5.0'
|
|
39
|
+
gem 'flog', '~> 4.2.1'
|
|
40
|
+
gem 'reek', '~> 1.3.7'
|
|
41
|
+
gem 'rubocop', '~> 0.23.0'
|
|
38
42
|
gem 'simplecov', '~> 0.7.1'
|
|
39
|
-
gem 'yardstick', '~> 0.9.
|
|
43
|
+
gem 'yardstick', '~> 0.9.9'
|
|
44
|
+
|
|
45
|
+
platforms :mri do
|
|
46
|
+
gem 'mutant', '~> 0.5.23'
|
|
47
|
+
gem 'mutant-rspec', '~> 0.5.21'
|
|
48
|
+
end
|
|
40
49
|
|
|
41
50
|
platforms :ruby_19, :ruby_20 do
|
|
42
|
-
gem 'mutant', git: 'https://github.com/mbj/mutant.git'
|
|
43
51
|
gem 'yard-spellcheck', '~> 0.1.5'
|
|
44
52
|
end
|
|
53
|
+
|
|
54
|
+
platform :rbx do
|
|
55
|
+
gem 'json', '~> 1.8.1'
|
|
56
|
+
gem 'racc', '~> 1.4.11'
|
|
57
|
+
gem 'rubysl-logger', '~> 2.0.0'
|
|
58
|
+
gem 'rubysl-open-uri', '~> 2.0.0'
|
|
59
|
+
gem 'rubysl-prettyprint', '~> 2.0.3'
|
|
60
|
+
end
|
|
45
61
|
end
|
|
46
62
|
|
|
47
63
|
group :benchmarks do
|
|
@@ -50,6 +66,6 @@ end
|
|
|
50
66
|
|
|
51
67
|
platform :jruby do
|
|
52
68
|
group :jruby do
|
|
53
|
-
gem 'jruby-openssl', '~> 0.
|
|
69
|
+
gem 'jruby-openssl', '~> 0.9.4'
|
|
54
70
|
end
|
|
55
71
|
end
|
data/Guardfile
CHANGED
|
@@ -4,7 +4,7 @@ guard :bundler do
|
|
|
4
4
|
watch('Gemfile')
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
guard :rspec, :all_on_start => false, :all_after_pass => false do
|
|
7
|
+
guard :rspec, :all_on_start => false, :all_after_pass => false, :cmd => 'bundle exec rspec --fail-fast --seed 1' do
|
|
8
8
|
# run all specs if the spec_helper or supporting files files are modified
|
|
9
9
|
watch('spec/spec_helper.rb') { 'spec/unit' }
|
|
10
10
|
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec/unit' }
|
data/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[][travis]
|
|
5
5
|
[][gemnasium]
|
|
6
6
|
[][codeclimate]
|
|
7
|
+
[](http://inch-ci.org/github/snusnu/substation)
|
|
7
8
|
[][coveralls]
|
|
8
9
|
|
|
9
10
|
[gem]: https://rubygems.org/gems/substation
|
|
@@ -22,7 +23,8 @@ receive arbitrary input data which will be available in `request.input`.
|
|
|
22
23
|
Additionally, `request.env` contains an arbitrary object that
|
|
23
24
|
represents your application environment and will typically provide access
|
|
24
25
|
to useful things like a logger and probably some sort of storage engine
|
|
25
|
-
abstraction object.
|
|
26
|
+
abstraction object. Furthermore, `request.name` will contain the action
|
|
27
|
+
name the `Substation::Dispatcher` used when dispatching to an action.
|
|
26
28
|
|
|
27
29
|
The contract further specifies that every action must return an instance
|
|
28
30
|
of either `Substation::Response::Success` or
|
|
@@ -509,9 +511,9 @@ In a typical application scenario, a few things need to happen before an
|
|
|
509
511
|
actual use case (an action) can be invoked. These things will often
|
|
510
512
|
include the following steps (probably in that order).
|
|
511
513
|
|
|
514
|
+
* Input data sanitization
|
|
512
515
|
* Authentication
|
|
513
516
|
* Authorization
|
|
514
|
-
* Input data sanitization
|
|
515
517
|
* Input data validation
|
|
516
518
|
|
|
517
519
|
We only want to invoke our action if all those steps succeed. If any of
|
|
@@ -541,129 +543,198 @@ b) If you need to return JSON, you might just
|
|
|
541
543
|
* Pass the response data to some serializer object and dump it to JSON
|
|
542
544
|
|
|
543
545
|
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:
|
|
546
|
+
provides an object called `Substation::Chain`. To construct a chain, you
|
|
547
|
+
need to pass an enumerable of processors to `Substation::Chain#initialize`.
|
|
548
|
+
Processors must support three methods:
|
|
560
549
|
|
|
561
550
|
1. `#call(<Substation::Request, Substation::Response>) => Substation::Response`
|
|
562
551
|
2. `#result(Substation::Response) => <Substation::Request, Substation::Response>`
|
|
552
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
563
553
|
|
|
564
|
-
### Incoming
|
|
554
|
+
### Incoming processors
|
|
565
555
|
|
|
566
556
|
All steps required *before* processing the action will potentially
|
|
567
557
|
produce a new, altered, `Substation::Request`. Therefore, the object
|
|
568
558
|
passed to `#call` must be an instance of `Substation::Request`.
|
|
569
559
|
|
|
570
560
|
Since `#call` must return a `Substation::Response` (because the chain
|
|
571
|
-
would halt and return that response in case calling
|
|
561
|
+
would halt and return that response in case calling `Processor#success?`
|
|
572
562
|
method would return `false`), we also need to implement `#result`
|
|
573
563
|
and have it return a `Substation::Request` instance that can be passed
|
|
574
564
|
on to the next handler.
|
|
575
565
|
|
|
576
|
-
The contract for incoming
|
|
566
|
+
The contract for incoming processors therefore is:
|
|
577
567
|
|
|
578
568
|
1. `#call(Substation::Request) => Substation::Response`
|
|
579
569
|
2. `#result(Substation::Response) => Substation::Request`
|
|
570
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
580
571
|
|
|
581
|
-
By including the `Substation::
|
|
582
|
-
class, you'll get the following for free:
|
|
572
|
+
By including the `Substation::Processor::Incoming` module into your
|
|
573
|
+
processor class, you'll get the following for free:
|
|
583
574
|
|
|
584
575
|
```ruby
|
|
576
|
+
def initialize(name, handler, failure_chain)
|
|
577
|
+
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
578
|
+
end
|
|
579
|
+
|
|
585
580
|
def result(response)
|
|
586
|
-
|
|
581
|
+
response.to_request
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def success?(response)
|
|
585
|
+
response.success?
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def with_failure_chain(chain)
|
|
589
|
+
self.class.new(name, handler, chain)
|
|
587
590
|
end
|
|
588
591
|
```
|
|
589
592
|
|
|
590
|
-
This shows that an incoming
|
|
593
|
+
This shows that an incoming processor can alter the incoming request in any
|
|
591
594
|
way that it wants to, as long as it returns the new request input data in
|
|
592
595
|
`Substation::Response#output` returned from `#call`.
|
|
593
596
|
|
|
594
|
-
|
|
597
|
+
Currently, `substation` provides the following incoming processors out
|
|
598
|
+
of the box:
|
|
599
|
+
|
|
600
|
+
* `Substation::Processor::Evaluator::Request` passes `request` to the handler
|
|
601
|
+
* `Substation::Processor::Evaluator::Data` passes `request.input` to the handler
|
|
602
|
+
|
|
603
|
+
### The pivot processor
|
|
595
604
|
|
|
596
605
|
Pivot is just another fancy name for the action in the context of a
|
|
597
|
-
chain. It's also the point where all subsequent
|
|
606
|
+
chain. It's also the point where all subsequent processors have to further
|
|
598
607
|
process the `Substation::Response` returned from invoking the action.
|
|
608
|
+
Therefore, the pivot processor is the last processor that expects a
|
|
609
|
+
`Substation::Request` as parameter to its `#call` method.
|
|
599
610
|
|
|
600
|
-
The contract for the pivot
|
|
611
|
+
The contract for the pivot processor therefore is:
|
|
601
612
|
|
|
602
613
|
1. `#call(Substation::Request) => Substation::Response`
|
|
603
614
|
2. `#result(Substation::Response) => Substation::Response`
|
|
615
|
+
3. `#success?(Substation::Response) => Boolean`
|
|
604
616
|
|
|
605
|
-
By including the `Substation::
|
|
617
|
+
By including the `Substation::Processor::Pivot` module into your handler
|
|
606
618
|
class, you'll get the following for free:
|
|
607
619
|
|
|
608
620
|
```ruby
|
|
621
|
+
def initialize(name, handler, failure_chain)
|
|
622
|
+
@name, @handler, @failure_chain = name, handler, failure_chain
|
|
623
|
+
end
|
|
624
|
+
|
|
609
625
|
def result(response)
|
|
610
626
|
response
|
|
611
627
|
end
|
|
628
|
+
|
|
629
|
+
def success?(response)
|
|
630
|
+
response.success?
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def with_failure_chain(chain)
|
|
634
|
+
self.class.new(name, handler, chain)
|
|
635
|
+
end
|
|
612
636
|
```
|
|
613
637
|
|
|
614
|
-
This reflects the fact that a pivot
|
|
615
|
-
producing the "raw" response, returns it unaltered.
|
|
638
|
+
This reflects the fact that a pivot processor (since it's the one actually
|
|
639
|
+
producing the "raw" response, returns it unaltered).
|
|
640
|
+
|
|
641
|
+
The pivot processor is shipped with `substation` and is implemented by
|
|
642
|
+
`Substation::Processor::Evaluator::Pivot`.
|
|
616
643
|
|
|
617
|
-
### Outgoing
|
|
644
|
+
### Outgoing processors
|
|
618
645
|
|
|
619
646
|
All steps required *after* processing the action will potentially
|
|
620
647
|
produce a new, altered, `Substation::Response` instance to be returned.
|
|
621
648
|
Therefore the object passed to `#call` must be an instance of
|
|
622
|
-
`Substation::Response`. Since subsequent outgoing
|
|
649
|
+
`Substation::Response`. Since subsequent outgoing processors might further
|
|
623
650
|
process the response, `#result` must be implemented so that it returns a
|
|
624
|
-
`Substation::Response` object that can be passed on to the next
|
|
651
|
+
`Substation::Response` object that can be passed on to the next
|
|
652
|
+
processor.
|
|
625
653
|
|
|
626
|
-
The contract for outgoing
|
|
654
|
+
The contract for outgoing processors therefore is:
|
|
627
655
|
|
|
628
656
|
1. `#call(Substation::Response) => Substation::Response`
|
|
629
657
|
2. `#result(Substation::Response) => Substation::Response`
|
|
658
|
+
3. `#success?(Substation::Response) => true`
|
|
630
659
|
|
|
631
|
-
By including the `Substation::
|
|
632
|
-
class, you'll get the following for free:
|
|
660
|
+
By including the `Substation::Processor::Outgoing` module into your
|
|
661
|
+
processor class, you'll get the following for free:
|
|
633
662
|
|
|
634
663
|
```ruby
|
|
664
|
+
def initialize(name, handler)
|
|
665
|
+
@name, @handler = name, handler
|
|
666
|
+
end
|
|
667
|
+
|
|
635
668
|
def result(response)
|
|
636
669
|
response
|
|
637
670
|
end
|
|
671
|
+
|
|
672
|
+
def success?(response)
|
|
673
|
+
true
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
private
|
|
677
|
+
|
|
678
|
+
def respond_with(response, output)
|
|
679
|
+
response.class.new(response.request, output)
|
|
680
|
+
end
|
|
638
681
|
```
|
|
639
682
|
|
|
640
|
-
This shows that an outgoing
|
|
683
|
+
This shows that an outgoing processor's `#call` can do anything with
|
|
641
684
|
the `Substation::Response#output` it received, as long as it makes
|
|
642
685
|
sure to return a new response with the new output properly set.
|
|
643
686
|
|
|
687
|
+
Currently, `substation` provides the following outgoing processors out
|
|
688
|
+
of the box:
|
|
689
|
+
|
|
690
|
+
* `Substation::Processor::Wrapper` wraps `response.output` in a new handler instance
|
|
691
|
+
* `Substation::Processor::Transformer` transforms `response.output` using a new handler instance
|
|
692
|
+
|
|
693
|
+
### Handlers
|
|
694
|
+
|
|
695
|
+
You might have noticed the `handler` param passed to any processor's
|
|
696
|
+
`#initialize` method. Handlers are the actual objects performing your
|
|
697
|
+
application logic. Processors use these handlers to produce the data
|
|
698
|
+
they're supposed to "pipe through the chain".
|
|
699
|
+
|
|
700
|
+
The interface your handlers must implement should be familiar by now.
|
|
701
|
+
|
|
702
|
+
All handlers to be used with incoming processors must accept an instance
|
|
703
|
+
of `Substation::Request` as parameter to `#call`. Handlers to be used
|
|
704
|
+
with `Substation::Processor::Evaluator` subclasses must furthermore
|
|
705
|
+
return an object that responds to `#success?` and `#output`.
|
|
706
|
+
|
|
707
|
+
Note how the interface required for evaluator handler return values
|
|
708
|
+
matches the interface a `Substation::Response` exposes. This means that
|
|
709
|
+
the pivot processor can be (and is) implemented using the builtin
|
|
710
|
+
`Substation::Processor::Evaluator::Request` processor. The handler you
|
|
711
|
+
pass to the pivot processor is the object that actually implements your
|
|
712
|
+
application usecase, the action, and it's response gets evaluated.
|
|
713
|
+
|
|
714
|
+
All handlers to be used with outgoing processors must accept an instance
|
|
715
|
+
of `Substation::Response` as parameter to `#call`. They can do whatever
|
|
716
|
+
they want with the passed in response, but they must make sure to return
|
|
717
|
+
another instance of `Substation::Response`. To help with this, outgoing
|
|
718
|
+
processors provide the `#respond_with(response, data)` method that
|
|
719
|
+
you'll typically call to return the response value for `#call`.
|
|
720
|
+
|
|
644
721
|
### Example
|
|
645
722
|
|
|
646
723
|
[substation-demo](https://github.com/snusnu/substation-demo) implements a
|
|
647
724
|
simple web application using `Substation::Chain`.
|
|
648
725
|
|
|
649
|
-
The demo
|
|
726
|
+
The demo uses a few of the above mentioned *incoming processors*
|
|
650
727
|
for
|
|
651
728
|
|
|
652
729
|
* [Sanitization](https://github.com/snusnu/substation-demo/blob/master/demo/web/sanitizers.rb) using [ducktrap](https://github.com/mbj/ducktrap)
|
|
653
730
|
* [Validation](https://github.com/snusnu/substation-demo/blob/master/demo/validators.rb) using [vanguard](https://github.com/mbj/vanguard)
|
|
654
731
|
|
|
655
|
-
and some simple *outgoing
|
|
732
|
+
and some simple *outgoing processors* for
|
|
656
733
|
|
|
657
734
|
* Wrapping response output in a
|
|
658
735
|
[presenter](https://github.com/snusnu/substation-demo/blob/master/demo/web/presenters.rb)
|
|
659
736
|
* [Serializing](https://github.com/snusnu/substation-demo/blob/master/demo/web/serializers.rb) response output to JSON
|
|
660
737
|
|
|
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
738
|
Have a look at the base
|
|
668
739
|
[actions](https://github.com/snusnu/substation-demo/blob/master/demo/web/actions.rb)
|
|
669
740
|
that are then used to either produce
|
|
@@ -679,6 +750,7 @@ Finally it's all hooked up behind a few
|
|
|
679
750
|
|
|
680
751
|
* [snusnu](https://github.com/snusnu)
|
|
681
752
|
* [mbj](https://github.com/mbj)
|
|
753
|
+
* [indrekj](https://github.com/indrekj)
|
|
682
754
|
|
|
683
755
|
## Contributing
|
|
684
756
|
|