wbailey/command_line_reporter

View on GitHub
lib/command_line_reporter/table.rb

Summary

Maintainability
A
35 mins
Test Coverage
module CommandLineReporter
  class Table
    include OptionsValidator

    VALID_OPTIONS = %i[border width encoding].freeze
    attr_accessor :rows, *VALID_OPTIONS

    def initialize(options = {})
      validate_options(options, *VALID_OPTIONS)

      self.border = options[:border] || false
      self.width = options[:width] || false
      self.encoding = options[:encoding] || CommandLineReporter::DEFAULTS[:encoding]

      @rows = []

      raise ArgumentError, 'Invalid encoding' unless %i[ascii unicode].include? encoding
    end

    def add(row)
      # Inheritance from the table
      row.border = border

      # Inherit properties from the appropriate row
      inherit_column_attrs(row) if rows[0]

      rows << row
    end

    # rubocop:disable Metrics/AbcSize
    # rubocop:disable Metrics/CyclomaticComplexity
    # rubocop:disable Metrics/MethodLength
    def output
      return if rows.empty? # we got here with nothing to print to the screen
      auto_adjust_widths if width == :auto

      puts separator('first') if border

      rows.each_with_index do |row, index|
        row.output
        puts separator('middle') if border && (index != rows.size - 1)
      end

      puts separator('last') if border
    end

    # TODO: This doesn't appear to be used and if it is, it will not handle span appropriately
    def auto_adjust_widths
      column_widths = []

      rows.each do |row|
        row.columns.each_with_index do |col, i|
          column_widths[i] = [col.required_width, (column_widths[i] || 0)].max
        end
      end

      rows.each do |row|
        row.columns.each_with_index do |col, i|
          col.width = column_widths[i]
        end
      end
    end

    private

    def separator(type = 'middle')
      left, center, right, bar = use_utf8? ? utf8_separator(type) : ascii_separator

      left + rows[0].columns.map { |c| bar * (c.width + 2) }.join(center) + right
    end

    def use_utf8?
      encoding == :unicode && "\u2501" != 'u2501'
    end

    def ascii_separator
      left = right = center = '+'
      bar = '-'
      [left, right, center, bar]
    end

    def utf8_separator(type)
      bar = "\u2501"

      left, center, right = case type
                            when 'first'
                              ["\u250F", "\u2533", "\u2513"]
                            when 'middle'
                              ["\u2523", "\u254A", "\u252B"]
                            when 'last'
                              ["\u2517", "\u253B", "\u251B"]
                            end

      [left, center, right, bar]
    end

    def inherit_column_attrs(row)
      row.columns.each_with_index do |c, i|
        use_positional_attrs(c, i)
        use_color(row, c, i)
        use_bold(row, c, i)
      end
    end

    def use_positional_attrs(c, i)
      return if c.span > 1

      # The positional attributes are always required to inherit to make sure the table
      # displays properly
      %w[align padding width].each do |attr|
        val = rows[0].columns[i].send(attr)
        c.send(attr + '=', val)
      end

      # spanning columns overrides inheritance for width
      # val = rows[0].columns[i].width
      # c.width = val unless c.span > 1
    end

    def inherit_from
      rows[0].header ? 1 : 0
    end

    def use_color(row, c, i)
      if c.color
        # keep default
      elsif row.color
        c.color = row.color
      elsif inherit_from != 1
        c.color = rows[inherit_from].columns[i].color
      end
    end

    def use_bold(row, c, i)
      if row.bold
        c.bold = row.bold
      elsif inherit_from != 1
        c.bold = rows[inherit_from].columns[i].bold
      end
    end
  end
end