rubocop-minitest 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ A [RuboCop](https://github.com/rubocop-hq/rubocop) extension focused on enforcing Minitest best practices and coding conventions.
@@ -0,0 +1,11 @@
1
+ Just install the `rubocop-minitest` gem
2
+
3
+ ```sh
4
+ gem install rubocop-minitest
5
+ ```
6
+
7
+ or if you use bundler put this in your `Gemfile`
8
+
9
+ ```ruby
10
+ gem 'rubocop-minitest'
11
+ ```
@@ -0,0 +1,27 @@
1
+ You need to tell RuboCop to load the Minitest extension. There are three
2
+ ways to do this:
3
+
4
+ ### RuboCop configuration file
5
+
6
+ Put this into your `.rubocop.yml`.
7
+
8
+ ```yaml
9
+ require: rubocop-minitest
10
+ ```
11
+
12
+ Now you can run `rubocop` and it will automatically load the RuboCop Minitest
13
+ cops together with the standard cops.
14
+
15
+ ### Command line
16
+
17
+ ```sh
18
+ rubocop --require rubocop-minitest
19
+ ```
20
+
21
+ ### Rake task
22
+
23
+ ```ruby
24
+ RuboCop::RakeTask.new do |task|
25
+ task.requires << 'rubocop-minitest'
26
+ end
27
+ ```
@@ -0,0 +1,13 @@
1
+ site_name: "A RuboCop extension focused on enforcing Minitest best practices and coding conventions."
2
+ repo_url: https://github.com/rubocop-hq/rubocop-minitest
3
+ edit_uri: edit/master/manual/
4
+ copyright: "Copyright &copy; 2019 Bozhidar Batsov, Jonas Arvidsson, Koichi ITO, and RuboCop contributors"
5
+ docs_dir: manual
6
+ pages:
7
+ - Home: index.md
8
+ - Installation: installation.md
9
+ - Usage: usage.md
10
+ - Cops: cops.md
11
+ - Cops Documentation:
12
+ - Minitest Cops: cops_minitest.md
13
+ theme: readthedocs
@@ -0,0 +1,5 @@
1
+ build:
2
+ image: stable
3
+
4
+ formats:
5
+ - htmlzip
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'rubocop/minitest/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'rubocop-minitest'
9
+ spec.version = RuboCop::Minitest::VERSION
10
+ spec.authors = ['Bozhidar Batsov', 'Jonas Arvidsson', 'Koichi ITO']
11
+
12
+ spec.summary = 'Automatic Minitest code style checking tool.'
13
+ spec.description = <<~DESCRIPTION
14
+ Automatic Minitest code style checking tool.
15
+ A RuboCop extension focused on enforcing Minitest best practices and coding conventions.
16
+ DESCRIPTION
17
+ spec.license = 'MIT'
18
+
19
+ spec.required_ruby_version = '>= 2.3.0'
20
+ spec.metadata = {
21
+ 'homepage_uri' => 'https://docs.rubocop.org/projects/minitest',
22
+ 'changelog_uri' => 'https://github.com/rubocop-hq/rubocop-minitest/blob/master/CHANGELOG.md',
23
+ 'source_code_uri' => 'https://github.com/rubocop-hq/rubocop-minitest',
24
+ 'documentation_uri' => 'https://docs.rubocop.org/projects/minitest',
25
+ 'bug_tracker_uri' => 'https://github.com/rubocop-hq/rubocop-minitest/issues'
26
+ }
27
+
28
+ # Specify which files should be added to the gem when it is released.
29
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
31
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ end
33
+ spec.bindir = 'exe'
34
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ['lib']
36
+
37
+ spec.add_runtime_dependency 'rubocop', '>= 0.74'
38
+ spec.add_development_dependency 'minitest', '~> 5.11'
39
+ end
@@ -0,0 +1,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yard'
4
+ require 'rubocop'
5
+ require 'rubocop-minitest'
6
+
7
+ YARD::Rake::YardocTask.new(:yard_for_generate_documentation) do |task|
8
+ task.files = ['lib/rubocop/cop/**/*.rb']
9
+ task.options = ['--no-output']
10
+ end
11
+
12
+ desc 'Generate docs of all cops departments'
13
+ task generate_cops_documentation: :yard_for_generate_documentation do
14
+ def cops_of_department(cops, department)
15
+ cops.with_department(department).sort!
16
+ end
17
+
18
+ def cops_body(config, cop, description, examples_objects, pars)
19
+ content = h2(cop.cop_name)
20
+ content << properties(config, cop)
21
+ content << "#{description}\n"
22
+ content << examples(examples_objects) if examples_objects.count.positive?
23
+ content << configurations(pars)
24
+ content << references(config, cop)
25
+ content
26
+ end
27
+
28
+ def examples(examples_object)
29
+ examples_object.each_with_object(h3('Examples').dup) do |example, content|
30
+ content << h4(example.name) unless example.name == ''
31
+ content << code_example(example)
32
+ end
33
+ end
34
+
35
+ # rubocop:disable Metrics/MethodLength
36
+ def properties(config, cop)
37
+ header = [
38
+ 'Enabled by default', 'Safe', 'Supports autocorrection', 'VersionAdded',
39
+ 'VersionChanged'
40
+ ]
41
+ config = config.for_cop(cop)
42
+ safe_auto_correct = config.fetch('SafeAutoCorrect', true)
43
+ autocorrect = if cop.new.support_autocorrect?
44
+ "Yes #{'(Unsafe)' unless safe_auto_correct}"
45
+ else
46
+ 'No'
47
+ end
48
+ content = [[
49
+ config.fetch('Enabled') ? 'Enabled' : 'Disabled',
50
+ config.fetch('Safe', true) ? 'Yes' : 'No',
51
+ autocorrect,
52
+ config.fetch('VersionAdded', '-'),
53
+ config.fetch('VersionChanged', '-')
54
+ ]]
55
+ to_table(header, content) + "\n"
56
+ end
57
+ # rubocop:enable Metrics/MethodLength
58
+
59
+ def h2(title)
60
+ content = +"\n"
61
+ content << "## #{title}\n"
62
+ content << "\n"
63
+ content
64
+ end
65
+
66
+ def h3(title)
67
+ content = +"\n"
68
+ content << "### #{title}\n"
69
+ content << "\n"
70
+ content
71
+ end
72
+
73
+ def h4(title)
74
+ content = +"#### #{title}\n"
75
+ content << "\n"
76
+ content
77
+ end
78
+
79
+ def code_example(ruby_code)
80
+ content = +"```ruby\n"
81
+ content << ruby_code.text
82
+ .gsub('@good', '# good').gsub('@bad', '# bad').strip
83
+ content << "\n```\n"
84
+ content
85
+ end
86
+
87
+ def configurations(pars)
88
+ return '' if pars.empty?
89
+
90
+ header = ['Name', 'Default value', 'Configurable values']
91
+ configs = pars.each_key.reject { |key| key.start_with?('Supported') }
92
+ content = configs.map do |name|
93
+ configurable = configurable_values(pars, name)
94
+ default = format_table_value(pars[name])
95
+ [name, default, configurable]
96
+ end
97
+
98
+ h3('Configurable attributes') + to_table(header, content)
99
+ end
100
+
101
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
102
+ def configurable_values(pars, name)
103
+ case name
104
+ when /^Enforced/
105
+ supported_style_name = RuboCop::Cop::Util.to_supported_styles(name)
106
+ format_table_value(pars[supported_style_name])
107
+ when 'IndentationWidth'
108
+ 'Integer'
109
+ when 'Database'
110
+ format_table_value(pars['SupportedDatabases'])
111
+ else
112
+ case pars[name]
113
+ when String
114
+ 'String'
115
+ when Integer
116
+ 'Integer'
117
+ when Float
118
+ 'Float'
119
+ when true, false
120
+ 'Boolean'
121
+ when Array
122
+ 'Array'
123
+ else
124
+ ''
125
+ end
126
+ end
127
+ end
128
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
129
+
130
+ def to_table(header, content)
131
+ table = [
132
+ header.join(' | '),
133
+ Array.new(header.size, '---').join(' | ')
134
+ ]
135
+ table.concat(content.map { |c| c.join(' | ') })
136
+ table.join("\n") + "\n"
137
+ end
138
+
139
+ def format_table_value(val)
140
+ value =
141
+ case val
142
+ when Array
143
+ if val.empty?
144
+ '`[]`'
145
+ else
146
+ val.map { |config| format_table_value(config) }.join(', ')
147
+ end
148
+ else
149
+ "`#{val.nil? ? '<none>' : val}`"
150
+ end
151
+ value.gsub("#{Dir.pwd}/", '').rstrip
152
+ end
153
+
154
+ def references(config, cop)
155
+ cop_config = config.for_cop(cop)
156
+ urls = RuboCop::Cop::MessageAnnotator.new(
157
+ config, cop.name, cop_config, {}
158
+ ).urls
159
+ return '' if urls.empty?
160
+
161
+ content = h3('References')
162
+ content << urls.map { |url| "* [#{url}](#{url})" }.join("\n")
163
+ content << "\n"
164
+ content
165
+ end
166
+
167
+ def print_cops_of_department(cops, department, config)
168
+ selected_cops = cops_of_department(cops, department).select do |cop|
169
+ cop.to_s.start_with?('RuboCop::Cop::Minitest')
170
+ end
171
+ return if selected_cops.empty?
172
+
173
+ content = +"# #{department}\n"
174
+ selected_cops.each do |cop|
175
+ content << print_cop_with_doc(cop, config)
176
+ end
177
+ file_name = "#{Dir.pwd}/manual/cops_#{department.downcase}.md"
178
+ File.open(file_name, 'w') do |file|
179
+ puts "* generated #{file_name}"
180
+ file.write(content.strip + "\n")
181
+ end
182
+ end
183
+
184
+ def print_cop_with_doc(cop, config)
185
+ t = config.for_cop(cop)
186
+ non_display_keys = %w[
187
+ Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
188
+ VersionChanged
189
+ ]
190
+ pars = t.reject { |k| non_display_keys.include? k }
191
+ description = 'No documentation'
192
+ examples_object = []
193
+ YARD::Registry.all(:class).detect do |code_object|
194
+ next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
195
+
196
+ description = code_object.docstring unless code_object.docstring.blank?
197
+ examples_object = code_object.tags('example')
198
+ end
199
+ cops_body(config, cop, description, examples_object, pars)
200
+ end
201
+
202
+ # rubocop:disable Metrics/AbcSize
203
+ def table_of_content_for_department(cops, department)
204
+ selected_cops = cops_of_department(cops, department.to_sym).select do |cop|
205
+ cop.to_s.start_with?('RuboCop::Cop::Minitest')
206
+ end
207
+ return if selected_cops.empty?
208
+
209
+ type_title = department[0].upcase + department[1..-1]
210
+ filename = "cops_#{department.downcase}.md"
211
+ content = +"#### Department [#{type_title}](#{filename})\n\n"
212
+ selected_cops.each do |cop|
213
+ anchor = cop.cop_name.sub('/', '').downcase
214
+ content << "* [#{cop.cop_name}](#{filename}##{anchor})\n"
215
+ end
216
+
217
+ content
218
+ end
219
+ # rubocop:enable Metrics/AbcSize
220
+
221
+ def print_table_of_contents(cops)
222
+ path = "#{Dir.pwd}/manual/cops.md"
223
+ original = File.read(path)
224
+ content = +"<!-- START_COP_LIST -->\n"
225
+
226
+ content << table_contents(cops)
227
+
228
+ content << "\n<!-- END_COP_LIST -->"
229
+
230
+ content = if original.empty?
231
+ content
232
+ else
233
+ original.sub(
234
+ /<!-- START_COP_LIST -->.+<!-- END_COP_LIST -->/m, content
235
+ )
236
+ end
237
+ File.write(path, content)
238
+ end
239
+
240
+ def table_contents(cops)
241
+ cops
242
+ .departments
243
+ .map(&:to_s)
244
+ .sort
245
+ .map { |department| table_of_content_for_department(cops, department) }
246
+ .reject(&:nil?)
247
+ .join("\n")
248
+ end
249
+
250
+ def assert_manual_synchronized
251
+ # Do not print diff and yield whether exit code was zero
252
+ sh('git diff --quiet manual') do |outcome, _|
253
+ return if outcome
254
+
255
+ # Output diff before raising error
256
+ sh('GIT_PAGER=cat git diff manual')
257
+
258
+ warn 'The manual directory is out of sync. ' \
259
+ 'Run `rake generate_cops_documentation` and commit the results.'
260
+ exit!
261
+ end
262
+ end
263
+
264
+ def main
265
+ cops = RuboCop::Cop::Cop.registry
266
+ config = RuboCop::ConfigLoader.load_file('config/default.yml')
267
+
268
+ YARD::Registry.load!
269
+ cops.departments.sort!.each do |department|
270
+ print_cops_of_department(cops, department, config)
271
+ end
272
+
273
+ print_table_of_contents(cops)
274
+
275
+ assert_manual_synchronized if ENV['CI'] == 'true'
276
+ ensure
277
+ RuboCop::ConfigLoader.default_configuration = nil
278
+ end
279
+
280
+ main
281
+ end
282
+
283
+ desc 'Syntax check for the documentation comments'
284
+ task documentation_syntax_check: :yard_for_generate_documentation do
285
+ require 'parser/ruby25'
286
+
287
+ ok = true
288
+ YARD::Registry.load!
289
+ cops = RuboCop::Cop::Cop.registry
290
+ cops.each do |cop|
291
+ examples = YARD::Registry.all(:class).find do |code_object|
292
+ next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
293
+
294
+ break code_object.tags('example')
295
+ end
296
+
297
+ examples.to_a.each do |example|
298
+ begin
299
+ buffer = Parser::Source::Buffer.new('<code>', 1)
300
+ buffer.source = example.text
301
+ parser = Parser::Ruby25.new(RuboCop::AST::Builder.new)
302
+ parser.diagnostics.all_errors_are_fatal = true
303
+ parser.parse(buffer)
304
+ rescue Parser::SyntaxError => e
305
+ path = example.object.file
306
+ puts "#{path}: Syntax Error in an example. #{e}"
307
+ ok = false
308
+ end
309
+ end
310
+ end
311
+ abort unless ok
312
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-minitest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Bozhidar Batsov
8
+ - Jonas Arvidsson
9
+ - Koichi ITO
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2019-09-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubocop
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0.74'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0.74'
29
+ - !ruby/object:Gem::Dependency
30
+ name: minitest
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '5.11'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '5.11'
43
+ description: |
44
+ Automatic Minitest code style checking tool.
45
+ A RuboCop extension focused on enforcing Minitest best practices and coding conventions.
46
+ email:
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".circleci/config.yml"
52
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
53
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
54
+ - ".github/PULL_REQUEST_TEMPLATE.md"
55
+ - ".gitignore"
56
+ - ".rubocop.yml"
57
+ - ".rubocop_todo.yml"
58
+ - CHANGELOG.md
59
+ - CONTRIBUTING.md
60
+ - Gemfile
61
+ - LICENSE.txt
62
+ - README.md
63
+ - Rakefile
64
+ - bin/console
65
+ - bin/setup
66
+ - config/default.yml
67
+ - lib/rubocop-minitest.rb
68
+ - lib/rubocop/cop/minitest/assert_empty.rb
69
+ - lib/rubocop/cop/minitest/assert_includes.rb
70
+ - lib/rubocop/cop/minitest/assert_nil.rb
71
+ - lib/rubocop/cop/minitest/assert_truthy.rb
72
+ - lib/rubocop/cop/minitest/refute_nil.rb
73
+ - lib/rubocop/cop/minitest_cops.rb
74
+ - lib/rubocop/minitest.rb
75
+ - lib/rubocop/minitest/inject.rb
76
+ - lib/rubocop/minitest/version.rb
77
+ - manual/cops.md
78
+ - manual/cops_minitest.md
79
+ - manual/index.md
80
+ - manual/installation.md
81
+ - manual/usage.md
82
+ - mkdocs.yml
83
+ - readthedocs.yml
84
+ - rubocop-minitest.gemspec
85
+ - tasks/cops_documentation.rake
86
+ homepage:
87
+ licenses:
88
+ - MIT
89
+ metadata:
90
+ homepage_uri: https://docs.rubocop.org/projects/minitest
91
+ changelog_uri: https://github.com/rubocop-hq/rubocop-minitest/blob/master/CHANGELOG.md
92
+ source_code_uri: https://github.com/rubocop-hq/rubocop-minitest
93
+ documentation_uri: https://docs.rubocop.org/projects/minitest
94
+ bug_tracker_uri: https://github.com/rubocop-hq/rubocop-minitest/issues
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.3.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.0.6
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Automatic Minitest code style checking tool.
114
+ test_files: []