coach 0.0.0
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 +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +123 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +104 -0
- data/README.md +260 -0
- data/coach.gemspec +28 -0
- data/lib/coach.rb +16 -0
- data/lib/coach/errors.rb +16 -0
- data/lib/coach/handler.rb +82 -0
- data/lib/coach/middleware.rb +96 -0
- data/lib/coach/middleware_item.rb +31 -0
- data/lib/coach/middleware_validator.rb +39 -0
- data/lib/coach/notifications.rb +88 -0
- data/lib/coach/request_benchmark.rb +59 -0
- data/lib/coach/request_serializer.rb +63 -0
- data/lib/coach/router.rb +70 -0
- data/lib/coach/version.rb +3 -0
- data/lib/spec/coach_helper.rb +113 -0
- data/spec/lib/coach/handler_spec.rb +138 -0
- data/spec/lib/coach/middleware_spec.rb +43 -0
- data/spec/lib/coach/middleware_validator_spec.rb +72 -0
- data/spec/lib/coach/notifications_spec.rb +73 -0
- data/spec/lib/coach/request_benchmark_spec.rb +42 -0
- data/spec/lib/coach/request_serializer_spec.rb +38 -0
- data/spec/lib/coach/router_spec.rb +78 -0
- data/spec/spec_helper.rb +14 -0
- metadata +162 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'coach/notifications'
|
3
|
+
|
4
|
+
describe Coach::Notifications do
|
5
|
+
subject(:notifications) { described_class.instance }
|
6
|
+
before { described_class.unsubscribe! }
|
7
|
+
|
8
|
+
# Remove need to fully mock a request object
|
9
|
+
before do
|
10
|
+
allow(Coach::RequestSerializer).
|
11
|
+
to receive(:new).and_return(double(serialize: {}))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Capture all Coach events
|
15
|
+
let(:events) { [] }
|
16
|
+
let(:middleware_event) do
|
17
|
+
event = events.find { |(name, _)| name == 'coach.request' }
|
18
|
+
event && event[1]
|
19
|
+
end
|
20
|
+
before do
|
21
|
+
ActiveSupport::Notifications.subscribe(/coach/) do |name, *_, event|
|
22
|
+
events << [name, event]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Mock a handler to simulate an endpoint call
|
27
|
+
let(:handler) do
|
28
|
+
middleware_a = build_middleware('A')
|
29
|
+
middleware_b = build_middleware('B')
|
30
|
+
|
31
|
+
middleware_a.uses(middleware_b)
|
32
|
+
|
33
|
+
terminal_middleware = build_middleware('Terminal')
|
34
|
+
terminal_middleware.uses(middleware_a)
|
35
|
+
|
36
|
+
Coach::Handler.new(terminal_middleware)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#subscribe!" do
|
40
|
+
before { notifications.subscribe! }
|
41
|
+
|
42
|
+
it "becomes active" do
|
43
|
+
expect(notifications.active?).to be(true)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "will now send coach.request" do
|
47
|
+
handler.call({})
|
48
|
+
expect(middleware_event).not_to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "broadcasts event containing all middleware that have been run" do
|
52
|
+
handler.call({})
|
53
|
+
middleware_names = middleware_event[:chain].map { |item| item[:name] }
|
54
|
+
expect(middleware_names).to include(*%w(Terminal A B))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#unsubscribe!" do
|
59
|
+
it "should disable any prior subscriptions" do
|
60
|
+
notifications.subscribe!
|
61
|
+
|
62
|
+
handler.call({})
|
63
|
+
expect(events.count { |(name, _)| name == 'coach.request' }).
|
64
|
+
to eq(1)
|
65
|
+
|
66
|
+
notifications.unsubscribe!
|
67
|
+
|
68
|
+
handler.call({})
|
69
|
+
expect(events.count { |(name, _)| name == 'coach.request' }).
|
70
|
+
to eq(1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'coach/request_benchmark'
|
3
|
+
|
4
|
+
describe Coach::RequestBenchmark do
|
5
|
+
subject(:event) { described_class.new('ENDPOINT') }
|
6
|
+
|
7
|
+
let(:base_time) { Time.now }
|
8
|
+
|
9
|
+
let(:start) { base_time + 0 }
|
10
|
+
let(:a_start) { base_time + 1 }
|
11
|
+
let(:b_start) { base_time + 2 }
|
12
|
+
let(:b_finish) { base_time + 3 }
|
13
|
+
let(:a_finish) { base_time + 4 }
|
14
|
+
let(:finish) { base_time + 5 }
|
15
|
+
|
16
|
+
before do
|
17
|
+
event.notify('B', b_start, b_finish)
|
18
|
+
event.notify('A', a_start, a_finish)
|
19
|
+
event.complete(start, finish)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#stats" do
|
23
|
+
subject(:stats) { event.stats }
|
24
|
+
|
25
|
+
it "computes overall duration" do
|
26
|
+
expect(stats[:duration]).to eq(5000)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "computes duration of middleware with no children" do
|
30
|
+
expect(stats[:chain]).to include(name: 'B', duration: 1000)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "adjusts duration of middleware for their children" do
|
34
|
+
expect(stats[:chain]).to include(name: 'A', duration: 2000)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "correctly orders chain" do
|
38
|
+
chain_names = stats[:chain].map { |item| item[:name] }
|
39
|
+
expect(chain_names).to eq %w(A B)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'coach/request_serializer'
|
3
|
+
|
4
|
+
describe Coach::RequestSerializer do
|
5
|
+
describe '.apply_header_rule' do
|
6
|
+
before { described_class.clear_header_rules! }
|
7
|
+
|
8
|
+
let(:header) { 'http_abc' }
|
9
|
+
let(:rule) { nil }
|
10
|
+
|
11
|
+
context "with header that has a rule that" do
|
12
|
+
before { described_class.sanitize_header(header, &rule) }
|
13
|
+
|
14
|
+
context "does not specify block" do
|
15
|
+
it "replaces blacklisted header with default text" do
|
16
|
+
sanitized = Coach::RequestSerializer.apply_header_rule(header, 'value')
|
17
|
+
expect(sanitized).not_to eq('value')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "specifies custom block" do
|
22
|
+
let(:rule) { ->(value) { "#{value}#{value}" } }
|
23
|
+
|
24
|
+
it "uses block to compute new filtered value" do
|
25
|
+
sanitized = Coach::RequestSerializer.apply_header_rule(header, 'value')
|
26
|
+
expect(sanitized).to eq('valuevalue')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with header that has no blacklist rule" do
|
32
|
+
it "does not modify value" do
|
33
|
+
sanitized = Coach::RequestSerializer.apply_header_rule(header, 'value')
|
34
|
+
expect(sanitized).to eq('value')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'coach/router'
|
4
|
+
require 'coach/handler'
|
5
|
+
|
6
|
+
describe Coach::Router do
|
7
|
+
subject(:router) { described_class.new(app) }
|
8
|
+
let(:app) { double(:rails_app, routes: double(draw: nil)) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Coach::Handler).to receive(:new) { |route| route }
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:resource_routes) do
|
15
|
+
Module.new do
|
16
|
+
class Index; end
|
17
|
+
class Show; end
|
18
|
+
class Create; end
|
19
|
+
class Update; end
|
20
|
+
class Destroy; end
|
21
|
+
class Refund; end # custom
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
shared_examples "mount action" do |action, params|
|
26
|
+
let(:actions) { [action] }
|
27
|
+
|
28
|
+
it "correctly mounts url for :#{action}" do
|
29
|
+
expect(router).to receive(:match).with(params[:url], anything)
|
30
|
+
draw
|
31
|
+
end
|
32
|
+
|
33
|
+
it "correctly mounts on method for :#{action}" do
|
34
|
+
expect(router).to receive(:match).
|
35
|
+
with(anything, hash_including(via: params[:method]))
|
36
|
+
draw
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#draw" do
|
41
|
+
subject(:draw) { router.draw(resource_routes, base: '/resource', actions: actions) }
|
42
|
+
|
43
|
+
context "with default action" do
|
44
|
+
it_behaves_like "mount action", :index, url: '/resource', method: :get
|
45
|
+
it_behaves_like "mount action", :show, url: '/resource/:id', method: :get
|
46
|
+
it_behaves_like "mount action", :create, url: '/resource', method: :post
|
47
|
+
it_behaves_like "mount action", :update, url: '/resource/:id', method: :put
|
48
|
+
it_behaves_like "mount action", :destroy, url: '/resource/:id', method: :delete
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with custom action" do
|
52
|
+
let(:actions) { [refund: { method: :post, url: custom_url }] }
|
53
|
+
|
54
|
+
context "with no slash" do
|
55
|
+
let(:custom_url) { ':id/refund' }
|
56
|
+
it "mounts correct url" do
|
57
|
+
expect(router).to receive(:match).with('/resource/:id/refund', anything)
|
58
|
+
draw
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with multiple /'s" do
|
63
|
+
let(:custom_url) { '//:id/refund' }
|
64
|
+
it "mounts correct url" do
|
65
|
+
expect(router).to receive(:match).with('/resource/:id/refund', anything)
|
66
|
+
draw
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with unknown default action" do
|
72
|
+
let(:actions) { [:unknown] }
|
73
|
+
it "raises RouterUnknownDefaultAction" do
|
74
|
+
expect { draw }.to raise_error(Coach::Errors::RouterUnknownDefaultAction)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rspec/its'
|
2
|
+
require 'pry'
|
3
|
+
require_relative '../lib/coach'
|
4
|
+
require_relative '../lib/spec/coach_helper'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.expect_with :rspec do |expectations|
|
8
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
9
|
+
end
|
10
|
+
|
11
|
+
config.mock_with :rspec do |mocks|
|
12
|
+
mocks.verify_partial_doubles = true
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: coach
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- GoCardless
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.2.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-its
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.2.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Controller framework
|
98
|
+
email:
|
99
|
+
- developers@gocardless.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".rspec"
|
105
|
+
- ".rubocop.yml"
|
106
|
+
- Gemfile
|
107
|
+
- Gemfile.lock
|
108
|
+
- README.md
|
109
|
+
- coach.gemspec
|
110
|
+
- lib/coach.rb
|
111
|
+
- lib/coach/errors.rb
|
112
|
+
- lib/coach/handler.rb
|
113
|
+
- lib/coach/middleware.rb
|
114
|
+
- lib/coach/middleware_item.rb
|
115
|
+
- lib/coach/middleware_validator.rb
|
116
|
+
- lib/coach/notifications.rb
|
117
|
+
- lib/coach/request_benchmark.rb
|
118
|
+
- lib/coach/request_serializer.rb
|
119
|
+
- lib/coach/router.rb
|
120
|
+
- lib/coach/version.rb
|
121
|
+
- lib/spec/coach_helper.rb
|
122
|
+
- spec/lib/coach/handler_spec.rb
|
123
|
+
- spec/lib/coach/middleware_spec.rb
|
124
|
+
- spec/lib/coach/middleware_validator_spec.rb
|
125
|
+
- spec/lib/coach/notifications_spec.rb
|
126
|
+
- spec/lib/coach/request_benchmark_spec.rb
|
127
|
+
- spec/lib/coach/request_serializer_spec.rb
|
128
|
+
- spec/lib/coach/router_spec.rb
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
homepage: https://github.com/gocardless/coach
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.0.14
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: coach middleware
|
154
|
+
test_files:
|
155
|
+
- spec/lib/coach/handler_spec.rb
|
156
|
+
- spec/lib/coach/middleware_spec.rb
|
157
|
+
- spec/lib/coach/middleware_validator_spec.rb
|
158
|
+
- spec/lib/coach/notifications_spec.rb
|
159
|
+
- spec/lib/coach/request_benchmark_spec.rb
|
160
|
+
- spec/lib/coach/request_serializer_spec.rb
|
161
|
+
- spec/lib/coach/router_spec.rb
|
162
|
+
- spec/spec_helper.rb
|