tasks/cops_documentation.rake in rubocop-minitest-0.10.1 vs tasks/cops_documentation.rake in rubocop-minitest-0.10.2
- old
+ new
@@ -1,322 +1,44 @@
# frozen_string_literal: true
require 'yard'
require 'rubocop'
require 'rubocop-minitest'
+require 'rubocop/cops_documentation_generator'
YARD::Rake::YardocTask.new(:yard_for_generate_documentation) do |task|
task.files = ['lib/rubocop/cop/**/*.rb']
task.options = ['--no-output']
end
desc 'Generate docs of all cops departments'
task generate_cops_documentation: :yard_for_generate_documentation do
- def cops_of_department(cops, department)
- cops.with_department(department).sort!
- end
+ deps = ['Minitest']
+ CopsDocumentationGenerator.new(departments: deps).call
+end
- def cops_body(config, cop, description, examples_objects, pars)
- content = h2(cop.cop_name)
- content << required_ruby_version(cop)
- content << properties(cop.new(config))
- content << "#{description}\n"
- content << examples(examples_objects) if examples_objects.count.positive?
- content << configurations(pars)
- content << references(config, cop)
- content
- end
+desc 'Verify that documentation is up to date'
+task verify_cops_documentation: :generate_cops_documentation do
+ # Do not print diff and yield whether exit code was zero
+ sh('git diff --quiet docs') do |outcome, _|
+ exit if outcome
- def examples(examples_object)
- examples_object.each_with_object(h3('Examples').dup) do |example, content|
- content << "\n" unless content.end_with?("\n\n")
- content << h4(example.name) unless example.name == ''
- content << code_example(example)
- end
- end
+ # Output diff before raising error
+ sh('GIT_PAGER=cat git diff docs')
- def required_ruby_version(cop)
- return '' unless cop.respond_to?(:required_minimum_ruby_version)
-
- "NOTE: Required Ruby version: #{cop.required_minimum_ruby_version}\n\n"
+ warn 'The docs directory is out of sync. ' \
+ 'Run `rake generate_cops_documentation` and commit the results.'
+ exit!
end
-
- # rubocop:disable Metrics/MethodLength
- def properties(cop_instance)
- header = [
- 'Enabled by default', 'Safe', 'Supports autocorrection', 'VersionAdded',
- 'VersionChanged'
- ]
- autocorrect = if cop_instance.support_autocorrect?
- "Yes#{' (Unsafe)' unless cop_instance.safe_autocorrect?}"
- else
- 'No'
- end
- cop_config = cop_instance.cop_config
- content = [[
- cop_status(cop_config.fetch('Enabled')),
- cop_config.fetch('Safe', true) ? 'Yes' : 'No',
- autocorrect,
- cop_config.fetch('VersionAdded', '-'),
- cop_config.fetch('VersionChanged', '-')
- ]]
- "#{to_table(header, content)}\n"
- end
- # rubocop:enable Metrics/MethodLength
-
- def h2(title)
- content = +"\n"
- content << "== #{title}\n"
- content << "\n"
- content
- end
-
- def h3(title)
- content = +"\n"
- content << "=== #{title}\n"
- content << "\n"
- content
- end
-
- def h4(title)
- content = +"==== #{title}\n"
- content << "\n"
- content
- end
-
- def code_example(ruby_code)
- content = +"[source,ruby]\n----\n"
- content << ruby_code.text.gsub('@good', '# good')
- .gsub('@bad', '# bad').strip
- content << "\n----\n"
- content
- end
-
- def configurations(pars)
- return '' if pars.empty?
-
- header = ['Name', 'Default value', 'Configurable values']
- configs = pars
- .each_key
- .reject { |key| key.start_with?('Supported') }
- .reject { |key| key.start_with?('AllowMultipleStyles') }
- content = configs.map do |name|
- configurable = configurable_values(pars, name)
- default = format_table_value(pars[name])
- [name, default, configurable]
- end
-
- h3('Configurable attributes') + to_table(header, content)
- end
-
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
- def configurable_values(pars, name)
- case name
- when /^Enforced/
- supported_style_name = RuboCop::Cop::Util.to_supported_styles(name)
- format_table_value(pars[supported_style_name])
- when 'IndentationWidth'
- 'Integer'
- else
- case pars[name]
- when String
- 'String'
- when Integer
- 'Integer'
- when Float
- 'Float'
- when true, false
- 'Boolean'
- when Array
- 'Array'
- else
- ''
- end
- end
- end
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
-
- def to_table(header, content)
- table = [
- '|===',
- "| #{header.join(' | ')}\n\n"
- ].join("\n")
- marked_contents = content.map do |plain_content|
- plain_content.map { |c| "| #{c}" }.join("\n")
- end
- table << marked_contents.join("\n\n")
- table << "\n|===\n"
- end
-
- def format_table_value(val)
- value =
- case val
- when Array
- if val.empty?
- '`[]`'
- else
- val.map { |config| format_table_value(config) }.join(', ')
- end
- else
- wrap_backtick(val.nil? ? '<none>' : val)
- end
- value.gsub("#{Dir.pwd}/", '').rstrip
- end
-
- def wrap_backtick(value)
- if value.is_a?(String)
- # Use `+` to prevent text like `**/*.gemspec` from being bold.
- value.start_with?('*') ? "`+#{value}+`" : "`#{value}`"
- else
- "`#{value}`"
- end
- end
-
- def references(config, cop)
- cop_config = config.for_cop(cop)
- urls = RuboCop::Cop::MessageAnnotator.new(
- config, cop.name, cop_config, {}
- ).urls
- return '' if urls.empty?
-
- content = h3('References')
- content << urls.map { |url| "* #{url}" }.join("\n")
- content << "\n"
- content
- end
-
- def print_cops_of_department(cops, department, config)
- selected_cops = cops_of_department(cops, department).select do |cop|
- cop.to_s.start_with?('RuboCop::Cop::Minitest')
- end
- return if selected_cops.empty?
-
- selected_cops = cops_of_department(cops, department)
- content = +"= #{department}\n"
- selected_cops.each do |cop|
- content << print_cop_with_doc(cop, config)
- end
- file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/cops_#{department.downcase}.adoc"
- File.open(file_name, 'w') do |file|
- puts "* generated #{file_name}"
- file.write("#{content.strip}\n")
- end
- end
-
- def print_cop_with_doc(cop, config)
- t = config.for_cop(cop)
- non_display_keys = %w[
- Description Enabled StyleGuide Reference Safe SafeAutoCorrect VersionAdded
- VersionChanged
- ]
- pars = t.reject { |k| non_display_keys.include? k }
- description = 'No documentation'
- examples_object = []
- cop_code(cop) do |code_object|
- description = code_object.docstring unless code_object.docstring.blank?
- examples_object = code_object.tags('example')
- end
- cops_body(config, cop, description, examples_object, pars)
- end
-
- def cop_code(cop)
- YARD::Registry.all(:class).detect do |code_object|
- next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
-
- yield code_object
- end
- end
-
- # rubocop:disable Metrics/AbcSize
- def table_of_content_for_department(cops, department)
- selected_cops = cops_of_department(cops, department.to_sym).select do |cop|
- cop.to_s.start_with?('RuboCop::Cop::Minitest')
- end
- return if selected_cops.empty?
-
- type_title = department[0].upcase + department[1..-1]
- filename = "cops_#{department.downcase}.adoc"
- content = +"= Department xref:#{filename}[#{type_title}]\n\n"
- cops_of_department(cops, department.to_sym).each do |cop|
- anchor = cop.cop_name.sub('/', '').downcase
- content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
- end
-
- content
- end
- # rubocop:enable Metrics/AbcSize
-
- def print_table_of_contents(cops)
- path = "#{Dir.pwd}/docs/modules/ROOT/pages/cops.adoc"
- original = File.read(path)
- content = +"// START_COP_LIST\n\n"
-
- content << table_contents(cops)
-
- content << "\n// END_COP_LIST"
-
- content = original.sub(
- %r{// START_COP_LIST.+// END_COP_LIST}m, content
- )
- File.write(path, content)
- end
-
- def table_contents(cops)
- cops
- .departments
- .map(&:to_s)
- .sort
- .map { |department| table_of_content_for_department(cops, department) }
- .compact
- .join("\n")
- end
-
- def cop_status(status)
- return 'Disabled' unless status
-
- status == 'pending' ? 'Pending' : 'Enabled'
- end
-
- def assert_docs_synchronized
- # Do not print diff and yield whether exit code was zero
- sh('git diff --quiet docs') do |outcome, _|
- return if outcome
-
- # Output diff before raising error
- sh('GIT_PAGER=cat git diff docs')
-
- warn 'The docs directory is out of sync. ' \
- 'Run `rake generate_cops_documentation` and commit the results.'
- exit!
- end
- end
-
- def main
- cops = RuboCop::Cop::Cop.registry
- config = RuboCop::ConfigLoader.load_file('config/default.yml')
-
- YARD::Registry.load!
- cops.departments.sort!.each do |department|
- print_cops_of_department(cops, department, config)
- end
-
- print_table_of_contents(cops)
-
- assert_docs_synchronized if ENV['CI'] == 'true'
- ensure
- RuboCop::ConfigLoader.default_configuration = nil
- end
-
- main
end
desc 'Syntax check for the documentation comments'
task documentation_syntax_check: :yard_for_generate_documentation do
require 'parser/ruby25'
ok = true
YARD::Registry.load!
- cops = RuboCop::Cop::Cop.registry
+ cops = RuboCop::Cop::Registry.global
cops.each do |cop|
examples = YARD::Registry.all(:class).find do |code_object|
next unless RuboCop::Cop::Badge.for(code_object.to_s) == cop.badge
break code_object.tags('example')