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_COLUMN_WIDTH =
12
- DEFAULT_COLUMN_PADDING =
1
- DEFAULT_HORIZONTAL_RULE_CHARACTER =
"-"
- DEFAULT_VERTICAL_RULE_CHARACTER =
"|"
- DEFAULT_INTERSECTION_CHARACTER =
"+"
- 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, header: nil, align_header: nil, align_body: nil, width: nil, formatter: :to_s.to_proc, styler: nil, header_styler: nil, &extractor) ⇒ Object
Adds a column to the Table.
- #each ⇒ Object
-
#formatted_header ⇒ String
An "ASCII" graphical representation of the Table column headers.
-
#horizontal_rule ⇒ String
An "ASCII" graphical representation of a horizontal dividing line suitable for printing at any point in the table.
-
#initialize(sources, *cols, columns: [], column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, horizontal_rule_character: nil, vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border_styler: nil) {|_self| ... } ⇒ Table
constructor
A new Table.
-
#pack(max_table_width: :auto) ⇒ Table
Reset 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.
-
#shrinkwrap!(max_table_width: nil) ⇒ Table
deprecated
Deprecated.
Use #pack instead.
-
#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, *cols, columns: [], column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, horizontal_rule_character: nil, vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border_styler: nil) {|_self| ... } ⇒ Table
Returns a new Tabulo::Table.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/tabulo/table.rb', line 101 def initialize(sources, *cols, columns: [], column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, horizontal_rule_character: nil, vertical_rule_character: nil, intersection_character: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border_styler: nil) if columns.any? Deprecation.warn("`columns' option to Tabulo::Table#initialize", "the variable length parameter `cols'", 2) end @sources = sources @header_frequency = header_frequency @wrap_header_cells_to = wrap_header_cells_to @wrap_body_cells_to = wrap_body_cells_to @default_column_width = (column_width || DEFAULT_COLUMN_WIDTH) @column_padding = (column_padding || DEFAULT_COLUMN_PADDING) @align_header = align_header @align_body = align_body @border_styler = border_styler @horizontal_rule_character = validate_character(horizontal_rule_character, DEFAULT_HORIZONTAL_RULE_CHARACTER, InvalidHorizontalRuleCharacterError, "horizontal rule character") @vertical_rule_character = validate_character(vertical_rule_character, DEFAULT_VERTICAL_RULE_CHARACTER, InvalidVerticalRuleCharacterError, "vertical rule character") @intersection_character = validate_character(intersection_character, DEFAULT_INTERSECTION_CHARACTER, InvalidIntersectionCharacterError, "intersection character") @truncation_indicator = validate_character(truncation_indicator, DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator") @column_registry = { } cols.each { |item| add_column(item) } 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.
37 38 39 |
# File 'lib/tabulo/table.rb', line 37 def sources @sources end |
Instance Method Details
#add_column(label, header: nil, align_header: nil, align_body: nil, width: nil, formatter: :to_s.to_proc, styler: nil, header_styler: nil, &extractor) ⇒ Object
Adds a column to the Table.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/tabulo/table.rb', line 202 def add_column(label, header: nil, align_header: nil, align_body: nil, width: nil, formatter: :to_s.to_proc, styler: nil, header_styler: nil, &extractor) column_label = case label when Integer, Symbol label when String label.to_sym end if column_registry.include?(column_label) raise InvalidColumnLabelError, "Column label already used in this table." end @column_registry[column_label] = Column.new( header: (header || label).to_s, align_header: align_header || @align_header, align_body: align_body || @align_body, width: (width || @default_column_width), formatter: formatter, extractor: (extractor || label.to_proc), styler: styler, header_styler: header_styler, truncation_indicator: @truncation_indicator, padding_character: PADDING_CHARACTER, ) end |
#each ⇒ Object
251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/tabulo/table.rb', line 251 def each @sources.each_with_index do |source, index| include_header = case @header_frequency when :start index == 0 when Integer index % @header_frequency == 0 else @header_frequency end yield body_row(source, with_header: include_header) end end |
#formatted_header ⇒ String
Returns an "ASCII" graphical representation of the Table column headers.
267 268 269 270 |
# File 'lib/tabulo/table.rb', line 267 def formatted_header cells = column_registry.map { |_, column| column.header_cell } format_row(cells, @wrap_header_cells_to) end |
#horizontal_rule ⇒ String
Returns an "ASCII" graphical representation of a horizontal dividing line suitable for printing at any point in the table.
280 281 282 283 284 285 286 |
# File 'lib/tabulo/table.rb', line 280 def horizontal_rule inner = column_registry.map do |_, column| @horizontal_rule_character * (column.width + @column_padding * 2) end styled_border(surround_join(inner, @intersection_character)) end |
#pack(max_table_width: :auto) ⇒ Table
Reset 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.
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.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/tabulo/table.rb', line 316 def pack(max_table_width: :auto) return self if column_registry.none? columns = column_registry.values columns.each { |column| column.width = wrapped_width(column.header) } @sources.each do |source| columns.each do |column| width = wrapped_width(column.body_cell(source).formatted_content) column.width = width if width > column.width end end if max_table_width max_table_width = TTY::Screen.width if max_table_width == :auto shrink_to(max_table_width) end self end |
#shrinkwrap!(max_table_width: nil) ⇒ Table
Use #pack instead.
Reset 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.
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.
444 445 446 447 |
# File 'lib/tabulo/table.rb', line 444 def shrinkwrap!(max_table_width: nil) Deprecation.warn("`Tabulo::Table#shrinkwrap!'", "`#pack'") pack(max_table_width: max_table_width) end |
#to_s ⇒ String
Returns a graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.
234 235 236 237 238 239 240 |
# File 'lib/tabulo/table.rb', line 234 def to_s if column_registry.any? join_lines(map(&:to_s)) 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.
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/tabulo/table.rb', line 381 def transpose(opts = {}) default_opts = [:column_width, :column_padding, :header_frequency, :wrap_header_cells_to, :wrap_body_cells_to, :horizontal_rule_character, :vertical_rule_character, :intersection_character, :truncation_indicator, :align_header, :align_body].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_width: nil, field_names_header: "", field_names_body_alignment: :right, field_names_header_alignment: :right, 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, header: extra_opts[:field_names_header], width: field_names_width, align_header: extra_opts[:field_names_header_alignment], align_body: extra_opts[:field_names_body_alignment], &: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) end end end end |