Class: Tabulo::Table
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
-
#sources ⇒ Enumerable
The underlying enumerable from which the table derives its data.
Instance Method Summary collapse
-
#add_column(label, align_body: nil, align_header: nil, before: nil, formatter: nil, header: nil, header_styler: nil, padding: nil, styler: nil, width: nil, &extractor) ⇒ Object
Adds a column to the Table.
- #each ⇒ Object
-
#formatted_header ⇒ String
A graphical representation of the Table column headers formatted with fixed width plain text.
-
#horizontal_rule(position = :bottom) ⇒ String
Produce a horizontal dividing line suitable for printing at the top, bottom or middle of the table.
-
#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_body_cells_to: nil, wrap_header_cells_to: nil) {|_self| ... } ⇒ Table
constructor
A new Table.
-
#pack(max_table_width: :auto) ⇒ 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 a single character of padding on either side of the column, without any wrapping.
-
#remove_column(label) ⇒ true, false
Removes the column identifed by the passed label.
-
#to_s ⇒ String
A graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.
-
#transpose(opts = {}) ⇒ Table
Creates a new 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.
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_body_cells_to: nil, wrap_header_cells_to: nil) {|_self| ... } ⇒ Table
Returns a new Tabulo::Table.
141 142 143 144 145 146 147 148 149 150 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 |
# File 'lib/tabulo/table.rb', line 141 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_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_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
#sources ⇒ Enumerable
Returns 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, &extractor) ⇒ Object
Adds a column to the Table.
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/tabulo/table.rb', line 307 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, &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, width: width || @column_width, ) if before == nil add_column_final(column, column_label) else add_column_before(column, column_label, before) end end |
#each ⇒ Object
390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/tabulo/table.rb', line 390 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_header ⇒ String
Returns a graphical representation of the Table column headers formatted with fixed width plain text.
407 408 409 410 |
# File 'lib/tabulo/table.rb', line 407 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.
432 433 434 435 |
# File 'lib/tabulo/table.rb', line 432 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) ⇒ 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 a single character of padding on either side of the column, without any wrapping. 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.
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/tabulo/table.rb', line 468 def pack(max_table_width: :auto) get_columns.each { |column| column.width = Util.wrapped_width(column.header) } @sources.each_with_index do |source, row_index| get_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 shrink_to(max_table_width == :auto ? TTY::Screen.width : max_table_width) if max_table_width if @title border_edge_width = (@border == :blank ? 0 : 2) columns = get_columns ( Unicode::DisplayWidth.of(@title) + columns.first.left_padding + columns.last.right_padding + border_edge_width ) end self end |
#remove_column(label) ⇒ true, false
Removes the column identifed by the passed label.
365 366 367 |
# File 'lib/tabulo/table.rb', line 365 def remove_column(label) !!column_registry.delete(Integer === label ? label : label.to_sym) end |
#to_s ⇒ String
Returns a graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.
371 372 373 374 375 376 377 378 379 |
# File 'lib/tabulo/table.rb', line 371 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.
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
# File 'lib/tabulo/table.rb', line 539 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 |