class ConsoleTable::ConsoleTableClass

Attributes

Public Class Methods

new(column_layout, options={}) click to toggle source
# File lib/console_table.rb, line 31
def initialize(column_layout, options={})
  @original_column_layout = []

  if column_layout.is_a? Integer
    column_layout = (1..column_layout).collect{|i| "Column #{i}"}
  end

  if column_layout.is_a? Array
    column_layout.each_with_index do |layout, i|
      if layout.is_a? String
        @original_column_layout << {:key => "col#{i+1}".to_sym, :title=>layout, :size=>"*"}
      elsif layout.is_a? Integer
        @original_column_layout << {:key => "col#{i+1}".to_sym, :title=>"Column #{i+1}", :size=>layout}
      elsif layout.is_a? Float
        @original_column_layout << {:key => "col#{i+1}".to_sym, :title=>"Column #{i+1}", :size=>layout}
      elsif layout[:key].nil? and layout[:title].nil?
        @original_column_layout << layout.merge({:key => "col#{i+1}".to_sym, :title=>"Column #{i+1}"})
      elsif layout[:title].nil?
        @original_column_layout << layout.merge({:title => layout[:key].to_s.capitalize})
      elsif layout[:key].nil?
        @original_column_layout << layout.merge({:key => "col#{i+1}".to_sym})
      else
        @original_column_layout << layout
      end
    end
  else
    raise("Column layout invalid, must be a num of columns or an array of column definitions")
  end

  #Mostly used for mocking/testing
  @out = options[:output] || $stdout
  @set_width = options[:width]

  @title = options[:title]
  @borders = options[:borders] || false #Lines between every cell, implies outline
  @left_margin = options[:left_margin] || 0
  @right_margin = options[:right_margin] || 0
  @headings = options[:headings].nil? ? true : options[:headings]
  @ellipse = options[:ellipse] || "..."

  #Set outline, just the upper and lower lines
  if @borders
    @outline = true
  elsif not options[:outline].nil?
    @outline = options[:outline]
  else
    @outline = true
  end

  @footer = []
  @headings_printed = false
  @count = 0

  calc_column_widths
  Signal.trap('SIGWINCH', proc { calc_column_widths })
end

Public Instance Methods

<<(options) click to toggle source

Add rows to the table. Prints immediately, consult documentation for examples

# File lib/console_table.rb, line 25
def <<(options)
  print(options)
end

Protected Instance Methods

print_header() click to toggle source

Private Instance Methods

calc_column_widths() click to toggle source
# File lib/console_table.rb, line 276
def calc_column_widths()
  @column_widths = []

  total_width = @set_width
  begin
    total_width = TermInfo.screen_columns
  rescue => ex
    total_width = ENV["COLUMNS"].to_i unless ENV["COLUMNS"].nil?
    total_width = 79 if total_width.nil?
  end if total_width.nil?

  keys = @original_column_layout.collect { |d| d[:key] }.uniq
  if keys.length < @original_column_layout.length
    raise("ConsoleTable configuration invalid, same key defined more than once")
  end

  num_spacers = @original_column_layout.length - 1
  num_spacers = num_spacers + 2 if @borders
  set_sizes = @original_column_layout.collect { |x| x[:size] }.find_all { |x| x.is_a? Integer }
  used_up = set_sizes.inject(:+) || 0
  available = total_width - used_up - @left_margin - @right_margin - num_spacers

  if available < 0
    raise("ConsoleTable configuration invalid, current window is too small to display required sizes")
  end

  percentages = @original_column_layout.collect { |x| x[:size] }.find_all { |x| x.is_a? Float }
  percent_used = percentages.inject(:+) || 0

  if percent_used > 1.0
    raise("ConsoleTable configuration invalid, percentages total value greater than 100%")
  end

  percent_available = 1 - percent_used
  stars = @original_column_layout.collect { |x| x[:size] or x[:size].nil? }.find_all { |x| x.is_a? String }
  num_stars = [stars.length, 1].max
  percent_for_stars = percent_available.to_f / num_stars

  @original_column_layout.each do |column_config|
    if column_config[:size].is_a? Integer
      @column_widths << column_config #As-is when integer
    elsif column_config[:size].is_a? Float
      @column_widths << column_config.merge({:size => (column_config[:size]*available).floor})
    elsif column_config[:size].nil? or column_config[:size].is_a?(String) && column_config[:size] == "*"
      @column_widths << column_config.merge({:size => (percent_for_stars*available).floor})
    else
      raise("ConsoleTable configuration invalid, '#{column_config[:size]}' is not a valid size")
    end
  end

  @working_width = (@column_widths.inject(0) { |res, c| res+c[:size] }) + @column_widths.length - 1
end
format(length, text, ellipsize=false, justify=:left) click to toggle source
# File lib/console_table.rb, line 334
def format(length, text, ellipsize=false, justify=:left)
  uncolorized = uncolorize(text)
  if uncolorized.length > length

    ellipsize = false if uncolorize(@ellipse).length >= length

    if ellipsize
      goal_length = length-uncolorize(@ellipse).length
    else
      goal_length = length
    end

    parts = text.scan(/(\e\[\d.*?m)|(.)/) #The idea here is to break the string up into control codes and single characters
    #We're going to now count up until we hit goal length, but we're not going to ever count control codes against the count
    #We're also going to keep track of if a non-resetting control code is 'active', so we know to reset at the end if need-be

    #I can't think of a better way to do this, it's probably dumb
    current_length = 0
    current_index = 0
    final_string_parts = []
    color_active = false

    while current_length < goal_length
      color_code, regular_text = parts[current_index]
      if not regular_text.nil?
        current_length = current_length + 1
        final_string_parts << regular_text
      elsif not color_code.nil?
        if color_code == "\e[0m"
          color_active = false if color_active
        else
          color_active = true
        end
        final_string_parts << color_code
      end
      current_index = current_index + 1
    end

    final_string_parts << @ellipse if ellipsize
    final_string_parts << "\e[0m" if color_active

    final_string_parts.join("")
  else
    space = length-uncolorized.length
    if justify == :right
      (" "*space) + text
    elsif justify == :center
      left_side = space/2
      right_side = space - left_side
      (" " * left_side) + text + (" "*right_side)
    else #assume left
      text + (" "*space)
    end
  end
end
infer_justify_from_string(to_print, justify) click to toggle source
# File lib/console_table.rb, line 252
def infer_justify_from_string(to_print, justify)
  uncolorized = uncolorize(to_print)
  if uncolorized.start_with?("\t") and uncolorized.end_with?("\t")
    justify = :center
  elsif uncolorized.start_with?("\t")
    justify = :right
  elsif uncolorized.end_with?("\t")
    justify = :left
  end
  justify
end
normalize(string) click to toggle source
# File lib/console_table.rb, line 264
def normalize(string)
  if string.nil?
    nil
  else
    normalized = string.to_s
    normalized = normalized.sub(/^(\e\[\d[^m]*?m)(\s+)/, '\2\1') #Any leading spaces preceeded by a color code should be swapped with the color code itself, so the spaces themselves aren't colored
    normalized = normalized.sub(/(\s+)(\e\[\d[^m]*?m)$/, '\2\1')
    normalized = normalized.gsub(/\s+/, " ").strip #Primarily to remove any tabs or newlines
    normalized
  end
end
print(options) click to toggle source
print_headings() click to toggle source
print_line(char="-", join_char="+", edge_join_only=false) click to toggle source
print_plain(to_print) click to toggle source
uncolorize(string) click to toggle source
# File lib/console_table.rb, line 330
def uncolorize(string)
  string.gsub(/\e\[\d[^m]*?m/m, "")
end