1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
require 'ansi/core'
require 'ansi/terminal'
module ANSI
class Table
# The Table class can be used to output nicely formatted
# tables with division lines and alignment.
#
# table - array of array
#
# options[:align] - align :left or :right
# options[:padding] - space to add to each cell
# options[:fit] - fit to screen width
# options[:border] -
#
# The +format+ block must return ANSI codes to apply
# to each cell.
#
# Other Implementations:
#
# * http://github.com/visionmedia/terminal-table
# * http://github.com/aptinio/text-table
#
# TODO: Support for table headers and footers.
def initialize(table, options={}, &format)
@table = table
@padding = options[:padding] || 0
@align = options[:align]
@fit = options[:fit]
@border = options[:border]
#@ansi = [options[:ansi]].flatten
@format = format
@pad = " " * @padding
end
#
attr_accessor :table
# Fit to scree width.
attr_accessor :fit
#
attr_accessor :padding
#
attr_accessor :align
#
attr_accessor :format
#
attr_accessor :border
#
def to_s #(fit=false)
#row_count = table.size
#col_count = table[0].size
max = max_columns(fit)
div = dividing_line
top = div #.gsub('+', ".")
bot = div #.gsub('+', "'")
body = []
table.each_with_index do |row, r|
body_row = []
row.each_with_index do |cell, c|
t = cell_template(max[c])
s = t % cell.to_s
body_row << apply_format(s, cell, c, r)
end
body << "| " + body_row.join(' | ') + " |"
end
if border
body = body.join("\n#{div}\n")
else
body = body.join("\n")
end
"#{top}\n#{body}\n#{bot}\n"
end
private
# TODO: look at the lines and figure out how many columns will fit
def fit_width
width = Terminal.terminal_width
((width.to_f / column_size) - (padding + 3)).to_i
end
# Calculate the maximun column sizes.
#
# @return [Array] maximum size for each column
def max_columns(fit=false)
max = Array.new(column_size, 0)
table.each do |row|
row.each_with_index do |col, index|
col = col.to_s
col = col.unansi
if fit
max[index] = [max[index], col.size, fit_width].max
else
max[index] = [max[index], col.size].max
end
end
end
max
end
# Number of columns based on the first row of table.
#
# @return [Integer] number of columns
def column_size
table.first.size
end
#
def cell_template(max)
case align
when :right, 'right'
"#{@pad}%#{max}s"
else
"%-#{max}s#{@pad}"
end
end
# TODO: make more efficient
def dividing_line
tmp = max_columns(fit).map{ |m| "%#{m}s" }.join(" | ")
tmp = "| #{tmp} |"
lin = (tmp % (['-'] * column_size)).gsub(/[^\|]/, '-').gsub('|', '+')
lin
end
#def dividing_line_top
# dividing_line.gsub('+', '.')
#end
#def dividing_line_bottom
# dividing_line.gsub('+', "'")
#end
#
def apply_format(str, cell, col, row)
if @format
str.ansi(*ansi_formating(cell, col, row))
else
str
end
end
#
def ansi_formating(cell, col, row)
if @format
case @format.arity
when 0
f = @format[]
when 1
f = @format[cell]
when 2
f = @format[row, col]
else
f = @format[cell, row, col]
end
else
f = nil
end
[f].flatten.compact
end
end
end
|