class Maximus::Lint

Parent class for all lints (inherited by children) @since 0.1.0 @attr_accessor output [Hash] result of a lint parsed by Lint#refine

Attributes

output[RW]

Public Class Methods

new(opts = {}) click to toggle source

Perform a lint of relevant code

All defined lints require a “result” method @example the result method in the child class

def result
  @task = __method__.to_s
  @path ||= 'path/or/**/glob/to/files''
  lint_data = JSON.parse(`some-command-line-linter`)
  @output[:files_inspected] ||= files_inspected(extension, delimiter, base_path_replacement)
  refine data_from_output
end

Inherits settings from {Config#initialize} @see Config#initialize

@param opts [Hash] ({}) options passed directly to the lint @option opts [Hash] :git_files filename: file location

@see GitControl#lints_and_stats

@option opts [Array, String] :file_paths lint only specific files or directories

Accepts globs too
which is used to define paths from the URL

@option opts [Config object] :config custom Maximus::Config object @return [void] this method is used to set up instance variables

# File lib/maximus/lint.rb, line 37
def initialize(opts = {})

  # Only run the config once
  @config = opts[:config] || Maximus::Config.new(opts)
  @settings = @config.settings

  @git_files = opts[:git_files]
  @path = opts[:file_paths] || @settings[:file_paths]
  @output = {}
end

Public Instance Methods

refine(data) click to toggle source

Convert raw data into warnings, errors, conventions or refactors. Use this wisely. @param data [Hash] unfiltered lint data @return [Hash] refined lint data and all the other bells and whistles

# File lib/maximus/lint.rb, line 51
def refine(data)
  @task ||= ''

  data = parse_data(data)
  return puts data if data.is_a?(String)

  evaluate_severities(data)

  puts summarize

  if @config.is_dev?
    puts dev_format(data)
    ceiling_warning
  else
    # Because this should be returned in the format it was received
    @output[:raw_data] = data.to_json
  end
  @output
end

Protected Instance Methods

evaluate_severities(data) click to toggle source

Add severities to @output @since 0.1.5 @param data [Hash]

# File lib/maximus/lint.rb, line 127
def evaluate_severities(data)
  @output[:lint_warnings] = []
  @output[:lint_errors] = []
  @output[:lint_conventions] = []
  @output[:lint_refactors] = []
  @output[:lint_fatals] = []

  return if data.blank?

  data.each do |filename, error_list|
    error_list.each do |message|
      # so that :raw_data remains unaffected
      message = message.clone
      message.delete('length')
      message['filename'] = filename.nil? ? '' : strip_working_dir(filename)
      severity = "lint_#{message['severity']}s".to_sym
      message.delete('severity')
      @output[severity] << message if @output.key?(severity)
    end
  end
  @output
end
files_inspected(ext, delimiter = ',', remove = @config.working_dir) click to toggle source

List all files inspected @param ext [String] extension to search for @param delimiter [String] comma or space separated @param remove [String] remove from all file names @return all_files [Array<string>] list of file names

# File lib/maximus/lint.rb, line 79
def files_inspected(ext, delimiter = ',', remove = @config.working_dir)
  @path.is_a?(Array) ? @path.split(delimiter) : file_list(@path, ext, remove)
end
lines_added_to_range(file) click to toggle source

Convert the array from lines_added into spelled-out ranges This is a GitControl helper but it's used in Lint @see GitControl#lines_added @see Lint#relevant_lint

@example typical output

lines_added = {changes: ['0..10', '11..14']}
lines_added_to_range(lines_added)
# output
[0,1,2,3,4,5,6,7,8,9,10, 11,12,13,14]

@return [Hash] changes_array of spelled-out arrays of integers

# File lib/maximus/lint.rb, line 162
def lines_added_to_range(file)
  changes_array = file[:changes].map { |ch| ch.split("..").map(&:to_i) }
  changes_array.map { |e| (e[0]..e[1]).to_a }.flatten!
end
relevant_output(lint, files) click to toggle source

Compare lint output with lines changed in commit @param lint [Hash] output lint data @param files [Hash<String: String>] filename: filepath @return [Array] lints that match the lines in commit

# File lib/maximus/lint.rb, line 87
def relevant_output(lint, files)
  all_files = {}
  files.each do |file|

    # sometimes data will be blank but this is good - it means no errors were raised in the lint
    next if lint.blank? || file.blank? || !file.is_a?(Hash) || !file.key?(:filename)
    lint_file = lint[file[:filename]]

    next if lint_file.blank?

    expanded = lines_added_to_range(file)
    revert_name = strip_working_dir(file[:filename])

    all_files[revert_name] = []

    lint_file.each do |l|
      if expanded.include?(l['line'].to_i)
        all_files[revert_name] << l
      end
    end

    # If there's nothing there, then it definitely isn't a relevant lint
    all_files.delete(revert_name) if all_files[revert_name].blank?
  end
  @output[:files_linted] = all_files.keys
  all_files
end
temp_config(search_for) click to toggle source

Look for a config defined from Config#initialize @since 0.1.2 @param search_for [String] @return [String, Boolean] path to temp file

# File lib/maximus/lint.rb, line 119
def temp_config(search_for)
  return false if @settings.nil?
  @settings[search_for.to_sym].blank? ? false : @settings[search_for.to_sym]
end

Private Instance Methods

ceiling_warning() click to toggle source

If there's just too much to handle, through a warning. @param lint_length [Integer] count of how many lints @return [String] console message to display

# File lib/maximus/lint.rb, line 190
def ceiling_warning
  lint_length = (@output[:lint_errors].length + @output[:lint_warnings].length + @output[:lint_conventions].length + @output[:lint_refactors].length + @output[:lint_fatals].length)
  return unless lint_length > 100

  failed_task = @task.color(:green)
  errors = "#{lint_length} failures.".color(:red)
  errormsg = [
    "You wouldn't stand a chance in Rome.\nResolve thy errors and train with #{failed_task} again.",
    "The gods frown upon you, mortal.\n#{failed_task}. Again.",
    "Do not embarrass the city. Fight another day. Use #{failed_task}.",
    "You are without honor. Replenish it with another #{failed_task}.",
    "You will never claim the throne with a performance like that.",
    "Pompeii has been lost.",
    "A wise choice. Do not be discouraged from another #{failed_task}."
  ].sample
  errormsg << "\n\n"

  go_on = prompt "\n#{errors} Continue? (y/n) "
  abort errormsg unless truthy?(go_on)
end
dev_format(errors = @output[:raw_data]) click to toggle source

Dev display, executed only when called from command line @param errors [Hash] data from lint @return [String] console message to display

# File lib/maximus/lint.rb, line 214
def dev_format(errors = @output[:raw_data])
  return if errors.blank?

  pretty_output = ''
  errors.each do |filename, error_list|
    filename = strip_working_dir(filename)
    pretty_output << "\n#{filename.color(:cyan).underline} \n"
    error_list.each do |message|
      pretty_output << severity_color(message['severity'])
      pretty_output << " #{message['line'].to_s.color(:blue)} #{message['linter'].color(:green)}: #{message['reason']} \n"
    end
  end
  pretty_output << "-----\n\n"
  pretty_output
end
parse_data(data) click to toggle source

Handle data and generate relevant_output if appropriate @since 0.1.6 @see refine @param data [String, Hash] @return [String, Hash] String if error, Hash if success

# File lib/maximus/lint.rb, line 243
def parse_data(data)
  # Prevent abortive empty JSON.parse error
  data = '{}' if data.blank?

  return "Error from #{@task}: #{data}" if data.is_a?(String) && data.include?('No such')

  data = JSON.parse(data) if data.is_a?(String)

  @output[:relevant_output] = relevant_output( data, @git_files ) unless @git_files.blank?
  data = @output[:relevant_output] unless @settings[:commit].blank?
  data
end
severity_color(severity) click to toggle source
# File lib/maximus/lint.rb, line 256
def severity_color(severity)
  case severity
    when 'warning' then 'W'.color(:yellow)
    when 'error' then 'E'.color(:red)
    when 'convention' then 'C'.color(:cyan)
    when 'refactor' then 'R'.color(:white)
    when 'fatal' then 'F'.color(:magenta)
    else '?'.color(:blue)
  end
end
strip_working_dir(path) click to toggle source

String working directory @since 0.1.6 @param path [String] @return [String]

# File lib/maximus/lint.rb, line 234
def strip_working_dir(path)
  path.gsub(@config.working_dir, '')
end
summarize() click to toggle source

Send abbreviated results to console or to the log @return [String] console message to display

# File lib/maximus/lint.rb, line 172
def summarize
  success = @task.color(:green)
  success << ": "
  success << "[#{@output[:lint_warnings].length}]".color(:yellow)
  success << " [#{@output[:lint_errors].length}]".color(:red)
  if @task == 'rubocop'
    success << " [#{@output[:lint_conventions].length}]".color(:cyan)
    success << " [#{@output[:lint_refactors].length}]".color(:white)
    success << " [#{@output[:lint_fatals].length}]".color(:magenta)
  end
  success << "\n#{'Warning'.color(:red)}: #{@output[:lint_errors].length} errors found in #{@task}" if @output[:lint_errors].length > 0

  success
end