class HamlLint::Linter::RuboCop

Runs RuboCop on Ruby code contained within HAML templates.

Constants

SEVERITY_MAP

Maps the ::RuboCop::Cop::Severity levels to our own levels.

Private Class Methods

rubocop_cli() click to toggle source

A single CLI instance is shared between files to avoid RuboCop having to repeatedly reload .rubocop.yml.

# File lib/haml_lint/linter/rubocop.rb, line 34
def self.rubocop_cli
  # The ivar is stored on the class singleton rather than the Linter instance
  # because it can't be Marshal.dump'd (as used by Parallel.map)
  @rubocop_cli ||= ::RuboCop::CLI.new
end

Public Instance Methods

visit_root(_node) click to toggle source
# File lib/haml_lint/linter/rubocop.rb, line 21
def visit_root(_node)
  extractor = HamlLint::RubyExtractor.new
  extracted_source = extractor.extract(document)

  return if extracted_source.source.empty?

  find_lints(extracted_source.source, extracted_source.source_map)
end

Private Instance Methods

extract_lints_from_offenses(offenses, source_map) click to toggle source

Aggregates RuboCop offenses and converts them to {HamlLint::Lint}s suitable for reporting.

@param offenses [Array<RuboCop::Cop::Offense>] @param source_map [Hash]

# File lib/haml_lint/linter/rubocop.rb, line 79
def extract_lints_from_offenses(offenses, source_map)
  dummy_node = Struct.new(:line)

  offenses.reject { |offense| Array(config['ignored_cops']).include?(offense.cop_name) }
          .each do |offense|
    record_lint(dummy_node.new(source_map[offense.line]), offense.message,
                offense.severity.name)
  end
end
find_lints(ruby, source_map) click to toggle source

Executes RuboCop against the given Ruby code and records the offenses as lints.

@param ruby [String] Ruby code @param source_map [Hash] map of Ruby code line numbers to original line

numbers in the template
# File lib/haml_lint/linter/rubocop.rb, line 46
def find_lints(ruby, source_map)
  filename =
    if document.file
      "#{document.file}.rb"
    else
      'ruby_script.rb'
    end

  with_ruby_from_stdin(ruby) do
    extract_lints_from_offenses(lint_file(self.class.rubocop_cli, filename), source_map)
  end
end
lint_file(rubocop, file) click to toggle source

Defined so we can stub the results in tests

@param rubocop [RuboCop::CLI] @param file [String] @return [Array<RuboCop::Cop::Offense>]

# File lib/haml_lint/linter/rubocop.rb, line 64
def lint_file(rubocop, file)
  status = rubocop.run(rubocop_flags << file)
  unless [::RuboCop::CLI::STATUS_SUCCESS, ::RuboCop::CLI::STATUS_OFFENSES].include?(status)
    raise HamlLint::Exceptions::ConfigurationError,
          "RuboCop exited unsuccessfully with status #{status}." \
          ' Check the stack trace to see if there was a misconfiguration.'
  end
  OffenseCollector.offenses
end
record_lint(node, message, severity) click to toggle source

Record a lint for reporting back to the user.

@param node [#line] node to extract the line number from @param message [String] error/warning to display to the user @param severity [Symbol] RuboCop severity level for the offense

# File lib/haml_lint/linter/rubocop.rb, line 94
def record_lint(node, message, severity)
  @lints << HamlLint::Lint.new(self, @document.file, node.line, message,
                               SEVERITY_MAP.fetch(severity, :warning))
end
rubocop_flags() click to toggle source

Returns flags that will be passed to RuboCop CLI.

@return [Array<String>]

# File lib/haml_lint/linter/rubocop.rb, line 102
def rubocop_flags
  flags = %w[--format HamlLint::OffenseCollector]
  flags += ['--config', ENV['HAML_LINT_RUBOCOP_CONF']] if ENV['HAML_LINT_RUBOCOP_CONF']
  flags += ['--stdin']
  flags
end
with_ruby_from_stdin(ruby) { || ... } click to toggle source

Overrides the global stdin to allow RuboCop to read Ruby code from it.

@param ruby [String] the Ruby code to write to the overridden stdin @param _block [Block] the block to perform with the overridden stdin @return [void]

# File lib/haml_lint/linter/rubocop.rb, line 114
def with_ruby_from_stdin(ruby, &_block)
  original_stdin = $stdin
  stdin = StringIO.new
  stdin.write(ruby)
  stdin.rewind
  $stdin = stdin
  yield
ensure
  $stdin = original_stdin
end