Class: FatTable::Formatter
- Inherits:
-
Object
- Object
- FatTable::Formatter
- Defined in:
- lib/fat_table/formatters/formatter.rb
Overview
A Formatter is for use in Table output routines, and provides methods for adding group and table footers to the output and instructions for how the table's cells ought to be formatted. The goal is to make subclasses of this class handle different output targets, such as aoa for an Array of Arrays (useful in Emacs org-mode code blocks), ANSI terminals, LaTeX, plain text, org mode table text, and so forth. Many of the formatting options, such as color, will be no-ops for some output targets, such as text, but will be valid nonetheless. Thus, a Formatter subclass should provide the best implementation for each formatting request available for the target. This base class will format output as pipe-separated values, but implementations provided by subclasses will override these for different output targets.
Direct Known Subclasses
AoaFormatter, AohFormatter, LaTeXFormatter, OrgFormatter, TermFormatter, TextFormatter
Constant Summary collapse
- LOCATIONS =
Valid locations in a Table as an array of symbols.
%i[header body bfirst gfirst gfooter footer].freeze
Instance Attribute Summary collapse
-
#footers ⇒ Object
readonly
A Hash of the table-wide footers to be added to the output.
-
#format_at ⇒ Object
readonly
A Hash of Hashes with the outer Hash keyed on location.
-
#gfooters ⇒ Object
readonly
A Hash of the group footers to be added to the output.
-
#options ⇒ Object
readonly
Options given to the Formatter constructor that allow variants for specific Formatters.
-
#table ⇒ Object
readonly
The table that is the subject of the Formatter.
Class Method Summary collapse
Instance Method Summary collapse
-
#avg_footer(*cols) ⇒ Object
Add a footer to average the +cols+ given as header symbols.
-
#avg_gfooter(*cols) ⇒ Object
Add a group footer to average the +cols+ given as header symbols.
-
#decorate_string(str, _istruct) ⇒ Object
Add LaTeX control sequences, ANSI terminal escape codes, or other decorations to string to decorate it with the given attributes.
-
#foot(label: 'Total', label_col: nil, **agg_cols) ⇒ Object
:category: Add Footers.
-
#footer(label, label_col = nil, *sum_cols, **agg_cols) ⇒ Object
Add Footer methods.
-
#format(**fmts) ⇒ Object
Set the formatting for all 6 "location" specifiers for the table: (1) the headline, :header, (2) the first row of the body, :bfirst, (3) the first row of each group, :gfirst, (4) all of the body rows, :body, (5) the footer rows, :footer, and (6) the group footer rows, :gfooter.
-
#format_cell(val, istruct, width: nil, decorate: false) ⇒ Object
Convert a value to a string based on the instructions in istruct, depending on the type of val.
-
#format_for(location, **fmts) ⇒ Object
Define a formatting directives for the given location.
-
#gfoot(label: 'Group Total', label_col: nil, **agg_cols) ⇒ Object
:category: Add Footers.
-
#gfooter(label, label_col = nil, *sum_cols, **agg_cols) ⇒ Object
Add a group footer to the output with a label given in the first parameter, defaulting to 'Total'.
-
#initialize(table = Table.new, **options) {|_self| ... } ⇒ Formatter
constructor
Return a new Formatter for the given +table+ which must be of the class FatTable::Table.
-
#max_footer(*cols) ⇒ Object
Add a footer to display the maximum value of the +cols+ given as header symbols.
-
#max_gfooter(*cols) ⇒ Object
Add a group footer to display the maximum value of the +cols+ given as header symbols.
-
#min_footer(*cols) ⇒ Object
Add a footer to display the minimum value of the +cols+ given as header symbols.
-
#min_gfooter(*cols) ⇒ Object
Add a group footer to display the minimum value of the +cols+ given as header symbols.
-
#output ⇒ Object
Return a representation of the +table+, along with all footers and group footers, as either a string in the target format or as a Ruby data structure if that is the target.
-
#sum_footer(*cols) ⇒ Object
Add a footer to sum the +cols+ given as header symbols.
-
#sum_gfooter(*cols) ⇒ Object
Add a group footer to sum the +cols+ given as header symbols.
Constructor Details
#initialize(table = Table.new, **options) {|_self| ... } ⇒ Formatter
Return a new Formatter for the given +table+ which must be of the class FatTable::Table. The +options+ hash can specify variants for the output for specific subclasses of Formatter. This base class outputs the +table+ as a string in the pipe-separated form, which is much like CSV except that it uses the ASCII pipe symbol +|+ to separate values rather than the comma, and therefore does not bother to quote strings since it assumes they will not contain any pipes. A new Formatter provides default formatting for all the cells in the table. If you give a block, the new Formatter is yielded to the block so that methods for formatting and adding footers can be called on it.
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 |
# File 'lib/fat_table/formatters/formatter.rb', line 100 def initialize(table = Table.new, **) unless table&.is_a?(Table) raise UserError, 'must initialize Formatter with a Table' end @table = table @options = @footers = {} @gfooters = {} # Formatting instructions for various "locations" within the Table, as a # hash of hashes. The outer hash is keyed on the location, and each inner # hash is keyed on either a column sym or a type sym, :string, :numeric, # :datetime, :boolean, or :nil. The value of the inner hashes are # OpenStruct structs. @format_at = {} %i[header bfirst gfirst body footer gfooter].each do |loc| @format_at[loc] = {} table.headers.each do |h| fmt_hash = self.class.default_format fmt_hash[:_h] = h fmt_hash[:_location] = loc format_at[loc][h] = OpenStruct.new(fmt_hash) end end yield self if block_given? end |
Instance Attribute Details
#footers ⇒ Object (readonly)
A Hash of the table-wide footers to be added to the output. The key is a string that is to serve as the label for the footer and inserted in the first column of the footer if that column is otherwise not populated with footer content. The value is Hash in which the keys are column symbols and the values are symbols for the aggregate method to be applied to the column to provide a value in the footer for that column. Thus, +footers['Total'][:shares]+ might be set to +:sum+ to indicate that the +:shares+ column is to be summed in the footer labeled 'Total'.
43 44 45 |
# File 'lib/fat_table/formatters/formatter.rb', line 43 def @footers end |
#format_at ⇒ Object (readonly)
A Hash of Hashes with the outer Hash keyed on location. The value for the outer Hash is an inner Hash keyed on column names. The values of the inner Hash are OpenStruct objects that contain the formatting instructions for the location and column. For example, +format_at[:body][:shares].commas+ is set either true or false depending on whether the +:shares+ column in the table body is to have grouping commas inserted in the output.
33 34 35 |
# File 'lib/fat_table/formatters/formatter.rb', line 33 def format_at @format_at end |
#gfooters ⇒ Object (readonly)
A Hash of the group footers to be added to the output. The key is a string that is to serve as the label for the footer and inserted in the first column of the footer if that column is otherwise not populated with group footer content. The value is Hash in which the keys are column symbols and the values are symbols for the aggregate method to be applied to the group's column to provide a value in the group footer for that column. Thus, +gfooters['Average'][:shares]+ might be set to +:avg+ to indicate that the +:shares+ column is to be averaged in the group footer labeled 'Average'.
54 55 56 |
# File 'lib/fat_table/formatters/formatter.rb', line 54 def @gfooters end |
#options ⇒ Object (readonly)
Options given to the Formatter constructor that allow variants for specific Formatters.
24 25 26 |
# File 'lib/fat_table/formatters/formatter.rb', line 24 def @options end |
#table ⇒ Object (readonly)
The table that is the subject of the Formatter.
20 21 22 |
# File 'lib/fat_table/formatters/formatter.rb', line 20 def table @table end |
Class Method Details
.default_format ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/fat_table/formatters/formatter.rb', line 59 def self.default_format { nil_text: '', case: :none, alignment: :left, bold: false, italic: false, color: 'none', bgcolor: 'none', hms: false, pre_digits: 0, post_digits: 0, commas: false, currency: false, datetime_fmt: '%F %H:%M:%S', date_fmt: '%F', true_text: 'T', false_text: 'F', true_color: 'none', true_bgcolor: 'none', false_color: 'none', false_bgcolor: 'none', underline: false, blink: false, _h: nil, _location: nil } end |
Instance Method Details
#avg_footer(*cols) ⇒ Object
Add a footer to average the +cols+ given as header symbols.
405 406 407 408 409 410 411 |
# File 'lib/fat_table/formatters/formatter.rb', line 405 def (*cols) hsh = {} cols.each do |c| hsh[c] = :avg end ('Average', **hsh) end |
#avg_gfooter(*cols) ⇒ Object
Add a group footer to average the +cols+ given as header symbols.
416 417 418 419 420 421 422 |
# File 'lib/fat_table/formatters/formatter.rb', line 416 def (*cols) hsh = {} cols.each do |c| hsh[c] = :avg end ('Group Average', **hsh) end |
#decorate_string(str, _istruct) ⇒ Object
Add LaTeX control sequences, ANSI terminal escape codes, or other decorations to string to decorate it with the given attributes. None of the decorations may affect the displayed width of the string. Return the decorated string.
987 988 989 |
# File 'lib/fat_table/formatters/formatter.rb', line 987 def decorate_string(str, _istruct) str end |
#foot(label: 'Total', label_col: nil, **agg_cols) ⇒ Object
:category: Add Footers
A keyword method for adding a footer to the formatted output having the label +label:+ (default 'Total') placed in the column with the header +label_col:+ or in the first column if +label_col+ is ommitted. This assigns a fixed group label to be placed in the :date column:
+begin_src ruby
fmtr.foot(label: "Year's Average", label_col: :date, temp: avg)
+end_src
Besides being a fixed string, the +label:+ can also be a proc or lambda taking one argument, the foooter itself. Thus, a label such as:
+begin_src ruby
fmtr.foot(label: -> (f) { "Average (latest year #{f.column(:date).max.year})" },
temp: :avg)
+end_src
And this would add the highest number to label, assuming the :date column of the footer's table had the year for each item.
The remaining hash arguments apply an aggregate to the values of the column, which can be:
- a symbol representing one of the builtin aggregates, i.e., :first, :last, :range, :sum, :count, :min, :max, :avg, :var, :pvar, :dev, :pdev, :any?, :all?, :none?, and :one?, or a symbol for your own aggregate defined as an instance method on FatTable::Column.
- a fixed string, but it the string can be converted into the Column's type, it will be converted, so the string '3.14159' will be converted to 3.14159 in a Numeric column.
- a value of the Column's type, so Date.today would simply be evaluated for a Numeric column.
- most flexibly of all, a proc or lambda taking arguments: f, the footer object itself; c, the column (or in the case of a group footer, the sub-column) corresponding to the current header, and in the case of a group footer, k, the number of the group (0-based).
- Any other value is converted to a string with #to_s.
Examples:
Put the label in the :dickens column of the footer and the maximum value from the :alpha column in the :alpha column of the footer.
fmtr.foot(label: 'Best', label_col: :dickens, alpha: :max)
Put the label 'Today' in the first column of the footer and today's date in the :beta column.
fmtr.foot(label: 'Today', beta: Date.today)
Put the label 'Best' in the :dickens column of the footer and the string 'Tale of Two Cities' in the :alpha column of the footer. Since it can't be interpreted as Boolean, Numeric, or DateTime, it is placed in the footer literally.
fmtr.foot(label: 'Best', label_col: :dickens, alpha: 'A Tale of Two Cities')
Use a lambda to calculate the value to be placed in the column :gamma.
fmtr.foot(label: 'Gamma', beta: :avg, gamma: ->(f, c) { (Math.gamma(c.count) + f[:beta] } )
Note that this way a footer can be made a function of the other footer values (using f[:other_col]) as well as the Column object corresponding to the lamda's column.
254 255 256 257 258 259 260 261 |
# File 'lib/fat_table/formatters/formatter.rb', line 254 def foot(label: 'Total', label_col: nil, **agg_cols) foot = Footer.new(label, table, label_col: label_col) agg_cols.each_pair do |h, agg| foot.add_value(h, agg) end @footers[label] = foot foot end |
#footer(label, label_col = nil, *sum_cols, **agg_cols) ⇒ Object
Add Footer methods
A Table may have any number of footers and any number of group footers. Footers are not part of the table's data and never participate in any of the operation methods on tables. They are never inherited by output tables from input tables in any of the transformation methods.
When output, a table footer will appear at the bottom of the table, and a group footer will appear at the bottom of each group.
Each footer must have a label, usually a string such as 'Total', to identify the purpose of the footer, and the label must be distinct among all footers of the same type. That is you may have a table footer labeled 'Total' and a group footer labeled 'Total', but you may not have two table footers with that label. If the first column of the table is not included in the footer, the footer's label will be placed there, otherwise, there will be no label output. The footers are accessible with the #footers method, which returns a hash indexed by the label converted to a symbol. The symbol is reconverted to a title-cased string on output.
Note that by adding footers or gfooters to the table, you are only stating what footers you want on output of the table. No actual calculation is performed until the table is output.
Add a table footer to the table with a label given in the first parameter, defaulting to 'Total'. After the label, you can given any number of headers (as symbols) for columns to be summed, and then any number of hash parameters for columns for with to apply an aggregate other than :sum. For example, these are valid footer definitions.
Just sum the shares column with a label of 'Total' fmtr.footer(:shares)
Change the label and sum the :price column as well fmtr.footer('Grand Total', :shares, :price)
Average then show standard deviation of several columns fmtr.footer.('Average', date: :avg, shares: :avg, price: :avg) fmtr.footer.('Sigma', date: :dev, shares: :dev, price: :dev)
Do some sums and some other aggregates: sum shares, average date and price. fmtr.footer.('Summary', :shares, date: :avg, price: :avg)
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/fat_table/formatters/formatter.rb', line 174 def (label, label_col = nil, *sum_cols, **agg_cols) foot = Footer.new(label, table, label_col: label_col) sum_cols.each do |h| foot.add_value(h, :sum) end agg_cols.each_pair do |h, agg| foot.add_value(h, agg) end @footers[label] = foot foot end |
#format(**fmts) ⇒ Object
Set the formatting for all 6 "location" specifiers for the table: (1) the headline, :header, (2) the first row of the body, :bfirst, (3) the first row of each group, :gfirst, (4) all of the body rows, :body, (5) the footer rows, :footer, and (6) the group footer rows, :gfooter.
584 585 586 587 588 589 |
# File 'lib/fat_table/formatters/formatter.rb', line 584 def format(**fmts) %i[header bfirst gfirst body footer gfooter].each do |loc| format_for(loc, **fmts) end self end |
#format_cell(val, istruct, width: nil, decorate: false) ⇒ Object
Convert a value to a string based on the instructions in istruct, depending on the type of val. "Formatting," which changes the content of the string, such as adding commas, is always performed, except alignment which is only performed when the width parameter is non-nil. "Decorating", which changes the appearance without changing the content, is performed only if the decorate parameter is true. Priority: lowest to highest: type, location, column_name
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 |
# File 'lib/fat_table/formatters/formatter.rb', line 946 def format_cell(val, istruct, width: nil, decorate: false) case val when Numeric str = format_numeric(val, istruct) str = format_string(str, istruct, width) decorate ? decorate_string(str, istruct) : str when DateTime, Date str = format_datetime(val, istruct) str = format_string(str, istruct, width) decorate ? decorate_string(str, istruct) : str when TrueClass str = format_boolean(val, istruct) str = format_string(str, istruct, width) true_istruct = istruct.dup true_istruct.color = istruct.true_color true_istruct.bgcolor = istruct.true_bgcolor decorate ? decorate_string(str, true_istruct) : str when FalseClass str = format_boolean(val, istruct) str = format_string(str, istruct, width) false_istruct = istruct.dup false_istruct.color = istruct.false_color false_istruct.bgcolor = istruct.false_bgcolor decorate ? decorate_string(str, false_istruct) : str when NilClass str = istruct.nil_text str = format_string(str, istruct, width) decorate ? decorate_string(str, istruct) : str when String str = format_string(val, istruct, width) decorate ? decorate_string(str, istruct) : str else raise UserError, "cannot format value '#{val}' of class #{val.class}" end end |
#format_for(location, **fmts) ⇒ Object
Define a formatting directives for the given location. The following are the valid +location+ symbols.
:header:: instructions for the headers of the table,
:bfirst:: instructions for the first row in the body of the table,
:gfirst:: instructions for the cells in the first row of a group, to the extent not governed by :bfirst.
:body:: instructions for the cells in the body of the table, to the extent they are not governed by :bfirst or :gfirst.
:gfooter:: instructions for the cells of a group footer, and
:footer:: instructions for the cells of a footer.
Formatting directives are specified with hash arguments where the keys are either
the name of a table column in symbol form, or
the name of a column type in symbol form, i.e., :string, :numeric, or :datetime, :boolean, or :nil (for empty cells or untyped columns).
The value given for the hash arguments should be strings that contain "directives" on how elements of that column or of that type are to be formatted on output. Formatting directives for a column name take precedence over those specified by type. And more specific locations take precedence over less specific ones.
For example, the first line of a table is part of :body, :gfirst, and :bfirst, but since its identity as the first row of the table is the most specific (there is only one of those, there may be many rows that qualify as :gfirst, and even more that qualify as :body rows) any :bfirst specification would have priority over :gfirst or :body.
For purposes of formatting, all headers are considered of the :string type and all nil cells are considered to be of the :nilclass type. All other cells have the type of the column to which they belong, including all cells in group or table footers. See ::format for details on formatting directives.
Set the formatting for the given location.
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
# File 'lib/fat_table/formatters/formatter.rb', line 637 def format_for(location, **fmts) unless LOCATIONS.include?(location) raise UserError, "unknown format location '#{location}'" end fmts = fmts.transform_keys do |k| if k == :nilclass :nil elsif k == :date :datetime else k end end @format_at[location] ||= {} table.headers.each do |h| # Build the inner hash of formatting instructions for this column h, # beginning with the default formatting hash or any existing inner # hash. format_h = if format_at[location][h].empty? default_format else format_at[location][h].to_h end # Merge in string and nil formatting for this column h, but not in # header location. Header is always typed a string, so it will get # formatted in type-based formatting below. And headers are never nil. unless location == :header if fmts.key?(:string) typ_fmt = parse_fmt_string(fmts[:string]) format_h = format_h.merge(typ_fmt) end if fmts.key?(:nil) typ_fmt = parse_nilclass_fmt(fmts[:nil], strict: false).first format_h = format_h.merge(typ_fmt) end end # Merge in formatting instructions for column h based on the column # name, or if there is no formatting instructions for the column by # name, merge in the formatting instructions based on the column's # type. Insist on only the string type for the header location. typ = (location == :header ? :string : table.type(h).as_sym) parse_typ_method_name = 'parse_' + typ.to_s + '_fmt' if fmts[h] # Merge in column formatting col_fmt = send(parse_typ_method_name, fmts[h], strict: location != :header).first format_h = format_h.merge(col_fmt) elsif fmts.key?(typ) # Merge in type-based formatting typ_fmt = send(parse_typ_method_name, fmts[typ]).first format_h = format_h.merge(typ_fmt) end # Copy :body formatting for column h to :bfirst and :gfirst if they # still have the default formatting. Can be overridden with a # format_for call with those locations. if location == :body format_h.each_pair do |k, v| if format_at[:bfirst][h].send(k) == self.class.default_format[k] format_at[:bfirst][h].send("#{k}=", v) end if format_at[:gfirst][h].send(k) == self.class.default_format[k] format_at[:gfirst][h].send("#{k}=", v) end end elsif location == :gfirst # Copy :gfirst formatting to :bfirst if it is still the default format_h.each_pair do |k, v| if format_at[:bfirst][h].send(k) == self.class.default_format[k] format_at[:bfirst][h].send("#{k}=", v) end end end # Record its origin (using leading underscore so not to clash with any # headers named h or location) and convert to struct format_h[:_h] = h format_h[:_location] = location format_at[location][h] = OpenStruct.new(format_h) end self end |
#gfoot(label: 'Group Total', label_col: nil, **agg_cols) ⇒ Object
:category: Add Footers
A keyword method for adding a group footer to the formatted output having the label +label:+ (default 'Total') placed in the column with the header +label_col:+ or in the first column if +label_col+ is ommitted.
This assigns a fixed group label to be placed in the :date column:
+begin_src ruby
fmtr.gfoot(label: "Year's Average", label_col: :date, temp: avg)
+end_src
Besides being a fixed string, the +label:+ can also be a proc or lambda taking one or two arguments. In the one argument form, the argument is the group number k. If a second argument is specified, the foooter itself is passed as the argument. Thus, a label such as:
+begin_src ruby
fmtr.gfoot(label: -> (k) { "Group #FatTable::Formatter.(k+1)(k+1).to_roman Average" }, temp: :avg)
+end_src
This would format the label with a roman numeral (assuming you defined a method to do so) for the group number.
+begin_src ruby
fmtr.gfoot(label: -> (k, f) { "Year #{f.column(:date, k).max.year} Group #{(k+1).to_roman} Average" },
temp: :avg)
+end_src
And this would add the group's year to label, assuming the :date column of the footer's table had the same year for each item in the group.
The remaining hash arguments apply an aggregate to the values of the column, which can be:
- a symbol representing one of the builtin aggregates, i.e., :first, :last, :range, :sum, :count, :min, :max, :avg, :var, :pvar, :dev, :pdev, :any?, :all?, :none?, and :one?, or a symbol for your own aggregate defined as an instance method on FatTable::Column.
- a fixed string, but it the string can be converted into the Column's type, it will be converted, so the string '3.14159' will be converted to 3.14159 in a Numeric column.
- a value of the Column's type, so Date.today would simply be evaluated for a Numeric column.
- most flexibly of all, a proc or lambda taking arguments: f, the footer object itself; c, the column (or in the case of a group footer, the sub-column) corresponding to the current header, and k, this group's group number (0-based).
- Any other value is converted to a string with #to_s.
Examples:
Put the label in the :dickens column of the footer and the maximum value from the :alpha column in the :alpha column of the footer.
+begin_src ruby
fmtr.gfoot(label: 'Best', label_col: :dickens, alpha: :max)
+end_src
Put the label 'Today' in the first column of the footer and today's date in the :beta column.
+begin_src ruby
fmtr.gfoot(label: 'Today', beta: Date.today)
+end_src
Put the label 'Best' in the :dickens column of the footer and the string 'Tale of Two Cities' in the :alpha column of the footer. Since it can't be interpreted as Boolean, Numeric, or DateTime, it is placed in the footer literally.
+begin_src ruby
fmtr.gfoot(label: 'Best', label_col: :dickens, alpha: 'A Tale of Two Cities')
+end_src
Use a lambda to calculate the value to be placed in the column :gamma.
+begin_src ruby
fmtr.gfoot(label: 'Gamma', beta: :avg, gamma: ->(f, c) { (Math.gamma(c.count) + f[:beta] } )
+end_src
Note that this way a footer can be made a function of the other footer values (using f[:other_col]) as well as the Column object corresponding to the lamda's column.
379 380 381 382 383 384 385 386 |
# File 'lib/fat_table/formatters/formatter.rb', line 379 def gfoot(label: 'Group Total', label_col: nil, **agg_cols) foot = Footer.new(label, table, label_col: label_col, group: true) agg_cols.each_pair do |h, agg| foot.add_value(h, agg) end @gfooters[label] = foot foot end |
#gfooter(label, label_col = nil, *sum_cols, **agg_cols) ⇒ Object
Add a group footer to the output with a label given in the first parameter, defaulting to 'Total'. After the label, you can given any number of headers (as symbols) for columns to be summed, and then any number of hash parameters for columns for with to apply an aggregate other than :sum. For example, these are valid gfooter definitions.
Just sum the shares column with a label of 'Total' tab.gfooter(:shares)
Change the label and sum the :price column as well tab.gfooter('Total', :shares, :price)
Average then show standard deviation of several columns fmtr.gfooter.('Average', date: :avg, shares: :avg, price: :avg) fmtr.gfooter.('Sigma', date: dev, shares: :dev, price: :dev)
Do some sums and some other aggregates: sum shares, average date and price. fmtr.gfooter.('Summary', :shares, date: :avg, price: :avg)
282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/fat_table/formatters/formatter.rb', line 282 def (label, label_col = nil, *sum_cols, **agg_cols) foot = Footer.new(label, table, label_col: label_col, group: true) sum_cols.each do |h| foot.add_value(h, :sum) end agg_cols.each_pair do |h, agg| foot.add_value(h, agg) end @gfooters[label] = foot foot end |
#max_footer(*cols) ⇒ Object
Add a footer to display the maximum value of the +cols+ given as header symbols.
452 453 454 455 456 457 458 |
# File 'lib/fat_table/formatters/formatter.rb', line 452 def (*cols) hsh = {} cols.each do |c| hsh[c] = :max end ('Maximum', **hsh) end |
#max_gfooter(*cols) ⇒ Object
Add a group footer to display the maximum value of the +cols+ given as header symbols.
464 465 466 467 468 469 470 |
# File 'lib/fat_table/formatters/formatter.rb', line 464 def (*cols) hsh = {} cols.each do |c| hsh[c] = :max end ('Group Maximum', **hsh) end |
#min_footer(*cols) ⇒ Object
Add a footer to display the minimum value of the +cols+ given as header symbols.
428 429 430 431 432 433 434 |
# File 'lib/fat_table/formatters/formatter.rb', line 428 def (*cols) hsh = {} cols.each do |c| hsh[c] = :min end ('Minimum', **hsh) end |
#min_gfooter(*cols) ⇒ Object
Add a group footer to display the minimum value of the +cols+ given as header symbols.
440 441 442 443 444 445 446 |
# File 'lib/fat_table/formatters/formatter.rb', line 440 def (*cols) hsh = {} cols.each do |c| hsh[c] = :min end ('Group Minimum', **hsh) end |
#output ⇒ Object
Return a representation of the +table+, along with all footers and group footers, as either a string in the target format or as a Ruby data structure if that is the target. In the latter case, all the cells are converted to strings formatted according to the Formatter's formatting directives given in Formatter.format_for or Formatter.format.
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 |
# File 'lib/fat_table/formatters/formatter.rb', line 1133 def output # If there are neither headers nor any rows in the table, return an # empty string. return '' if table.empty? && table.headers.empty? # This results in a hash of two-element arrays. The key # is the header and the value is an array of the header and formatted # header. We do the latter so the structure parallels the structure for # rows explained next. formatted_headers = build_formatted_headers # These produce an array with each element representing a row of the # table. Each element of the array is a two-element array. The location of # the row in the table (:bfirst, :body, :gfooter, etc.) is the first # element and a hash of the row is the second element. The keys for the # hash are the row headers as in the Table, but the values are two element # arrays as well. First is the raw, unformatted value of the cell, the # second is a string of the first value formatted according to the # instructions for the column and location in which it appears. The # formatting done on this pass is only formatting that affects the # contents of the cells, such as inserting commas, that would affect the # width of the columns as displayed. We keep both the raw value and # unformatted value around because we have to make two passes over the # table if there is any alignment, and we want to know the type of the raw # element for the second pass of formatting for type-specific formatting # (e.g., true_color, false_color, etc.). new_rows = build_formatted_body new_rows += # Having formatted the cells, we can now compute column widths so we can # do any alignment called for if this is a Formatter that performs its own # alignment. On this pass, we also decorate the cells with colors, bold, # etc. if aligned? widths = width_map(formatted_headers, new_rows) table.headers.each do |h| fmt_h = formatted_headers[h].last istruct = format_at[:header][h] formatted_headers[h] = [h, format_cell(fmt_h, istruct, width: widths[h], decorate: true)] end aligned_rows = [] new_rows.each do |loc_row| if loc_row.nil? aligned_rows << nil next end loc, row = *loc_row aligned_row = {} row.each_pair do |h, (val, _fmt_v)| istruct = format_at[loc][h] aligned_row[h] = [val, format_cell(val, istruct, width: widths[h], decorate: true)] end aligned_rows << [loc, aligned_row] end new_rows = aligned_rows end # Now that the contents of the output table cells have been computed and # alignment applied, we can actually construct the table using the methods # for constructing table parts, pre_table, etc. We expect that these will # be overridden by subclasses of Formatter for specific output targets. In # any event, the result is a single string (or ruby object if eval is true # for the Formatter) representing the table in the syntax of the output # target. result = '' result += pre_table if include_header_row? result += pre_header(widths) result += pre_row cells = [] formatted_headers.each_pair do |h, (_v, fmt_v)| cells << pre_cell(h) + quote_cell(fmt_v) + post_cell end result += cells.join(inter_cell) result += post_row result += post_header(widths) end new_rows.each do |loc_row| if loc_row.nil? result += hline(widths) next end _loc, row = *loc_row result += pre_row cells = [] row.each_pair do |h, (_v, fmt_v)| cells << pre_cell(h) + quote_cell(fmt_v) + post_cell end result += cells.join(inter_cell) result += post_row end result += (widths) result += post_table # If this Formatter targets a ruby data structure (e.g., AoaFormatter), we # eval the string to get the object. evaluate? ? eval(result) : result end |
#sum_footer(*cols) ⇒ Object
Add a footer to sum the +cols+ given as header symbols.
391 392 393 |
# File 'lib/fat_table/formatters/formatter.rb', line 391 def (*cols) ('Total', nil, *cols) end |
#sum_gfooter(*cols) ⇒ Object
Add a group footer to sum the +cols+ given as header symbols.
398 399 400 |
# File 'lib/fat_table/formatters/formatter.rb', line 398 def (*cols) ('Group Total', nil, *cols) end |