class Terminal::Table
Constants
- VERSION
Attributes
headings[R]
title[R]
Public Class Methods
new(options = {})
click to toggle source
Generates a ASCII table with the given options.
# File lib/terminal-table/table.rb, line 12 def initialize options = {}, &block @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 175 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()
click to toggle source
Add a separator.
# File lib/terminal-table/table.rb, line 49 def add_separator self << :separator 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 28 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
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.length 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
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 123 def render separator = Separator.new(self) buffer = style.border_top ? [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 if style.border_bottom end buffer.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 149 def rows @rows.reject { |row| row.is_a? Separator } end
rows=(array)
click to toggle source
# File lib/terminal-table/table.rb, line 153 def rows= array @rows = [] array.each { |arr| self << arr } end
style()
click to toggle source
# File lib/terminal-table/table.rb, line 162 def style @style ||= Style.new end
style=(options)
click to toggle source
# File lib/terminal-table/table.rb, line 158 def style=(options) style.apply options end
title=(title)
click to toggle source
# File lib/terminal-table/table.rb, line 166 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 338 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 183 def columns_width column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y.length end
headings_with_rows()
click to toggle source
Return headings combined with rows.
# File lib/terminal-table/table.rb, line 317 def headings_with_rows @headings + rows end
recalc_column_widths()
click to toggle source
# File lib/terminal-table/table.rb, line 187 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.length 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 334 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 330 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 321 def yield_or_eval &block return unless block if block.arity > 0 yield self else self.instance_eval(&block) end end