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
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Core
5
+ module Observers
6
+ LOG_EVENT = ->(response) { }
7
+ SEND_EMAIL = ->(response) { }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Core
5
+ module Validator
6
+
7
+ # substation support
8
+
9
+ DECOMPOSER = ->(request) {
10
+ request.input.data
11
+ }
12
+
13
+ COMPOSER = ->(request, output) {
14
+ Input::Incomplete.new(request.input.session, output)
15
+ }
16
+
17
+ EXECUTOR = Substation::Processor::Executor.new(DECOMPOSER, COMPOSER)
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Domain
5
+
6
+ class Actor
7
+ class Session
8
+ include Equalizer.new(:data)
9
+
10
+ attr_reader :account_id
11
+
12
+ def initialize(data)
13
+ @data = data
14
+ @account_id = @data.fetch('account_id')
15
+ end
16
+
17
+ protected
18
+
19
+ attr_reader :data
20
+ end
21
+
22
+ def self.coerce(session_data, person)
23
+ new(Session.new(session_data), person)
24
+ end
25
+
26
+ include Concord::Public.new(:session, :person)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Domain
5
+ module DTO
6
+
7
+ NEW_PERSON_VALIDATOR = Vanguard::Validator.build do
8
+ validates_presence_of :name
9
+ validates_length_of :name, :length => 3..20
10
+ end
11
+
12
+ class Person
13
+ include Anima.new(:id, :name)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Domain
5
+
6
+ class Environment
7
+
8
+ def self.build(name, storage, logger)
9
+ coerced_name = name.to_sym
10
+ klass = case coerced_name
11
+ when :development
12
+ Development
13
+ when :test
14
+ Test
15
+ when :production
16
+ Production
17
+ end
18
+ klass.new(coerced_name, storage, logger)
19
+ end
20
+
21
+ class Development < self
22
+ def development?
23
+ true
24
+ end
25
+ end
26
+
27
+ class Test < self
28
+ def test?
29
+ true
30
+ end
31
+ end
32
+
33
+ class Production < self
34
+ def production?
35
+ true
36
+ end
37
+ end
38
+
39
+ include AbstractType
40
+ include Concord::Public.new(:name, :storage, :logger)
41
+
42
+ def development?
43
+ false
44
+ end
45
+
46
+ def test?
47
+ false
48
+ end
49
+
50
+ def production?
51
+ false
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Domain
5
+
6
+ class Storage
7
+ include Concord::Public.new(:db)
8
+
9
+ INITIAL_SEQUENCE_VALUE = 2 # Demo::DATABASE is using 1 and 2
10
+
11
+ @sequence = INITIAL_SEQUENCE_VALUE
12
+
13
+ def self.next_key
14
+ @sequence += 1
15
+ end
16
+
17
+ def create_person(person)
18
+ db[:accounts] << new_person_tuple(next_key, person)
19
+ person # make specs easier by not testing "server generated" keys
20
+ end
21
+
22
+ def load_person(account_id)
23
+ tuple = db[:accounts].detect { |person| person[:id] == account_id }
24
+ tuple ? new_person_instance(tuple) : nil
25
+ end
26
+
27
+ def load_account_privilege(account_id, privilege_name)
28
+ db[:account_privileges].detect { |account_privilege|
29
+ account_privilege[:account_id] == account_id &&
30
+ account_privilege[:privilege_name] == privilege_name
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ def new_person_tuple(id, person)
37
+ { :id => id, :name => person.name }
38
+ end
39
+
40
+ def new_person_instance(tuple)
41
+ DTO::Person.new(:id => tuple[:id], :name => tuple[:name])
42
+ end
43
+
44
+ def next_key
45
+ self.class.next_key
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'multi_json'
4
+
5
+ require 'demo/core'
6
+
7
+ require 'demo/web/errors'
8
+ require 'demo/web/handler/deserializer'
9
+ require 'demo/web/sanitizer'
10
+ require 'demo/web/sanitizer/person'
11
+ require 'demo/web/presenter'
12
+ require 'demo/web/presenter/person'
13
+ require 'demo/web/views'
14
+ require 'demo/web/renderer'
15
+
16
+ class Demo
17
+ module Web
18
+ ENV = Core::ENV.inherit do
19
+ register :deserialize, Substation::Processor::Transformer::Incoming, Web::Handler::Deserializer::EXECUTOR
20
+ register :sanitize, Substation::Processor::Evaluator::Request, Web::Sanitizer::EXECUTOR
21
+ register :render, Substation::Processor::Transformer::Outgoing
22
+ end
23
+ end
24
+ end
25
+
26
+ require 'demo/web/facade'
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+ class Error < Core::Error
6
+ SanitizationError = Class.new(self)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+
6
+ SANITIZATION_ERROR = Web::ENV.chain { wrap Error::SanitizationError }
7
+
8
+ INTERNAL_ERROR = Web::ENV.chain(nil, Core::App::INTERNAL_ERROR) do
9
+ wrap Views::InternalError
10
+ end
11
+
12
+ CREATE_PERSON = Web::ENV.chain do
13
+ deserialize Handler::Deserializer
14
+ sanitize Sanitizer::NEW_PERSON, SANITIZATION_ERROR
15
+ chain Core::ENV[:create_person]
16
+ end
17
+
18
+ module HTML
19
+
20
+ SANITIZATION_ERROR = Web::ENV.chain(nil, Web::SANITIZATION_ERROR) do
21
+ wrap Views::SanitizationError
22
+ render Renderer::SanitizationError
23
+ end
24
+
25
+ AUTHENTICATION_ERROR = Web::ENV.chain(nil, Core::App::AUTHENTICATION_ERROR) do
26
+ render Renderer::AuthenticationError
27
+ end
28
+
29
+ AUTHORIZATION_ERROR = Web::ENV.chain(nil, Core::App::AUTHORIZATION_ERROR) do
30
+ render Renderer::AuthorizationError
31
+ end
32
+
33
+ VALIDATION_ERROR = Web::ENV.chain(nil, Core::App::VALIDATION_ERROR) do
34
+ render Renderer::ValidationError
35
+ end
36
+
37
+ APPLICATION_ERROR = Web::ENV.chain(nil, Core::App::APPLICATION_ERROR) do
38
+ render Renderer::ApplicationError
39
+ end
40
+
41
+ INTERNAL_ERROR = Web::ENV.chain(nil, Web::INTERNAL_ERROR) do
42
+ render Renderer::InternalError
43
+ end
44
+
45
+ Web::ENV.register(:create_person, Web::CREATE_PERSON, INTERNAL_ERROR) do
46
+ failure_chain :sanitize, SANITIZATION_ERROR
47
+ failure_chain :authenticate, AUTHENTICATION_ERROR
48
+ failure_chain :authorize, AUTHORIZATION_ERROR
49
+ failure_chain :validate, VALIDATION_ERROR
50
+ failure_chain :call, APPLICATION_ERROR
51
+ wrap Presenter::Person
52
+ wrap Views::Person
53
+ end
54
+
55
+ # The application
56
+ APP = Web::ENV.dispatcher
57
+
58
+ end # module HTML
59
+ end # module Web
60
+ end # class Demo
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+ module Handler
6
+
7
+ class Deserializer
8
+ extend Core::Handler
9
+ include Concord.new(:input)
10
+
11
+ DECOMPOSER = ->(request) {
12
+ request.input
13
+ }
14
+
15
+ COMPOSER = ->(request, output) {
16
+ Core::Input::Incomplete.new(output.fetch('session'), output.fetch('data'))
17
+ }
18
+
19
+ EXECUTOR = Substation::Processor::Executor.new(DECOMPOSER, COMPOSER)
20
+
21
+ def call
22
+ {
23
+ 'session' => deserialize(input.fetch('session')),
24
+ 'data' => deserialize(input.fetch('data'))
25
+ }
26
+ end
27
+
28
+ private
29
+
30
+ def deserialize(json)
31
+ MultiJson.load(json)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+
6
+ class Presenter
7
+ include AbstractType
8
+ include Adamantium::Flat
9
+ include Concord.new(:data)
10
+
11
+ def method_missing(method, *args, &block)
12
+ @data.send(method, *args, &block)
13
+ end
14
+
15
+ def respond_to?(method)
16
+ super || @data.respond_to?(method)
17
+ end
18
+
19
+ class Collection < self
20
+
21
+ def self.member(presenter = Undefined)
22
+ return @member if presenter.equal?(Undefined)
23
+ @member = presenter
24
+ end
25
+
26
+ alias_method :entries, :data
27
+
28
+ protected :entries
29
+
30
+ def each
31
+ return to_enum unless block_given?
32
+ entries.each { |entry| yield self.class.member.new(entry) }
33
+ self
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+ class Presenter
6
+
7
+ class Person < self
8
+
9
+ def name
10
+ "Presenting: #{super}"
11
+ end
12
+
13
+ class Collection < Presenter::Collection
14
+ member(Person)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+
6
+ module Renderer
7
+
8
+ class SanitizationError
9
+ def self.call(response)
10
+ "Don't mess with the input params: #{response.input.data.inspect}"
11
+ end
12
+ end
13
+
14
+ class AuthenticationError
15
+ def self.call(response)
16
+ "Failed to authenticate: #{response.input.data.inspect}"
17
+ end
18
+ end
19
+
20
+ class AuthorizationError
21
+ def self.call(response)
22
+ "Failed to authorize: #{response.input.data.inspect}"
23
+ end
24
+ end
25
+
26
+ class ValidationError
27
+ def self.call(response)
28
+ "Failed to validate: #{response.input.data.inspect}"
29
+ end
30
+ end
31
+
32
+ class ApplicationError
33
+ def self.call(response)
34
+ "Failed to process: #{response.output.inspect}"
35
+ end
36
+ end
37
+
38
+ class InternalError
39
+ def self.call(response)
40
+ "Something bad happened: #{response.output.inspect}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ class Demo
4
+ module Web
5
+
6
+ module Sanitizer
7
+
8
+ # substation support
9
+
10
+ DECOMPOSER = ->(request) {
11
+ request.input.data
12
+ }
13
+
14
+ COMPOSER = ->(request, output) {
15
+ Core::Input::Incomplete.new(request.input.session, output)
16
+ }
17
+
18
+ EXECUTOR = Substation::Processor::Executor.new(DECOMPOSER, COMPOSER)
19
+
20
+ # sanitizers
21
+
22
+ ID_TRAP = Ducktrap.build do
23
+ custom do
24
+ forward { |input| input.merge(:id => nil) }
25
+ inverse { |input|
26
+ input = input.dup
27
+ input.delete(:id)
28
+ input
29
+ }
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end