coach 0.2.2 → 0.2.3
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 +4 -4
- data/Gemfile.lock +14 -14
- data/README.md +28 -0
- data/lib/coach/handler.rb +4 -13
- data/lib/coach/middleware_item.rb +26 -11
- data/lib/coach/version.rb +1 -1
- data/lib/spec/matchers.rb +1 -1
- data/spec/lib/coach/handler_spec.rb +15 -10
- metadata +20 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0032ec1d54bbe5de07362959a0e534824bc9913c
|
4
|
+
data.tar.gz: 1ce0cc458cc9529ccd90f5c915258a7192c97ca6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f3869bbafb466fb9f20437de88b0ed1ec194ace97bd71705e8822ca1ec06b4b675d787191d137e64b9aa1081332094441a53799ac25c322722d9275d91734cd
|
7
|
+
data.tar.gz: e358f26406181bda45c1569d1fc6c5e05ef5723c81f9510199c2fa2fcdfedc8f32651446cc7edc1925e6ca7f932f46fc1d026a6cdec2518a316fad3c3091c329
|
data/Gemfile.lock
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
coach (0.2.
|
4
|
+
coach (0.2.3)
|
5
5
|
actionpack (~> 4.2)
|
6
6
|
activesupport (~> 4.2)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionpack (4.2.
|
12
|
-
actionview (= 4.2.
|
13
|
-
activesupport (= 4.2.
|
11
|
+
actionpack (4.2.3)
|
12
|
+
actionview (= 4.2.3)
|
13
|
+
activesupport (= 4.2.3)
|
14
14
|
rack (~> 1.6)
|
15
15
|
rack-test (~> 0.6.2)
|
16
16
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
17
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.
|
18
|
-
actionview (4.2.
|
19
|
-
activesupport (= 4.2.
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
18
|
+
actionview (4.2.3)
|
19
|
+
activesupport (= 4.2.3)
|
20
20
|
builder (~> 3.1)
|
21
21
|
erubis (~> 2.7.0)
|
22
22
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
23
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.
|
24
|
-
activesupport (4.2.
|
23
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
24
|
+
activesupport (4.2.3)
|
25
25
|
i18n (~> 0.7)
|
26
26
|
json (~> 1.7, >= 1.7.7)
|
27
27
|
minitest (~> 5.1)
|
@@ -36,11 +36,11 @@ GEM
|
|
36
36
|
erubis (2.7.0)
|
37
37
|
i18n (0.7.0)
|
38
38
|
json (1.8.3)
|
39
|
-
loofah (2.0.
|
39
|
+
loofah (2.0.3)
|
40
40
|
nokogiri (>= 1.5.9)
|
41
41
|
method_source (0.8.2)
|
42
42
|
mini_portile (0.6.2)
|
43
|
-
minitest (5.
|
43
|
+
minitest (5.8.0)
|
44
44
|
nokogiri (1.6.6.2)
|
45
45
|
mini_portile (~> 0.6.0)
|
46
46
|
parser (2.2.2.5)
|
@@ -50,12 +50,12 @@ GEM
|
|
50
50
|
coderay (~> 1.1.0)
|
51
51
|
method_source (~> 0.8.1)
|
52
52
|
slop (~> 3.4)
|
53
|
-
rack (1.6.
|
53
|
+
rack (1.6.4)
|
54
54
|
rack-test (0.6.3)
|
55
55
|
rack (>= 1.0)
|
56
56
|
rails-deprecated_sanitizer (1.0.3)
|
57
57
|
activesupport (>= 4.2.0.alpha)
|
58
|
-
rails-dom-testing (1.0.
|
58
|
+
rails-dom-testing (1.0.7)
|
59
59
|
activesupport (>= 4.2.0.beta, < 5.0)
|
60
60
|
nokogiri (~> 1.6.0)
|
61
61
|
rails-deprecated_sanitizer (>= 1.0.1)
|
@@ -101,4 +101,4 @@ DEPENDENCIES
|
|
101
101
|
rubocop
|
102
102
|
|
103
103
|
BUNDLED WITH
|
104
|
-
1.10.
|
104
|
+
1.10.6
|
data/README.md
CHANGED
@@ -142,6 +142,34 @@ running code before hitting controller methods. It allows you to be confident th
|
|
142
142
|
data you require has been loaded, and makes tracing the origin of that data as simple as
|
143
143
|
looking up the chain.
|
144
144
|
|
145
|
+
## Configuring middlewares
|
146
|
+
|
147
|
+
By making use of middleware config hashes, you can build generalised middlewares that can
|
148
|
+
be configured specifically for the chain that they are used in.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class Logger < Coach::Middleware
|
152
|
+
def call
|
153
|
+
# Logs the incoming request path, with a configured prefix
|
154
|
+
Rails.logger.info("[#{config[:prefix]}] - #{request.path}")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class HelloUser < Coach::Middleware
|
159
|
+
uses Logger, prefix: 'HelloUser'
|
160
|
+
uses Authentication
|
161
|
+
|
162
|
+
def call
|
163
|
+
...
|
164
|
+
end
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
The above configures a `Logger` middleware to prefix it's log entries with `'HelloUser'`.
|
169
|
+
This is a contrived example, but at GoCardless we've created middlewares that can act as
|
170
|
+
generalised resource endpoints (show, index, etc) when given the model class and some
|
171
|
+
extra configuration.
|
172
|
+
|
145
173
|
## Testing
|
146
174
|
|
147
175
|
The basic strategy is to test each middleware in isolation, covering all the edge cases,
|
data/lib/coach/handler.rb
CHANGED
@@ -40,21 +40,18 @@ module Coach
|
|
40
40
|
# Traverse the middlware tree to build a linear middleware sequence,
|
41
41
|
# containing only middlewares that apply to this request.
|
42
42
|
def build_sequence(item, context)
|
43
|
-
|
44
|
-
|
45
|
-
flattened_sub_sequence = filtered_sub_sequence.flat_map do |child_item|
|
46
|
-
build_sequence(child_item, context)
|
43
|
+
sequence = item.middleware.middleware_dependencies.map do |child_item|
|
44
|
+
build_sequence(child_item.set_parent(item), context)
|
47
45
|
end
|
48
46
|
|
49
|
-
dedup_sequence(
|
47
|
+
dedup_sequence([*sequence, item].flatten)
|
50
48
|
end
|
51
49
|
|
52
50
|
# Given a middleware sequence, filter out items not applicable to the
|
53
51
|
# current request, and set up a chain of instantiated middleware objects,
|
54
52
|
# ready to serve a request.
|
55
53
|
def build_request_chain(sequence, context)
|
56
|
-
|
57
|
-
chain_items.reverse.reduce(nil) do |successor, item|
|
54
|
+
sequence.reverse.reduce(nil) do |successor, item|
|
58
55
|
item.build_middleware(context, successor)
|
59
56
|
end
|
60
57
|
end
|
@@ -72,12 +69,6 @@ module Coach
|
|
72
69
|
sequence.uniq { |item| [item.class, item.middleware, item.config] }
|
73
70
|
end
|
74
71
|
|
75
|
-
# Filter out middleware items that don't apply to this request - i.e. those
|
76
|
-
# that have defined an `if` condition that doesn't match this context.
|
77
|
-
def filter_sequence(sequence, context)
|
78
|
-
sequence.select { |item| item.use_with_context?(context) }
|
79
|
-
end
|
80
|
-
|
81
72
|
# Event to send for start of handler
|
82
73
|
def start_event(context)
|
83
74
|
{
|
@@ -3,23 +3,18 @@ require "coach/middleware_validator"
|
|
3
3
|
|
4
4
|
module Coach
|
5
5
|
class MiddlewareItem
|
6
|
-
attr_accessor :middleware, :
|
6
|
+
attr_accessor :middleware, :parent
|
7
7
|
|
8
8
|
def initialize(middleware, config = {})
|
9
9
|
@middleware = middleware
|
10
|
-
@
|
10
|
+
@config_value = config
|
11
11
|
end
|
12
12
|
|
13
13
|
def build_middleware(context, successor)
|
14
|
-
@middleware.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# `uses` call is made.
|
19
|
-
def use_with_context?(context)
|
20
|
-
return true if @config[:if].nil?
|
21
|
-
return @config[:if].call(context) if @config[:if].respond_to?(:call)
|
22
|
-
middleware.send(@config[:if], context)
|
14
|
+
@middleware.
|
15
|
+
new(context,
|
16
|
+
successor && successor.instrument,
|
17
|
+
config)
|
23
18
|
end
|
24
19
|
|
25
20
|
# Runs validation against the middleware chain, raising if any unmet dependencies are
|
@@ -27,5 +22,25 @@ module Coach
|
|
27
22
|
def validate!
|
28
23
|
MiddlewareValidator.new(middleware).validated_provides!
|
29
24
|
end
|
25
|
+
|
26
|
+
# Assigns the parent for this middleware, allowing config inheritance
|
27
|
+
def set_parent(parent)
|
28
|
+
@parent = parent
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generates config by either cloning our given config (if it's a hash) else if a
|
34
|
+
# lambda value, then will compute the config by calling the lambda with this
|
35
|
+
# middlewares parent config.
|
36
|
+
def config
|
37
|
+
@config ||= lambda_config? ? @config_value.call(parent.config) : @config_value.clone
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def lambda_config?
|
43
|
+
@config_value.respond_to?(:call)
|
44
|
+
end
|
30
45
|
end
|
31
46
|
end
|
data/lib/coach/version.rb
CHANGED
data/lib/spec/matchers.rb
CHANGED
@@ -16,7 +16,7 @@ def build_middleware(name)
|
|
16
16
|
|
17
17
|
# Build up a list of middleware called, in the order they were called
|
18
18
|
if next_middleware
|
19
|
-
[name].concat(next_middleware.call)
|
19
|
+
[name + config.except(:callback).inspect.to_s].concat(next_middleware.call)
|
20
20
|
else
|
21
21
|
[name]
|
22
22
|
end
|
@@ -8,6 +8,7 @@ describe Coach::Handler do
|
|
8
8
|
let(:middleware_a) { build_middleware("A") }
|
9
9
|
let(:middleware_b) { build_middleware("B") }
|
10
10
|
let(:middleware_c) { build_middleware("C") }
|
11
|
+
let(:middleware_d) { build_middleware("D") }
|
11
12
|
|
12
13
|
let(:terminal_middleware) { build_middleware("Terminal") }
|
13
14
|
let(:handler) { Coach::Handler.new(terminal_middleware) }
|
@@ -25,7 +26,7 @@ describe Coach::Handler do
|
|
25
26
|
result = handler.call({})
|
26
27
|
expect(a_spy).to have_received(:call)
|
27
28
|
expect(b_spy).to have_received(:call)
|
28
|
-
expect(result).to eq(%w(A B Terminal))
|
29
|
+
expect(result).to eq(%w(A{} B{} Terminal))
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -79,27 +80,31 @@ describe Coach::Handler do
|
|
79
80
|
|
80
81
|
describe "#build_request_chain" do
|
81
82
|
before { terminal_middleware.uses(middleware_a) }
|
82
|
-
before { terminal_middleware.uses(middleware_b,
|
83
|
-
before { terminal_middleware.uses(middleware_c) }
|
83
|
+
before { terminal_middleware.uses(middleware_b, b: true) }
|
84
84
|
|
85
85
|
let(:root_item) { Coach::MiddlewareItem.new(terminal_middleware) }
|
86
86
|
let(:sequence) { handler.build_sequence(root_item, {}) }
|
87
87
|
|
88
88
|
it "instantiates all matching middleware items in the sequence" do
|
89
89
|
expect(middleware_a).to receive(:new)
|
90
|
-
expect(
|
90
|
+
expect(middleware_b).to receive(:new)
|
91
91
|
expect(terminal_middleware).to receive(:new)
|
92
92
|
handler.build_request_chain(sequence, {})
|
93
93
|
end
|
94
94
|
|
95
|
-
it "doesn't instantiate non-matching middleware items" do
|
96
|
-
expect(middleware_b).not_to receive(:new)
|
97
|
-
handler.build_request_chain(sequence, {})
|
98
|
-
end
|
99
|
-
|
100
95
|
it "sets up the chain correctly, calling each item in the correct order" do
|
101
96
|
expect(handler.build_request_chain(sequence, {}).call).
|
102
|
-
to eq(%w(A
|
97
|
+
to eq(%w(A{} B{:b=>true} Terminal))
|
98
|
+
end
|
99
|
+
|
100
|
+
context "with inheriting config" do
|
101
|
+
before { middleware_b.uses(middleware_c, ->(config) { config.slice(:b) }) }
|
102
|
+
before { middleware_b.uses(middleware_d) }
|
103
|
+
|
104
|
+
it "calls lambda with parent middlewares config" do
|
105
|
+
expect(handler.build_request_chain(sequence, {}).call).
|
106
|
+
to eq(%w(A{} C{:b=>true} D{} B{:b=>true} Terminal))
|
107
|
+
end
|
103
108
|
end
|
104
109
|
end
|
105
110
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coach
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GoCardless
|
@@ -14,84 +14,84 @@ dependencies:
|
|
14
14
|
name: actionpack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 3.2.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 3.2.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec-its
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 1.2.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.2.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rubocop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: Controller framework
|
@@ -101,10 +101,10 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
-
- .gitignore
|
105
|
-
- .rspec
|
106
|
-
- .rubocop.yml
|
107
|
-
- .travis.yml
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".rubocop.yml"
|
107
|
+
- ".travis.yml"
|
108
108
|
- Gemfile
|
109
109
|
- Gemfile.lock
|
110
110
|
- README.md
|
@@ -139,12 +139,12 @@ require_paths:
|
|
139
139
|
- lib
|
140
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
|
-
- -
|
142
|
+
- - ">="
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0'
|
145
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
146
|
requirements:
|
147
|
-
- -
|
147
|
+
- - ">="
|
148
148
|
- !ruby/object:Gem::Version
|
149
149
|
version: '0'
|
150
150
|
requirements: []
|
@@ -162,3 +162,4 @@ test_files:
|
|
162
162
|
- spec/lib/coach/request_serializer_spec.rb
|
163
163
|
- spec/lib/coach/router_spec.rb
|
164
164
|
- spec/spec_helper.rb
|
165
|
+
has_rdoc:
|