coach 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f98b2d520ff9d3365bc21440436f1505c1f691bc
4
- data.tar.gz: 0ef36903075d6e1339f028c7e41bed7f9ca96ee5
3
+ metadata.gz: 0032ec1d54bbe5de07362959a0e534824bc9913c
4
+ data.tar.gz: 1ce0cc458cc9529ccd90f5c915258a7192c97ca6
5
5
  SHA512:
6
- metadata.gz: 9356101215b2e311132501a81f609a4ace4a76de13d109ee6fb962ba28303edfc65882b0edd26cc6aef19b1296aa5c023f3e834465feeea115a2e8637b2314fa
7
- data.tar.gz: 8901071c03b028612902d07b143595bcfc02b187a09da697c5b87c8b4395f9be1fca0105caaa2d2368ade0ae1f07676a1ea4eeee871215c70d01a19b441e242e
6
+ metadata.gz: 3f3869bbafb466fb9f20437de88b0ed1ec194ace97bd71705e8822ca1ec06b4b675d787191d137e64b9aa1081332094441a53799ac25c322722d9275d91734cd
7
+ data.tar.gz: e358f26406181bda45c1569d1fc6c5e05ef5723c81f9510199c2fa2fcdfedc8f32651446cc7edc1925e6ca7f932f46fc1d026a6cdec2518a316fad3c3091c329
@@ -1,27 +1,27 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- coach (0.2.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.1)
12
- actionview (= 4.2.1)
13
- activesupport (= 4.2.1)
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.1)
18
- actionview (4.2.1)
19
- activesupport (= 4.2.1)
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.1)
24
- activesupport (4.2.1)
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.2)
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.7.0)
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.1)
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.6)
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.5
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,
@@ -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
- sub_sequence = item.middleware.middleware_dependencies
44
- filtered_sub_sequence = filter_sequence(sub_sequence, context)
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(flattened_sub_sequence + [item])
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
- chain_items = filter_sequence(sequence, context)
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, :config
6
+ attr_accessor :middleware, :parent
7
7
 
8
8
  def initialize(middleware, config = {})
9
9
  @middleware = middleware
10
- @config = config
10
+ @config_value = config
11
11
  end
12
12
 
13
13
  def build_middleware(context, successor)
14
- @middleware.new(context, successor && successor.instrument, @config)
15
- end
16
-
17
- # Requires tweaking to make it run methods by symbol on the class from which the
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
@@ -1,3 +1,3 @@
1
1
  module Coach
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.3'
3
3
  end
@@ -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, if: ->(ctx) { false }) }
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(middleware_c).to receive(:new)
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 C Terminal))
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.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: