123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Team

Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/cop/team.rb

Overview

A group of cops, ready to be called on duty to inspect files. Team is responsible for selecting only relevant cops to be sent on duty, as well as insuring that the needed forces are sent along with them.

For performance reasons, Team will first dispatch cops & forces in two groups, first the ones needed for autocorrection (if any), then the rest (unless autocorrections happened).

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(cop_or_classes, config, options = {}) ⇒ Team

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 14

def self.new(cop_or_classes, config, options = {})
  # Support v0 api:
  return mobilize(cop_or_classes, config, options) if cop_or_classes.first.is_a?(Class)

  super
end

#initialize(cops, config = nil, options = {}) ⇒ Team

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 55

def initialize(cops, config = nil, options = {})
  @cops = cops
  @config = config
  @options = options
  reset
  @ready = true
  @registry = Registry.new(cops, options.dup)

  validate_config
end

Class Method Details

.forces_for(cops) ⇒ Array<Force>

Returns:

  • (Array<Force>)

    needed for the given cops

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 37

def self.forces_for(cops)
  needed = Hash.new { |h, k| h[k] = [] }
  cops.each do |cop|
    forces = cop.class.joining_forces
    if forces.is_a?(Array)
      forces.each { |force| needed[force] << cop }
    elsif forces
      needed[forces] << cop
    end
  end

  needed.map { |force_class, joining_cops| force_class.new(joining_cops) }
end

.mobilize(cop_classes, config, options = {}) ⇒ Team

Returns:

  • (Team)

    with cops assembled from the given cop_classes

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 22

def self.mobilize(cop_classes, config, options = {})
  cops = mobilize_cops(cop_classes, config, options)
  new(cops, config, options)
end

.mobilize_cops(cop_classes, config, options = {}) ⇒ Array<Cop::Base>

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 28

def self.mobilize_cops(cop_classes, config, options = {})
  cop_classes = Registry.new(cop_classes.to_a, options) unless cop_classes.is_a?(Registry)

  cop_classes.map do |cop_class|
    cop_class.new(config, options)
  end
end

Instance Attribute Details

#autocorrect?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 66

def autocorrect?
  @options[:autocorrect]
end

#cops (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 51

attr_reader :errors, :warnings, :updated_source_file, :cops

#debug?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 70

def debug?
  @options[:debug]
end

#errors (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 51

attr_reader :errors, :warnings, :updated_source_file, :cops

#updated_source_file? (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 53

alias updated_source_file? updated_source_file

#warnings (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 51

attr_reader :errors, :warnings, :updated_source_file, :cops

Instance Method Details

#autocorrect(processed_source, report, original:, offset:) (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 129

def autocorrect(processed_source, report, original:, offset:)
  @updated_source_file = false
  return unless autocorrect?
  return if report.processed_source.parser_error

  new_source = autocorrect_report(report, original: original, offset: offset)

  return unless new_source

  if @options[:stdin]
    # holds source read in from stdin, when --stdin option is used
    @options[:stdin] = new_source
  else
    filename = processed_source.file_path
    File.write(filename, new_source)
  end
  @updated_source_file = true
end

#autocorrect_report(report, offset:, original:) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 193

def autocorrect_report(report, offset:, original:)
  corrector = collate_corrections(report, offset: offset, original: original)

  corrector.rewrite unless corrector.empty?
end

#be_ready (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 148

def be_ready
  return if @ready

  reset
  @cops.map!(&:ready)
  @ready = true
end

#collate_corrections(report, offset:, original:) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 199

def collate_corrections(report, offset:, original:)
  corrector = Corrector.new(original)

  each_corrector(report) do |to_merge|
    suppress_clobbering do
      if offset.positive?
        corrector.import!(to_merge, offset: offset)
      else
        corrector.merge!(to_merge)
      end
    end
  end

  corrector
end

#each_corrector(report) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 215

def each_corrector(report)
  skips = Set.new
  report.cop_reports.each do |cop_report|
    cop = cop_report.cop
    corrector = cop_report.corrector

    next if corrector.nil? || corrector.empty?
    next if skips.include?(cop.class)

    yield corrector

    skips.merge(cop.class.autocorrect_incompatible_with)
  end
end

#external_dependency_checksum

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 122

def external_dependency_checksum
  keys = cops.filter_map(&:external_dependency_checksum)
  Digest::SHA1.hexdigest(keys.join)
end

#forces

Deprecated.
[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 114

def forces
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
    `forces` is deprecated.
  WARNING

  @forces ||= self.class.forces_for(cops)
end

#handle_error(error, location, cop) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 267

def handle_error(error, location, cop)
  message = Rainbow("An error occurred while #{cop.name} cop was inspecting #{location}.").red
  @errors << message
  warn message
  if debug?
    puts error.message, error.backtrace
  else
    warn 'To see the complete backtrace run rubocop -d.'
  end
end

#handle_warning(error, location) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 259

def handle_warning(error, location)
  message = Rainbow("#{error.message} (from file: #{location})").yellow

  @warnings << message
  warn message
  puts error.backtrace if debug?
end

#inspect_file(processed_source)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 76

def inspect_file(processed_source)
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
    `inspect_file` is deprecated. Use `investigate` instead.
  WARNING

  investigate(processed_source).offenses
end

#investigate(processed_source, offset: 0, original: processed_source) ⇒ Commissioner::InvestigationReport

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 85

def investigate(processed_source, offset: 0, original: processed_source)
  be_ready

  # The autocorrection process may have to be repeated multiple times
  # until there are no corrections left to perform
  # To speed things up, run autocorrecting cops by themselves, and only
  # run the other cops when no corrections are left
  on_duty = roundup_relevant_cops(processed_source)

  autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?)
  report = investigate_partial(autocorrect_cops, processed_source,
                               offset: offset, original: original)

  unless autocorrect(processed_source, report, offset: offset, original: original)
    # If we corrected some errors, another round of inspection will be
    # done, and any other offenses will be caught then, so only need
    # to check other_cops if no correction was done
    report = report.merge(investigate_partial(other_cops, processed_source,
                                              offset: offset, original: original))
  end

  process_errors(processed_source.path, report.errors)

  report
ensure
  @ready = false
end

#investigate_partial(cops, processed_source, offset:, original:) ⇒ Commissioner::InvestigationReport (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 162

def investigate_partial(cops, processed_source, offset:, original:)
  commissioner = Commissioner.new(cops, self.class.forces_for(cops), @options)
  commissioner.investigate(processed_source, offset: offset, original: original)
end

#process_errors(file, errors) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 242

def process_errors(file, errors)
  errors.each do |error|
    line = ":#{error.line}" if error.line
    column = ":#{error.column}" if error.column
    location = "#{file}#{line}#{column}"
    cause = error.cause

    if cause.is_a?(Warning)
      handle_warning(cause, location)
    elsif cause.is_a?(Force::HookError)
      handle_error(cause.cause, location, cause.joining_cop)
    else
      handle_error(cause, location, error.cop)
    end
  end
end

#reset (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 156

def reset
  @errors = []
  @warnings = []
end

#roundup_relevant_cops(processed_source) ⇒ Array<cop> (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 168

def roundup_relevant_cops(processed_source)
  cops.select do |cop|
    next true if processed_source.comment_config.cop_opted_in?(cop)
    next false if cop.excluded_file?(processed_source.file_path)
    next false unless @registry.enabled?(cop, @config)

    support_target_ruby_version?(cop) && support_target_rails_version?(cop)
  end
end

#support_target_rails_version?(cop) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 184

def support_target_rails_version?(cop)
  # In this case, the rails version was already checked by `#excluded_file?`
  return true if defined?(RuboCop::Rails::TargetRailsVersion::USES_REQUIRES_GEM_API)

  return true unless cop.class.respond_to?(:support_target_rails_version?)

  cop.class.support_target_rails_version?(cop.target_rails_version)
end

#support_target_ruby_version?(cop) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 178

def support_target_ruby_version?(cop)
  return true unless cop.class.respond_to?(:support_target_ruby_version?)

  cop.class.support_target_ruby_version?(cop.target_ruby_version)
end

#suppress_clobbering (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 230

def suppress_clobbering
  yield
rescue ::Parser::ClobberingError
  # ignore Clobbering errors
end

#updated_source_file (readonly) Also known as: #updated_source_file?

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 51

attr_reader :errors, :warnings, :updated_source_file, :cops

#validate_config (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/team.rb', line 236

def validate_config
  cops.each do |cop|
    cop.validate_config if cop.respond_to?(:validate_config)
  end
end