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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d5d8146dab264d6d7e2f1177101b9f7f5c62f44840964fb9672b7de7d01fe20
4
- data.tar.gz: '058153f00f46e7eac0d26eb7039af4624e9636954fd59999f9bcf419fe559abf'
3
+ metadata.gz: c085b582900931acb9b56a21ee03e9b8b1695e8b9e36c23b5286acb8cc949868
4
+ data.tar.gz: ab1dd0c12bfcbad8b93832649f96fa3699cc036d392129f4b6e1e8aca36bb72e
5
5
  SHA512:
6
- metadata.gz: 04204eb3b4029928a706b5d9894984462698ee337ccdb3d96e224da1bfc8debf4970b1eb5ba0b8b8cd14960171b8e926d5369f12eea66fcd42ded4bad5de1738
7
- data.tar.gz: a3db075dddeff6e111cdac3bdef7236e8d461fb86c9aa7b696dd15ffd2b8cb5db2b755c07f2465e51eeee5963461a5ae72985aa924e4d9a1340a18987f428391
6
+ metadata.gz: 94525ce01d5c1b0dc75314c47f2901bdd33946fbe63ae847bf0ce6f1a1099e57c1db72f1c146199d0f77462eca826f47d0784aa21eeab41fb8b30aee3aa3dc0c
7
+ data.tar.gz: ccd13d3dcb3623c01f6650ce6efb842c892663afaaffa30db8e8781380a8ffcdbc9be809b0d171c224d7c494448cabf7b38d9dae989af1a0c5256bee1a949758
@@ -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/UncommunicativeMethodParamName:
21
+ Naming/MethodParameterName:
17
22
  AllowedNames:
18
23
  # These are the default allowed names, set by Rubocop
19
24
  - io
@@ -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).
@@ -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
@@ -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.6.1"
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
@@ -64,7 +64,7 @@ module Coach
64
64
  header_value_pairs = @request.filtered_env.map do |key, value|
65
65
  key = if RACK_UNPREFIXED_HEADERS.include?(key)
66
66
  "http_#{key.downcase}"
67
- elsif key =~ /^HTTP_/
67
+ elsif key.match?(/^HTTP_/)
68
68
  key.downcase
69
69
  end
70
70
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coach
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.0"
5
5
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spring
4
+ module Commands
5
+ class Coach
6
+ def env(*)
7
+ "development"
8
+ end
9
+
10
+ def exec_name
11
+ "coach"
12
+ end
13
+
14
+ Spring.register_command "coach", Coach.new
15
+ end
16
+ end
17
+ end
@@ -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.1.0
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: 2019-11-14 00:00:00.000000000 Z
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.6.1
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.6.1
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
- rubyforge_project:
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