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 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