# frozen_string_literal: true require_relative 'generator/dept_readme_injector' require_relative 'generator/cop_readme_injector' module RuboCop module Cop module RubomaticRails class Generator COP_DOC = <<~RUBY # TODO: Write cop description and example of bad / good code. For every # `SupportedStyle` and unique configuration, there needs to be examples. # Examples must have valid Ruby syntax. Do not use upticks. # # @safety # Delete this section if the cop is not unsafe (`Safe: false` or # `SafeAutoCorrect: false`), or use it to explain how the cop is # unsafe. # # @example EnforcedStyle: bar (default) # # Description of the `bar` style. # # # bad # bad_bar_method # # # bad # bad_bar_method(args) # # # good # good_bar_method # # # good # good_bar_method(args) # # @example EnforcedStyle: foo # # Description of the `foo` style. # # # bad # bad_foo_method # # # bad # bad_foo_method(args) # # # good # good_foo_method # # # good # good_foo_method(args) # RUBY SOURCE_TEMPLATE = <<~RUBY # frozen_string_literal: true module RuboCop module Cop module %{department} class %{cop_name} < Base # TODO: Implement the cop in here. # # In many cases, you can use a node matcher for matching node pattern. # See https://github.com/rubocop/rubocop-ast/blob/master/lib/rubocop/ast/node_pattern.rb # # For example MSG = 'Use `#good_method` instead of `#bad_method`.' # TODO: Don't call `on_send` unless the method name is in this list # If you don't need `on_send` in the cop you created, remove it. RESTRICT_ON_SEND = %%i[bad_method].freeze # @!method bad_method?(node) def_node_matcher :bad_method?, <<~PATTERN (send nil? :bad_method ...) PATTERN def on_send(node) return unless bad_method?(node) add_offense(node) end end end end end RUBY README_ADDED_MESSAGE = '[modify] A link for the %{dept_vs_cop} has been added into %{readme_file_path}.' DEPT_README_TEMPLATE = <<~ADOC = %{department} Describe the department here == Cops ADOC COP_README_TEMPLATE = <<~ADOC = ``%{department}/%{cop_name}`` == Description Add a description here == Examples [source,ruby] ---- # Bad # Add a bad example here # Good # Add a good example here ---- == Configurable Attributes |=== |Name |Default value |Configurable values |Max |120 |Integer |=== == References https://github.com/BrandsInsurance/expert-chainsaw/issues ADOC # :nodoc: def initialize(name, output: $stdout) name = ['RubomaticRails', name].join('/') unless name.start_with?('RubomaticRails/') unless name.count('/') == 2 raise( [ 'You must provide a single department under RubomaticRails i.e. RubomaticRails/Department/CopName', 'or Department/CopName' ].join(' ') ) end @base_gen = RuboCop::Cop::Generator.new(name, output: output) end # Calls methods in the base class # # @return [*] # def method_missing(...) @base_gen.__send__(...) end # `self` responds to `method_name` if `@base_gen` does # def respond_to_missing?(method_name, include_private = false) @base_gen.respond_to?(method_name, include_private) end # Creates the department readme if it doesn't exist # Modified version of `wirte_source` from RuboCop::Cop::Generator # # @return [void] # def write_dept_readme return if File.exist?(dept_docs_path) write_unless_file_exists(dept_docs_path, generated_dept_docs) end # Creates the cop readme if it doesn't exist # Modified version of `wirte_source` from RuboCop::Cop::Generator # # @return [void] # def write_cop_readme write_unless_file_exists(docs_path, generated_cop_docs) end # Injects the, possibly new, department readme link into the base readme # Modified version of `inject_config` from RuboCop::Cop::Generator # # @return [void] # def inject_dept_readme(readme_file_path: 'README.adoc') # Add this dept to base readme if not already there injector = DeptReadmeInjector.new( readme_file_path: readme_file_path, badge: badge, department: department ) injector.inject_string do output.puts(format(README_ADDED_MESSAGE, readme_file_path: readme_file_path, dept_vs_cop: 'department')) end end # Injects the new cop readme link into the department readme # Modified version of `inject_config` from RuboCop::Cop::Generator # # @return [void] # def inject_cop_readme(readme_file_path: dept_docs_path) # Add this cop to the dept readme injector = CopReadmeInjector.new( readme_file_path: readme_file_path, badge: badge, department: department ) injector.inject_string do output.puts(format(README_ADDED_MESSAGE, readme_file_path: readme_file_path, dept_vs_cop: 'cop')) end end private # The rubocop department without the RubomaticRails prefix # # @return [String] # def department badge.department_name.gsub('RubomaticRails/', '') end # Modified version of `generated_source` from Rubocop::Cop::Generator # # @return [String] # def generated_dept_docs generate_readme(DEPT_README_TEMPLATE) end # Modified version of `generated_source` from Rubocop::Cop::Generator # # @return [String] # def generated_cop_docs generate_readme(COP_README_TEMPLATE) end # Modified version from Rubocop::Cop::Generator # # @return [String] # def generate_readme(template) format(template, { department: department, cop_name: badge.cop_name, cop_folder: snake_case(badge.cop_name.to_s) }) end # Path to /README.adoc # # @return [String] # def dept_docs_path File.join( 'docs', 'cops', snake_case(department), 'README.adoc' ) end # Path to //README.adoc # # @return [String] # def docs_path File.join( 'docs', 'cops', snake_case(department), snake_case(badge.cop_name.to_s), 'README.adoc' ) end end end end end