rspec-specification-coverage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1011217d1c9f1417e26c1f5d05c0d52eea22a37c95c4ee68885f99aaee5f4eb7
4
+ data.tar.gz: 2701216d0b6d077ae7197984287ec8405205205773c0b8b6c853523a7e76997a
5
+ SHA512:
6
+ metadata.gz: cf63950e38feb0a043a0cb7a3f0d402d1eb81897a0e0c4dd33b5c1f22b85d74bf79b68ec774b39f107a7f21f52063d2ad02ade544c271ac444c10f8a68cb944f
7
+ data.tar.gz: c93b517f646ecdd3ddf88e55fb69c127615fd1c60dc1e0f0092d07f33214d4b023d971ceacdb64530dbe479f268e43d1cfc5a2f1cf1bce9b6de73d0fc88e1aab
data/LICENSE.md ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+ =====================
3
+
4
+ * Copyright © 2023 Marcin Nowicki
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA3RE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # rspec-specification-coverage
2
+
3
+ Allow specify coverage of files.
4
+
5
+ ## Why
6
+
7
+ Allow specify the way RSpec files cover the implementation.
8
+
9
+ ### Why such long name
10
+
11
+ There is a common misconception in the community that RSpec is mainly
12
+ about testing - where it is mostly about specifying with ability to run
13
+ that specification. Well written specification makes most documentation
14
+ redundant - by reading the specification you should understand the
15
+ implementation.
16
+
17
+ ## Usage
18
+
19
+ ### Install
20
+
21
+ Add following to your Gemfile:
22
+
23
+ ```ruby
24
+ group :test do
25
+ gem 'rspec-specification-coverage', '~> 0.1', require: false
26
+ end
27
+ ```
28
+
29
+ And run:
30
+
31
+ ```bash
32
+ bundle install
33
+ ```
34
+
35
+ ### Basic
36
+
37
+ Ensures is respective spec file existing for implementation.
38
+
39
+ The following will ensure that `spec/models/user_spec.rb` file exist:
40
+
41
+ ```ruby
42
+ require 'rspec/specification_coverage'
43
+
44
+ ::RSpec.describe(::RSpec::SpecificationCoverage) do
45
+ subject(:file) { 'app/models/user.rb' }
46
+
47
+ it { is_expected.to(be_covered_with_specification) }
48
+ end
49
+ ```
50
+
51
+ ### With Reflection
52
+
53
+ The following will ensure that in case there are git changes to
54
+ `app/models/user.rb`, respective specification in
55
+ `spec/models/user_spec.rb` also needs to be updated:
56
+
57
+ ```ruby
58
+ it { is_expected.to(be_covered_with_specification.with_reflection) }
59
+ ```
60
+
61
+ ### Spec Bigger
62
+
63
+ The following will ensure that `spec/models/user_spec.rb`
64
+ file is bigger than `app/models/user.rb`.
65
+
66
+ ```ruby
67
+ it { is_expected.to(be_covered_with_specification.spec_bigger) }
68
+ ```
69
+
70
+ ### Mentioning Methods
71
+
72
+ The following will ensure that `spec/models/user_spec.rb`
73
+ file include method names from `app/models/user.rb`.
74
+
75
+ ```ruby
76
+ it { is_expected.to(be_covered_with_specification.mentioning_methods) }
77
+ ```
78
+
79
+ ### All
80
+
81
+ We can mix all above ways together:
82
+
83
+ ```ruby
84
+ it { is_expected.to(be_covered_with_specification.with_reflection.spec_bigger.mentioning_methods) }
85
+ ```
86
+
87
+ ## Real Life Example
88
+
89
+ The following is a running example in one of my projects:
90
+
91
+ ```ruby
92
+ # frozen_string_literal: true
93
+
94
+ require 'rspec'
95
+ require 'rspec/specification_coverage'
96
+
97
+ ::RSpec.describe(::RSpec::SpecificationCoverage) do
98
+ ::Dir.glob('app/{helpers,models,channels,mailers}/**/*.rb').each do |a_file|
99
+ describe a_file do
100
+ subject(:file) { a_file }
101
+
102
+ it { is_expected.to(be_covered_with_specification.with_reflection.spec_bigger.mentioning_methods) }
103
+ end
104
+ end
105
+
106
+ ::Dir.glob('app/jobs/**/*.rb').each do |a_file|
107
+ describe a_file do
108
+ subject(:file) { a_file }
109
+
110
+ it { is_expected.to(be_covered_with_specification.with_reflection) }
111
+ end
112
+ end
113
+
114
+ ::Dir.glob('app/views/**/*.erb').each do |a_file|
115
+ describe a_file do
116
+ subject(:file) { a_file }
117
+
118
+ it { is_expected.to(be_covered_with_specification.with_reflection) }
119
+ end
120
+ end
121
+
122
+ ::Dir.glob('lib/**/*.rb').each do |a_file|
123
+ describe a_file do
124
+ subject(:file) { a_file }
125
+
126
+ it { is_expected.to(be_covered_with_specification(type: :lib).with_reflection.mentioning_methods) }
127
+ end
128
+ end
129
+
130
+ ::Dir.glob('app/controllers/**/*.rb').each do |a_file|
131
+ describe a_file do
132
+ subject(:file) { a_file }
133
+
134
+ it {
135
+ is_expected.to(
136
+ be_covered_with_specification(type: :requests).with_reflection.spec_bigger.mentioning_methods
137
+ )
138
+ }
139
+
140
+ it {
141
+ is_expected.to(
142
+ be_covered_with_specification(type: :routing).with_reflection.mentioning_methods
143
+ )
144
+ }
145
+ end
146
+ end
147
+ end
148
+ ```
149
+
150
+ ## Development
151
+
152
+ We use TDD on Guard.
153
+
154
+ ### Setup
155
+
156
+ ```bash
157
+ bash util/setup.sh
158
+ ```
159
+
160
+ ### Check if overcommit is operational
161
+
162
+ ```bash
163
+ bash util/overcommit_spec.sh
164
+ ```
165
+
166
+ ### Run your TDD reactor
167
+
168
+ ```bash
169
+ bundle exec guard
170
+ ```
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require 'active_support/core_ext/module'
5
+ require 'rspec'
6
+ require 'singleton'
7
+
8
+ # We store it within rspec as it relies on it.
9
+ module RSpec
10
+ # "RSpec specification coverage" is a term we use to go beyond testing.
11
+ module SpecificationCoverage
12
+ # Stores instance of currently changed git files.
13
+ class GitFilesChanged
14
+ include ::Singleton
15
+
16
+ delegate :include?, to: :ALL
17
+
18
+ # All git files changed.
19
+ ALL = `git status --porcelain | grep -v "^D" | cut -b4-`.split("\n").reject(&:empty?).freeze
20
+
21
+ private_constant :ALL
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'rspec'
6
+ require_relative '../spec_from_file'
7
+
8
+ ::RSpec::Matchers.define(:be_covered_with_specification) do |type: nil|
9
+ match do |actual|
10
+ @spec_file = ::RSpec::SpecificationCoverage::SpecFromFile.is(actual, type)
11
+
12
+ return false unless ::File.exist?(@spec_file)
13
+
14
+ if @spec_bigger || @mentioning_methods
15
+ if ::File.exist?(actual)
16
+ file_contents = ::File.read(actual)
17
+ @file_size = file_contents.size
18
+ else
19
+ file_contents = nil
20
+ @file_size = 0
21
+ end
22
+ if ::File.exist?(@spec_file)
23
+ spec_contents = ::File.read(@spec_file)
24
+ @spec_size = spec_contents.size
25
+ else
26
+ spec_contents = nil
27
+ @spec_size = 0
28
+ end
29
+ end
30
+
31
+ if @spec_bigger
32
+ @spec_is_bigger = @spec_size > @file_size
33
+
34
+ return false unless @spec_is_bigger
35
+ end
36
+
37
+ if @with_reflection
38
+ require_relative '../git_files_changed'
39
+ file_changed = ::RSpec::SpecificationCoverage::GitFilesChanged.instance.include?(actual)
40
+
41
+ if file_changed
42
+ @have_reflection = ::RSpec::SpecificationCoverage::GitFilesChanged.instance.include?(@spec_file)
43
+
44
+ return false unless @have_reflection
45
+ end
46
+ end
47
+
48
+ if @mentioning_methods && @file_size.positive? && @spec_size.positive?
49
+ @missing_methods = []
50
+
51
+ require_relative '../ruby_file_details'
52
+ file_details = ::RSpec::SpecificationCoverage::RubyFileDetails.new(file_contents)
53
+ file_details.methods.each do |method|
54
+ @missing_methods << method unless spec_contents.include?(method)
55
+ end
56
+
57
+ return false if @missing_methods.present?
58
+ end
59
+
60
+ true
61
+ end
62
+
63
+ failure_message do |actual|
64
+ message = "expected that '#{actual}'\n"
65
+ message += "would have respective spec '#{@spec_file}'\n"
66
+ if @spec_bigger && @spec_is_bigger == false
67
+ message += "which is bigger than the implementation (#{@spec_size} > #{@file_size})\n"
68
+ end
69
+ if @with_reflection && @have_reflection == false
70
+ message += "in case you change '#{actual}' then you have to specify it in '#{@spec_file}'\n"
71
+ end
72
+ if @mentioning_methods && @missing_methods.present?
73
+ message += 'the spec is missing mention of following methods: '
74
+ message += @missing_methods.join(', ')
75
+ end
76
+ message
77
+ end
78
+
79
+ chain :with_reflection do
80
+ @with_reflection = true
81
+ end
82
+
83
+ chain :spec_bigger do
84
+ @spec_bigger = true
85
+ end
86
+
87
+ chain :mentioning_methods do
88
+ @mentioning_methods = true
89
+ end
90
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require 'rspec'
5
+
6
+ # We store it within rspec as it relies on it.
7
+ module RSpec
8
+ # "RSpec specification coverage" is a term we use to go beyond testing.
9
+ module SpecificationCoverage
10
+ # Text-based ruby file details analysis.
11
+ class RubyFileDetails
12
+ # Match method definitions with empty spaces at the beginning.
13
+ REGEXP_METHODS = /\s+def ([a-z_.?]+)/
14
+
15
+ # @param file_contents [String]
16
+ def initialize(file_contents)
17
+ @file_contents = file_contents
18
+ end
19
+
20
+ # All methods matching regexp except initialize.
21
+ #
22
+ # @return [String]
23
+ def methods
24
+ @methods ||= scan(REGEXP_METHODS)
25
+ end
26
+
27
+ private
28
+
29
+ # Using external method with "!" methods due to improved performance.
30
+ # @return [String]
31
+ def scan(regexp)
32
+ output = @file_contents.scan(regexp)
33
+ output.flatten!
34
+ output.reject! { |method| method == 'initialize' }
35
+ output.map! { |method| method.gsub(/^self\./, '') }
36
+ output
37
+ end
38
+
39
+ private_constant :REGEXP_METHODS
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require 'rspec'
5
+
6
+ # We store it within rspec as it relies on it.
7
+ module RSpec
8
+ # "RSpec specification coverage" is a term we use to go beyond testing.
9
+ module SpecificationCoverage
10
+ # Allows getting getting spec name from file name.
11
+ class SpecFromFile
12
+ # @param actual [String] for example `app/models/user.rb`
13
+ # @param type [Symbol] the type, `nil`, `:routing`, `:requests`, `:lib`
14
+ def self.is(actual, type)
15
+ new(actual, type).is
16
+ end
17
+
18
+ # @param actual [String] for example `app/models/user.rb`
19
+ # @param type [Symbol] the type, `nil`, `:routing`, `:requests`, `:lib`
20
+ def initialize(actual, type)
21
+ @actual = actual
22
+ @type = type
23
+
24
+ raise(::ArgumentError) unless valid?
25
+ end
26
+
27
+ # @return [String] like `spec/models/user_spec.rb`
28
+ def is
29
+ case @type
30
+ when :routing
31
+ controller_replaced.gsub(/\.rb$/, "_#{@type}_spec.rb")
32
+ when :requests
33
+ controller_replaced.gsub(/\.rb$/, '_spec.rb')
34
+ when :lib
35
+ suffix_replaced.gsub(/^lib/, 'spec/lib')
36
+ else
37
+ suffix_replaced.gsub(/^lib/, 'spec')
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # @return [String]
44
+ def controller_replaced
45
+ app_replaced.gsub('_controller.', '.').gsub(%r{/controllers/}, "/#{@type}/")
46
+ end
47
+
48
+ # @return [String]
49
+ def app_replaced
50
+ @actual.gsub(/^app/, 'spec')
51
+ end
52
+
53
+ # @return [String]
54
+ def suffix_replaced
55
+ app_replaced.gsub(/\.rb$/, '_spec.rb').gsub(/\.erb$/, '.erb_spec.rb')
56
+ end
57
+
58
+ # @return [Boolean]
59
+ def valid?
60
+ @actual.is_a?(::String) && (@type.is_a?(::Symbol) || @type.is_a?(::NilClass))
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'specification_coverage/matchers/be_covered_with_specification'
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2023 by Marcin Nowicki
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'rspec/specification_coverage/matchers/be_covered_with_specification'
metadata ADDED
@@ -0,0 +1,320 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-specification-coverage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcin Nowicki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: alfonsox
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundle-audit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.18'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.18'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mdl
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.12'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.12'
125
+ - !ruby/object:Gem::Dependency
126
+ name: overcommit
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.60'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.60'
139
+ - !ruby/object:Gem::Dependency
140
+ name: parallel_tests
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '4.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '4.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 13.0.6
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 13.0.6
167
+ - !ruby/object:Gem::Dependency
168
+ name: reek
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '6.1'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '6.1'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '3.12'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '3.12'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '1.41'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '1.41'
209
+ - !ruby/object:Gem::Dependency
210
+ name: rubocop-performance
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '1.15'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '1.15'
223
+ - !ruby/object:Gem::Dependency
224
+ name: rubocop-require_tools
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: '0.1'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '0.1'
237
+ - !ruby/object:Gem::Dependency
238
+ name: rubocop-rspec
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '2.16'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '2.16'
251
+ - !ruby/object:Gem::Dependency
252
+ name: rubocop-thread_safety
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - "~>"
256
+ - !ruby/object:Gem::Version
257
+ version: '0.4'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - "~>"
263
+ - !ruby/object:Gem::Version
264
+ version: '0.4'
265
+ - !ruby/object:Gem::Dependency
266
+ name: simplecov
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - "~>"
270
+ - !ruby/object:Gem::Version
271
+ version: '0.22'
272
+ type: :development
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - "~>"
277
+ - !ruby/object:Gem::Version
278
+ version: '0.22'
279
+ description: Allow specify how RSpec specification files cover the implementation.
280
+ email: pr0d1r2@gmail.com
281
+ executables: []
282
+ extensions: []
283
+ extra_rdoc_files:
284
+ - LICENSE.md
285
+ - README.md
286
+ files:
287
+ - LICENSE.md
288
+ - README.md
289
+ - lib/rspec-specification-coverage.rb
290
+ - lib/rspec/specification_coverage.rb
291
+ - lib/rspec/specification_coverage/git_files_changed.rb
292
+ - lib/rspec/specification_coverage/matchers/be_covered_with_specification.rb
293
+ - lib/rspec/specification_coverage/ruby_file_details.rb
294
+ - lib/rspec/specification_coverage/spec_from_file.rb
295
+ homepage: https://github.com/pr0d1r2/rspec-specification-coverage
296
+ licenses:
297
+ - MIT
298
+ metadata:
299
+ source_code_uri: https://github.com/pr0d1r2/rspec-specification-coverage
300
+ rubygems_mfa_required: 'true'
301
+ post_install_message:
302
+ rdoc_options: []
303
+ require_paths:
304
+ - lib
305
+ required_ruby_version: !ruby/object:Gem::Requirement
306
+ requirements:
307
+ - - "~>"
308
+ - !ruby/object:Gem::Version
309
+ version: '3.1'
310
+ required_rubygems_version: !ruby/object:Gem::Requirement
311
+ requirements:
312
+ - - ">="
313
+ - !ruby/object:Gem::Version
314
+ version: '0'
315
+ requirements: []
316
+ rubygems_version: 3.3.26
317
+ signing_key:
318
+ specification_version: 4
319
+ summary: Specify RSpec specification coverage.
320
+ test_files: []