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, 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(position = :bottom) ⇒ String
It may be that
:top
,:middle
and:bottom
all look the same. -
#initialize(sources, *columns, column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border: nil, 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.
-
#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, column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border: nil, border_styler: nil) {|_self| ... } ⇒ Table
Returns a new Tabulo::Table
95 96 97 98 99 100 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 |
# File 'lib/tabulo/table.rb', line 95 def initialize(sources, *columns, column_width: nil, column_padding: nil, header_frequency: :start, wrap_header_cells_to: nil, wrap_body_cells_to: nil, truncation_indicator: nil, align_header: :center, align_body: :auto, border: nil, border_styler: nil) @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) @align_header = align_header @align_body = align_body @column_padding = (column_padding || DEFAULT_COLUMN_PADDING) @left_column_padding, @right_column_padding = case @column_padding when Array @column_padding else [@column_padding, @column_padding] end @border = (border || DEFAULT_BORDER) @border_styler = border_styler @border_instance = Border.from(@border, @border_styler) @truncation_indicator = validate_character(truncation_indicator, DEFAULT_TRUNCATION_INDICATOR, InvalidTruncationIndicatorError, "truncation indicator") @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, 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.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/tabulo/table.rb', line 195 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
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/tabulo/table.rb', line 246 def each @sources.each_with_index do |source, index| header = case @header_frequency when :start :top if index == 0 when Integer if index == 0 :top elsif index % @header_frequency == 0 :middle end else @header_frequency end yield body_row(source, header: header) end end |
#formatted_header ⇒ String
Returns an "ASCII" graphical representation of the Table column headers.
266 267 268 269 |
# File 'lib/tabulo/table.rb', line 266 def formatted_header cells = column_registry.map { |_, column| column.header_cell } format_row(cells, @wrap_header_cells_to) end |
#horizontal_rule(position = :bottom) ⇒ String
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.
288 289 290 291 |
# File 'lib/tabulo/table.rb', line 288 def horizontal_rule(position = :bottom) column_widths = column_registry.map { |_, column| column.width + total_column_padding } @border_instance.horizontal_rule(column_widths, position) 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.
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/tabulo/table.rb', line 321 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 |
#to_s ⇒ String
Returns a graphical "ASCII" representation of the Table, suitable for display in a fixed-width font.
227 228 229 230 231 232 233 234 235 |
# File 'lib/tabulo/table.rb', line 227 def to_s if column_registry.any? bottom_edge = horizontal_rule(:bottom) rows = map(&:to_s) bottom_edge.empty? ? join_lines(rows) : 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.
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 413 414 415 |
# File 'lib/tabulo/table.rb', line 384 def transpose(opts = {}) default_opts = [:column_width, :column_padding, :header_frequency, :wrap_header_cells_to, :wrap_body_cells_to, :truncation_indicator, :align_header, :align_body, :border, :border_styler].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 |