substation 0.0.10.beta2 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. data/.travis.yml +7 -3
  2. data/Changelog.md +99 -24
  3. data/Gemfile +8 -1
  4. data/Gemfile.devtools +37 -21
  5. data/Guardfile +1 -1
  6. data/README.md +118 -46
  7. data/TODO.md +1 -0
  8. data/config/flay.yml +2 -2
  9. data/config/flog.yml +1 -1
  10. data/config/reek.yml +41 -16
  11. data/config/rubocop.yml +44 -0
  12. data/config/yardstick.yml +1 -1
  13. data/lib/substation.rb +41 -5
  14. data/lib/substation/chain.rb +73 -84
  15. data/lib/substation/chain/definition.rb +147 -0
  16. data/lib/substation/chain/dsl.rb +150 -112
  17. data/lib/substation/chain/dsl/config.rb +55 -0
  18. data/lib/substation/chain/dsl/module_builder.rb +84 -0
  19. data/lib/substation/dispatcher.rb +20 -229
  20. data/lib/substation/dsl/guard.rb +96 -0
  21. data/lib/substation/dsl/registry.rb +181 -0
  22. data/lib/substation/environment.rb +126 -23
  23. data/lib/substation/environment/dsl.rb +31 -12
  24. data/lib/substation/processor.rb +238 -7
  25. data/lib/substation/processor/builder.rb +26 -0
  26. data/lib/substation/processor/config.rb +24 -0
  27. data/lib/substation/processor/evaluator.rb +66 -42
  28. data/lib/substation/processor/evaluator/handler.rb +54 -0
  29. data/lib/substation/processor/evaluator/result.rb +24 -0
  30. data/lib/substation/processor/executor.rb +46 -0
  31. data/lib/substation/processor/nest.rb +40 -0
  32. data/lib/substation/processor/transformer.rb +46 -0
  33. data/lib/substation/processor/wrapper.rb +16 -5
  34. data/lib/substation/request.rb +29 -27
  35. data/lib/substation/response.rb +19 -34
  36. data/lib/substation/response/api.rb +37 -0
  37. data/lib/substation/response/exception.rb +20 -0
  38. data/lib/substation/response/exception/output.rb +59 -0
  39. data/lib/substation/response/failure.rb +11 -0
  40. data/lib/substation/response/success.rb +11 -0
  41. data/lib/substation/version.rb +3 -1
  42. data/spec/demo/core.rb +64 -0
  43. data/spec/demo/core/action.rb +49 -0
  44. data/spec/demo/core/action/create_person.rb +28 -0
  45. data/spec/demo/core/errors.rb +16 -0
  46. data/spec/demo/core/facade.rb +38 -0
  47. data/spec/demo/core/handler.rb +21 -0
  48. data/spec/demo/core/handler/acceptor.rb +47 -0
  49. data/spec/demo/core/handler/authenticator.rb +36 -0
  50. data/spec/demo/core/handler/authorizer.rb +38 -0
  51. data/spec/demo/core/input.rb +15 -0
  52. data/spec/demo/core/observers.rb +10 -0
  53. data/spec/demo/core/validator.rb +21 -0
  54. data/spec/demo/domain/actor.rb +29 -0
  55. data/spec/demo/domain/dto/person.rb +18 -0
  56. data/spec/demo/domain/environment.rb +55 -0
  57. data/spec/demo/domain/storage.rb +49 -0
  58. data/spec/demo/web.rb +26 -0
  59. data/spec/demo/web/errors.rb +9 -0
  60. data/spec/demo/web/facade.rb +60 -0
  61. data/spec/demo/web/handler/deserializer.rb +36 -0
  62. data/spec/demo/web/presenter.rb +38 -0
  63. data/spec/demo/web/presenter/person.rb +19 -0
  64. data/spec/demo/web/renderer.rb +45 -0
  65. data/spec/demo/web/sanitizer.rb +35 -0
  66. data/spec/demo/web/sanitizer/person.rb +20 -0
  67. data/spec/demo/web/views.rb +28 -0
  68. data/spec/integration/demo/core_spec.rb +97 -0
  69. data/spec/integration/demo/web_spec.rb +114 -0
  70. data/spec/shared/context/integration/demo.rb +33 -0
  71. data/spec/shared/context/unit/chain.rb +13 -0
  72. data/spec/shared/context/unit/processor.rb +58 -0
  73. data/spec/shared/context/unit/request.rb +8 -0
  74. data/spec/shared/examples/integration/demo.rb +35 -0
  75. data/spec/shared/examples/unit/processor.rb +72 -0
  76. data/spec/spec_helper.rb +52 -23
  77. data/spec/unit/substation/chain/definition_spec.rb +141 -0
  78. data/spec/unit/substation/chain/dsl/config/dsl_module_spec.rb +13 -0
  79. data/spec/unit/substation/chain/dsl/config/registry_spec.rb +13 -0
  80. data/spec/unit/substation/chain/dsl/config_spec.rb +18 -0
  81. data/spec/unit/substation/chain/dsl/module_builder_spec.rb +77 -0
  82. data/spec/unit/substation/chain/dsl_spec.rb +175 -0
  83. data/spec/unit/substation/chain_spec.rb +303 -0
  84. data/spec/unit/substation/dispatcher_spec.rb +68 -0
  85. data/spec/unit/substation/dsl/guard_spec.rb +72 -0
  86. data/spec/unit/substation/dsl/registry_spec.rb +181 -0
  87. data/spec/unit/substation/environment/dsl_spec.rb +156 -0
  88. data/spec/unit/substation/environment_spec.rb +259 -0
  89. data/spec/unit/substation/processor/builder_spec.rb +21 -0
  90. data/spec/unit/substation/processor/config_spec.rb +40 -0
  91. data/spec/unit/substation/processor/evaluator/handler_spec.rb +20 -0
  92. data/spec/unit/substation/processor/evaluator/pivot_spec.rb +42 -0
  93. data/spec/unit/substation/processor/evaluator/request_spec.rb +11 -0
  94. data/spec/unit/substation/processor/evaluator/result/failure_spec.rb +14 -0
  95. data/spec/unit/substation/processor/evaluator/result/success_spec.rb +14 -0
  96. data/spec/unit/substation/processor/evaluator/result_spec.rb +13 -0
  97. data/spec/unit/substation/processor/evaluator_spec.rb +18 -0
  98. data/spec/unit/substation/processor/executor/null_spec.rb +25 -0
  99. data/spec/unit/substation/processor/executor_spec.rb +32 -0
  100. data/spec/unit/substation/processor/fallible_spec.rb +24 -0
  101. data/spec/unit/substation/processor/incoming_spec.rb +17 -0
  102. data/spec/unit/substation/processor/nest/incoming_spec.rb +56 -0
  103. data/spec/unit/substation/processor/nest_spec.rb +6 -0
  104. data/spec/unit/substation/processor/outgoing_spec.rb +47 -0
  105. data/spec/unit/substation/processor/transformer/incoming_spec.rb +17 -0
  106. data/spec/unit/substation/processor/transformer/outgoing_spec.rb +17 -0
  107. data/spec/unit/substation/processor/wrapper/incoming_spec.rb +15 -0
  108. data/spec/unit/substation/processor/wrapper/outgoing_spec.rb +15 -0
  109. data/spec/unit/substation/processor/wrapper_spec.rb +24 -0
  110. data/spec/unit/substation/processor_spec.rb +68 -0
  111. data/spec/unit/substation/request_spec.rb +70 -0
  112. data/spec/unit/substation/response/api_spec.rb +22 -0
  113. data/spec/unit/substation/response/exception/output_spec.rb +46 -0
  114. data/spec/unit/substation/response/exception_spec.rb +25 -0
  115. data/spec/unit/substation/response/failure_spec.rb +25 -0
  116. data/spec/unit/substation/response/success_spec.rb +24 -0
  117. data/spec/unit/substation/response_spec.rb +73 -0
  118. data/substation.gemspec +7 -6
  119. metadata +157 -67
  120. checksums.yaml +0 -7
  121. data/TODO +0 -0
  122. data/lib/substation/observer.rb +0 -66
  123. data/lib/substation/processor/pivot.rb +0 -25
  124. data/lib/substation/utils.rb +0 -68
  125. data/spec/integration/substation/dispatcher/call_spec.rb +0 -260
  126. data/spec/unit/substation/chain/call_spec.rb +0 -63
  127. data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +0 -19
  128. data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +0 -21
  129. data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +0 -30
  130. data/spec/unit/substation/chain/dsl/chain_spec.rb +0 -15
  131. data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -24
  132. data/spec/unit/substation/chain/dsl/initialize_spec.rb +0 -19
  133. data/spec/unit/substation/chain/dsl/processors_spec.rb +0 -42
  134. data/spec/unit/substation/chain/dsl/use_spec.rb +0 -14
  135. data/spec/unit/substation/chain/each_spec.rb +0 -46
  136. data/spec/unit/substation/chain/incoming/result_spec.rb +0 -21
  137. data/spec/unit/substation/chain/outgoing/call_spec.rb +0 -25
  138. data/spec/unit/substation/chain/outgoing/result_spec.rb +0 -21
  139. data/spec/unit/substation/dispatcher/action/call_spec.rb +0 -23
  140. data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +0 -61
  141. data/spec/unit/substation/dispatcher/action_names_spec.rb +0 -14
  142. data/spec/unit/substation/dispatcher/call_spec.rb +0 -47
  143. data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +0 -20
  144. data/spec/unit/substation/environment/chain_spec.rb +0 -50
  145. data/spec/unit/substation/environment/class_methods/build_spec.rb +0 -11
  146. data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +0 -18
  147. data/spec/unit/substation/environment/dsl/register_spec.rb +0 -14
  148. data/spec/unit/substation/environment/dsl/registry_spec.rb +0 -19
  149. data/spec/unit/substation/observer/chain/call_spec.rb +0 -26
  150. data/spec/unit/substation/observer/class_methods/coerce_spec.rb +0 -33
  151. data/spec/unit/substation/observer/null/call_spec.rb +0 -12
  152. data/spec/unit/substation/processor/evaluator/call_spec.rb +0 -49
  153. data/spec/unit/substation/processor/pivot/call_spec.rb +0 -17
  154. data/spec/unit/substation/processor/wrapper/call_spec.rb +0 -20
  155. data/spec/unit/substation/request/env_spec.rb +0 -14
  156. data/spec/unit/substation/request/error_spec.rb +0 -15
  157. data/spec/unit/substation/request/input_spec.rb +0 -14
  158. data/spec/unit/substation/request/success_spec.rb +0 -15
  159. data/spec/unit/substation/response/env_spec.rb +0 -16
  160. data/spec/unit/substation/response/failure/success_predicate_spec.rb +0 -15
  161. data/spec/unit/substation/response/input_spec.rb +0 -16
  162. data/spec/unit/substation/response/output_spec.rb +0 -16
  163. data/spec/unit/substation/response/request_spec.rb +0 -16
  164. data/spec/unit/substation/response/success/success_predicate_spec.rb +0 -15
  165. data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +0 -46
  166. data/spec/unit/substation/utils/class_methods/const_get_spec.rb +0 -46
  167. data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +0 -20
@@ -6,9 +6,13 @@ rvm:
6
6
  - 1.9.3
7
7
  - 2.0.0
8
8
  - ruby-head
9
- - jruby-19mode
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
@@ -1,40 +1,115 @@
1
- # v0.0.9 (not yet released)
1
+ # v0.0.10 2014-08-11
2
2
 
3
- * [feature] Support definining failure chains
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
- env = Substation::Environment.build do
6
- register :evaluate, Substation::Processor::Evaluator
7
- register :wrap, Substation::Processor::Wrapper
8
- end
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
- class Error
11
- attr_reader :data
12
- def initialize(data)
13
- @data = data
51
+ ValidationError = Class.new(self)
52
+ ApplicationError = Class.new(self)
53
+ InternalError = Class.new(self)
14
54
  end
15
55
 
16
- class ValidationError < self
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
- chain = env.chain do
21
- evaluate Vanguard::Validator do
22
- wrap Errors::ValidationError
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
- env = Object.new
29
- invalid_request = Substation::Requet.new(env, :invalid)
30
- response = chain.call(invalid_request)
87
+ * [feature] Support (re)defining chain specific failure chains in case of uncaught exceptions.
88
+
89
+ module Demo
31
90
 
32
- response.data.instance_of?(Errors::ValidationError)
33
- # => true
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
- response.data.data # => the actual vanguard violation set
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..master](https://github.com/snusnu/substation/compare/v0.0.8...master)
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
@@ -1,47 +1,63 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  group :development do
4
- gem 'rake', '~> 10.1.0'
5
- gem 'rspec', '~> 2.14.1'
6
- gem 'yard', '~> 0.8.7'
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.2.0'
15
+ gem 'kramdown', '~> 1.3.3'
11
16
  end
12
17
 
13
18
  group :guard do
14
- gem 'guard', '~> 1.8.1'
15
- gem 'guard-bundler', '~> 1.0.0'
16
- gem 'guard-rspec', '~> 3.0.2'
17
- gem 'guard-rubocop', '~> 0.2.0'
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', '~> 1.3.0'
25
+ gem 'listen', '~> 2.7.7'
22
26
  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
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.0', require: false
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.6.7'
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
+ 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.7', git: 'https://github.com/dkubb/yardstick.git'
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.8.5'
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
  [![Build Status](https://secure.travis-ci.org/snusnu/substation.png?branch=master)][travis]
5
5
  [![Dependency Status](https://gemnasium.com/snusnu/substation.png)][gemnasium]
6
6
  [![Code Climate](https://codeclimate.com/github/snusnu/substation.png)][codeclimate]
7
+ [![Inline docs](http://inch-ci.org/github/snusnu/substation.png)](http://inch-ci.org/github/snusnu/substation)
7
8
  [![Coverage Status](https://coveralls.io/repos/snusnu/substation/badge.png?branch=master)][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`. 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:
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 handlers
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 its `#success?`
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 handlers therefore is:
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::Chain::Incoming` module into your handler
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
- Request.new(response.env, response.output)
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 handler can alter the incoming request in any
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
- ### The pivot handler
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 handlers have to further
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 handler therefore is:
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::Chain::Pivot` module into your handler
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 handler (since it's the one actually
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 handlers
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 handlers might further
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 handler.
651
+ `Substation::Response` object that can be passed on to the next
652
+ processor.
625
653
 
626
- The contract for outgoing handlers therefore is:
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::Chain::Outgoing` module into your handler
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 handler's `#call` can do anything with
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 implements a few of the above mentioned *incoming handlers*
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 handlers* for
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