class Terminal::Table
Constants
- VERSION
Attributes
headings[R]
title[R]
Public Class Methods
new(options = {})
click to toggle source
Generates a ASCII/Unicode table with the given options.
# File lib/terminal-table/table.rb, line 12 def initialize options = {}, &block @elaborated = false @headings = [] @rows = [] @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 style.on_change(:width) { require_column_widths_recalc } end
Public Instance Methods
==(other)
click to toggle source
Check if other is equal to self. other is considered equal if it contains the same headings and rows.
# File lib/terminal-table/table.rb, line 204 def == other if other.respond_to? :render and other.respond_to? :rows self.headings == other.headings and self.rows == other.rows end end
add_row(array)
click to toggle source
Add a row.
# File lib/terminal-table/table.rb, line 39 def add_row array row = array == :separator ? Separator.new(self) : Row.new(self, array) @rows << row require_column_widths_recalc unless row.is_a?(Separator) end
Also aliased as: <<
add_separator(border_type: :div)
click to toggle source
Add a separator.
# File lib/terminal-table/table.rb, line 49 def add_separator(border_type: :div) @rows << Separator.new(self, border_type: border_type) end
align_column(n, alignment)
click to toggle source
Align column n to the given alignment of :center, :left, or :right.
# File lib/terminal-table/table.rb, line 29 def align_column n, alignment # nil forces the column method to return the cell itself column(n, nil).each do |cell| cell.alignment = alignment unless cell.alignment? end end
cell_padding()
click to toggle source
# File lib/terminal-table/table.rb, line 57 def cell_padding style.padding_left + style.padding_right end
cell_spacing()
click to toggle source
# File lib/terminal-table/table.rb, line 53 def cell_spacing cell_padding + style.border_y_width end
column(n, method = :value, array = rows)
click to toggle source
Return column n.
# File lib/terminal-table/table.rb, line 64 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
column_width(n)
click to toggle source
Return length of column n.
# File lib/terminal-table/table.rb, line 96 def column_width n column_widths[n] || 0 end
Also aliased as: length_of_column
column_with_headings(n, method = :value)
click to toggle source
Return n column including headings.
# File lib/terminal-table/table.rb, line 82 def column_with_headings n, method = :value column n, method, headings_with_rows end
columns()
click to toggle source
Return columns.
# File lib/terminal-table/table.rb, line 89 def columns (0...number_of_columns).map { |n| column n } end
elaborate_rows()
click to toggle source
Elaborate rows to form an Array of Rows and Separators with adjacency properties added.
This is separated from the String rendering so that certain features may be tweaked before the String is built.
# File lib/terminal-table/table.rb, line 126 def elaborate_rows buffer = style.border_top ? [Separator.new(self, border_type: :top, implicit: true)] : [] unless @title.nil? buffer << Row.new(self, [title_cell_options]) buffer << Separator.new(self, implicit: true) end @headings.each do |row| unless row.cells.empty? buffer << row buffer << Separator.new(self, border_type: :double, implicit: true) end end if style.all_separators @rows.each_with_index do |row, idx| # last separator is bottom, others are :div border_type = (idx == @rows.size - 1) ? :bot : :div buffer << row buffer << Separator.new(self, border_type: border_type, implicit: true) end else buffer += @rows buffer << Separator.new(self, border_type: :bot, implicit: true) if style.border_bottom end # After all implicit Separators are inserted we need to save off the # adjacent rows so that we can decide what type of intersections to use # based on column spans in the adjacent row(s). buffer.each_with_index do |r, idx| if r.is_a?(Separator) prev_row = idx > 0 ? buffer[idx - 1] : nil next_row = buffer.fetch(idx + 1, nil) r.save_adjacent_rows(prev_row, next_row) end end @elaborated = true @rows = buffer end
headings=(arrays)
click to toggle source
Set the headings
# File lib/terminal-table/table.rb, line 111 def headings= arrays arrays = [arrays] unless arrays.first.is_a?(Array) @headings = arrays.map do |array| row = Row.new(self, array) require_column_widths_recalc row end end
number_of_columns()
click to toggle source
Return total number of columns available.
# File lib/terminal-table/table.rb, line 104 def number_of_columns headings_with_rows.map { |r| r.number_of_columns }.max || 0 end
render()
click to toggle source
Render the table.
# File lib/terminal-table/table.rb, line 169 def render elaborate_rows unless @elaborated @rows.map { |r| style.margin_left + r.render.rstrip }.join("\n") end
Also aliased as: to_s
rows()
click to toggle source
Return rows without separator rows.
# File lib/terminal-table/table.rb, line 178 def rows @rows.reject { |row| row.is_a? Separator } end
rows=(array)
click to toggle source
# File lib/terminal-table/table.rb, line 182 def rows= array @rows = [] array.each { |arr| self << arr } end
style()
click to toggle source
# File lib/terminal-table/table.rb, line 191 def style @style ||= Style.new end
style=(options)
click to toggle source
# File lib/terminal-table/table.rb, line 187 def style=(options) style.apply options end
title=(title)
click to toggle source
# File lib/terminal-table/table.rb, line 195 def title=(title) @title = title require_column_widths_recalc end
Private Instance Methods
column_widths()
click to toggle source
# File lib/terminal-table/table.rb, line 367 def column_widths recalc_column_widths if @require_column_widths_recalc @column_widths end
columns_width()
click to toggle source
# File lib/terminal-table/table.rb, line 212 def columns_width column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y_width end
headings_with_rows()
click to toggle source
Return headings combined with rows.
# File lib/terminal-table/table.rb, line 346 def headings_with_rows @headings + rows end
recalc_column_widths()
click to toggle source
# File lib/terminal-table/table.rb, line 216 def recalc_column_widths @require_column_widths_recalc = false n_cols = number_of_columns space_width = cell_spacing return if n_cols == 0 # prepare rows all_rows = headings_with_rows all_rows << Row.new(self, [title_cell_options]) unless @title.nil? # DP states, dp[colspan][index][split_offset] => column_width. dp = [] # prepare initial value for DP. all_rows.each do |row| index = 0 row.cells.each do |cell| cell_value = cell.value_for_column_width_recalc cell_width = Unicode::DisplayWidth.of(cell_value.to_s) colspan = cell.colspan # find column width from each single cell. dp[colspan] ||= [] dp[colspan][index] ||= [0] # add a fake cell with length 0. dp[colspan][index][colspan] ||= 0 # initialize column length to 0. # the last index `colspan` means width of the single column (split # at end of each column), not a width made up of multiple columns. single_column_length = [cell_width, dp[colspan][index][colspan]].max dp[colspan][index][colspan] = single_column_length index += colspan end end # run DP. (1..n_cols).each do |colspan| dp[colspan] ||= [] (0..n_cols-colspan).each do |index| dp[colspan][index] ||= [1] (1...colspan).each do |offset| # processed level became reverse map from width => [offset, ...]. left_colspan = offset left_index = index left_width = dp[left_colspan][left_index].keys.first right_colspan = colspan - left_colspan right_index = index + offset right_width = dp[right_colspan][right_index].keys.first dp[colspan][index][offset] = left_width + right_width + space_width end # reverse map it for resolution (max width and short offset first). rmap = {} dp[colspan][index].each_with_index do |width, offset| rmap[width] ||= [] rmap[width] << offset end # sort reversely and store it back. dp[colspan][index] = Hash[rmap.sort.reverse] end end resolve = lambda do |colspan, full_width, index = 0| # stop if reaches the bottom level. return @column_widths[index] = full_width if colspan == 1 # choose best split offset for partition, or second best result # if first one is not dividable. candidate_offsets = dp[colspan][index].collect(&:last).flatten offset = candidate_offsets[0] offset = candidate_offsets[1] if offset == colspan # prepare for next round. left_colspan = offset left_index = index left_width = dp[left_colspan][left_index].keys.first right_colspan = colspan - left_colspan right_index = index + offset right_width = dp[right_colspan][right_index].keys.first # calculate reference column width, give remaining spaces to left. total_non_space_width = full_width - (colspan - 1) * space_width ref_column_width = total_non_space_width / colspan remainder = total_non_space_width % colspan rem_left_width = [remainder, left_colspan].min rem_right_width = remainder - rem_left_width ref_left_width = ref_column_width * left_colspan + (left_colspan - 1) * space_width + rem_left_width ref_right_width = ref_column_width * right_colspan + (right_colspan - 1) * space_width + rem_right_width # at most one width can be greater than the reference width. if left_width <= ref_left_width and right_width <= ref_right_width # use refernce width (evenly partition). left_width = ref_left_width right_width = ref_right_width else # the wider one takes its value, shorter one takes the rest. if left_width > ref_left_width right_width = full_width - left_width - space_width else left_width = full_width - right_width - space_width end end # run next round. resolve.call(left_colspan, left_width, left_index) resolve.call(right_colspan, right_width, right_index) end full_width = dp[n_cols][0].keys.first unless style.width.nil? new_width = style.width - space_width - style.border_y_width if new_width < full_width raise "Table width exceeds wanted width " + "of #{style.width} characters." end full_width = new_width end resolve.call(n_cols, full_width) end
require_column_widths_recalc()
click to toggle source
# File lib/terminal-table/table.rb, line 363 def require_column_widths_recalc @require_column_widths_recalc = true end
title_cell_options()
click to toggle source
# File lib/terminal-table/table.rb, line 359 def title_cell_options {:value => @title, :alignment => :center, :colspan => number_of_columns} end
yield_or_eval() { |self| ... }
click to toggle source
# File lib/terminal-table/table.rb, line 350 def yield_or_eval &block return unless block if block.arity > 0 yield self else self.instance_eval(&block) end end