class Terminal::Table
def == other
def == other if other.respond_to? :render and other.respond_to? :rows self.headings == other.headings and self.rows == other.rows end end
def add_row array
def add_row array row = array == :separator ? Separator.new(self) : Row.new(self, array) @rows << row recalc_column_widths row end
def add_separator
def add_separator self << :separator end
def additional_column_widths
def additional_column_widths return [] if style.width.nil? spacing = style.width - columns_width if spacing < 0 raise "Table width exceeds wanted width of #{style.width} characters." else per_col = spacing / number_of_columns arr = (1...number_of_columns).to_a.map { |i| per_col } other_cols = arr.inject(0) { |s, i| s + i } arr << spacing - other_cols arr end end
def align_column n, alignment
def align_column n, alignment r = rows column(n).each_with_index do |col, i| cell = r[i][n] cell.alignment = alignment unless cell.alignment? end end
def cell_padding
def cell_padding style.padding_left + style.padding_right end
def cell_spacing
def cell_spacing cell_padding + style.border_y.length end
def column n, method = :value, array = rows
def column n, method = :value, array = rows array.map { |row| # for each cells in a row, find the column with index # just greater than the required one, and go back one. index = col = 0 row.cells.each do |cell| break if index > n index += cell.colspan col += 1 end cell = row[col - 1] cell && method ? cell.__send__(method) : cell }.compact end
def column_width n
def column_width n width = @column_widths[n] || 0 width + additional_column_widths[n].to_i end
def column_with_headings n, method = :value
def column_with_headings n, method = :value column n, method, headings_with_rows end
def columns
def columns (0...number_of_columns).map { |n| column n } end
def columns_width
def columns_width @column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y.length end
def headings= arrays
def headings= arrays arrays = [arrays] unless arrays.first.is_a?(Array) @headings = arrays.map do |array| row = Row.new(self, array) recalc_column_widths row row end end
def headings_with_rows
def headings_with_rows @headings + rows end
def initialize options = {}, &block
def initialize options = {}, &block @column_widths = [] self.style = options.fetch :style, {} self.headings = options.fetch :headings, [] self.rows = options.fetch :rows, [] self.title = options.fetch :title, nil yield_or_eval(&block) if block end
def number_of_columns
def number_of_columns headings_with_rows.map { |r| r.cells.size }.max end
def recalc_column_widths row
def recalc_column_widths row return if row.is_a? Separator i = 0 row.cells.each do |cell| colspan = cell.colspan cell_value = cell.value_for_column_width_recalc colspan.downto(1) do |j| cell_length = Unicode::DisplayWidth.of(cell_value.to_s) if colspan > 1 spacing_length = cell_spacing * (colspan - 1) length_in_columns = (cell_length - spacing_length) cell_length = (length_in_columns.to_f / colspan).ceil end if @column_widths[i].to_i < cell_length @column_widths[i] = cell_length end i = i + 1 end end end
def render
def render separator = Separator.new(self) buffer = [separator] unless @title.nil? buffer << Row.new(self, [title_cell_options]) buffer << separator end @headings.each do |row| unless row.cells.empty? buffer << row buffer << separator end end if style.all_separators buffer += @rows.product([separator]).flatten else buffer += @rows buffer << separator end buffer.map { |r| style.margin_left + r.render.rstrip }.join("\n") end
def rows
def rows @rows.reject { |row| row.is_a? Separator } end
def rows= array
def rows= array @rows = [] array.each { |arr| self << arr } end
def style
def style @style ||= Style.new end
def style=(options)
def style=(options) style.apply options end
def title=(title)
def title=(title) @title = title recalc_column_widths Row.new(self, [title_cell_options]) end
def title_cell_options
def title_cell_options {:value => @title, :alignment => :center, :colspan => number_of_columns} end
def yield_or_eval &block
def yield_or_eval &block return unless block if block.arity > 0 yield self else self.instance_eval(&block) end end