Class: Tabulo::Table

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/tabulo/table.rb

Overview

Represents a table primarily intended for "pretty-printing" in a fixed-width font.

A Table is also an Enumerable, of which each element is a Row.

Constant Summary collapse

DEFAULT_BORDER =
:ascii
DEFAULT_COLUMN_WIDTH =
12
DEFAULT_COLUMN_PADDING =
1
DEFAULT_TRUNCATION_INDICATOR =
"~"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sources, *columns, align_body: :auto, align_header: :center, align_title: :center, border: nil, border_styler: nil, column_padding: nil, column_width: nil, formatter: :to_s.to_proc, header_frequency: :start, header_styler: nil, row_divider_frequency: nil, styler: nil, title: nil, title_styler: nil, truncation_indicator: nil, wrap_preserve: :rune, wrap_body_cells_to: nil, wrap_header_cells_to: nil) {|_self| ... } ⇒ Table

Returns a new Tabulo::Table.

Parameters:

  • sources (Enumerable)

    the underlying Enumerable from which the table will derive its data

  • columns (Array[Symbol])

    Specifies the initial columns. The Symbols provided must be unique. Each element of the Array will be used to create a column whose content is created by calling the corresponding method on each element of sources. Note the #add_column method is a much more flexible way to set up columns on the table.

  • align_body (:left, :right, :center, :auto) (defaults to: :auto)

    Determines the alignment of body cell (i.e. non-header) content within columns in this Table. Can be overridden for individual columns using the align_body option passed to #add_column. If passed :auto, alignment is determined by cell content, with numbers aligned right, booleans center-aligned, and other values left-aligned.

  • align_header (:left, :right, :center) (defaults to: :center)

    Determines the alignment of header text for columns in this Table. Can be overridden for individual columns using the align_header option passed to #add_column

  • align_header (:left, :right, :center) (defaults to: :center)

    Determines the alignment of the table title, if present.

  • border (:ascii, :markdown, :modern, :blank, nil) (defaults to: nil)

    Determines the characters used for the Table border, including both the characters around the outside of table, and the lines drawn within the table to separate columns from each other and the header row from the Table body. If nil, then the value of DEFAULT_BORDER will be used. Possible values are:

    • :ascii Uses ASCII characters only
    • :markdown Produces a GitHub-flavoured Markdown table. Note: Using the title option in combination with this border type will cause the rendered table not to be valid Markdown, since Markdown engines do not generally support adding a caption element (i.e. title) to tables.
    • :modern Uses non-ASCII Unicode characters to render a border with smooth continuous lines
    • :blank No border characters are rendered
    • :reduced_ascii Like :ascii, but without left or right borders, and with internal vertical borders and intersection characters consisting of whitespace only
    • :reduced_modern Like :modern, but without left or right borders, and with internal vertical borders and intersection characters consisting of whitespace only
    • :classic Like :ascii, but does not have a horizontal line at the bottom of the table. This reproduces the default behaviour in tabulo v1.
  • border_styler (nil, #to_proc) (defaults to: nil)

    A lambda or other callable object taking a single parameter, representing a section of the table's borders (which for this purpose include any horizontal and vertical lines inside the table), and returning a string. If passed nil, then no additional styling will be applied to borders. If passed a callable, then that callable will be called for each border section, with the resulting string rendered in place of that border. The extra width of the string returned by the border_styler is not taken into consideration by the internal table rendering calculations Thus it can be used to apply ANSI escape codes to border characters, to colour the borders for example, without breaking the table formatting.

  • column_padding (nil, Integer, Array) (defaults to: nil)

    Determines the amount of blank space with which to pad either side of each column. If passed an Integer, then the given amount of padding is applied to each side of each column. If passed a two-element Array, then the first element of the Array indicates the amount of padding to apply to the left of each column, and the second element indicates the amount to apply to the right. This setting can be overridden for individual columns using the padding option of #add_column.

  • column_width (Integer, nil) (defaults to: nil)

    The default column width for columns in this table, not excluding padding. If nil, then DEFAULT_COLUMN_WIDTH will be used.

  • formatter (nil, #to_proc) (defaults to: :to_s.to_proc)

    The default formatter for columns in this table. See formatter option of #add_column for details.

  • header_frequency (:start, nil, Integer) (defaults to: :start)

    (:start) Controls the display of column headers. If passed :start, headers will be shown at the top of the table only. If passed nil, headers will not be shown. If passed an Integer N (> 0), headers will be shown at the top of the table, then repeated every N rows.

  • header_styler (nil, #to_proc) (defaults to: nil)

    The default header styler for columns in this table. See header_styler option of #add_column for details.

  • row_divider_frequency (nil, Integer) (defaults to: nil)

    Controls the display of horizontal row dividers within the table body. If passed nil, dividers will not be shown. If passed an Integer N (> 0), dividers will be shown after every N rows. The characters used to form the dividers are determined by the border option, and are the same as those used to form the bottom edge of the header row.

  • styler (nil, #to_proc) (defaults to: nil)

    The default styler for columns in this table. See styler option of #add_column for details.

  • title (nil, String) (defaults to: nil)

    If passed a String, will arrange for a title to be shown at the top of the table. Note: If the border option is set to :markdown, adding a title to the table will cause it to cease being valid Markdown when rendered, since Markdown engines do not generally support adding a caption element (i.e. title) to tables.

  • title_styler (nil, #to_proc) (defaults to: nil)

    A lambda or other callable object that will determine the colors or other styling applied to the table title. Can be passed nil, or can be passed a callable that takes either 1 or 2 parametes:

    • If passed nil, then no additional styling will be applied to the title.
    • If passed a callable, then that callable will be called for each line of the title, and the resulting string rendered in place of that line. The extra width of the string returned by the title_styler is not taken into consideration by the internal table and cell width calculations involved in rendering the table. Thus it can be used to apply ANSI escape codes to title content, to color the content for example, without breaking the table formatting.
      • If the passed callable takes 1 parameter, then the first parameter is a string representing a single line within the title. For example, if the title is wrapped over three lines, then the title_styler will be called three times, once for each line of content.
      • If the passed callable takes 2 parameters, then the first parameter is as above, and the second parameter is an Integer representing the index of the line within the title that is currently being styled. For example, if the title is wrapped over 3 lines, then the callable will be called first with a line index of 0, to style the first line, then with a line index of 1, to style the second line, and finally with a line index of 2, for the third and final wrapped line of the cell.
  • truncation_indicator (nil, String) (defaults to: nil)

    Determines the character used to indicate that a cell's content has been truncated. If omitted or passed nil, defaults to DEFAULT_TRUNCATION_INDICATOR. If passed something other than nil or a single-character String, raises InvalidTruncationIndicatorError.

  • wrap_preserve (Symbol) (defaults to: :rune)

    Specifies what unit of text the wrapping mechanism will try to preserve intact when wrapping column content when the column width is reached:

    • If passed :rune (the default), then it will wrap at the "character" level (approximately speaking, the Unicode grapheme cluster level). This means the maximum number of what readers usually think of as "characters" will be fit on each line, within the column's allocated width, before contininuing to a new line, even if it means splitting a word in the middle.
    • If passed :word, then it will wrap in such a way as to avoid splitting words, where "words" are defined as units of text separated by spaces or dashes (hyphens, m-dashes and n-dashes). Whitespace will be used to pad lines as required. Already-hyphenated may will be split at the hyphen, however hyphens will not be inserted in non-hyphenated words.
  • wrap_body_cells_to (nil, Integer) (defaults to: nil)

    Controls wrapping behaviour for table cells (excluding headers), if their content is longer than the column's fixed width. If passed nil, content will be wrapped for as many rows as required to accommodate it. If passed an Integer N (> 0), content will be wrapped up to N rows and then truncated thereafter. headers), if their content is longer than the column's fixed width. If passed nil, content will be wrapped for as many rows as required to accommodate it. If passed an Integer N (> 0), content will be wrapped up to N rows and then truncated thereafter.

  • wrap_header_cells_to (nil, Integer) (defaults to: nil)

    Controls wrapping behaviour for header cells if the content thereof is longer than the column's fixed width. If passed nil (default), content will be wrapped for as many rows as required to accommodate it. If passed an Integer N (> 0), content will be wrapped up to N rows and then truncated thereafter.

Yields:

  • (_self)

Yield Parameters:

  • _self (Tabulo::Table)

    the object that the method was called on

Raises:



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
180
181
182
183
184
185
186
187
188
# File 'lib/tabulo/table.rb', line 151

def initialize(sources, *columns, align_body: :auto, align_header: :center, align_title: :center,
  border: nil, border_styler: nil, column_padding: nil, column_width: nil, formatter: :to_s.to_proc,
  header_frequency: :start, header_styler: nil, row_divider_frequency: nil, styler: nil,
  title: nil, title_styler: nil, truncation_indicator: nil, wrap_preserve: :rune, wrap_body_cells_to: nil,
  wrap_header_cells_to: nil)

  @sources = sources

  @align_body = align_body
  @align_header = align_header
  @align_title = align_title
  @border = (border || DEFAULT_BORDER)
  @border_styler = border_styler
  @border_instance = Border.from(@border, @border_styler)
  @column_padding = (column_padding || DEFAULT_COLUMN_PADDING)

  @left_column_padding, @right_column_padding =
    (Array === @column_padding ? @column_padding : [@column_padding, @column_padding])

  @column_width = (column_width || DEFAULT_COLUMN_WIDTH)
  @formatter = formatter
  @header_frequency = header_frequency
  @header_styler = header_styler
  @row_divider_frequency = row_divider_frequency
  @styler = styler
  @title = title
  @title_styler = title_styler
  @truncation_indicator = validate_character(truncation_indicator,
    DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator")
  @wrap_preserve = wrap_preserve
  @wrap_body_cells_to = wrap_body_cells_to
  @wrap_header_cells_to = wrap_header_cells_to

  @column_registry = { }
  columns.each { |item| add_column(item) }

  yield self if block_given?
end

Instance Attribute Details

#sourcesEnumerable

Returns the underlying enumerable from which the table derives its data.

Returns:

  • (Enumerable)

    the underlying enumerable from which the table derives its data



31
32
33
# File 'lib/tabulo/table.rb', line 31

def sources
  @sources
end

Instance Method Details

#add_column(label, align_body: nil, align_header: nil, before: nil, formatter: nil, header: nil, header_styler: nil, padding: nil, styler: nil, width: nil, wrap_preserve: nil, &extractor) ⇒ Object

Adds a column to the Table.

Parameters:

  • label (Symbol, String, Integer)

    A unique identifier for this column, which by default will also be used as the column header text (see also the header param). If the extractor argument is not also provided, then the label argument should correspond to a method to be called on each item in the table sources to provide the content for this column. If a String is passed as the label, then it will be converted to a Symbol for the purpose of serving as this label.

  • align_body (:left, :center, :right, :auto, nil) (defaults to: nil)

    Specifies how the cell body contents should be aligned. If nil is passed, then the alignment is determined by the Table-level setting passed to the align_body option on Table initialization (which itself defaults to :auto). Otherwise this option determines the alignment of this column. If :auto is passed, the alignment is determined by the type of the cell value, with numbers aligned right, booleans center-aligned, and other values left-aligned. Note header text alignment is configured separately using the :align_header param.

  • align_header (:left, :center, :right, nil) (defaults to: nil)

    Specifies how the header text should be aligned. If nil is passed, then the alignment is determined by the Table-level setting passed to the align_header (which itself defaults to :center). Otherwise, this option determines the alignment of the header content for this column.

  • before (Symbol, Integer, nil) (defaults to: nil)

    The label of the column before (i.e. to the left of) which the new column should inserted. If nil is passed, it will be inserted after all other columns. If there is no column with the given label, then an InvalidColumnLabelError will be raised. A non-Integer labelled column can be identified in Symbol form for this purpose.

  • formatter (#to_proc) (defaults to: nil)

    A lambda or other callable object that will be passed the calculated value of each cell to determine how it should be displayed. This is distinct from the extractor and the styler (see below). For example, if the extractor for this column generates a Date, then the formatter might format that Date in a particular way.

    • If nil is provided, then the callable that was passed to the formatter option of the table itself on its creation (see #initialize) (which itself defaults to :to_s.to_proc), will be used as the formatter for the column.
    • If a 1-parameter callable is passed, then this callable will be called with the calculated value of the cell; it should then return a String, and this String will be displayed as the formatted value of the cell.
    • If a 2-parameter callable is passed, then the first parameter represents the calculated value of the cell, and the second parameter is a CellData instance, containing additional information about the cell that may be relevant to what formatting should be applied. For example, the CellData#row_index attribute can be inspected to determine whether the Cell is an odd- or even-numbered Row, to arrange for different formatting to be applied to alternating rows. See the documentation for CellData for more.
  • header (nil, #to_s) (defaults to: nil)

    Text to be displayed in the column header. If passed nil, the column's label will also be used as its header text.

  • header_styler (nil, #to_proc) (defaults to: nil)

    (nil) A lambda or other callable object that will determine the colors or other styling applied to the header content. Can be passed nil, or can be passed a callable that takes 1, 2 or 3 parameters:

    • If passed nil, then no additional styling will be applied to the cell content (other than what was already applied by the formatter).
    • If passed a callable, then that callable will be called for each line of content within the header cell, and the resulting string rendered in place of that line. The extra width of the string returned by the header_styler is not taken into consideration by the internal table and cell width calculations involved in rendering the table. Thus it can be used to apply ANSI escape codes to header cell content, to color the cell content for example, without breaking the table formatting.
      • If the passed callable takes 1 parameter, then the first parameter is a string representing a single formatted line within the header cell. For example, if the header cell content is wrapped over three lines, then the header_styler will be called three times for that header cell, once for each line of content.
      • If the passed callable takes 2 parameters, then the first parameter is as above, and the second parameter is an Integer representing the positional index of this header's Column, with the leftmost column having index 0, the next having index 1 etc.. This can be used, for example, to apply different styles to alternating Columns.
      • If the passed callable takes 3 parameters, then the first and second parameters are as above, and the third parameter is an Integer representing the index of the line within the header cell that is currently being styled. For example, if the cell content is wrapped over 3 lines, then the callable will be called first with a line index of 0, to style the first line, then with a line index of 1, to style the second line, and finally with a line index of 2, for the third and final wrapped line of the cell.

    Note that if the header content is truncated, then any header_styler will be applied to the truncation indicator character as well as to the truncated content.

  • padding (nil, Integer, Array) (defaults to: nil)

    Determines the amount of blank space with which to pad either side of the column. If passed nil, then the column_padding setting of the Tabulo::Table will determine the column's padding. (See #initialize.) Otherwise, this option overrides, for this column, the column_padding that was set at the table level: if passed an Integer, then the given amount of padding is applied to either side of the column; or if passed a two-element Array, then the first element of the Array indicates the amount of padding to apply to the left of the column, and the second element indicates the amount to apply to the right.

  • styler (nil, #to_proc) (defaults to: nil)

    A lambda or other callable object that will determine the colors or other styling applied to the formatted value of the cell. Can be passed nil, or can be passed a callable that takes either 2 or 3 parameters:

    • If passed nil, then no additional styling will be applied to the cell content (other than what was already applied by the formatter).
    • If passed a callable, then that callable will be called for each line of content within the cell, and the resulting string rendered in place of that line. The styler option differs from the formatter option in that the extra width of the string returned by styler is not taken into consideration by the internal table and cell width calculations involved in rendering the table. Thus it can be used to apply ANSI escape codes to cell content, to color the cell content for example, without breaking the table formatting.
      • If the passed callable takes 2 parameters, then the first parameter is the calculated value of the cell (prior to the formatter being applied); and the second parameter is a string representing a single formatted line within the cell. For example, if the cell content is wrapped over three lines, then for that cell, the styler will be called three times, once for each line of content within the cell.
      • If the passed callable takes 3 parameters, then the first two parameters are as above, and the third parameter is a CellData instance, containing additional information about the cell that may be relevant to what styles should be applied. For example, the CellData#row_index attribute can be inspected to determine whether the Cell is an odd- or even-numbered Row, to arrange for different styling to be applied to alternating rows. See the documentation for CellData for more.
      • If the passed callable takes 4 parameters, then the first three parameters are as above, and the fourth parameter is an Integer representing the index of the line within the cell that is currently being styled. For example, if the cell content is wrapped over 3 lines, then the callable will be called first with a line index of 0, to style the first line, then with a line index of 1, to style the second line, and finally with a line index of 2, to style the third and final wrapped line of the cell.

    Note that if the content of a cell is truncated, then the whatever styling is applied by the styler to the cell content will also be applied to the truncation indicator character.

  • width (Integer) (defaults to: nil)

    Specifies the width of the column, excluding padding. If nil, then the column will take the width provided by the column_width param with which the Table was initialized.

  • wrap_preserve (Symbol) (defaults to: nil)

    Specifies how to wrap column content when the column width is reached.

    • If passed nil, or not provided, then the value passed to the wrap_preserve param of #initialize will be used.
    • If passed rune or word, then the option passed to #initialize will be overridden for this column. See the documentation under #initialize for the behaviour of each option.
  • extractor (#to_proc)

    A block or other callable that will be passed each of the Tabulo::Table sources to determine the value in each cell of this column.

    • If this is not provided, then the column label will be treated as a method to be called on each source item to determine each cell's value.
    • If provided a single-parameter callable, then this callable will be passed each of the Tabulo::Table sources to determine the cell value for each row in this column.
    • If provided a 2-parameter callable, then for each of the Tabulo::Table sources, this callable will be passed the source, and the row index, to determine the cell value for that row. For this purpose, the first body row (not counting the header row) has an index of 0, the next an index of 1, etc..

Raises:

  • (InvalidColumnLabelError)

    if label has already been used for another column in this Table. (This is case-sensitive, but is insensitive to whether a String or Symbol is passed to the label parameter.)



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/tabulo/table.rb', line 322

def add_column(label, align_body: nil, align_header: nil, before: nil, formatter: nil,
  header: nil, header_styler: nil, padding: nil, styler: nil, width: nil, wrap_preserve: nil, &extractor)

  column_label = normalize_column_label(label)

  left_padding, right_padding =
    if padding
      Array === padding ? padding : [padding, padding]
    else
      [@left_column_padding, @right_column_padding]
    end

  if column_registry.include?(column_label)
    raise InvalidColumnLabelError, "Column label already used in this table."
  end

  column = Column.new(
    align_body: align_body || @align_body,
    align_header: align_header || @align_header,
    extractor: extractor || label.to_proc,
    formatter: formatter || @formatter,
    header: (header || label).to_s,
    header_styler: header_styler || @header_styler,
    index: column_registry.count,
    left_padding: left_padding,
    padding_character: PADDING_CHARACTER,
    right_padding: right_padding,
    styler: styler || @styler,
    truncation_indicator: @truncation_indicator,
    wrap_preserve: wrap_preserve || @wrap_preserve,
    width: width || @column_width,
  )

  if before == nil
    add_column_final(column, column_label)
  else
    add_column_before(column, column_label, before)
  end
end

#autosize_columns(except: nil) ⇒ Table

Resets all the column widths so that each column is just wide enough to accommodate its header text as well as the formatted content of each its cells for the entire collection, together with padding (by default 1 character either side), without wrapping.

Parameters:

  • except (nil, Symbol, Integer, Array[Symbol|Integer]) (defaults to: nil)

    If passed one or multiple column labels, these columns will be excluded from resizing and will keep their current width. (Note if using integers, these are not necessarily positional: only columns explicitly given an integer label will have these as labels.)

Returns:

  • (Table)

    the Table itself



524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/tabulo/table.rb', line 524

def autosize_columns(except: nil)
  columns = get_columns(except: except)
  columns.each { |column| column.width = Util.wrapped_width(column.header) }
  @sources.each_with_index do |source, row_index|
    columns.each_with_index do |column, column_index|
      cell = column.body_cell(source, row_index: row_index, column_index: column_index)
      cell_width = Util.wrapped_width(cell.formatted_content)
      column.width = Util.max(column.width, cell_width)
    end
  end
  self
end

#eachObject

Calls the given block once for each Row in the Table, passing that Row as parameter.

Note that when printed, the first row will visually include the headers (assuming these were not disabled when the Table was initialized).

Examples:

table.each do |row|
  puts row
end


406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/tabulo/table.rb', line 406

def each
  @sources.each_with_index do |source, index|
    header =
      if (index == 0) && @header_frequency
        :top
      elsif (Integer === @header_frequency) && Util.divides?(@header_frequency, index)
        :middle
      end

    show_divider = @row_divider_frequency && (index != 0) && Util.divides?(@row_divider_frequency, index)

    yield Row.new(self, source, header: header, divider: show_divider, index: index)
  end
end

#formatted_headerString

Returns a graphical representation of the Table column headers formatted with fixed width plain text, excluding any horizontal borders above or below.

Returns:

  • (String)

    a graphical representation of the Table column headers formatted with fixed width plain text, excluding any horizontal borders above or below.



423
424
425
426
# File 'lib/tabulo/table.rb', line 423

def formatted_header
  cells = get_columns.map(&:header_cell)
  format_row(cells, @wrap_header_cells_to)
end

#horizontal_rule(position = :bottom) ⇒ String

Produce a horizontal dividing line suitable for printing at the top, bottom or middle of the table.

It may be that :top, :middle and :bottom all look the same. Whether this is the case depends on the characters used for the table border.

Examples:

Print a horizontal divider between each pair of rows, and again at the bottom:


table.each_with_index do |row, i|
  puts table.horizontal_rule(:middle) unless i == 0
  puts row
end
puts table.horizontal_rule(:bottom)

Parameters:

  • position (:top, :middle, :bottom, :title_top, :title_bottom) (defaults to: :bottom)

    Specifies the position for which the resulting horizontal dividing line is intended to be printed. This determines the border characters that are used to construct the line. The :title_top and :title_bottom options are used internally for adding borders above and below the table title text.

Returns:

  • (String)

    an "ASCII" graphical representation of a horizontal dividing line.



448
449
450
451
# File 'lib/tabulo/table.rb', line 448

def horizontal_rule(position = :bottom)
  column_widths = get_columns.map { |column| column.width + column.total_padding }
  @border_instance.horizontal_rule(column_widths, position)
end

#pack(max_table_width: :auto, except: nil) ⇒ Table

Resets all the column widths so that each column is just wide enough to accommodate its header text as well as the formatted content of each its cells for the entire collection, together with padding (by default 1 character on either side of the column), without wrapping. Other adjustments may also be performed (see below).

In addition, if the table has a title but is not wide enough to accommodate (without wrapping) the title text (with a character of padding either side), widens the columns roughly evenly until the table as a whole is just wide enough to accommodate the title text.

Note that calling this method will cause the entire source Enumerable to be traversed and all the column extractors and formatters to be applied in order to calculate the required widths.

Note also that this method causes column widths to be fixed as appropriate to the formatted cell contents given the state of the source Enumerable at the point it is called. If the source Enumerable changes between that point, and the point when the Table is printed, then columns will not be resized yet again on printing.

Parameters:

  • max_table_width (nil, :auto, Numeric) (defaults to: :auto)

    With no args, or if passed :auto, stops the total table width (including padding and borders) from expanding beyond the bounds of the terminal screen. If passed nil, the table width will not be capped. Width is deducted from columns if required to achieve this, with one character progressively deducted from the width of the widest column until the target is reached. When the table is printed, wrapping or truncation will then occur in these columns as required (depending on how they were configured). Note that regardless of the value passed to max_table_width, the table will always be left wide enough to accommodate at least 1 character's width of content for each column, and the padding configured for each column (by default 1 character either side), together with border characters (1 on each side of the table and 1 between adjacent columns). I.e. there is a certain width below width the Table will refuse to shrink itself.

  • except (nil, Symbol, Integer, Array[Symbol|Integer]) (defaults to: nil)

    If passed one or multiple column labels, these columns will be excluded from resizing and will keep their current width. (Note if passing integers, these are not necessarily positional: only columns explicitly given an integer label will have these as labels.)

Returns:

  • (Table)

    the Table itself



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/tabulo/table.rb', line 490

def pack(max_table_width: :auto, except: nil)
  autosize_columns(except: except)

  max_width = nil
  if max_table_width
    max_width = (max_table_width == :auto ? TTY::Screen.width : max_table_width)
    shrink_to(max_width, except: except)
  end

  if @title
    border_edge_width = (@border == :blank ? 0 : 2)
    all_columns = get_columns
    min_width =
      Unicode::DisplayWidth.of(@title) +
      all_columns.first.left_padding +
      all_columns.last.right_padding +
      border_edge_width

    min_width = max_width if max_width && max_width < min_width
    expand_to(min_width, except: except)
  end

  self
end

#remove_column(label) ⇒ true, false

Removes the column identifed by the passed label.

Examples:

table = Table.new(1..10, :itself, :even?, :odd?)
table.add_column(:even2, header: "even?") { |n| n.even? }
table.remove_column(:even2)
table.remove_column(:odd?)

Parameters:

  • label (Symbol, String, Integer)

    The unique identifier for the column to be removed, corresponding to the label that was passed as the first parameter to #add_column (or was used in the table initializer) when the column was originally added. For columns that were originally added with a String or Symbol label, either a String or Symbol form of that label can be passed to #remove_column, indifferently. For example, if the label passed to #add_column had been "height", then that column could be removed by passing either "height" or :height to #remove_column. (However, if an Integer was originally passed as the label to #add_column, then only that same Integer, as an Integer, can be passed to #remove_column to remove that column.)

Returns:

  • (true, false)

    If the label identifies a column in the table, then the column will be removed and true will be returned; otherwise no column will be removed, and false will be returned.



381
382
383
# File 'lib/tabulo/table.rb', line 381

def remove_column(label)
  !!column_registry.delete(Integer === label ? label : label.to_sym)
end

#shrink_to(max_table_width, except: nil) ⇒ Table

If max_table_width is passed an integer, then column widths will be adjusted downward so that the total table width is reduced to the passed target width. (If the total width of the table exceeds the passed max_table_width, then this method is a no-op.)

Width is deducted from columns if required to achieve this, with one character progressively deducted from the width of the widest column until the target is reached. When the table is printed, wrapping or truncation will then occur in these columns as required (depending on how they were configured).

Note that regardless of the value passed to max_table_width, the table will always be left wide enough to accommodate at least 1 character's width of content for each column, and the padding configured for each column (by default 1 character either side), together with border characters (1 on each side of the table and 1 between adjacent columns). I.e. there is a certain width below width the Table will refuse to shrink itself.

If max_table_width is passed the symbol :screen, then this method will behave as if it were passed an integer, with that integer being the width of the terminal.

Parameters:

  • the (Integer, :screen)

    desired maximum table width

  • except (nil, Symbol, Integer, Array[Symbol|Integer]) (defaults to: nil)

    If passed one or multiple column labels, these columns will be excluded from resizing and will keep their current width. (Note if passing integers, these are not necessarily positional: only columns explicitly given an integer label will have these as labels.)

Returns:

  • (Table)

    the Table itself



561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/tabulo/table.rb', line 561

def shrink_to(max_table_width, except: nil)
  min_content_width_per_column = 1
  min_total_column_content_width = num_columns * min_content_width_per_column
  min_table_width = total_padding_width + total_borders_width + min_total_column_content_width

  max_table_width = (max_table_width == :screen ? TTY::Screen.width : max_table_width)
  max_table_width = Util.max(min_table_width, max_table_width)

  required_reduction = Util.max(total_table_width - max_table_width, 0)

  shrinkable_columns = get_columns(except: except)
  required_reduction.times do
    widest_column = shrinkable_columns.inject(shrinkable_columns.first) do |widest, column|
      column.width >= widest.width ? column : widest
    end

    widest_column.width -= 1
  end

  self
end

#to_sString

Returns a graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.

Returns:

  • (String)

    a graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.



387
388
389
390
391
392
393
394
395
# File 'lib/tabulo/table.rb', line 387

def to_s
  if column_registry.any?
    bottom_edge = horizontal_rule(:bottom)
    rows = map(&:to_s)
    bottom_edge.empty? ? Util.join_lines(rows) : Util.join_lines(rows + [bottom_edge])
  else
    ""
  end
end

#transpose(opts = {}) ⇒ Table

Creates a new Tabulo::Table from the current Table, transposed, that is rotated 90 degrees, relative to the current Table, so that the header names of the current Table form the content of left-most column of the new Table, and each column thereafter corresponds to one of the elements of the current Table's sources, with the header of that column being the String value of that element.

Examples:

puts Tabulo::Table.new(-1..1, :even?, :odd?, :abs).transpose
  # => +-------+--------------+--------------+--------------+
  #    |       |      -1      |       0      |       1      |
  #    +-------+--------------+--------------+--------------+
  #    | even? |     false    |     true     |     false    |
  #    |  odd? |     true     |     false    |     true     |
  #    |   abs |            1 |            0 |            1 |

Parameters:

  • opts (Hash) (defaults to: {})

    Options for configuring the new, transposed Tabulo::Table. The following options are the same as the keyword params for the #initialize method for Tabulo::Table: column_width, column_padding, formatter, header_frequency, row_divider_frequency, wrap_header_cells_to, wrap_body_cells_to, border, border_styler, title, title_styler, truncation_indicator, align_header, align_body, align_title. These are applied in the same way as documented for #initialize, when creating the new, transposed Table. Any options not specified explicitly in the call to #transpose will inherit their values from the original Tabulo::Table (with the exception of settings for the left-most column, containing the field names, which are determined as described below). In addition, the following options also apply to #transpose:

Options Hash (opts):

  • :field_names_width (nil, Integer)

    Determines the width of the left-most column of the new Table, which contains the names of "fields" (corresponding to the original Table's column headings). If this is not provided, then by default this column will be made just wide enough to accommodate its contents.

  • :field_names_header (String)

    By default the left-most column will have a blank header; but this can be overridden by passing a String to this option.

  • :field_names_header_alignment (:left, :center, :right)

    Specifies how the header text of the left-most column (if it has header text) should be aligned.

  • :field_names_body_alignment (:left, :center, :right)

    Specifies how the body text of the left-most column should be aligned.

  • :headers (#to_proc)

    A lambda or other callable object that will be passed in turn each of the elements of the current Table's sources Enumerable, to determine the text to be displayed in the header of each column of the new Table (other than the left-most column's header, which is determined as described above).

Returns:

Raises:



627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/tabulo/table.rb', line 627

def transpose(opts = {})
  default_opts = [:align_body, :align_header, :align_title, :border, :border_styler, :column_padding,
    :column_width, :formatter, :header_frequency, :row_divider_frequency, :title, :title_styler,
    :truncation_indicator, :wrap_body_cells_to, :wrap_header_cells_to].map do |sym|
    [sym, instance_variable_get("@#{sym}")]
  end.to_h

  initializer_opts = default_opts.merge(Util.slice_hash(opts, *default_opts.keys))
  default_extra_opts = { field_names_body_alignment: :right, field_names_header: "",
    field_names_header_alignment: :right, field_names_width: nil, headers: :to_s.to_proc }
  extra_opts = default_extra_opts.merge(Util.slice_hash(opts, *default_extra_opts.keys))

  # The underlying enumerable for the new table, is the columns of the original table.
  fields = column_registry.values

  Table.new(fields, **initializer_opts) do |t|

    # Left hand column of new table, containing field names
    width_opt = extra_opts[:field_names_width]
    field_names_width = (width_opt.nil? ? fields.map { |f| f.header.length }.max : width_opt)

    t.add_column(:dummy, align_body: extra_opts[:field_names_body_alignment],
      align_header: extra_opts[:field_names_header_alignment], header: extra_opts[:field_names_header],
      width: field_names_width, &:header)

    # Add a column to the new table for each of the original table's sources
    sources.each_with_index do |source, i|
      t.add_column(i, header: extra_opts[:headers].call(source)) do |original_column|
        original_column.body_cell_value(source, row_index: i, column_index: original_column.index)
      end
    end
  end
end