class Athena::Console::Helper::Table

Overview

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:

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 |
+-----+

Defined in:

helper/table.cr

Constructors

Class Method Summary

Instance Method Summary

Constructor Detail

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

[View source]

Class Method Detail

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

Registers the provided style with the provided name.

See [custom styles][Athena::Console::Helper::Table--custom-styles].


[View source]
def self.style_definition(name : String) : ACON::Helper::Table::Style #

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


[View source]

Instance Method Detail

def 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

[View source]
def 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

[View source]
def 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

[View source]
def append_row(row : RowType) : self #

Appends row to an already rendered table.

See [modifying rendered tables][Athena::Console::Helper::Table--modifying-rendered-tables]


[View source]
def append_row(*columns : CellType) : self #

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

See [modifying rendered tables][Athena::Console::Helper::Table--modifying-rendered-tables]


[View source]
def column_max_width(index : Int32, width : Int32) : self #

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

See [column sizing][Athena::Console::Helper::Table--column-sizing].


[View source]
def 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.


[View source]
def 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.


[View source]
def column_width(index : Int32, width : Int32) : self #

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

See [column sizing][Athena::Console::Helper::Table--column-sizing].


[View source]
def column_widths(widths : Enumerable(Int32)) : self #

Sets the minimum column widths to the provided widths.

See [column sizing][Athena::Console::Helper::Table--column-sizing].


[View source]
def column_widths(*widths : Int32) : self #

Sets the minimum column widths to the provided widths.

See [column sizing][Athena::Console::Helper::Table--column-sizing].


[View source]
def footer_title(footer_title : String | Nil) : self #

Sets the table [footer title][Athena::Console::Helper::Table--headerfooter-titles].


[View source]
def header_title(header_title : String | Nil) : self #

Sets the table [header title][Athena::Console::Helper::Table--headerfooter-titles].


[View source]
def headers(headers : RowType) : self #

[View source]
def headers(headers : Enumerable(RowType)) : self #

[View source]
def headers(*names : CellType) : self #

[View source]
def horizontal : self #

Changes this table's [orientation][Athena::Console::Helper::Table--orientation] to horizontal.


[View source]
def render #

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

ameba:disable Metrics/CyclomaticComplexity


[View source]
def 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

[View source]
def rows(rows : RowType) : self #

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

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

[View source]
def 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

[View source]
def 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][Athena::Console::Helper::Table--styles] and [custom styles][Athena::Console::Helper::Table--custom-styles].


[View source]

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


[View source]
def vertical : self #

Changes this table's [orientation][Athena::Console::Helper::Table--orientation] to vertical.


[View source]