coach 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -29
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +30 -0
- data/Gemfile +2 -1
- data/README.md +4 -2
- data/coach.gemspec +5 -2
- data/docs/COMPATIBILITY.md +17 -0
- data/lib/coach.rb +2 -0
- data/lib/coach/errors.rb +2 -0
- data/lib/coach/handler.rb +7 -28
- data/lib/coach/middleware.rb +9 -30
- data/lib/coach/middleware_item.rb +3 -1
- data/lib/coach/middleware_validator.rb +2 -0
- data/lib/coach/notifications.rb +5 -9
- data/lib/coach/request_benchmark.rb +8 -1
- data/lib/coach/request_serializer.rb +6 -1
- data/lib/coach/router.rb +6 -4
- data/lib/coach/rspec.rb +2 -2
- data/lib/coach/version.rb +3 -1
- data/spec/lib/coach/handler_spec.rb +14 -80
- data/spec/lib/coach/middleware_spec.rb +25 -0
- data/spec/lib/coach/middleware_validator_spec.rb +4 -0
- data/spec/lib/coach/notifications_spec.rb +2 -0
- data/spec/lib/coach/request_benchmark_spec.rb +5 -2
- data/spec/lib/coach/request_serializer_spec.rb +7 -5
- data/spec/lib/coach/router_spec.rb +2 -0
- data/spec/spec_helper.rb +2 -0
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f314e7cc1779f326294cede2142167c325fd5182cc403b17dcdf2ffa4a0ab2f0
|
4
|
+
data.tar.gz: c29c7bc00999c22d65ed1da8268761ff1633f936e58e856dd9f2b3dad189ce80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da2a45e51025660adc160604297d3614fea894b79b203be1ee8b337832281d59d4a4f8e3e4aa222209754251763cecced1e225a039921e9f8a979a2b741410e6
|
7
|
+
data.tar.gz: fc581d18446b2fbab925e49a4cb984d06d7112f1e90bde8dffb07ae124d0a061a9f6c107afd4addc1c8058ec5be503316d5a7437042b9d9c3ffbf602c6522af4
|
data/.circleci/config.yml
CHANGED
@@ -11,7 +11,7 @@ references:
|
|
11
11
|
- type: cache-restore
|
12
12
|
key: coach-bundler-{{ checksum "coach.gemspec" }}-{{ checksum "~/RAILS_VERSION.txt" }}
|
13
13
|
|
14
|
-
- run: gem install bundler
|
14
|
+
- run: gem install bundler -v 1.11.2
|
15
15
|
|
16
16
|
- run: bundle install --path vendor/bundle
|
17
17
|
|
@@ -33,30 +33,6 @@ references:
|
|
33
33
|
|
34
34
|
- run: bundle exec rubocop
|
35
35
|
jobs:
|
36
|
-
build-ruby22-rails515:
|
37
|
-
docker:
|
38
|
-
- image: ruby:2.2
|
39
|
-
environment:
|
40
|
-
- RAILS_VERSION=5.1.5
|
41
|
-
steps: *steps
|
42
|
-
build-ruby22-rails4210:
|
43
|
-
docker:
|
44
|
-
- image: ruby:2.2
|
45
|
-
environment:
|
46
|
-
- RAILS_VERSION=4.2.10
|
47
|
-
steps: *steps
|
48
|
-
build-ruby23-rails515:
|
49
|
-
docker:
|
50
|
-
- image: ruby:2.3
|
51
|
-
environment:
|
52
|
-
- RAILS_VERSION=5.1.5
|
53
|
-
steps: *steps
|
54
|
-
build-ruby23-rails4210:
|
55
|
-
docker:
|
56
|
-
- image: ruby:2.3
|
57
|
-
environment:
|
58
|
-
- RAILS_VERSION=4.2.10
|
59
|
-
steps: *steps
|
60
36
|
build-ruby24-rails515:
|
61
37
|
docker:
|
62
38
|
- image: ruby:2.4
|
@@ -86,10 +62,6 @@ workflows:
|
|
86
62
|
version: 2
|
87
63
|
tests:
|
88
64
|
jobs:
|
89
|
-
- build-ruby22-rails515
|
90
|
-
- build-ruby22-rails4210
|
91
|
-
- build-ruby23-rails515
|
92
|
-
- build-ruby23-rails4210
|
93
65
|
- build-ruby24-rails515
|
94
66
|
- build-ruby24-rails4210
|
95
67
|
- build-ruby25-rails515
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,35 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
# Unreleased
|
4
|
+
|
5
|
+
No unreleased changes.
|
6
|
+
|
7
|
+
# 2.0.0 / 2019-06-13
|
8
|
+
|
9
|
+
## Breaking changes
|
10
|
+
|
11
|
+
* [#70](https://github.com/gocardless/coach/pull/70) The following deprecated event names have been removed:
|
12
|
+
* `coach.handler.start`
|
13
|
+
* `coach.middleware.start`
|
14
|
+
* `coach.middleware.finish`
|
15
|
+
* `coach.handler.finish`
|
16
|
+
* `coach.request`
|
17
|
+
|
18
|
+
* [#56](https://github.com/gocardless/coach/pull/56) Support for Ruby 2.2 and 2.3 has been
|
19
|
+
dropped. See our [compatibility policy](https://github.com/gocardless/coach/blob/master/docs/COMPATIBILITY.md) for more information.
|
20
|
+
|
21
|
+
## Other changes
|
22
|
+
|
23
|
+
* [#59](https://github.com/gocardless/coach/pull/59) Request data included in the
|
24
|
+
`request.coach` event includes a `session_ip` field which replaces the erroneously named
|
25
|
+
`session_id`. The latter is deprecated and will be removed in a future release.
|
26
|
+
|
27
|
+
* [#60](https://github.com/gocardless/coach/pull/60) Add `Middleware.requires?`, which
|
28
|
+
behaves like `Middleware.provides?` but for requirements.
|
29
|
+
|
30
|
+
* [#52](https://github.com/gocardless/coach/pull/52) Add `duration_seconds` to statistic
|
31
|
+
logging. The `duration` field is now deprecated and will be removed in a future release.
|
32
|
+
|
3
33
|
# 1.0.0 / 2018-04-19
|
4
34
|
|
5
35
|
## Breaking changes
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Coach
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/coach.svg)](http://badge.fury.io/rb/coach)
|
4
|
-
[![
|
4
|
+
[![CircleCI](https://circleci.com/gh/gocardless/coach.svg?style=svg)](https://circleci.com/gh/gocardless/coach)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/gocardless/coach.svg)](https://codeclimate.com/github/gocardless/coach)
|
6
6
|
|
7
7
|
Coach improves your controller code by encouraging:
|
@@ -13,6 +13,8 @@ Coach improves your controller code by encouraging:
|
|
13
13
|
- **Testability** - Test each middleware in isolation, with effortless mocking of test
|
14
14
|
data and natural RSpec matchers.
|
15
15
|
|
16
|
+
For our policy on compatibility with Ruby and Rails versions, see [COMPATIBILITY.md](docs/COMPATIBILITY.md).
|
17
|
+
|
16
18
|
# Installation
|
17
19
|
|
18
20
|
To get started, just add Coach to your `Gemfile`, and then run `bundle`:
|
@@ -21,7 +23,7 @@ To get started, just add Coach to your `Gemfile`, and then run `bundle`:
|
|
21
23
|
gem 'coach'
|
22
24
|
```
|
23
25
|
|
24
|
-
Coach works with Ruby versions 2.
|
26
|
+
Coach works with Ruby versions 2.4 and onwards.
|
25
27
|
|
26
28
|
## Coach by example
|
27
29
|
|
data/coach.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
lib = File.expand_path("lib", __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require "coach/version"
|
@@ -10,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
10
12
|
spec.homepage = "https://github.com/gocardless/coach"
|
11
13
|
spec.email = %w[developers@gocardless.com]
|
12
14
|
spec.license = "MIT"
|
13
|
-
spec.required_ruby_version = ">= 2.
|
15
|
+
spec.required_ruby_version = ">= 2.4"
|
14
16
|
|
15
17
|
spec.files = `git ls-files -z`.split("\x0")
|
16
18
|
spec.test_files = spec.files.grep(%r{^spec/})
|
@@ -19,8 +21,9 @@ Gem::Specification.new do |spec|
|
|
19
21
|
spec.add_dependency "actionpack", ">= 4.2"
|
20
22
|
spec.add_dependency "activesupport", ">= 4.2"
|
21
23
|
|
24
|
+
spec.add_development_dependency "gc_ruboconfig", "= 2.4.0"
|
22
25
|
spec.add_development_dependency "pry", "~> 0.10"
|
23
26
|
spec.add_development_dependency "rspec", "~> 3.2"
|
24
27
|
spec.add_development_dependency "rspec-its", "~> 1.2"
|
25
|
-
spec.add_development_dependency "rspec_junit_formatter", "~> 0.
|
28
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.0"
|
26
29
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Compatibility
|
2
|
+
|
3
|
+
Our goal as Coach maintainers is for the library to be compatible with all supported versions of Ruby and Rails.
|
4
|
+
|
5
|
+
Specifically, any CRuby/MRI version that has not received an End of Life notice ([e.g. this notice for Ruby 2.1](https://www.ruby-lang.org/en/news/2017/04/01/support-of-ruby-2-1-has-ended/)) is supported. Similarly, any version of Rails listed as currently supported on [this page](http://guides.rubyonrails.org/maintenance_policy.html) is one we aim to support in Coach.
|
6
|
+
|
7
|
+
To that end, [our build matrix](../.circleci/config.yml) includes all these versions.
|
8
|
+
|
9
|
+
Any time Coach doesn't work on a supported combination of Ruby and Rails, it's a bug, and can be reported [here](https://github.com/gocardless/coach/issues).
|
10
|
+
|
11
|
+
# Deprecation
|
12
|
+
|
13
|
+
Whenever a version of Ruby or Rails falls out of support, we will mirror that change in Coach by updating the build matrix and releasing a new major version.
|
14
|
+
|
15
|
+
At that point, we will close any issues that only affect the unsupported version, and may choose to remove any workarounds from the code that are only necessary for the unsupported version.
|
16
|
+
|
17
|
+
We will then bump the major version of Coach, to indicate the break in compatibility. Even if the new version of Coach happens to work on the unsupported version of Ruby or Rails, we consider compatibility to be broken at this point.
|
data/lib/coach.rb
CHANGED
data/lib/coach/errors.rb
CHANGED
data/lib/coach/handler.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "coach/errors"
|
2
4
|
require "active_support/core_ext/object/try"
|
3
5
|
|
@@ -8,10 +10,10 @@ module Coach
|
|
8
10
|
def initialize(middleware, config = {})
|
9
11
|
@root_item = MiddlewareItem.new(middleware, config)
|
10
12
|
validate!
|
11
|
-
rescue Coach::Errors::MiddlewareDependencyNotMet =>
|
13
|
+
rescue Coach::Errors::MiddlewareDependencyNotMet => e
|
12
14
|
# Remove noise of validation stack trace, reset to the handler callsite
|
13
|
-
|
14
|
-
raise
|
15
|
+
e.backtrace.clear.concat(Thread.current.backtrace)
|
16
|
+
raise e
|
15
17
|
end
|
16
18
|
|
17
19
|
# Run validation on the root of the middleware chain
|
@@ -28,8 +30,8 @@ module Coach
|
|
28
30
|
|
29
31
|
event = build_event(context)
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
+
publish("start_handler.coach", event.dup)
|
34
|
+
instrument("finish_handler.coach", event) do
|
33
35
|
begin
|
34
36
|
response = chain.instrument.call
|
35
37
|
ensure
|
@@ -87,28 +89,5 @@ module Coach
|
|
87
89
|
request: context[:request],
|
88
90
|
}
|
89
91
|
end
|
90
|
-
|
91
|
-
def publish_start(event)
|
92
|
-
if notifier.listening?("coach.handler.start")
|
93
|
-
ActiveSupport::Deprecation.warn("The 'coach.handler.start' event has been " \
|
94
|
-
"renamed to 'start_handler.coach' and the old name will be removed in a " \
|
95
|
-
"future version.")
|
96
|
-
publish("coach.handler.start", event)
|
97
|
-
end
|
98
|
-
publish("start_handler.coach", event)
|
99
|
-
end
|
100
|
-
|
101
|
-
def instrumented_call(event, &block)
|
102
|
-
if notifier.listening?("coach.handler.finish")
|
103
|
-
ActiveSupport::Deprecation.warn("The 'coach.handler.find' event has been " \
|
104
|
-
"renamed to 'finish_handler.coach' and the old name will be removed in a " \
|
105
|
-
"future version.")
|
106
|
-
instrument("coach.handler.finish", event) do
|
107
|
-
instrument("finish_handler.coach", event, &block)
|
108
|
-
end
|
109
|
-
else
|
110
|
-
instrument("finish_handler.coach", event, &block)
|
111
|
-
end
|
112
|
-
end
|
113
92
|
end
|
114
93
|
end
|
data/lib/coach/middleware.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "coach/middleware_item"
|
2
4
|
|
3
5
|
module Coach
|
@@ -40,6 +42,10 @@ module Coach
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
def self.requires?(provision)
|
46
|
+
requirements.include?(provision)
|
47
|
+
end
|
48
|
+
|
43
49
|
attr_reader :next_middleware, :config
|
44
50
|
|
45
51
|
# Middleware gets access to a shared context, which is populated by other
|
@@ -73,14 +79,10 @@ module Coach
|
|
73
79
|
# Use ActiveSupport to instrument the execution of the subsequent chain.
|
74
80
|
def instrument
|
75
81
|
proc do
|
76
|
-
|
82
|
+
ActiveSupport::Notifications.publish("start_middleware.coach", middleware_event)
|
77
83
|
|
78
|
-
|
79
|
-
|
80
|
-
else
|
81
|
-
ActiveSupport::Notifications.
|
82
|
-
instrument("finish_middleware.coach", middleware_event) { call }
|
83
|
-
end
|
84
|
+
ActiveSupport::Notifications.
|
85
|
+
instrument("finish_middleware.coach", middleware_event) { call }
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
@@ -106,28 +108,5 @@ module Coach
|
|
106
108
|
request: request,
|
107
109
|
}
|
108
110
|
end
|
109
|
-
|
110
|
-
def publish_start
|
111
|
-
if ActiveSupport::Notifications.notifier.listening?("coach.middleware.start")
|
112
|
-
ActiveSupport::Deprecation.warn("The 'coach.middleware.start' event has " \
|
113
|
-
"been renamed to 'start_middleware.coach' and the old name will be " \
|
114
|
-
"removed in a future version.")
|
115
|
-
ActiveSupport::Notifications.
|
116
|
-
publish("coach.middleware.start", middleware_event)
|
117
|
-
end
|
118
|
-
ActiveSupport::Notifications.
|
119
|
-
publish("start_middleware.coach", middleware_event)
|
120
|
-
end
|
121
|
-
|
122
|
-
def instrument_deprecated(&block)
|
123
|
-
ActiveSupport::Deprecation.warn("The 'coach.middleware.finish' event has " \
|
124
|
-
"been renamed to 'finish_middleware.coach' and the old name will be " \
|
125
|
-
"removed in a future version.")
|
126
|
-
ActiveSupport::Notifications.
|
127
|
-
instrument("coach.middleware.finish", middleware_event) do
|
128
|
-
ActiveSupport::Notifications.
|
129
|
-
instrument("finish_middleware.coach", middleware_event, &block)
|
130
|
-
end
|
131
|
-
end
|
132
111
|
end
|
133
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "coach/errors"
|
2
4
|
require "coach/middleware_validator"
|
3
5
|
|
@@ -13,7 +15,7 @@ module Coach
|
|
13
15
|
def build_middleware(context, successor)
|
14
16
|
@middleware.
|
15
17
|
new(context,
|
16
|
-
successor
|
18
|
+
successor&.instrument,
|
17
19
|
config)
|
18
20
|
end
|
19
21
|
|
data/lib/coach/notifications.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "request_benchmark"
|
2
4
|
require_relative "request_serializer"
|
3
5
|
|
@@ -43,6 +45,7 @@ module Coach
|
|
43
45
|
|
44
46
|
def unsubscribe!
|
45
47
|
return unless active?
|
48
|
+
|
46
49
|
while @subscriptions.any?
|
47
50
|
ActiveSupport::Notifications.unsubscribe(@subscriptions.pop)
|
48
51
|
end
|
@@ -63,15 +66,13 @@ module Coach
|
|
63
66
|
end
|
64
67
|
|
65
68
|
def subscribe(event, &block)
|
66
|
-
#
|
67
|
-
# format. No need to warn here since the warnings will show up from elsewhere.
|
68
|
-
key = event.include?(".") ? "coach.#{event}" : "#{event}.coach"
|
69
|
-
ActiveSupport::Notifications.subscribe(key, &block)
|
69
|
+
ActiveSupport::Notifications.subscribe("#{event}.coach", &block)
|
70
70
|
end
|
71
71
|
|
72
72
|
def log_middleware_finish(event, start, finish)
|
73
73
|
benchmark_for_request = @benchmarks[event[:request].uuid]
|
74
74
|
return unless benchmark_for_request.present?
|
75
|
+
|
75
76
|
benchmark_for_request.notify(event[:middleware], start, finish)
|
76
77
|
end
|
77
78
|
|
@@ -87,11 +88,6 @@ module Coach
|
|
87
88
|
serialized = RequestSerializer.new(event[:request]).serialize.
|
88
89
|
merge(benchmark.stats).
|
89
90
|
merge(event.slice(:response, :metadata))
|
90
|
-
if ActiveSupport::Notifications.notifier.listening?("coach.request")
|
91
|
-
ActiveSupport::Deprecation.warn("The 'coach.request' event has been renamed " \
|
92
|
-
"to 'request.coach' and the old name will be removed in a future version.")
|
93
|
-
ActiveSupport::Notifications.publish("coach.request", serialized)
|
94
|
-
end
|
95
91
|
ActiveSupport::Notifications.publish("request.coach", serialized)
|
96
92
|
end
|
97
93
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Coach
|
2
4
|
# This class is built to aggregate data during the course of the request. It relies on
|
3
5
|
# 'start_middleware.coach' and 'finish_middleware.coach' events to register the
|
@@ -32,8 +34,13 @@ module Coach
|
|
32
34
|
endpoint_name: @endpoint_name,
|
33
35
|
started_at: @start,
|
34
36
|
duration: format_ms(@duration),
|
37
|
+
duration_seconds: @duration,
|
35
38
|
chain: sorted_chain.map do |event|
|
36
|
-
{
|
39
|
+
{
|
40
|
+
name: event[:name],
|
41
|
+
duration: format_ms(event[:duration]),
|
42
|
+
duration_seconds: event[:duration],
|
43
|
+
}
|
37
44
|
end,
|
38
45
|
}
|
39
46
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Coach
|
2
4
|
class RequestSerializer
|
3
5
|
def self.header_rules
|
@@ -14,6 +16,7 @@ module Coach
|
|
14
16
|
# Applies sanitizing rules. Expects `header` to be in 'http_header_name' form.
|
15
17
|
def self.apply_header_rule(header, value)
|
16
18
|
return value if header_rules[header].nil?
|
19
|
+
|
17
20
|
header_rules[header].call(value)
|
18
21
|
end
|
19
22
|
|
@@ -39,7 +42,8 @@ module Coach
|
|
39
42
|
|
40
43
|
# Extra request info
|
41
44
|
headers: filtered_headers,
|
42
|
-
session_id: @request.remote_ip,
|
45
|
+
session_id: @request.remote_ip, # TODO: remove in a future release
|
46
|
+
session_ip: @request.remote_ip,
|
43
47
|
}
|
44
48
|
end
|
45
49
|
|
@@ -54,6 +58,7 @@ module Coach
|
|
54
58
|
def filtered_headers
|
55
59
|
header_value_pairs = @request.filtered_env.map do |key, value|
|
56
60
|
next unless key =~ /^HTTP_/
|
61
|
+
|
57
62
|
[key.downcase, self.class.apply_header_rule(key.downcase, value)]
|
58
63
|
end.compact
|
59
64
|
|
data/lib/coach/router.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "handler"
|
2
4
|
require_relative "errors"
|
3
5
|
|
4
6
|
module Coach
|
5
7
|
class Router
|
6
8
|
ACTION_TRAITS = {
|
7
|
-
index:
|
8
|
-
show:
|
9
|
-
create:
|
10
|
-
update:
|
9
|
+
index: { method: :get },
|
10
|
+
show: { method: :get, url: ":id" },
|
11
|
+
create: { method: :post },
|
12
|
+
update: { method: :put, url: ":id" },
|
11
13
|
destroy: { method: :delete, url: ":id" },
|
12
14
|
}.each_value(&:freeze).freeze
|
13
15
|
|
data/lib/coach/rspec.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "rspec/expectations"
|
2
4
|
require "coach/middleware"
|
3
5
|
|
4
6
|
# Middleware stubbing ######################################
|
5
7
|
|
6
|
-
# rubocop:disable Metrics/MethodLength
|
7
8
|
# rubocop:disable Metrics/AbcSize
|
8
9
|
def build_middleware(name)
|
9
10
|
Class.new(Coach::Middleware) do
|
@@ -28,7 +29,6 @@ def build_middleware(name)
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
# rubocop:enable Metrics/AbcSize
|
31
|
-
# rubocop:enable Metrics/MethodLength
|
32
32
|
|
33
33
|
def null_middleware
|
34
34
|
double(call: nil)
|
data/lib/coach/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
require "coach/handler"
|
@@ -123,27 +125,25 @@ describe Coach::Handler do
|
|
123
125
|
before { terminal_middleware.uses(middleware_a) }
|
124
126
|
|
125
127
|
describe "notifications" do
|
126
|
-
before do
|
127
|
-
Coach::Notifications.subscribe!
|
128
|
-
|
129
|
-
# Prevent RequestSerializer from erroring due to insufficient request mock
|
130
|
-
allow(Coach::RequestSerializer).
|
131
|
-
to receive(:new).
|
132
|
-
and_return(instance_double("Coach::RequestSerializer", serialize: {}))
|
133
|
-
end
|
134
|
-
|
135
128
|
subject(:coach_events) do
|
136
129
|
events = []
|
137
130
|
subscription = ActiveSupport::Notifications.
|
138
|
-
subscribe(/\.coach$/)
|
139
|
-
events << name
|
140
|
-
end
|
131
|
+
subscribe(/\.coach$/) { |name, *_args| events << name }
|
141
132
|
|
142
133
|
handler.call({})
|
143
134
|
ActiveSupport::Notifications.unsubscribe(subscription)
|
144
135
|
events
|
145
136
|
end
|
146
137
|
|
138
|
+
before do
|
139
|
+
Coach::Notifications.subscribe!
|
140
|
+
|
141
|
+
# Prevent RequestSerializer from erroring due to insufficient request mock
|
142
|
+
allow(Coach::RequestSerializer).
|
143
|
+
to receive(:new).
|
144
|
+
and_return(instance_double("Coach::RequestSerializer", serialize: {}))
|
145
|
+
end
|
146
|
+
|
147
147
|
it { is_expected.to include("start_handler.coach") }
|
148
148
|
it { is_expected.to include("start_middleware.coach") }
|
149
149
|
it { is_expected.to include("request.coach") }
|
@@ -160,7 +160,7 @@ describe Coach::Handler do
|
|
160
160
|
|
161
161
|
begin
|
162
162
|
handler.call({})
|
163
|
-
rescue
|
163
|
+
rescue StandardError
|
164
164
|
:continue_anyway
|
165
165
|
end
|
166
166
|
ActiveSupport::Notifications.unsubscribe(subscription)
|
@@ -172,7 +172,7 @@ describe Coach::Handler do
|
|
172
172
|
before { terminal_middleware.uses(middleware_a, callback: explosive_action) }
|
173
173
|
|
174
174
|
it "captures the error event with the metadata" do
|
175
|
-
|
175
|
+
expect(coach_events).
|
176
176
|
to include(["finish_handler.coach", hash_including(
|
177
177
|
response: { status: 500 },
|
178
178
|
metadata: { A: true },
|
@@ -183,72 +183,6 @@ describe Coach::Handler do
|
|
183
183
|
expect { handler.call({}) }.to raise_error(StandardError, "AH")
|
184
184
|
end
|
185
185
|
end
|
186
|
-
|
187
|
-
context "deprecations" do
|
188
|
-
subject(:coach_events) do
|
189
|
-
events = []
|
190
|
-
subscription = ActiveSupport::Notifications.
|
191
|
-
subscribe(/^coach\./) do |name, *_args|
|
192
|
-
events << name
|
193
|
-
end
|
194
|
-
|
195
|
-
handler.call({})
|
196
|
-
ActiveSupport::Notifications.unsubscribe(subscription)
|
197
|
-
events
|
198
|
-
end
|
199
|
-
|
200
|
-
let!(:deprecations_silenced) do
|
201
|
-
ActiveSupport::Deprecation.silenced
|
202
|
-
end
|
203
|
-
|
204
|
-
before do
|
205
|
-
ActiveSupport::Deprecation.silenced = true
|
206
|
-
end
|
207
|
-
|
208
|
-
after do
|
209
|
-
ActiveSupport::Deprecation.silenced = deprecations_silenced
|
210
|
-
end
|
211
|
-
|
212
|
-
it { is_expected.to include("coach.handler.start") }
|
213
|
-
it { is_expected.to include("coach.middleware.start") }
|
214
|
-
it { is_expected.to include("coach.request") }
|
215
|
-
it { is_expected.to include("coach.middleware.finish") }
|
216
|
-
it { is_expected.to include("coach.handler.finish") }
|
217
|
-
|
218
|
-
context "when an exception is raised in the chain" do
|
219
|
-
subject(:coach_events) do
|
220
|
-
events = []
|
221
|
-
subscription = ActiveSupport::Notifications.
|
222
|
-
subscribe(/^coach\./) do |name, *args|
|
223
|
-
events << [name, args.last]
|
224
|
-
end
|
225
|
-
|
226
|
-
begin
|
227
|
-
handler.call({})
|
228
|
-
rescue
|
229
|
-
:continue_anyway
|
230
|
-
end
|
231
|
-
ActiveSupport::Notifications.unsubscribe(subscription)
|
232
|
-
events
|
233
|
-
end
|
234
|
-
|
235
|
-
let(:explosive_action) { -> { raise "AH" } }
|
236
|
-
|
237
|
-
before { terminal_middleware.uses(middleware_a, callback: explosive_action) }
|
238
|
-
|
239
|
-
it "captures the error event with the metadata" do
|
240
|
-
is_expected.
|
241
|
-
to include(["coach.handler.finish", hash_including(
|
242
|
-
response: { status: 500 },
|
243
|
-
metadata: { A: true },
|
244
|
-
)])
|
245
|
-
end
|
246
|
-
|
247
|
-
it "bubbles the error to the next handler" do
|
248
|
-
expect { handler.call({}) }.to raise_error(StandardError, "AH")
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
186
|
end
|
253
187
|
end
|
254
188
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "coach/middleware"
|
2
4
|
|
3
5
|
describe Coach::Middleware do
|
@@ -35,6 +37,29 @@ describe Coach::Middleware do
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
40
|
+
describe ".requires?" do
|
41
|
+
context "given names it does require" do
|
42
|
+
before { middleware_class.requires(:foo, :bar) }
|
43
|
+
|
44
|
+
it "returns true" do
|
45
|
+
# rubocop:disable RSpec/PredicateMatcher
|
46
|
+
expect(middleware_class.requires?(:foo)).to be_truthy
|
47
|
+
expect(middleware_class.requires?(:bar)).to be_truthy
|
48
|
+
# rubocop:enable RSpec/PredicateMatcher
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "given names it doesn't require" do
|
53
|
+
before { middleware_class.requires(:foo) }
|
54
|
+
|
55
|
+
it "returns false" do
|
56
|
+
# rubocop:disable RSpec/PredicateMatcher
|
57
|
+
expect(middleware_class.requires?(:bar)).to be_falsy
|
58
|
+
# rubocop:enable RSpec/PredicateMatcher
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
38
63
|
describe "#provide" do
|
39
64
|
before { middleware_class.provides(:foo) }
|
40
65
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "coach/middleware_validator"
|
3
5
|
|
@@ -37,6 +39,7 @@ describe Coach::MiddlewareValidator do
|
|
37
39
|
head_middleware.requires :c
|
38
40
|
middleware_c.provides :c
|
39
41
|
end
|
42
|
+
|
40
43
|
it { is_expected.to_not raise_error }
|
41
44
|
end
|
42
45
|
|
@@ -48,6 +51,7 @@ describe Coach::MiddlewareValidator do
|
|
48
51
|
middleware_a.provides :a
|
49
52
|
middleware_b.requires :a
|
50
53
|
end
|
54
|
+
|
51
55
|
it { is_expected.to_not raise_error }
|
52
56
|
end
|
53
57
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "coach/request_benchmark"
|
3
5
|
|
@@ -24,6 +26,7 @@ describe Coach::RequestBenchmark do
|
|
24
26
|
|
25
27
|
it "computes overall duration" do
|
26
28
|
expect(stats[:duration]).to eq(5000)
|
29
|
+
expect(stats[:duration_seconds]).to eq(5.0)
|
27
30
|
end
|
28
31
|
|
29
32
|
it "captures the endpoint_name" do
|
@@ -35,11 +38,11 @@ describe Coach::RequestBenchmark do
|
|
35
38
|
end
|
36
39
|
|
37
40
|
it "computes duration of middleware with no children" do
|
38
|
-
expect(stats[:chain]).to include(name: "B", duration: 1000)
|
41
|
+
expect(stats[:chain]).to include(name: "B", duration: 1000, duration_seconds: 1.0)
|
39
42
|
end
|
40
43
|
|
41
44
|
it "adjusts duration of middleware for their children" do
|
42
|
-
expect(stats[:chain]).to include(name: "A", duration: 2000)
|
45
|
+
expect(stats[:chain]).to include(name: "A", duration: 2000, duration_seconds: 2.0)
|
43
46
|
end
|
44
47
|
|
45
48
|
it "correctly orders chain" do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
require "active_support/core_ext/object/try"
|
3
5
|
require "coach/request_serializer"
|
@@ -40,12 +42,12 @@ describe Coach::RequestSerializer do
|
|
40
42
|
subject(:request_serializer) { described_class.new(mock_request) }
|
41
43
|
|
42
44
|
let(:mock_request) do
|
43
|
-
instance_double("ActionDispatch::Request", format:
|
44
|
-
remote_ip:
|
45
|
-
uuid:
|
46
|
-
method:
|
45
|
+
instance_double("ActionDispatch::Request", format: nil,
|
46
|
+
remote_ip: nil,
|
47
|
+
uuid: nil,
|
48
|
+
method: nil,
|
47
49
|
filtered_parameters: nil,
|
48
|
-
filtered_env:
|
50
|
+
filtered_env: {
|
49
51
|
"foo" => "bar",
|
50
52
|
"HTTP_foo" => "bar",
|
51
53
|
})
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coach
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GoCardless
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: gc_ruboconfig
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.4.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.4.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: pry
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +100,14 @@ dependencies:
|
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
103
|
+
version: 0.4.0
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
110
|
+
version: 0.4.0
|
97
111
|
description:
|
98
112
|
email:
|
99
113
|
- developers@gocardless.com
|
@@ -111,6 +125,7 @@ files:
|
|
111
125
|
- LICENSE.txt
|
112
126
|
- README.md
|
113
127
|
- coach.gemspec
|
128
|
+
- docs/COMPATIBILITY.md
|
114
129
|
- lib/coach.rb
|
115
130
|
- lib/coach/errors.rb
|
116
131
|
- lib/coach/handler.rb
|
@@ -143,15 +158,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
158
|
requirements:
|
144
159
|
- - ">="
|
145
160
|
- !ruby/object:Gem::Version
|
146
|
-
version: '2.
|
161
|
+
version: '2.4'
|
147
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
163
|
requirements:
|
149
164
|
- - ">="
|
150
165
|
- !ruby/object:Gem::Version
|
151
166
|
version: '0'
|
152
167
|
requirements: []
|
153
|
-
|
154
|
-
rubygems_version: 2.7.4
|
168
|
+
rubygems_version: 3.0.2
|
155
169
|
signing_key:
|
156
170
|
specification_version: 4
|
157
171
|
summary: Alternative controllers built with middleware
|