coach 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +5 -0
- data/README.md +71 -0
- data/bin/coach +57 -0
- data/coach.gemspec +5 -1
- data/lib/coach/cli/errors.rb +41 -0
- data/lib/coach/cli/provider_finder.rb +93 -0
- data/lib/coach/request_serializer.rb +1 -1
- data/lib/coach/version.rb +1 -1
- data/lib/spring/commands/coach.rb +17 -0
- data/spec/lib/coach/cli/provider_finder_spec.rb +248 -0
- metadata +27 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c085b582900931acb9b56a21ee03e9b8b1695e8b9e36c23b5286acb8cc949868
|
4
|
+
data.tar.gz: ab1dd0c12bfcbad8b93832649f96fa3699cc036d392129f4b6e1e8aca36bb72e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94525ce01d5c1b0dc75314c47f2901bdd33946fbe63ae847bf0ce6f1a1099e57c1db72f1c146199d0f77462eca826f47d0784aa21eeab41fb8b30aee3aa3dc0c
|
7
|
+
data.tar.gz: ccd13d3dcb3623c01f6650ce6efb842c892663afaaffa30db8e8781380a8ffcdbc9be809b0d171c224d7c494448cabf7b38d9dae989af1a0c5256bee1a949758
|
data/.rubocop.yml
CHANGED
@@ -9,11 +9,16 @@ AllCops:
|
|
9
9
|
Metrics/MethodLength:
|
10
10
|
Max: 15
|
11
11
|
|
12
|
+
# Even after some serious refactoring, the default of 15 can be hard to meet
|
13
|
+
# if you favour saving intermediate state into well-named variables
|
14
|
+
Metrics/AbcSize:
|
15
|
+
Max: 20
|
16
|
+
|
12
17
|
Style/RescueStandardError:
|
13
18
|
Exclude:
|
14
19
|
- "*/**/*_spec.rb"
|
15
20
|
|
16
|
-
Naming/
|
21
|
+
Naming/MethodParameterName:
|
17
22
|
AllowedNames:
|
18
23
|
# These are the default allowed names, set by Rubocop
|
19
24
|
- io
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
|
5
5
|
No unreleased changes.
|
6
6
|
|
7
|
+
# 2.2.0 / 2019-01-08
|
8
|
+
|
9
|
+
* [#68](https://github.com/gocardless/coach/pull/68) Add `coach` CLI and add
|
10
|
+
documentation to README
|
11
|
+
|
7
12
|
# 2.1.0 / 2019-11-14
|
8
13
|
|
9
14
|
* [#79](https://github.com/gocardless/coach/pull/79) RequestSerializer will now
|
data/README.md
CHANGED
@@ -334,6 +334,77 @@ class Tracking < Coach::Middleware
|
|
334
334
|
end
|
335
335
|
```
|
336
336
|
|
337
|
+
# Coach CLI
|
338
|
+
|
339
|
+
As well as the library, the Coach gem comes with a command line tool - `coach`.
|
340
|
+
|
341
|
+
When working in a large codebase that uses Coach, one of the challenges you may run into
|
342
|
+
is understanding the `provide`/`require` graph made up of all the middleware chains you've
|
343
|
+
built. While the library enforces the correctness of those chains at boot time, it doesn't
|
344
|
+
help you understand those dependencies. That's where the `coach` CLI comes in!
|
345
|
+
|
346
|
+
Currently, the `coach` CLI supports two commands.
|
347
|
+
|
348
|
+
## `find-provider`
|
349
|
+
|
350
|
+
`find-provider` is the simpler of the two commands. Given the name of a Coach middleware
|
351
|
+
and a value that it requires, it outputs the name of the middleware that provides it.
|
352
|
+
|
353
|
+
```bash
|
354
|
+
$ bundle exec coach find-provider HelloUser user
|
355
|
+
Value `user` is provided to `HelloUser` by:
|
356
|
+
|
357
|
+
Authentication
|
358
|
+
```
|
359
|
+
|
360
|
+
If there are multiple middlewares in the chain that provide the same value, all of them
|
361
|
+
will be listed.
|
362
|
+
|
363
|
+
## `find-chain`
|
364
|
+
|
365
|
+
`find-chain` is the more advanced of the two commands, and is most useful in larger
|
366
|
+
codebases. Given the name of a Coach middleware and a value it requires, it outputs the
|
367
|
+
chains of middleware between the specified middleware and the one that provides the
|
368
|
+
required value.
|
369
|
+
|
370
|
+
```bash
|
371
|
+
# Note that we've assumed an intermediate middleware - `UserDecorator` exists in this
|
372
|
+
# example to make the functionality of the command clearer.
|
373
|
+
$ bundle exec coach find-chain HelloUser user
|
374
|
+
Value `user` is provided to `HelloUser` by:
|
375
|
+
|
376
|
+
HelloUser -> UserDecorator -> Authentication
|
377
|
+
```
|
378
|
+
|
379
|
+
If there are multiple paths to a middleware that provides that value, all of them will be
|
380
|
+
listed. Similarly, if multiple middlewares provide the same value, all of them will be
|
381
|
+
listed.
|
382
|
+
|
383
|
+
## Spring integration
|
384
|
+
|
385
|
+
Given that the Coach CLI is mostly aimed at large Rails apps using Coach, it would be an
|
386
|
+
oversight for us not to integrate it with [Spring](https://github.com/rails/spring/).
|
387
|
+
|
388
|
+
To enable the use of Spring with the Coach CLI, add the following to `config/spring.rb` or
|
389
|
+
an equivalent Rails config file.
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
require "spring/commands/coach"
|
393
|
+
```
|
394
|
+
|
395
|
+
On GoCardless' main Rails app, using Spring reduces the time to run `coach` commands from
|
396
|
+
around 15s to 1s.
|
397
|
+
|
398
|
+
## Future work
|
399
|
+
|
400
|
+
While we think the commands we've already built are useful, we do have some ideas to go
|
401
|
+
further, including:
|
402
|
+
|
403
|
+
- Better formatting of provider chains
|
404
|
+
- Outputting DOT format files to visualise with Graphviz
|
405
|
+
- Editor integrations (e.g. showing the provider chains when hovering a `requires`
|
406
|
+
statement)
|
407
|
+
|
337
408
|
# License & Contributing
|
338
409
|
|
339
410
|
* Coach is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/bin/coach
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "slop"
|
4
|
+
require "coach/cli/provider_finder"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require File.join(Dir.pwd, "config/environment")
|
8
|
+
rescue LoadError
|
9
|
+
puts <<~EOS
|
10
|
+
Could not load your Rails app
|
11
|
+
=============================
|
12
|
+
|
13
|
+
Currently the coach CLI assumes you have a config/environment.rb file that
|
14
|
+
we can load. We believe this is true of Rails apps in general.
|
15
|
+
|
16
|
+
Please raise an issue if that's not the case!
|
17
|
+
|
18
|
+
https://github.com/gocardless/coach/issues
|
19
|
+
EOS
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
Slop.parse do
|
24
|
+
command "find-provider" do
|
25
|
+
run do |_, args|
|
26
|
+
middleware_name, value_name = *args
|
27
|
+
raise ArgumentError, "middleware_name and value_name required" unless middleware_name && value_name
|
28
|
+
|
29
|
+
result = Coach::Cli::ProviderFinder.new(args[0], args[1]).find_provider
|
30
|
+
|
31
|
+
puts "Value `#{value_name}` is provided to `#{middleware_name}` by:\n\n"
|
32
|
+
puts result.to_a.join("\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
command "find-chain" do
|
37
|
+
run do |_, args|
|
38
|
+
middleware_name, value_name = *args
|
39
|
+
raise ArgumentError, "middleware_name and value_name required" unless middleware_name && value_name
|
40
|
+
|
41
|
+
chains = Coach::Cli::ProviderFinder.new(middleware_name, value_name).find_chain
|
42
|
+
|
43
|
+
if chains.size > 1
|
44
|
+
puts "Value `#{value_name}` is provided to `#{middleware_name}` " \
|
45
|
+
"by multiple middleware chains:\n\n"
|
46
|
+
else
|
47
|
+
puts "Value `#{value_name}` is provided to `#{middleware_name}` by:\n\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
formatted_chains = chains.map do |chain|
|
51
|
+
chain.join(" -> ")
|
52
|
+
end.join("\n---\n")
|
53
|
+
|
54
|
+
puts formatted_chains
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/coach.gemspec
CHANGED
@@ -17,11 +17,15 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0")
|
18
18
|
spec.test_files = spec.files.grep(%r{^spec/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
|
+
spec.executables = ["coach"]
|
20
21
|
|
21
22
|
spec.add_dependency "actionpack", ">= 4.2"
|
22
23
|
spec.add_dependency "activesupport", ">= 4.2"
|
24
|
+
# TODO: Find another CLI parser that supports subcommands
|
25
|
+
# Slop v4 got rid of them :(
|
26
|
+
spec.add_dependency "slop", "~> 3.6"
|
23
27
|
|
24
|
-
spec.add_development_dependency "gc_ruboconfig", "= 2.
|
28
|
+
spec.add_development_dependency "gc_ruboconfig", "= 2.9.0"
|
25
29
|
spec.add_development_dependency "pry", "~> 0.10"
|
26
30
|
spec.add_development_dependency "rspec", "~> 3.2"
|
27
31
|
spec.add_development_dependency "rspec-its", "~> 1.2"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Coach
|
4
|
+
module Cli
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
module Errors
|
8
|
+
class MiddlewareNotFoundError < Error
|
9
|
+
attr_reader :middleware_name
|
10
|
+
|
11
|
+
def initialize(middleware_name)
|
12
|
+
@middleware_name = middleware_name
|
13
|
+
|
14
|
+
super("Middleware #{@middleware_name} not found")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ValueNotRequiredError < Error
|
19
|
+
attr_reader :value_name
|
20
|
+
|
21
|
+
def initialize(middleware_name, value_name)
|
22
|
+
@middleware_name = middleware_name
|
23
|
+
@value_name = value_name
|
24
|
+
|
25
|
+
super("Middleware #{@middleware_name} doesn't require value #{value_name}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ValueNotProvidedError < Error
|
30
|
+
attr_reader :value_name
|
31
|
+
|
32
|
+
def initialize(middleware_name, value_name)
|
33
|
+
@middleware_name = middleware_name
|
34
|
+
@value_name = value_name
|
35
|
+
|
36
|
+
super("Middleware #{@middleware_name} isn't provided with value #{value_name}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "coach/cli/errors"
|
4
|
+
require "set"
|
5
|
+
|
6
|
+
module Coach
|
7
|
+
module Cli
|
8
|
+
class ProviderFinder
|
9
|
+
def initialize(middleware_name, value_name)
|
10
|
+
@middleware_name = middleware_name
|
11
|
+
@value_name = value_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_provider
|
15
|
+
enforce_middleware_exists!
|
16
|
+
middleware = Module.const_get(@middleware_name)
|
17
|
+
|
18
|
+
enforce_middleware_requires_value!(middleware)
|
19
|
+
|
20
|
+
provider_mapping = build_provider_mapping(middleware, Hash.new { Set.new })
|
21
|
+
|
22
|
+
if provider_mapping.key?(@value_name.to_sym)
|
23
|
+
providers = provider_mapping[@value_name.to_sym]
|
24
|
+
else
|
25
|
+
err = Errors::ValueNotProvidedError.new(@middleware_name, @value_name)
|
26
|
+
raise err
|
27
|
+
end
|
28
|
+
|
29
|
+
providers.to_a.map(&:to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_chain
|
33
|
+
enforce_middleware_exists!
|
34
|
+
middleware = Module.const_get(@middleware_name)
|
35
|
+
|
36
|
+
enforce_middleware_requires_value!(middleware)
|
37
|
+
|
38
|
+
provider_chain = build_provider_chain(middleware, Hash.new { Set.new }, [])
|
39
|
+
|
40
|
+
if provider_chain.key?(@value_name.to_sym)
|
41
|
+
chains = provider_chain[@value_name.to_sym]
|
42
|
+
else
|
43
|
+
err = Errors::ValueNotProvidedError.new(@middleware_name, @value_name)
|
44
|
+
raise err
|
45
|
+
end
|
46
|
+
|
47
|
+
chains.map { |chain| chain.map(&:to_s).reverse }.to_set
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def enforce_middleware_exists!
|
53
|
+
unless Module.const_defined?(@middleware_name)
|
54
|
+
err = Errors::MiddlewareNotFoundError.new(@middleware_name)
|
55
|
+
raise err
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def enforce_middleware_requires_value!(middleware)
|
60
|
+
unless middleware.requires?(@value_name.to_sym)
|
61
|
+
err = Errors::ValueNotRequiredError.new(@middleware_name, @value_name)
|
62
|
+
raise err
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_provider_mapping(middleware, mapping)
|
67
|
+
middleware.provided.each do |p|
|
68
|
+
mapping[p] = mapping[p].add(middleware)
|
69
|
+
end
|
70
|
+
|
71
|
+
middleware.middleware_dependencies.each do |dep|
|
72
|
+
build_provider_mapping(dep.middleware, mapping)
|
73
|
+
end
|
74
|
+
|
75
|
+
mapping
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_provider_chain(middleware, mapping, chain)
|
79
|
+
new_chain = chain + [middleware]
|
80
|
+
|
81
|
+
middleware.provided.each do |p|
|
82
|
+
mapping[p] = mapping[p].add(new_chain)
|
83
|
+
end
|
84
|
+
|
85
|
+
middleware.middleware_dependencies.each do |dep|
|
86
|
+
build_provider_chain(dep.middleware, mapping, new_chain)
|
87
|
+
end
|
88
|
+
|
89
|
+
mapping
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/coach/version.rb
CHANGED
@@ -0,0 +1,248 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
require "coach/cli/provider_finder"
|
6
|
+
|
7
|
+
describe Coach::Cli::ProviderFinder do
|
8
|
+
subject(:provider_finder) { described_class.new(middleware_name, value_name) }
|
9
|
+
|
10
|
+
let(:middleware_name) { "" }
|
11
|
+
let(:value_name) { "" }
|
12
|
+
|
13
|
+
describe "#find_provider" do
|
14
|
+
context "when there is a single provider" do
|
15
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
16
|
+
let(:value_name) { "provided_value" }
|
17
|
+
|
18
|
+
before do
|
19
|
+
stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do
|
20
|
+
provides :provided_value
|
21
|
+
end)
|
22
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
23
|
+
uses ProvidingMiddleware
|
24
|
+
|
25
|
+
requires :provided_value
|
26
|
+
end)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the providing middleware" do
|
30
|
+
expect(provider_finder.find_provider).to eq %w[ProvidingMiddleware]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when there are multiple providers" do
|
35
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
36
|
+
let(:value_name) { "provided_value" }
|
37
|
+
|
38
|
+
before do
|
39
|
+
stub_const("FirstProvidingMiddleware", Class.new(Coach::Middleware) do
|
40
|
+
provides :provided_value
|
41
|
+
end)
|
42
|
+
stub_const("SecondProvidingMiddleware", Class.new(Coach::Middleware) do
|
43
|
+
provides :provided_value
|
44
|
+
end)
|
45
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
46
|
+
uses FirstProvidingMiddleware
|
47
|
+
uses SecondProvidingMiddleware
|
48
|
+
|
49
|
+
requires :provided_value
|
50
|
+
end)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns the providing middleware" do
|
54
|
+
expect(provider_finder.find_provider).
|
55
|
+
to eq %w[FirstProvidingMiddleware SecondProvidingMiddleware]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when there's an intermediate middleware after the provider" do
|
60
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
61
|
+
let(:value_name) { "provided_value" }
|
62
|
+
|
63
|
+
before do
|
64
|
+
stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do
|
65
|
+
provides :provided_value
|
66
|
+
end)
|
67
|
+
stub_const("IntermediateMiddleware", Class.new(Coach::Middleware) do
|
68
|
+
uses ProvidingMiddleware
|
69
|
+
end)
|
70
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
71
|
+
uses IntermediateMiddleware
|
72
|
+
|
73
|
+
requires :provided_value
|
74
|
+
end)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns the providing middleware" do
|
78
|
+
expect(provider_finder.find_provider).to eq %w[ProvidingMiddleware]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when the middleware can't be found" do
|
83
|
+
let(:middleware_name) { "MiddlewareThatDoesntExist" }
|
84
|
+
|
85
|
+
it "raises a MiddlewareNotFoundError" do
|
86
|
+
expect { provider_finder.find_provider }.
|
87
|
+
to raise_error(Coach::Cli::Errors::MiddlewareNotFoundError)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when the middleware doesn't require the specified value" do
|
92
|
+
let(:middleware_name) { "MiddlewareWithoutRequires" }
|
93
|
+
let(:value_name) { "value_that_doesnt_exist" }
|
94
|
+
|
95
|
+
before do
|
96
|
+
stub_const(middleware_name, Class.new(Coach::Middleware))
|
97
|
+
end
|
98
|
+
|
99
|
+
it "raises a ValueNotRequiredError" do
|
100
|
+
expect { provider_finder.find_provider }.
|
101
|
+
to raise_error(Coach::Cli::Errors::ValueNotRequiredError)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the middleware isn't provided with a value it requires" do
|
106
|
+
let(:middleware_name) { "MiddlewareWithMissingProvide" }
|
107
|
+
let(:value_name) { "value_that_isnt_provided" }
|
108
|
+
|
109
|
+
before do
|
110
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
111
|
+
requires :value_that_isnt_provided
|
112
|
+
end)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "raises a ValueNotProvidedError" do
|
116
|
+
expect { provider_finder.find_provider }.
|
117
|
+
to raise_error(Coach::Cli::Errors::ValueNotProvidedError)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#find_chain" do
|
123
|
+
context "when there is a single provider" do
|
124
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
125
|
+
let(:value_name) { "provided_value" }
|
126
|
+
|
127
|
+
before do
|
128
|
+
stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do
|
129
|
+
provides :provided_value
|
130
|
+
end)
|
131
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
132
|
+
uses ProvidingMiddleware
|
133
|
+
|
134
|
+
requires :provided_value
|
135
|
+
end)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "returns the providing middleware" do
|
139
|
+
expect(provider_finder.find_chain).
|
140
|
+
to eq [%w[ProvidingMiddleware RequiringMiddleware]].to_set
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "when there are multiple paths to a single provider" do
|
145
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
146
|
+
let(:value_name) { "provided_value" }
|
147
|
+
|
148
|
+
before do
|
149
|
+
stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do
|
150
|
+
provides :provided_value
|
151
|
+
end)
|
152
|
+
stub_const("FirstIntermediateMiddleware", Class.new(Coach::Middleware) do
|
153
|
+
uses ProvidingMiddleware
|
154
|
+
end)
|
155
|
+
stub_const("SecondIntermediateMiddleware", Class.new(Coach::Middleware) do
|
156
|
+
uses ProvidingMiddleware
|
157
|
+
end)
|
158
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
159
|
+
uses FirstIntermediateMiddleware
|
160
|
+
uses SecondIntermediateMiddleware
|
161
|
+
|
162
|
+
requires :provided_value
|
163
|
+
end)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "returns multiple middleware chains" do
|
167
|
+
expect(provider_finder.find_chain).
|
168
|
+
to eq [
|
169
|
+
%w[ProvidingMiddleware FirstIntermediateMiddleware RequiringMiddleware],
|
170
|
+
%w[ProvidingMiddleware SecondIntermediateMiddleware RequiringMiddleware],
|
171
|
+
].to_set
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when there are multiple paths to multiple providers" do
|
176
|
+
let(:middleware_name) { "RequiringMiddleware" }
|
177
|
+
let(:value_name) { "provided_value" }
|
178
|
+
|
179
|
+
before do
|
180
|
+
stub_const("FirstProvidingMiddleware", Class.new(Coach::Middleware) do
|
181
|
+
provides :provided_value
|
182
|
+
end)
|
183
|
+
stub_const("SecondProvidingMiddleware", Class.new(Coach::Middleware) do
|
184
|
+
provides :provided_value
|
185
|
+
end)
|
186
|
+
stub_const("FirstIntermediateMiddleware", Class.new(Coach::Middleware) do
|
187
|
+
uses FirstProvidingMiddleware
|
188
|
+
end)
|
189
|
+
stub_const("SecondIntermediateMiddleware", Class.new(Coach::Middleware) do
|
190
|
+
uses SecondProvidingMiddleware
|
191
|
+
end)
|
192
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
193
|
+
uses FirstIntermediateMiddleware
|
194
|
+
uses SecondIntermediateMiddleware
|
195
|
+
|
196
|
+
requires :provided_value
|
197
|
+
end)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "returns multiple middleware chains" do
|
201
|
+
expect(provider_finder.find_chain).
|
202
|
+
to eq([
|
203
|
+
%w[FirstProvidingMiddleware FirstIntermediateMiddleware RequiringMiddleware],
|
204
|
+
%w[SecondProvidingMiddleware SecondIntermediateMiddleware RequiringMiddleware], # rubocop:disable Metrics/LineLength
|
205
|
+
].to_set)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "when the middleware can't be found" do
|
210
|
+
let(:middleware_name) { "MiddlewareThatDoesntExist" }
|
211
|
+
|
212
|
+
it "raises a MiddlewareNotFoundError" do
|
213
|
+
expect { provider_finder.find_chain }.
|
214
|
+
to raise_error(Coach::Cli::Errors::MiddlewareNotFoundError)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "when the middleware doesn't require the specified value" do
|
219
|
+
let(:middleware_name) { "MiddlewareWithoutRequires" }
|
220
|
+
let(:value_name) { "value_that_doesnt_exist" }
|
221
|
+
|
222
|
+
before do
|
223
|
+
stub_const(middleware_name, Class.new(Coach::Middleware))
|
224
|
+
end
|
225
|
+
|
226
|
+
it "raises a ValueNotRequiredError" do
|
227
|
+
expect { provider_finder.find_chain }.
|
228
|
+
to raise_error(Coach::Cli::Errors::ValueNotRequiredError)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when the middleware isn't provided with a value it requires" do
|
233
|
+
let(:middleware_name) { "MiddlewareWithMissingProvide" }
|
234
|
+
let(:value_name) { "value_that_isnt_provided" }
|
235
|
+
|
236
|
+
before do
|
237
|
+
stub_const(middleware_name, Class.new(Coach::Middleware) do
|
238
|
+
requires :value_that_isnt_provided
|
239
|
+
end)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "raises a ValueNotProvidedError" do
|
243
|
+
expect { provider_finder.find_chain }.
|
244
|
+
to raise_error(Coach::Cli::Errors::ValueNotProvidedError)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
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: 2.
|
4
|
+
version: 2.2.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: 2020-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -38,20 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: slop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.6'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: gc_ruboconfig
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - '='
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: 2.
|
61
|
+
version: 2.9.0
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - '='
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: 2.
|
68
|
+
version: 2.9.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: pry
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,7 +125,8 @@ dependencies:
|
|
111
125
|
description:
|
112
126
|
email:
|
113
127
|
- developers@gocardless.com
|
114
|
-
executables:
|
128
|
+
executables:
|
129
|
+
- coach
|
115
130
|
extensions: []
|
116
131
|
extra_rdoc_files: []
|
117
132
|
files:
|
@@ -124,9 +139,12 @@ files:
|
|
124
139
|
- Gemfile
|
125
140
|
- LICENSE.txt
|
126
141
|
- README.md
|
142
|
+
- bin/coach
|
127
143
|
- coach.gemspec
|
128
144
|
- docs/COMPATIBILITY.md
|
129
145
|
- lib/coach.rb
|
146
|
+
- lib/coach/cli/errors.rb
|
147
|
+
- lib/coach/cli/provider_finder.rb
|
130
148
|
- lib/coach/errors.rb
|
131
149
|
- lib/coach/handler.rb
|
132
150
|
- lib/coach/middleware.rb
|
@@ -138,6 +156,8 @@ files:
|
|
138
156
|
- lib/coach/router.rb
|
139
157
|
- lib/coach/rspec.rb
|
140
158
|
- lib/coach/version.rb
|
159
|
+
- lib/spring/commands/coach.rb
|
160
|
+
- spec/lib/coach/cli/provider_finder_spec.rb
|
141
161
|
- spec/lib/coach/handler_spec.rb
|
142
162
|
- spec/lib/coach/middleware_spec.rb
|
143
163
|
- spec/lib/coach/middleware_validator_spec.rb
|
@@ -165,12 +185,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
185
|
- !ruby/object:Gem::Version
|
166
186
|
version: '0'
|
167
187
|
requirements: []
|
168
|
-
|
169
|
-
rubygems_version: 2.7.6
|
188
|
+
rubygems_version: 3.0.3
|
170
189
|
signing_key:
|
171
190
|
specification_version: 4
|
172
191
|
summary: Alternative controllers built with middleware
|
173
192
|
test_files:
|
193
|
+
- spec/lib/coach/cli/provider_finder_spec.rb
|
174
194
|
- spec/lib/coach/handler_spec.rb
|
175
195
|
- spec/lib/coach/middleware_spec.rb
|
176
196
|
- spec/lib/coach/middleware_validator_spec.rb
|