class Tablo::Summary(T, U, V)
- Tablo::Summary(T, U, V)
- Reference
- Object
Overview
The purpose of the Summary class is to calculate, format and display aggregated source data in a specific table.
This table is usually closely linked to the main table, but it can also be
presented separately (see the omit_last_rule parameter governing this link
in Tablo::Table#initialize
method).
The definition of the summary table is based on the following data structures:
UserProc
for aggregate data calculationHeaderColumn
for header content and formattingBodyColumn
for formatting calculated data columnsBodyRow
for data placement in the table
Below, a complete example illustrates how this works in practice.
Defined in:
summary.crConstructors
-
.new(table : Table(T), summary_definition : U, summary_options : V)
Summary class constructor
Class Method Summary
-
.use(key)
Class method to retrieve and use results of saved calculations by key (which is of type Symbol).
(seeSummary::UserProc
)
Constructor Detail
Summary class constructor
Mandatory parameters:
-
table: This parameter references the main table
-
summary_definition: a user defined array containing
n
instances ofSummary::UserProc
,Summary::HeaderColumn
,Summary::BodyColumn
andSummary::BodyRow
structs) -
summary_options: a NamedTuple of Table initializers (can be empty, as the main table initializers are included by default)
Here is a complete and functional example of Detail and Summary tables "working" together (See relevant infos on usage in structs listed above)
require "tablo"
require "colorize"
require "big"
struct BigDecimal
include Tablo::CellType
end
struct InvoiceItem
getter product, quantity, price
def initialize(@product : String, @quantity : Int32?, @price : BigDecimal?)
end
end
invoice = [
InvoiceItem.new("Laptop", 3, BigDecimal.new(980)),
InvoiceItem.new("Printer", 2, BigDecimal.new(154.99)),
InvoiceItem.new("Router", 1, BigDecimal.new(99)),
InvoiceItem.new("Switch", nil, BigDecimal.new(45)),
InvoiceItem.new("Accessories", 5, BigDecimal.new(64.50)),
]
invoice_summary_definition = [
Tablo::Summary::UserProc.new(
proc: ->(tbl : Tablo::Table(InvoiceItem)) {
total_sum = BigDecimal.new(0)
tbl.column_data(:total).each do |tot|
total_sum += tot.as(BigDecimal) unless tot.nil?
end
discount = total_sum * 0.05
total_after_discount = total_sum - discount
tax = total_after_discount * 0.2
total_due = total_after_discount + tax
{
:total_sum => total_sum.as(Tablo::CellType),
:discount => discount.as(Tablo::CellType),
:total_after_discount => total_after_discount.as(Tablo::CellType),
:tax => tax.as(Tablo::CellType),
:total_due => total_due.as(Tablo::CellType),
}
}),
Tablo::Summary::BodyColumn.new("Price", alignment: Tablo::Justify::Right),
Tablo::Summary::BodyColumn.new(:total, alignment: Tablo::Justify::Right,
formatter: ->(value : Tablo::CellType) {
value.is_a?(String) ? value : (
value.nil? ? "" : "%.2f" % value.as(BigDecimal)
)
},
styler: ->(_value : Tablo::CellType, coords : Tablo::Cell::Data::Coords, content : String) {
case coords.row_index
when 0, 2, 5 then content.colorize.mode(:bold).to_s
when 1 then content.colorize.mode(:italic).to_s
else content
end
}),
Tablo::Summary::HeaderColumn.new("Product", content: ""),
Tablo::Summary::HeaderColumn.new("Quantity", content: ""),
Tablo::Summary::HeaderColumn.new("Price", content: "Total Invoice",
alignment: Tablo::Justify::Right),
Tablo::Summary::HeaderColumn.new(:total, content: "Amounts"),
Tablo::Summary::BodyRow.new("Price", 10, "SubTotal"),
Tablo::Summary::BodyRow.new("Price", 20, "Discount 5%"),
Tablo::Summary::BodyRow.new("Price", 30, "S/T after discount"),
Tablo::Summary::BodyRow.new("Price", 40, "Tax (20%)"),
Tablo::Summary::BodyRow.new("Price", 60, "Balance due"),
Tablo::Summary::BodyRow.new(:total, 10, -> { Tablo::Summary.use(:total_sum) }),
Tablo::Summary::BodyRow.new(:total, 20, -> { Tablo::Summary.use(:discount) }),
Tablo::Summary::BodyRow.new(:total, 30, -> { Tablo::Summary.use(:total_after_discount) }),
Tablo::Summary::BodyRow.new(:total, 40, -> { Tablo::Summary.use(:tax) }),
Tablo::Summary::BodyRow.new(:total, 50, "========"),
Tablo::Summary::BodyRow.new(:total, 60, -> { Tablo::Summary.use(:total_due) }),
]
table = Tablo::Table.new(invoice,
omit_last_rule: true,
border: Tablo::Border.new(Tablo::Border::PreSet::Fancy),
title: Tablo::Heading.new("\nInvoice\n=======\n"),
subtitle: Tablo::Heading.new("Details", framed: true)) do |t|
t.add_column("Product",
&.product)
t.add_column("Quantity",
body_formatter: ->(value : Tablo::CellType) {
(value.nil? ? "N/A" : value.to_s)
}, &.quantity)
t.add_column("Price",
body_formatter: ->(value : Tablo::CellType) {
"%.2f" % value.as(BigDecimal)
}, &.price.as(Tablo::CellType))
t.add_column(:total, header: "Total",
body_formatter: ->(value : Tablo::CellType) {
value.nil? ? "" : "%.2f" % value.as(BigDecimal)
}) { |n| n.price.nil? || n.quantity.nil? ? nil : (
n.price.as(BigDecimal) *
n.quantity.as(Int32)
).as(Tablo::CellType) }
end
table.pack
table.add_summary(invoice_summary_definition,
title: Tablo::Heading.new("Summary", framed: true))
table.summary.as(Tablo::Table).pack
puts table
puts table.summary

A few points of note:
-
Use of the
BigDecimal
type (not included in Tablo by default, but made possible by reopening theBigDecimal
struct and adding theinclude Tablo::CellType
statement). -
Joining of the summary table to the main table, with the main table's
omit_last_rule
parameter set totrue
. -
Row numbers need not be consecutive. What's important is that their order is well defined, as they will ultimately be replaced by their index in a sorted array of row values.
-
To obtain optimal result in packing, the main table must be packed before summary table definition.
Class Method Detail
Class method to retrieve and use results of saved calculations
by key (which is of type Symbol).
(see Summary::UserProc
)
For example, to populate row 1 of column :total
with the result of
a previous calculation identified by :total_sum
:
Tablo::Summary::BodyRow.new(:total, 1, -> { Tablo::Summary.use(:total_sum) })