Class: RuboCop::Cop::Generator::RegistrationInjector Private
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/rubocop/cop/generator/registration_injector.rb |
Overview
A class that injects a register_cop directive into the cop’s department module
so that the new cop is registered for lazy loading.
The directive is injected in alphabetical order among the existing directives without
reordering them, since the registration order defines the cop execution order.
When the department module file does not exist yet, it is created, and a require for
it is injected into the root RuboCop file.
Anything unexpected in the department module, such as a commented-out or otherwise
unparsable register_cop line, raises an error instead of silently producing
a broken registration.
Constant Summary
-
DEPARTMENT_REQUIRE_PATTERN =
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 23%r{\Arequire_relative 'rubocop/cop/[a-z0-9_/]+'\n\z}.freeze
-
ENTRY_PATTERN =
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 18%r{ \A(?<indent>\s*)register_cop\s :(?<constant>[A-Za-z0-9]+),\s "\#\{__dir__\}/(?<path>[a-z0-9_/]+)"\n\z }x.freeze
-
EXTEND_PATTERN =
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 17/\A(?<indent>\s*)extend LazyLoader\n\z/.freeze
Class Method Summary
- .new(source_path:, badge:, root_file_path: 'lib/rubocop.rb', output: $stdout) ⇒ RegistrationInjector constructor Internal use only
Instance Attribute Summary
- #badge readonly private Internal use only
- #department_file_path readonly private Internal use only
- #output readonly private Internal use only
- #root_file_path readonly private Internal use only
- #source_path readonly private Internal use only
Instance Method Summary
- #inject Internal use only
-
#cop_path
private
Internal use only
The directive path is relative to the department module file’s directory, which
dirresolves to at load time. - #create_department_file private Internal use only
- #department_module_source private Internal use only
-
#department_require_index(lines, require_directive)
private
Internal use only
The department requires form the contiguous block right before the
rubocop/cop/teamrequire in the root RuboCop file. - #directive private Internal use only
- #first_directive_position(lines) private Internal use only
- #inject_department_require private Internal use only
- #inject_directive private Internal use only
- #insertion_point(lines, entries) private Internal use only
- #module_closing_lines(modules) private Internal use only
- #module_opening_lines(modules) private Internal use only
- #parse_entries(lines) ⇒ Array<Hash> private Internal use only
- #write_department_file(lines) private Internal use only
Constructor Details
.new(source_path:, badge:, root_file_path: 'lib/rubocop.rb', output: $stdout) ⇒ RegistrationInjector
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 25
def initialize(source_path:, badge:, root_file_path: 'lib/rubocop.rb', output: $stdout) @source_path = Pathname(source_path) @badge = badge @root_file_path = root_file_path @department_file_path = Pathname("#{@source_path.dirname}.rb") @output = output end
Instance Attribute Details
#badge (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 44
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
#department_file_path (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 44
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
#output (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 44
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
#root_file_path (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 44
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
#source_path (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 44
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
Instance Method Details
#cop_path (private)
The directive path is relative to the department module file’s directory,
which dir resolves to at load time.
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 99
def cop_path source_path.relative_path_from(department_file_path.dirname).to_s.delete_suffix('.rb') end
#create_department_file (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 57
def create_department_file File.write(department_file_path, department_module_source) output.puts "[create] #{department_file_path}" end
#department_module_source (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 152
def department_module_source modules = %w[RuboCop Cop] + badge.department.to_s.split('/') body_indent = ' ' * modules.size lines = ['# frozen_string_literal: true', ''] lines.concat(module_opening_lines(modules)) lines << "#{body_indent}extend LazyLoader" lines << '' lines << "#{body_indent}#{directive}" lines.concat(module_closing_lines(modules)) "#{lines.join("\n")}\n" end
#department_require_index(lines, require_directive) (private)
The department requires form the contiguous block right before
the rubocop/cop/team require in the root RuboCop file.
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 120
def department_require_index(lines, require_directive) anchor = lines.index("require_relative 'rubocop/cop/team'\n") unless anchor raise Error, "could not find the department require block in #{root_file_path}" end block_end = anchor - 1 block_end -= 1 while lines[block_end] == "\n" block_start = block_end block_start -= 1 while lines[block_start - 1]&.match?(DEPARTMENT_REQUIRE_PATTERN) (block_start..block_end).find do |index| lines[index] > require_directive end || (block_end + 1) end
#directive (private)
[ GitHub ]#first_directive_position(lines) (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 136
def first_directive_position(lines) extend_lines = lines.filter_map.with_index do |line, index| [index, EXTEND_PATTERN.match(line)[:indent]] if EXTEND_PATTERN.match?(line) end unless extend_lines.size == 1 raise Error, "expected `extend LazyLoader` in #{department_file_path}; " \ 'add it or register the cop manually' end index, indent = extend_lines.first blank_after_extend = lines[index + 1] == "\n" [index + (blank_after_extend ? 2 : 1), indent] end
#inject
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 33
def inject if File.exist?(department_file_path) inject_directive else create_department_file inject_department_require end end
#inject_department_require (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 63
def inject_department_require require_path = department_file_path.sub_ext('').to_s.delete_prefix('lib/') require_directive = "require_relative '#{require_path}'\n" lines = File.readlines(root_file_path) return if lines.include?(require_directive) lines.insert(department_require_index(lines, require_directive), require_directive) File.write(root_file_path, lines.join) output.puts "[modify] #{root_file_path} - `#{require_directive.strip}` was injected." end
#inject_directive (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 46
def inject_directive lines = File.readlines(department_file_path) entries = parse_entries(lines) return if entries.any? { |entry| entry[:path] == cop_path } line, indent = insertion_point(lines, entries) lines.insert(line, "#{indent}#{directive}\n") write_department_file(lines) end
#insertion_point(lines, entries) (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 103
def insertion_point(lines, entries) if entries.empty? first_directive_position(lines) else successor = entries.find { |entry| entry[:path] > cop_path } entry = successor || entries.last [entry[:line] + (successor ? 0 : 1), entry[:indent]] end end
#module_closing_lines(modules) (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 182
def module_closing_lines(modules) (modules.size - 1).downto(0).map { |nesting| "#{' ' * nesting}end" } end
#module_opening_lines(modules) (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 166
def module_opening_lines(modules) modules.flat_map.with_index do |mod, nesting| indent = ' ' * nesting lines = [] if mod == modules.last lines << "#{indent}# Cops for the `#{badge.department}` department. " \ "The department's cops are" lines << "#{indent}# registered for lazy loading and their files are " \ 'loaded on demand.' end lines << "#{indent}module #{mod}" end end
#parse_entries(lines) ⇒ Array<Hash> (private)
# File 'lib/rubocop/cop/generator/registration_injector.rb', line 83
def parse_entries(lines) lines.filter_map.with_index do |line, index| match = ENTRY_PATTERN.match(line) if match { line: index, indent: match[:indent], constant: match[:constant], path: match[:path] } elsif line.include?('register_cop') raise Error, "unexpected `register_cop` line in #{department_file_path}:" \ "#{index + 1}; fix it or register the cop manually: #{line}" end end end
#write_department_file(lines) (private)
[ GitHub ]# File 'lib/rubocop/cop/generator/registration_injector.rb', line 76
def write_department_file(lines) File.write(department_file_path, lines.join) output.puts "[modify] #{department_file_path} - `#{directive}` was injected." end