codeclimate-community/codeclimate-foodcritic

View on GitHub
lib/cc/engine.rb

Summary

Maintainability
A
0 mins
Test Coverage
require "foodcritic"
require "json"
require "yaml"

module CC
  module Engine
    def self.each_issue
      rules = Rules.new

      warnings.each do |lint|
        yield Issue.new(lint, rules)
      end
    end

    def self.warnings
      config = Config.new
      options = {
        cookbook_paths: config.cookbook_paths,
        include_rules: config.include_rules,
        progress: false,
        tags: config.tags,
      }

      $stderr.puts "foodcritic options: #{options.inspect}"
      linter = FoodCritic::Linter.new
      linter.check(options).warnings
    end

    class Config
      DEFAULT_INCLUDE_PATHS = ["./"]
      DEFAULT_TAGS = ["~FC011", "~FC033"]

      def initialize(path = "/config.json")
        if File.exist?(path)
          @config = JSON.parse(File.read(path))
        else
          @config = {}
        end
      end

      def cookbook_paths
        engine_config.fetch("cookbook_paths") { expand_include_paths }
      end

      def include_rules
        engine_config.fetch("include_rules", [])
      end

      def tags
        engine_config.fetch("tags", DEFAULT_TAGS)
      end

      private

      attr_reader :config

      def engine_config
        config.fetch("config", {})
      end

      def expand_include_paths
        include_paths = config.fetch("include_paths", DEFAULT_INCLUDE_PATHS)
        include_paths.flat_map do |path|
          if path.end_with?("/")
            Dir.glob("#{path}**/*.rb")
          elsif path.end_with?(".rb")
            [path]
          else
            []
          end
        end
      end
    end

    class Rules
      def initialize(path = "/rules.yml")
        if File.exist?(path)
          @config = YAML.load_file(path)
        else
          @config = {}
        end
      end

      def summary(code)
        @config.fetch(code, {}).fetch("summary", "")
      end

      def categories(code)
        @config.fetch(code, {}).fetch("categories", ["Style"])
      end

      def remediation_points(code)
        @config.fetch(code, {}).fetch("remediation_points", 50_000)
      end
    end

    class Issue
      def initialize(lint, rules)
        @lint = lint
        @rules = rules
      end

      def filename
        lint.match[:filename]
      end

      def to_json
        {
          "type" => "issue",
          "check_name" => "FoodCritic/#{lint.rule.code}",
          "description" => lint.rule.name,
          "categories" => rules.categories(lint.rule.code),
          "location" => {
            "path" => filename,
            "lines" => {
              "begin" => lint.match[:line],
              "end" => lint.match[:line],
            }
          },
          "content" => { "body" => rules.summary(lint.rule.code) },
          "remediation_points" => rules.remediation_points(lint.rule.code),
        }.to_json
      end

      private

      attr_reader :lint, :rules
    end
  end
end