coach 2.1.0 → 2.2.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 +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
|