fastruby/skunk

View on GitHub
lib/skunk/commands/status_reporter.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require "erb"
require "rubycritic/commands/status_reporter"
require "terminal-table"

module Skunk
  module Command
    # Knows how to report status for stinky files
    class StatusReporter < RubyCritic::Command::StatusReporter
      attr_accessor :analysed_modules

      HEADINGS = %w[file skunk_score churn_times_cost churn cost coverage].freeze
      HEADINGS_WITHOUT_FILE = HEADINGS - %w[file]
      HEADINGS_WITHOUT_FILE_WIDTH = HEADINGS_WITHOUT_FILE.size * 17 # padding

      TEMPLATE = ERB.new(<<-TEMPL
<%= _ttable %>\n
SkunkScore Total: <%= total_skunk_score %>
Modules Analysed: <%= analysed_modules_count %>
SkunkScore Average: <%= skunk_score_average %>
<% if worst %>Worst SkunkScore: <%= worst.skunk_score %> (<%= worst.pathname %>)<% end %>

Generated with Skunk v<%= Skunk::VERSION %>
TEMPL
                        )

      # Returns a status message with a table of all analysed_modules and
      # a skunk score average
      def update_status_message
        opts = table_options.merge(headings: HEADINGS, rows: table)

        _ttable = Terminal::Table.new(opts)

        @status_message = TEMPLATE.result(binding)
      end

      private

      def analysed_modules_count
        @analysed_modules_count ||= non_test_modules.count
      end

      def non_test_modules
        @non_test_modules ||= analysed_modules.reject do |a_module|
          module_path = a_module.pathname.dirname.to_s
          module_path.start_with?("test", "spec") || module_path.end_with?("test", "spec")
        end
      end

      def worst
        @worst ||= sorted_modules.first
      end

      def sorted_modules
        @sorted_modules ||= non_test_modules.sort_by(&:skunk_score).reverse!
      end

      def total_skunk_score
        @total_skunk_score ||= non_test_modules.sum(&:skunk_score)
      end

      def total_churn_times_cost
        non_test_modules.sum(&:churn_times_cost)
      end

      def skunk_score_average
        return 0 if analysed_modules_count.zero?

        (total_skunk_score.to_d / analysed_modules_count).to_f.round(2)
      end

      def table_options
        max = sorted_modules.max_by { |a_mod| a_mod.pathname.to_s.length }
        width = max.pathname.to_s.length + HEADINGS_WITHOUT_FILE_WIDTH
        {
          style: {
            width: width
          }
        }
      end

      def table
        sorted_modules.map do |a_mod|
          [
            a_mod.pathname,
            a_mod.skunk_score,
            a_mod.churn_times_cost,
            a_mod.churn,
            a_mod.cost.round(2),
            a_mod.coverage.round(2)
          ]
        end
      end
    end
  end
end