Skip to content

class Athena::Console::Helper::Table
inherits Reference #

The Table helper can be used to display tabular data rendered to any ACON::Output::Interface.

+---------------+--------------------------+------------------+
| ISBN          | Title                    | Author           |
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens  |
| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
+---------------+--------------------------+------------------+

Usage#

Most commonly, a table will consist of a header row followed by one or more data rows:

@[ACONA::AsCommand("table")]
class TableCommand < ACON::Command
  protected def execute(input : ACON::Input::Interface, output : ACON::Output::Interface) : ACON::Command::Status
    ACON::Helper::Table.new(output)
      .headers("ISBN", "Title", "Author")
      .rows([
        ["99921-58-10-7", "Divine Comedy", "Dante Alighieri"],
        ["9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens"],
        ["960-425-059-0", "The Lord of the Rings", "J. R. R. Tolkien"],
        ["80-902734-1-6", "And Then There Were None", "Agatha Christie"],
      ])
      .render

    ACON::Command::Status::SUCCESS
  end
end

Separating Rows#

Row separators can be added anywhere in the output by passing an ACON::Helper::Table::Separator as a row.

table
  .rows([
    ["99921-58-10-7", "Divine Comedy", "Dante Alighieri"],
    ["9971-5-0210-0", "A Tale of Two Cities", "Charles Dickens"],
    ACON::Helper::Table::Separator.new,
    ["960-425-059-0", "The Lord of the Rings", "J. R. R. Tolkien"],
    ["80-902734-1-6", "And Then There Were None", "Agatha Christie"],
  ])
+---------------+--------------------------+------------------+
| ISBN          | Title                    | Author           |
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens  |
+---------------+--------------------------+------------------+
| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
+---------------+--------------------------+------------------+

Header/Footer Titles#

Header and/or footer titles can optionally be added via the #header_title and/or #footer_title methods.

table
  .header_title("Books")
  .footer_title("Page 1/2")
+---------------+----------- Books --------+------------------+
| ISBN          | Title                    | Author           |
+---------------+--------------------------+------------------+
| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens  |
+---------------+--------------------------+------------------+
| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
+---------------+--------- Page 1/2 -------+------------------+

Column Sizing#

By default, the width of each column is calculated automatically based on their contents. The #column_widths method can be used to set the column widths explicitly.

table
  .column_widths(10, 0, 30)
  .render

In this example, the first column's width will be 10, the last column's width will be 30, and the second column's width will be calculated automatically since it is zero. If you only want to set the width of a specific column, the #column_width method can be used.

table
  .column_width(0, 10)
  .column_width(2, 30)
  .render

The resulting table would be:

+---------------+------------------ Books -+--------------------------------+
| ISBN          | Title                    | Author                         |
+---------------+--------------------------+--------------------------------+
| 99921-58-10-7 | Divine Comedy            | Dante Alighieri                |
| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens                |
+---------------+--------------------------+--------------------------------+
| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien               |
| 80-902734-1-6 | And Then There Were None | Agatha Christie                |
+---------------+--------------------------+--------------------------------+

Notice that the width of the first column is greater than 10 characters wide. This is because column widths are always considered as the minimum width. If the content doesn't fit, it will be automatically increased to the longest content length.

Max Width#

If you would rather wrap the contents in multiple rows, the #column_max_width method can be used.

table
  .column_max_width(0, 5)
  .column_max_width(1, 10)
  .render

This would cause the table to now be:

+-------+------------+-- Books -----------------------+
| ISBN  | Title      | Author                         |
+-------+------------+--------------------------------+
| 99921 | Divine Com | Dante Alighieri                |
| -58-1 | edy        |                                |
| 0-7   |            |                                |
|                (the rest of the rows...)            |
+-------+------------+--------------------------------+

Orientation#

By default, the table contents are displayed as a normal table with the data being in rows, the first being the header row(s). The table can also be rendered vertically or horizontally via the #vertical and #horizontal methods respectively.

For example, the same contents rendered vertically would be:

+----------------------------------+
|   ISBN: 99921-58-10-7            |
|  Title: Divine Comedy            |
| Author: Dante Alighieri          |
|----------------------------------|
|   ISBN: 9971-5-0210-0            |
|  Title: A Tale of Two Cities     |
| Author: Charles Dickens          |
|----------------------------------|
|   ISBN: 960-425-059-0            |
|  Title: The Lord of the Rings    |
| Author: J. R. R. Tolkien         |
|----------------------------------|
|   ISBN: 80-902734-1-6            |
|  Title: And Then There Were None |
| Author: Agatha Christie          |
+----------------------------------+

While horizontally, it would be:

+--------+-----------------+----------------------+-----------------------+--------------------------+
| ISBN   | 99921-58-10-7   | 9971-5-0210-0        | 960-425-059-0         | 80-902734-1-6            |
| Title  | Divine Comedy   | A Tale of Two Cities | The Lord of the Rings | And Then There Were None |
| Author | Dante Alighieri | Charles Dickens      | J. R. R. Tolkien      | Agatha Christie          |
+--------+-----------------+----------------------+-----------------------+--------------------------+

Styles#

Up until now, all the tables have been rendered using the default style. The table helper comes with a few additional built in styles, including:

  • borderless
  • compact
  • box
  • double-box

The desired can be set via the #style method.

table
  .style("default") # Same as not calling the method
  .render

borderless#

=============== ========================== ==================
 ISBN            Title                      Author
=============== ========================== ==================
 99921-58-10-7   Divine Comedy              Dante Alighieri
 9971-5-0210-0   A Tale of Two Cities       Charles Dickens
=============== ========================== ==================
 960-425-059-0   The Lord of the Rings      J. R. R. Tolkien
 80-902734-1-6   And Then There Were None   Agatha Christie
=============== ========================== ==================

compact#

ISBN          Title                    Author
99921-58-10-7 Divine Comedy            Dante Alighieri
9971-5-0210-0 A Tale of Two Cities     Charles Dickens
960-425-059-0 The Lord of the Rings    J. R. R. Tolkien
80-902734-1-6 And Then There Were None Agatha Christie

box#

┌───────────────┬──────────────────────────┬──────────────────┐
│ ISBN          │ Title                    │ Author           │
├───────────────┼──────────────────────────┼──────────────────┤
│ 99921-58-10-7 │ Divine Comedy            │ Dante Alighieri  │
│ 9971-5-0210-0 │ A Tale of Two Cities     │ Charles Dickens  │
├───────────────┼──────────────────────────┼──────────────────┤
│ 960-425-059-0 │ The Lord of the Rings    │ J. R. R. Tolkien │
│ 80-902734-1-6 │ And Then There Were None │ Agatha Christie  │
└───────────────┴──────────────────────────┴──────────────────┘

double-box#

╔═══════════════╤══════════════════════════╤══════════════════╗
║ ISBN          │ Title                    │ Author           ║
╠═══════════════╪══════════════════════════╪══════════════════╣
║ 99921-58-10-7 │ Divine Comedy            │ Dante Alighieri  ║
║ 9971-5-0210-0 │ A Tale of Two Cities     │ Charles Dickens  ║
╟───────────────┼──────────────────────────┼──────────────────╢
║ 960-425-059-0 │ The Lord of the Rings    │ J. R. R. Tolkien ║
║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie  ║
╚═══════════════╧══════════════════════════╧══════════════════╝

Custom Styles#

If you would rather something more personal, custom styles can also be defined by providing #style with an ACON::Helper::Table::Style instance.

table_style = ACON::Helper::Table::Style.new
  .horizontal_border_chars("<fg=magenta>|</>")
  .vertical_border_chars("<info>-</>")
  .default_crossing_char(' ')

table
  .style(table_style)
  .render

Notice you can use the same style tags as you can with ACON::Formatter::OutputStyleInterfaces. This is used by default to give some color to headers when allowed.

Tip

Custom styles can also be registered globally:

ACON::Helper::Table.set_style_definition "colorful", table_style

# ...

table.style("colorful")
This method can also be used to override the built-in styles.

See ACON::Helper::Table::Style for more information.

Table Cells#

The ACON::Helper::Table::Cell type can be used to style a specific cell. Such as customizing the fore/background color, the alignment of the text, or the overall format of the cell.

See the related type for more information/examples.

Spanning Multiple Columns and Rows#

The ACON::Helper::Table::Cell type can also be used to add colspan and/or rowspan to a cell; which would make it span more than one column/row.

ACON::Helper::Table.new(output)
  .headers("ISBN", "Title", "Author")
  .rows([
    ["99921-58-10-7", "Divine Comedy", "Dante Alighieri"],
    ACON::Helper::Table::Separator.new,
    [ACON::Helper::Table::Cell.new("This value spans 3 columns.", colspan: 3)],
  ])
  .render

This would result in:

+---------------+---------------+-----------------+
| ISBN          | Title         | Author          |
+---------------+---------------+-----------------+
| 99921-58-10-7 | Divine Comedy | Dante Alighieri |
+---------------+---------------+-----------------+
| This value spans 3 columns.                     |
+---------------+---------------+-----------------+

Tip

This table cells with colspan and center alignment can be used to create header cells that span the entire table width:

table
  .headers([
    [ACON::Helper::Table::Cell.new(
      "Main table title",
      colspan: 3,
      style: ACON::Helper::Table::CellStyle.new(
        align: :center
      )
    )],
    %w(ISBN Title Author),
  ])
Would generate:
+--------+--------+--------+
|     Main table title     |
+--------+--------+--------+
| ISBN   | Title  | Author |
+--------+--------+--------+

In a similar way, rowspan can be used to have a column span multiple rows. This is especially helpful for columns with line breaks.

ACON::Helper::Table.new(output)
  .headers("ISBN", "Title", "Author")
  .rows([
    [
      "978-0521567817",
      "De Monarchia",
      ACON::Helper::Table::Cell.new("Dante Alighieri\nspans multiple rows", rowspan: 2),
    ],
    ["978-0804169127", "Divine Comedy"],
  ])
  .render

This would result in:

+----------------+---------------+---------------------+
| ISBN           | Title         | Author              |
+----------------+---------------+---------------------+
| 978-0521567817 | De Monarchia  | Dante Alighieri     |
| 978-0804169127 | Divine Comedy | spans multiple rows |
+----------------+---------------+---------------------+

colspan and rowspan may also be used together to create any layout you can think of.

Modifying Rendered Tables#

The #render method requires providing the entire table's content in order to fully render the table. In some cases, that may not be possible if the data is generated dynamically. In such cases, the #append_row method can be used which functions similarly to #add_row, but will append the rows to an already rendered table.

Info

This feature is only available when the table is rendered in an ACON::Output::Section.

@[ACONA::AsCommand("table")]
class TableCommand < ACON::Command
  protected def execute(input : ACON::Input::Interface, output : ACON::Output::Interface) : ACON::Command::Status
    section = output.section
    table = ACON::Helper::Table.new(section)
      .add_row("Foo")

    table.render

    table.append_row "Bar"

    ACON::Command::Status::SUCCESS
  end
end

This ultimately results in:

+-----+
| Foo |
| Bar |
+-----+

Constructors#

.new(output : ACON::Output::Interface)#

Class methods#

.set_style_definition(name : String, style : ACON::Helper::Table::Style) : Nil#

Registers the provided style with the provided name.

See custom styles.

.style_definition(name : String) : ACON::Helper::Table::Style#

Returns the ACON::Helper::Table::Style style with the provided name, raising an ACON::Exception::InvalidArgument if no style with that name is defined.

Methods#

#add_row(row : RowType) : self#

Adds a single new row to this table.

# Existing rows are not removed.
table
  .add_row(%w(One Two Three))
  .add_row(%w(Foo Bar Baz))
  .render

#add_row(*columns : CellType) : self#

Adds the provided columns as a single row to this table.

# Existing rows are not removed.
table
  .add_row("One", "Two", "Three")
  .add_row("Foo", "Bar", "Baz")
  .render

#add_rows(rows : Enumerable(RowType)) : self#

Similar to #rows(rows : Enumerable(RowType)), but appends the provided rows to this table.

# Existing rows are not removed.
table
  .add_rows([
    %w(One Two Three),
    %w(Foo Bar Baz),
  ])
  .render

#append_row(row : RowType) : self#

Appends row to an already rendered table.

See modifying rendered tables

#append_row(*columns : CellType) : self#

Appends the provided columns as a single row to an already rendered table.

See modifying rendered tables

#column_max_width(index : Int32, width : Int32) : self#

Sets the maximum width for the column at the provided index.

See column sizing.

#column_style(index : Int32, style : ACON::Helper::Table::Style | String) : self#

Sets the style of the column at the provided index. style may either be an explicit ACON::Helper::Table::Style, or the name of the style to use if it is built-in, or was registered via .set_style_definition.

#column_style(index : Int32) : ACON::Helper::Table::Style#

Returns the ACON::Helper::Table::Style the column at the provided index is using, falling back on #style.

#column_width(index : Int32, width : Int32) : self#

Sets the minimum width for the column at the provided index.

See column sizing.

#column_widths(widths : Enumerable(Int32)) : self#

Sets the minimum column widths to the provided widths.

See column sizing.

#column_widths(*widths : Int32) : self#

Sets the minimum column widths to the provided widths.

See column sizing.

#footer_title(footer_title : String | Nil) : self#

Sets the table footer title.

#header_title(header_title : String | Nil) : self#

Sets the table header title.

#headers(headers : RowType) : self#

#headers(headers : Enumerable(RowType)) : self#

#headers(*names : CellType) : self#

#horizontal : self#

Changes this table's orientation to horizontal.

#render#

Renders this table to the ACON::Output::Interface it was instantiated with.

ameba:disable Metrics/CyclomaticComplexity

#row(index : Int32, row : RowType) : self#

Manually sets the provided row to the provided index.

# Existing rows are not removed.
table
  .add_row(%w(One Two Three))
  .row(0, %w(Foo Bar Baz)) # Overrides row 0 to this row
  .render

#rows(rows : RowType) : self#

Overrides the rows of this table to those provided in rows.

table
  .rows(%w(Foo Bar Baz))
  .render

#rows(rows : Enumerable(RowType)) : self#

Overrides the rows of this table to those provided in rows.

table
  .rows([
    %w(One Two Three),
    %w(Foo Bar Baz),
  ])
  .render

#style(style : String | ACON::Helper::Table::Style) : self#

Sets the style of this table. style may either be an explicit ACON::Helper::Table::Style, or the name of the style to use if it is built-in, or was registered via .set_style_definition.

See styles and custom styles.

#style : ACON::Helper::Table::Style#

Returns the ACON::Helper::Table::Style used by this table.

#vertical : self#

Changes this table's orientation to vertical.