fat_table 0.3.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.rspec +2 -1
 - data/.rubocop.yml +3 -5
 - data/README.org +1334 -457
 - data/README.rdoc +1 -2
 - data/TODO.org +17 -10
 - data/examples/create_trans.sql +14 -0
 - data/examples/quick.pdf +0 -0
 - data/examples/quick.png +0 -0
 - data/examples/quick.ppm +0 -0
 - data/examples/quick.tex +8 -0
 - data/examples/quick_small.png +0 -0
 - data/examples/quicktable.tex +123 -0
 - data/examples/trades.db +0 -0
 - data/examples/trans.csv +13 -0
 - data/fat_table.gemspec +2 -2
 - data/lib/ext/array.rb +15 -0
 - data/lib/fat_table/column.rb +71 -208
 - data/lib/fat_table/convert.rb +173 -0
 - data/lib/fat_table/evaluator.rb +7 -0
 - data/lib/fat_table/footer.rb +228 -0
 - data/lib/fat_table/formatters/formatter.rb +200 -163
 - data/lib/fat_table/formatters/latex_formatter.rb +9 -7
 - data/lib/fat_table/table.rb +229 -57
 - data/lib/fat_table/version.rb +1 -1
 - data/lib/fat_table.rb +5 -2
 - data/md/README.md +1 -2
 - metadata +30 -18
 
| 
         @@ -23,12 +23,13 @@ module FatTable 
     | 
|
| 
       23 
23 
     | 
    
         
             
                # specific Formatters.
         
     | 
| 
       24 
24 
     | 
    
         
             
                attr_reader :options
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                # A Hash of Hashes with the outer Hash keyed on location.  The value for 
     | 
| 
       27 
     | 
    
         
            -
                # outer Hash is  
     | 
| 
       28 
     | 
    
         
            -
                # Hash are OpenStruct objects that contain the formatting 
     | 
| 
       29 
     | 
    
         
            -
                # the location and column.  For example, 
     | 
| 
       30 
     | 
    
         
            -
                # is set either true or false depending 
     | 
| 
       31 
     | 
    
         
            -
                # the table body is to have grouping 
     | 
| 
      
 26 
     | 
    
         
            +
                # A Hash of Hashes with the outer Hash keyed on location.  The value for
         
     | 
| 
      
 27 
     | 
    
         
            +
                # the outer Hash is an inner Hash keyed on column names.  The values of
         
     | 
| 
      
 28 
     | 
    
         
            +
                # the inner Hash are OpenStruct objects that contain the formatting
         
     | 
| 
      
 29 
     | 
    
         
            +
                # instructions for the location and column.  For example,
         
     | 
| 
      
 30 
     | 
    
         
            +
                # +format_at[:body][:shares].commas+ is set either true or false depending
         
     | 
| 
      
 31 
     | 
    
         
            +
                # on whether the +:shares+ column in the table body is to have grouping
         
     | 
| 
      
 32 
     | 
    
         
            +
                # commas inserted in the output.
         
     | 
| 
       32 
33 
     | 
    
         
             
                attr_reader :format_at
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
35 
     | 
    
         
             
                # A Hash of the table-wide footers to be added to the output.  The key is a
         
     | 
| 
         @@ -102,6 +103,7 @@ module FatTable 
     | 
|
| 
       102 
103 
     | 
    
         
             
                  @options = options
         
     | 
| 
       103 
104 
     | 
    
         
             
                  @footers = {}
         
     | 
| 
       104 
105 
     | 
    
         
             
                  @gfooters = {}
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
       105 
107 
     | 
    
         
             
                  # Formatting instructions for various "locations" within the Table, as a
         
     | 
| 
       106 
108 
     | 
    
         
             
                  # hash of hashes. The outer hash is keyed on the location, and each inner
         
     | 
| 
       107 
109 
     | 
    
         
             
                  # hash is keyed on either a column sym or a type sym, :string, :numeric,
         
     | 
| 
         @@ -120,14 +122,14 @@ module FatTable 
     | 
|
| 
       120 
122 
     | 
    
         
             
                  yield self if block_given?
         
     | 
| 
       121 
123 
     | 
    
         
             
                end
         
     | 
| 
       122 
124 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 125 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       124 
126 
     | 
    
         | 
| 
       125 
127 
     | 
    
         
             
                ############################################################################
         
     | 
| 
       126 
     | 
    
         
            -
                # Footer methods
         
     | 
| 
      
 128 
     | 
    
         
            +
                # Add Footer methods
         
     | 
| 
       127 
129 
     | 
    
         
             
                #
         
     | 
| 
       128 
130 
     | 
    
         
             
                # A Table may have any number of footers and any number of group footers.
         
     | 
| 
       129 
131 
     | 
    
         
             
                # Footers are not part of the table's data and never participate in any of
         
     | 
| 
       130 
     | 
    
         
            -
                # the  
     | 
| 
      
 132 
     | 
    
         
            +
                # the operation methods on tables.  They are never inherited by output
         
     | 
| 
       131 
133 
     | 
    
         
             
                # tables from input tables in any of the transformation methods.
         
     | 
| 
       132 
134 
     | 
    
         
             
                #
         
     | 
| 
       133 
135 
     | 
    
         
             
                # When output, a table footer will appear at the bottom of the table, and a
         
     | 
| 
         @@ -154,46 +156,81 @@ module FatTable 
     | 
|
| 
       154 
156 
     | 
    
         
             
                # For example, these are valid footer definitions.
         
     | 
| 
       155 
157 
     | 
    
         
             
                #
         
     | 
| 
       156 
158 
     | 
    
         
             
                # Just sum the shares column with a label of 'Total'
         
     | 
| 
       157 
     | 
    
         
            -
                #    
     | 
| 
      
 159 
     | 
    
         
            +
                #   fmtr.footer(:shares)
         
     | 
| 
       158 
160 
     | 
    
         
             
                #
         
     | 
| 
       159 
161 
     | 
    
         
             
                # Change the label and sum the :price column as well
         
     | 
| 
       160 
     | 
    
         
            -
                #    
     | 
| 
      
 162 
     | 
    
         
            +
                #   fmtr.footer('Grand Total', :shares, :price)
         
     | 
| 
       161 
163 
     | 
    
         
             
                #
         
     | 
| 
       162 
164 
     | 
    
         
             
                # Average then show standard deviation of several columns
         
     | 
| 
       163 
     | 
    
         
            -
                #    
     | 
| 
       164 
     | 
    
         
            -
                #    
     | 
| 
      
 165 
     | 
    
         
            +
                #   fmtr.footer.('Average', date: :avg, shares: :avg, price: :avg)
         
     | 
| 
      
 166 
     | 
    
         
            +
                #   fmtr.footer.('Sigma', date: :dev, shares: :dev, price: :dev)
         
     | 
| 
       165 
167 
     | 
    
         
             
                #
         
     | 
| 
       166 
168 
     | 
    
         
             
                # Do some sums and some other aggregates: sum shares, average date and
         
     | 
| 
       167 
169 
     | 
    
         
             
                # price.
         
     | 
| 
       168 
     | 
    
         
            -
                #    
     | 
| 
       169 
     | 
    
         
            -
                def footer(label, *sum_cols, **agg_cols)
         
     | 
| 
       170 
     | 
    
         
            -
                   
     | 
| 
       171 
     | 
    
         
            -
                  foot = {}
         
     | 
| 
      
 170 
     | 
    
         
            +
                #   fmtr.footer.('Summary', :shares, date: :avg, price: :avg)
         
     | 
| 
      
 171 
     | 
    
         
            +
                def footer(label, label_col = nil, *sum_cols, **agg_cols)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  foot = Footer.new(label, table, label_col: label_col)
         
     | 
| 
       172 
173 
     | 
    
         
             
                  sum_cols.each do |h|
         
     | 
| 
       173 
     | 
    
         
            -
                     
     | 
| 
       174 
     | 
    
         
            -
                      raise UserError, "No '#{h}' column in table to sum in the footer"
         
     | 
| 
       175 
     | 
    
         
            -
                    end
         
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                    foot[h] = :sum
         
     | 
| 
      
 174 
     | 
    
         
            +
                    foot.add_value(h, :sum)
         
     | 
| 
       178 
175 
     | 
    
         
             
                  end
         
     | 
| 
       179 
     | 
    
         
            -
                  agg_cols. 
     | 
| 
       180 
     | 
    
         
            -
                     
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
      
 176 
     | 
    
         
            +
                  agg_cols.each_pair do |h, agg|
         
     | 
| 
      
 177 
     | 
    
         
            +
                    foot.add_value(h, agg)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  end
         
     | 
| 
      
 179 
     | 
    
         
            +
                  @footers[label] = foot
         
     | 
| 
      
 180 
     | 
    
         
            +
                  foot
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
       183 
182 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
             
     | 
| 
      
 183 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                # A simpler method for adding a footer to the formatted output having the
         
     | 
| 
      
 186 
     | 
    
         
            +
                # label +label+ placed in the column with the header +label_col+ or in the
         
     | 
| 
      
 187 
     | 
    
         
            +
                # first column if +label_col+ is ommitted.  The remaining hash arguments
         
     | 
| 
      
 188 
     | 
    
         
            +
                # apply an aggregate to the values of the column, which can be:
         
     | 
| 
      
 189 
     | 
    
         
            +
                #
         
     | 
| 
      
 190 
     | 
    
         
            +
                # (1) a symbol representing one of the builtin aggregates, i.e., :first,
         
     | 
| 
      
 191 
     | 
    
         
            +
                # :last, :range, :sum, :count, :min, :max, :avg, :var, :pvar, :dev, :pdev,
         
     | 
| 
      
 192 
     | 
    
         
            +
                # :any?, :all?, :none?, and :one?,
         
     | 
| 
      
 193 
     | 
    
         
            +
                #
         
     | 
| 
      
 194 
     | 
    
         
            +
                # (2) an arbitrary value of one of the four supported types: Boolean,
         
     | 
| 
      
 195 
     | 
    
         
            +
                # DateTime, Numeric, or String: true, false, 3.14159, 'Total debits', or a
         
     | 
| 
      
 196 
     | 
    
         
            +
                # string that is convertible into one of these types by the usual methods
         
     | 
| 
      
 197 
     | 
    
         
            +
                # used in contructing a table,
         
     | 
| 
      
 198 
     | 
    
         
            +
                #
         
     | 
| 
      
 199 
     | 
    
         
            +
                # Examples:
         
     | 
| 
      
 200 
     | 
    
         
            +
                #
         
     | 
| 
      
 201 
     | 
    
         
            +
                # Put the label in the :dickens column of the footer and the maximum value
         
     | 
| 
      
 202 
     | 
    
         
            +
                # from the :alpha column in the :alpha column of the footer.
         
     | 
| 
      
 203 
     | 
    
         
            +
                #
         
     | 
| 
      
 204 
     | 
    
         
            +
                #   fmtr.foot('Best', :dickens, alpha: :max)
         
     | 
| 
      
 205 
     | 
    
         
            +
                #
         
     | 
| 
      
 206 
     | 
    
         
            +
                # Put the label 'Today' in the first column of the footer and today's date
         
     | 
| 
      
 207 
     | 
    
         
            +
                # in the :beta column.
         
     | 
| 
      
 208 
     | 
    
         
            +
                #
         
     | 
| 
      
 209 
     | 
    
         
            +
                #   fmtr.foot('Today', beta: Date.today)
         
     | 
| 
      
 210 
     | 
    
         
            +
                #
         
     | 
| 
      
 211 
     | 
    
         
            +
                # Put the label 'Best' in the :dickens column of the footer and the string
         
     | 
| 
      
 212 
     | 
    
         
            +
                # 'Tale of Two Cities' in the :alpha column of the footer.  Since it can't
         
     | 
| 
      
 213 
     | 
    
         
            +
                # be interpreted as Boolean, Numeric, or DateTime, it is placed in the
         
     | 
| 
      
 214 
     | 
    
         
            +
                # footer literally.
         
     | 
| 
      
 215 
     | 
    
         
            +
                #
         
     | 
| 
      
 216 
     | 
    
         
            +
                #   fmtr.foot('Best', :dickens, alpha: 'A Tale of Two Cities')
         
     | 
| 
      
 217 
     | 
    
         
            +
                #
         
     | 
| 
      
 218 
     | 
    
         
            +
                def foot(label, label_col = nil, **agg_cols)
         
     | 
| 
      
 219 
     | 
    
         
            +
                  foot = Footer.new(label, table, label_col: label_col)
         
     | 
| 
      
 220 
     | 
    
         
            +
                  agg_cols.each_pair do |h, agg|
         
     | 
| 
      
 221 
     | 
    
         
            +
                    foot.add_value(h, agg)
         
     | 
| 
       185 
222 
     | 
    
         
             
                  end
         
     | 
| 
       186 
223 
     | 
    
         
             
                  @footers[label] = foot
         
     | 
| 
       187 
     | 
    
         
            -
                   
     | 
| 
      
 224 
     | 
    
         
            +
                  foot
         
     | 
| 
       188 
225 
     | 
    
         
             
                end
         
     | 
| 
       189 
226 
     | 
    
         | 
| 
       190 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 227 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       191 
228 
     | 
    
         | 
| 
       192 
     | 
    
         
            -
                # Add a group footer to the  
     | 
| 
       193 
     | 
    
         
            -
                # defaulting to 'Total'. After the label, you can given any 
     | 
| 
       194 
     | 
    
         
            -
                # headers (as symbols) for columns to be summed, and then any 
     | 
| 
       195 
     | 
    
         
            -
                # parameters for columns for with to apply an aggregate 
     | 
| 
       196 
     | 
    
         
            -
                # example, these are valid gfooter definitions.
         
     | 
| 
      
 229 
     | 
    
         
            +
                # Add a group footer to the output with a label given in the first
         
     | 
| 
      
 230 
     | 
    
         
            +
                # parameter, defaulting to 'Total'. After the label, you can given any
         
     | 
| 
      
 231 
     | 
    
         
            +
                # number of headers (as symbols) for columns to be summed, and then any
         
     | 
| 
      
 232 
     | 
    
         
            +
                # number of hash parameters for columns for with to apply an aggregate
         
     | 
| 
      
 233 
     | 
    
         
            +
                # other than :sum. For example, these are valid gfooter definitions.
         
     | 
| 
       197 
234 
     | 
    
         
             
                #
         
     | 
| 
       198 
235 
     | 
    
         
             
                # Just sum the shares column with a label of 'Total' tab.gfooter(:shares)
         
     | 
| 
       199 
236 
     | 
    
         
             
                #
         
     | 
| 
         @@ -201,49 +238,51 @@ module FatTable 
     | 
|
| 
       201 
238 
     | 
    
         
             
                #   :shares, :price)
         
     | 
| 
       202 
239 
     | 
    
         
             
                #
         
     | 
| 
       203 
240 
     | 
    
         
             
                # Average then show standard deviation of several columns
         
     | 
| 
       204 
     | 
    
         
            -
                #    
     | 
| 
       205 
     | 
    
         
            -
                #    
     | 
| 
      
 241 
     | 
    
         
            +
                #   fmtr.gfooter.('Average', date: :avg, shares: :avg, price: :avg)
         
     | 
| 
      
 242 
     | 
    
         
            +
                #   fmtr.gfooter.('Sigma', date: dev, shares: :dev, price: :dev)
         
     | 
| 
       206 
243 
     | 
    
         
             
                #
         
     | 
| 
       207 
244 
     | 
    
         
             
                # Do some sums and some other aggregates: sum shares, average date and
         
     | 
| 
       208 
     | 
    
         
            -
                # price.  
     | 
| 
       209 
     | 
    
         
            -
                def gfooter(label, *sum_cols, **agg_cols)
         
     | 
| 
       210 
     | 
    
         
            -
                   
     | 
| 
       211 
     | 
    
         
            -
                  foot = {}
         
     | 
| 
      
 245 
     | 
    
         
            +
                # price. fmtr.gfooter.('Summary', :shares, date: :avg, price: :avg)
         
     | 
| 
      
 246 
     | 
    
         
            +
                def gfooter(label, label_col = nil, *sum_cols, **agg_cols)
         
     | 
| 
      
 247 
     | 
    
         
            +
                  foot = Footer.new(label, table, label_col: label_col, group: true)
         
     | 
| 
       212 
248 
     | 
    
         
             
                  sum_cols.each do |h|
         
     | 
| 
       213 
     | 
    
         
            -
                     
     | 
| 
       214 
     | 
    
         
            -
                      raise UserError, "No '#{h}' column in table for group sum footer"
         
     | 
| 
       215 
     | 
    
         
            -
                    end
         
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
                    foot[h] = :sum
         
     | 
| 
      
 249 
     | 
    
         
            +
                    foot.add_value(h, :sum)
         
     | 
| 
       218 
250 
     | 
    
         
             
                  end
         
     | 
| 
       219 
     | 
    
         
            -
                  agg_cols. 
     | 
| 
       220 
     | 
    
         
            -
                     
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
             
     | 
| 
      
 251 
     | 
    
         
            +
                  agg_cols.each_pair do |h, agg|
         
     | 
| 
      
 252 
     | 
    
         
            +
                    foot.add_value(h, agg)
         
     | 
| 
      
 253 
     | 
    
         
            +
                  end
         
     | 
| 
      
 254 
     | 
    
         
            +
                  @gfooters[label] = foot
         
     | 
| 
      
 255 
     | 
    
         
            +
                  foot
         
     | 
| 
      
 256 
     | 
    
         
            +
                end
         
     | 
| 
       223 
257 
     | 
    
         | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
      
 258 
     | 
    
         
            +
                # Add a group footer to the formatted output.  This method has the same
         
     | 
| 
      
 259 
     | 
    
         
            +
                # usage as the #foot method, but it adds group footers.
         
     | 
| 
      
 260 
     | 
    
         
            +
                def gfoot(label, label_col = nil, **agg_cols)
         
     | 
| 
      
 261 
     | 
    
         
            +
                  foot = Footer.new(label, table, label_col: label_col, group: true)
         
     | 
| 
      
 262 
     | 
    
         
            +
                  agg_cols.each_pair do |h, agg|
         
     | 
| 
      
 263 
     | 
    
         
            +
                    foot.add_value(h, agg)
         
     | 
| 
       225 
264 
     | 
    
         
             
                  end
         
     | 
| 
       226 
265 
     | 
    
         
             
                  @gfooters[label] = foot
         
     | 
| 
       227 
     | 
    
         
            -
                   
     | 
| 
      
 266 
     | 
    
         
            +
                  foot
         
     | 
| 
       228 
267 
     | 
    
         
             
                end
         
     | 
| 
       229 
268 
     | 
    
         | 
| 
       230 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 269 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       231 
270 
     | 
    
         | 
| 
       232 
     | 
    
         
            -
                # Add  
     | 
| 
      
 271 
     | 
    
         
            +
                # Add a footer to sum the +cols+ given as header symbols.
         
     | 
| 
       233 
272 
     | 
    
         
             
                def sum_footer(*cols)
         
     | 
| 
       234 
     | 
    
         
            -
                  footer('Total', *cols)
         
     | 
| 
      
 273 
     | 
    
         
            +
                  footer('Total', nil, *cols)
         
     | 
| 
       235 
274 
     | 
    
         
             
                end
         
     | 
| 
       236 
275 
     | 
    
         | 
| 
       237 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 276 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       238 
277 
     | 
    
         | 
| 
       239 
     | 
    
         
            -
                # Add group footer to sum the +cols+ given as header symbols.
         
     | 
| 
      
 278 
     | 
    
         
            +
                # Add a group footer to sum the +cols+ given as header symbols.
         
     | 
| 
       240 
279 
     | 
    
         
             
                def sum_gfooter(*cols)
         
     | 
| 
       241 
     | 
    
         
            -
                  gfooter('Group Total', *cols)
         
     | 
| 
      
 280 
     | 
    
         
            +
                  gfooter('Group Total', nil, *cols)
         
     | 
| 
       242 
281 
     | 
    
         
             
                end
         
     | 
| 
       243 
282 
     | 
    
         | 
| 
       244 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 283 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       245 
284 
     | 
    
         | 
| 
       246 
     | 
    
         
            -
                # Add  
     | 
| 
      
 285 
     | 
    
         
            +
                # Add a footer to average the +cols+ given as header symbols.
         
     | 
| 
       247 
286 
     | 
    
         
             
                def avg_footer(*cols)
         
     | 
| 
       248 
287 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
       249 
288 
     | 
    
         
             
                  cols.each do |c|
         
     | 
| 
         @@ -252,9 +291,9 @@ module FatTable 
     | 
|
| 
       252 
291 
     | 
    
         
             
                  footer('Average', **hsh)
         
     | 
| 
       253 
292 
     | 
    
         
             
                end
         
     | 
| 
       254 
293 
     | 
    
         | 
| 
       255 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 294 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       256 
295 
     | 
    
         | 
| 
       257 
     | 
    
         
            -
                # Add group footer to average the +cols+ given as header symbols.
         
     | 
| 
      
 296 
     | 
    
         
            +
                # Add a group footer to average the +cols+ given as header symbols.
         
     | 
| 
       258 
297 
     | 
    
         
             
                def avg_gfooter(*cols)
         
     | 
| 
       259 
298 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
       260 
299 
     | 
    
         
             
                  cols.each do |c|
         
     | 
| 
         @@ -263,9 +302,9 @@ module FatTable 
     | 
|
| 
       263 
302 
     | 
    
         
             
                  gfooter('Group Average', **hsh)
         
     | 
| 
       264 
303 
     | 
    
         
             
                end
         
     | 
| 
       265 
304 
     | 
    
         | 
| 
       266 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 305 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       267 
306 
     | 
    
         | 
| 
       268 
     | 
    
         
            -
                # Add  
     | 
| 
      
 307 
     | 
    
         
            +
                # Add a footer to display the minimum value of the +cols+ given as
         
     | 
| 
       269 
308 
     | 
    
         
             
                # header symbols.
         
     | 
| 
       270 
309 
     | 
    
         
             
                def min_footer(*cols)
         
     | 
| 
       271 
310 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
         @@ -275,9 +314,9 @@ module FatTable 
     | 
|
| 
       275 
314 
     | 
    
         
             
                  footer('Minimum', **hsh)
         
     | 
| 
       276 
315 
     | 
    
         
             
                end
         
     | 
| 
       277 
316 
     | 
    
         | 
| 
       278 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 317 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       279 
318 
     | 
    
         | 
| 
       280 
     | 
    
         
            -
                # Add group footer to display the minimum value of the +cols+ given as
         
     | 
| 
      
 319 
     | 
    
         
            +
                # Add a group footer to display the minimum value of the +cols+ given as
         
     | 
| 
       281 
320 
     | 
    
         
             
                # header symbols.
         
     | 
| 
       282 
321 
     | 
    
         
             
                def min_gfooter(*cols)
         
     | 
| 
       283 
322 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
         @@ -287,10 +326,10 @@ module FatTable 
     | 
|
| 
       287 
326 
     | 
    
         
             
                  gfooter('Group Minimum', **hsh)
         
     | 
| 
       288 
327 
     | 
    
         
             
                end
         
     | 
| 
       289 
328 
     | 
    
         | 
| 
       290 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 329 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       291 
330 
     | 
    
         | 
| 
       292 
     | 
    
         
            -
                # Add  
     | 
| 
       293 
     | 
    
         
            -
                #  
     | 
| 
      
 331 
     | 
    
         
            +
                # Add a footer to display the maximum value of the +cols+ given as header
         
     | 
| 
      
 332 
     | 
    
         
            +
                # symbols.
         
     | 
| 
       294 
333 
     | 
    
         
             
                def max_footer(*cols)
         
     | 
| 
       295 
334 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
       296 
335 
     | 
    
         
             
                  cols.each do |c|
         
     | 
| 
         @@ -299,9 +338,9 @@ module FatTable 
     | 
|
| 
       299 
338 
     | 
    
         
             
                  footer('Maximum', **hsh)
         
     | 
| 
       300 
339 
     | 
    
         
             
                end
         
     | 
| 
       301 
340 
     | 
    
         | 
| 
       302 
     | 
    
         
            -
                # :category: Footers
         
     | 
| 
      
 341 
     | 
    
         
            +
                # :category: Add Footers
         
     | 
| 
       303 
342 
     | 
    
         | 
| 
       304 
     | 
    
         
            -
                # Add group footer to display the maximum value of the +cols+ given as
         
     | 
| 
      
 343 
     | 
    
         
            +
                # Add a group footer to display the maximum value of the +cols+ given as
         
     | 
| 
       305 
344 
     | 
    
         
             
                # header symbols.
         
     | 
| 
       306 
345 
     | 
    
         
             
                def max_gfooter(*cols)
         
     | 
| 
       307 
346 
     | 
    
         
             
                  hsh = {}
         
     | 
| 
         @@ -320,7 +359,7 @@ module FatTable 
     | 
|
| 
       320 
359 
     | 
    
         
             
                # columns by using the column head as a key and the value as the format
         
     | 
| 
       321 
360 
     | 
    
         
             
                # instructions. In addition, the keys, :numeric, :string, :datetime,
         
     | 
| 
       322 
361 
     | 
    
         
             
                # :boolean, and :nil, can be used to specify the default format instructions
         
     | 
| 
       323 
     | 
    
         
            -
                # for columns of the given type  
     | 
| 
      
 362 
     | 
    
         
            +
                # for columns of the given type if no other instructions have been given.
         
     | 
| 
       324 
363 
     | 
    
         
             
                #
         
     | 
| 
       325 
364 
     | 
    
         
             
                # Formatting instructions are strings, and what are valid strings depend on
         
     | 
| 
       326 
365 
     | 
    
         
             
                # the type of the column:
         
     | 
| 
         @@ -418,6 +457,11 @@ module FatTable 
     | 
|
| 
       418 
457 
     | 
    
         
             
                # instructions valid for strings are available:
         
     | 
| 
       419 
458 
     | 
    
         
             
                #
         
     | 
| 
       420 
459 
     | 
    
         
             
                # \n\[niltext\]:: render a nil item with the given text.
         
     | 
| 
      
 460 
     | 
    
         
            +
             
     | 
| 
      
 461 
     | 
    
         
            +
                # Set the formatting for all 6 "location" specifiers for the table: (1)
         
     | 
| 
      
 462 
     | 
    
         
            +
                # the headline, :header, (2) the first row of the body, :bfirst, (3) the
         
     | 
| 
      
 463 
     | 
    
         
            +
                # first row of each group, :gfirst, (4) all of the body rows, :body, (5)
         
     | 
| 
      
 464 
     | 
    
         
            +
                # the footer rows, :footer, and (6) the group footer rows, :gfooter.
         
     | 
| 
       421 
465 
     | 
    
         
             
                def format(**fmts)
         
     | 
| 
       422 
466 
     | 
    
         
             
                  %i[header bfirst gfirst body footer gfooter].each do |loc|
         
     | 
| 
       423 
467 
     | 
    
         
             
                    format_for(loc, **fmts)
         
     | 
| 
         @@ -469,11 +513,22 @@ module FatTable 
     | 
|
| 
       469 
513 
     | 
    
         
             
                # cells have the type of the column to which they belong, including all
         
     | 
| 
       470 
514 
     | 
    
         
             
                # cells in group or table footers. See ::format for details on formatting
         
     | 
| 
       471 
515 
     | 
    
         
             
                # directives.
         
     | 
| 
      
 516 
     | 
    
         
            +
                #
         
     | 
| 
      
 517 
     | 
    
         
            +
                # Set the formatting for the given location.
         
     | 
| 
       472 
518 
     | 
    
         
             
                def format_for(location, **fmts)
         
     | 
| 
       473 
519 
     | 
    
         
             
                  unless LOCATIONS.include?(location)
         
     | 
| 
       474 
520 
     | 
    
         
             
                    raise UserError, "unknown format location '#{location}'"
         
     | 
| 
       475 
521 
     | 
    
         
             
                  end
         
     | 
| 
       476 
522 
     | 
    
         | 
| 
      
 523 
     | 
    
         
            +
                  fmts = fmts.transform_keys do |k|
         
     | 
| 
      
 524 
     | 
    
         
            +
                    if k == :nilclass
         
     | 
| 
      
 525 
     | 
    
         
            +
                      :nil
         
     | 
| 
      
 526 
     | 
    
         
            +
                    elsif k == :date
         
     | 
| 
      
 527 
     | 
    
         
            +
                      :datetime
         
     | 
| 
      
 528 
     | 
    
         
            +
                    else
         
     | 
| 
      
 529 
     | 
    
         
            +
                      k
         
     | 
| 
      
 530 
     | 
    
         
            +
                    end
         
     | 
| 
      
 531 
     | 
    
         
            +
                  end
         
     | 
| 
       477 
532 
     | 
    
         
             
                  valid_keys = table.headers + %i[string numeric datetime boolean nil]
         
     | 
| 
       478 
533 
     | 
    
         
             
                  invalid_keys = (fmts.keys - valid_keys).uniq
         
     | 
| 
       479 
534 
     | 
    
         
             
                  unless invalid_keys.empty?
         
     | 
| 
         @@ -483,7 +538,9 @@ module FatTable 
     | 
|
| 
       483 
538 
     | 
    
         | 
| 
       484 
539 
     | 
    
         
             
                  @format_at[location] ||= {}
         
     | 
| 
       485 
540 
     | 
    
         
             
                  table.headers.each do |h|
         
     | 
| 
       486 
     | 
    
         
            -
                    #  
     | 
| 
      
 541 
     | 
    
         
            +
                    # Build the inner hash of formatting instructions for this column h,
         
     | 
| 
      
 542 
     | 
    
         
            +
                    # beginning with the default formatting hash or any existing inner
         
     | 
| 
      
 543 
     | 
    
         
            +
                    # hash.
         
     | 
| 
       487 
544 
     | 
    
         
             
                    format_h =
         
     | 
| 
       488 
545 
     | 
    
         
             
                      if format_at[location][h].empty?
         
     | 
| 
       489 
546 
     | 
    
         
             
                        default_format.dup
         
     | 
| 
         @@ -491,37 +548,40 @@ module FatTable 
     | 
|
| 
       491 
548 
     | 
    
         
             
                        format_at[location][h].to_h
         
     | 
| 
       492 
549 
     | 
    
         
             
                      end
         
     | 
| 
       493 
550 
     | 
    
         | 
| 
      
 551 
     | 
    
         
            +
                    # Merge in string and nil formatting for this column h, but not in
         
     | 
| 
      
 552 
     | 
    
         
            +
                    # header location.  Header is always typed a string, so it will get
         
     | 
| 
      
 553 
     | 
    
         
            +
                    # formatted in type-based formatting below. And headers are never nil.
         
     | 
| 
       494 
554 
     | 
    
         
             
                    unless location == :header
         
     | 
| 
       495 
     | 
    
         
            -
                      # Merge in string and nil formatting, but not in header.  Header is
         
     | 
| 
       496 
     | 
    
         
            -
                      # always typed a string, so it will get formatted in type-based
         
     | 
| 
       497 
     | 
    
         
            -
                      # formatting below. And headers are never nil.
         
     | 
| 
       498 
555 
     | 
    
         
             
                      if fmts.key?(:string)
         
     | 
| 
       499 
     | 
    
         
            -
                        typ_fmt =  
     | 
| 
      
 556 
     | 
    
         
            +
                        typ_fmt = parse_fmt_string(fmts[:string])
         
     | 
| 
       500 
557 
     | 
    
         
             
                        format_h = format_h.merge(typ_fmt)
         
     | 
| 
       501 
558 
     | 
    
         
             
                      end
         
     | 
| 
       502 
559 
     | 
    
         
             
                      if fmts.key?(:nil)
         
     | 
| 
       503 
     | 
    
         
            -
                        typ_fmt =  
     | 
| 
      
 560 
     | 
    
         
            +
                        typ_fmt = parse_nilclass_fmt(fmts[:nil], strict: false).first
         
     | 
| 
       504 
561 
     | 
    
         
             
                        format_h = format_h.merge(typ_fmt)
         
     | 
| 
       505 
562 
     | 
    
         
             
                      end
         
     | 
| 
       506 
563 
     | 
    
         
             
                    end
         
     | 
| 
       507 
     | 
    
         
            -
             
     | 
| 
      
 564 
     | 
    
         
            +
             
     | 
| 
      
 565 
     | 
    
         
            +
                    # Merge in formatting for column h based on the column type, or based
         
     | 
| 
      
 566 
     | 
    
         
            +
                    # on the string type for the header location.
         
     | 
| 
      
 567 
     | 
    
         
            +
                    typ = (location == :header ? :string : table.type(h).as_sym)
         
     | 
| 
       508 
568 
     | 
    
         
             
                    parse_typ_method_name = 'parse_' + typ.to_s + '_fmt'
         
     | 
| 
       509 
569 
     | 
    
         
             
                    if fmts.key?(typ)
         
     | 
| 
       510 
570 
     | 
    
         
             
                      # Merge in type-based formatting
         
     | 
| 
       511 
     | 
    
         
            -
                      typ_fmt = send(parse_typ_method_name, fmts[typ])
         
     | 
| 
      
 571 
     | 
    
         
            +
                      typ_fmt = send(parse_typ_method_name, fmts[typ]).first
         
     | 
| 
       512 
572 
     | 
    
         
             
                      format_h = format_h.merge(typ_fmt)
         
     | 
| 
       513 
573 
     | 
    
         
             
                    end
         
     | 
| 
       514 
574 
     | 
    
         
             
                    if fmts[h]
         
     | 
| 
       515 
575 
     | 
    
         
             
                      # Merge in column formatting
         
     | 
| 
       516 
576 
     | 
    
         
             
                      col_fmt = send(parse_typ_method_name, fmts[h],
         
     | 
| 
       517 
     | 
    
         
            -
                                     strict: location != :header)
         
     | 
| 
      
 577 
     | 
    
         
            +
                                     strict: location != :header).first
         
     | 
| 
       518 
578 
     | 
    
         
             
                      format_h = format_h.merge(col_fmt)
         
     | 
| 
       519 
579 
     | 
    
         
             
                    end
         
     | 
| 
       520 
580 
     | 
    
         | 
| 
      
 581 
     | 
    
         
            +
                    # Copy :body formatting for column h to :bfirst and :gfirst if they
         
     | 
| 
      
 582 
     | 
    
         
            +
                    # still have the default formatting. Can be overridden with a
         
     | 
| 
      
 583 
     | 
    
         
            +
                    # format_for call with those locations.
         
     | 
| 
       521 
584 
     | 
    
         
             
                    if location == :body
         
     | 
| 
       522 
     | 
    
         
            -
                      # Copy :body formatting for column h to :bfirst and :gfirst if they
         
     | 
| 
       523 
     | 
    
         
            -
                      # still have the default formatting. Can be overridden with a
         
     | 
| 
       524 
     | 
    
         
            -
                      # format_for call with those locations.
         
     | 
| 
       525 
585 
     | 
    
         
             
                      format_h.each_pair do |k, v|
         
     | 
| 
       526 
586 
     | 
    
         
             
                        if format_at[:bfirst][h].send(k) == default_format[k]
         
     | 
| 
       527 
587 
     | 
    
         
             
                          format_at[:bfirst][h].send("#{k}=", v)
         
     | 
| 
         @@ -561,8 +621,8 @@ module FatTable 
     | 
|
| 
       561 
621 
     | 
    
         
             
                # string fmt. Raise an error if it contains invalid formatting instructions.
         
     | 
| 
       562 
622 
     | 
    
         
             
                # If fmt contains conflicting instructions, say C and L, there is no
         
     | 
| 
       563 
623 
     | 
    
         
             
                # guarantee which will win, but it will not be considered an error to do so.
         
     | 
| 
       564 
     | 
    
         
            -
                def  
     | 
| 
       565 
     | 
    
         
            -
                  format, fmt =  
     | 
| 
      
 624 
     | 
    
         
            +
                def parse_fmt_string(fmt, strict: true)
         
     | 
| 
      
 625 
     | 
    
         
            +
                  format, fmt = parse_string_fmt(fmt)
         
     | 
| 
       566 
626 
     | 
    
         
             
                  unless fmt.blank? || !strict
         
     | 
| 
       567 
627 
     | 
    
         
             
                    raise UserError, "unrecognized string formatting instructions '#{fmt}'"
         
     | 
| 
       568 
628 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -574,7 +634,7 @@ module FatTable 
     | 
|
| 
       574 
634 
     | 
    
         
             
                # of the instructions and the unconsumed part of the instruction string.
         
     | 
| 
       575 
635 
     | 
    
         
             
                # This is called to cull string-based instructions from a formatting string
         
     | 
| 
       576 
636 
     | 
    
         
             
                # intended for other types, such as numeric, etc.
         
     | 
| 
       577 
     | 
    
         
            -
                def  
     | 
| 
      
 637 
     | 
    
         
            +
                def parse_string_fmt(fmt, strict: true)
         
     | 
| 
       578 
638 
     | 
    
         
             
                  # We parse the more complex formatting constructs first, and after each
         
     | 
| 
       579 
639 
     | 
    
         
             
                  # parse, we remove the matched construct from fmt.  At the end, any
         
     | 
| 
       580 
640 
     | 
    
         
             
                  # remaining characters in fmt should be invalid.
         
     | 
| 
         @@ -587,7 +647,7 @@ module FatTable 
     | 
|
| 
       587 
647 
     | 
    
         
             
                    fmt = fmt.sub($&, '')
         
     | 
| 
       588 
648 
     | 
    
         
             
                  end
         
     | 
| 
       589 
649 
     | 
    
         
             
                  # Nil formatting can apply to strings as well
         
     | 
| 
       590 
     | 
    
         
            -
                  nil_hash, fmt =  
     | 
| 
      
 650 
     | 
    
         
            +
                  nil_hash, fmt = parse_nilclass_fmt(fmt)
         
     | 
| 
       591 
651 
     | 
    
         
             
                  fmt_hash = fmt_hash.merge(nil_hash)
         
     | 
| 
       592 
652 
     | 
    
         
             
                  if fmt =~ /u/
         
     | 
| 
       593 
653 
     | 
    
         
             
                    fmt_hash[:case] = :lower
         
     | 
| 
         @@ -636,11 +696,11 @@ module FatTable 
     | 
|
| 
       636 
696 
     | 
    
         
             
                # instructions and the unconsumed part of the instruction string. This is
         
     | 
| 
       637 
697 
     | 
    
         
             
                # called to cull nil-based instructions from a formatting string intended
         
     | 
| 
       638 
698 
     | 
    
         
             
                # for other types, such as numeric, etc.
         
     | 
| 
       639 
     | 
    
         
            -
                def  
     | 
| 
      
 699 
     | 
    
         
            +
                def parse_nilclass_fmt(fmt, strict: true)
         
     | 
| 
       640 
700 
     | 
    
         
             
                  # We parse the more complex formatting constructs first, and after each
         
     | 
| 
       641 
701 
     | 
    
         
             
                  # parse, we remove the matched construct from fmt.  At the end, any
         
     | 
| 
       642 
702 
     | 
    
         
             
                  # remaining characters in fmt should be invalid.
         
     | 
| 
       643 
     | 
    
         
            -
                  fmt_hash = {}
         
     | 
| 
      
 703 
     | 
    
         
            +
                  fmt_hash, fmt = strict ? [{}, fmt] : parse_string_fmt(fmt)
         
     | 
| 
       644 
704 
     | 
    
         
             
                  if fmt =~ /n\[\s*(?<bdy>[^\]]*)\s*\]/
         
     | 
| 
       645 
705 
     | 
    
         
             
                    fmt_hash[:nil_text] = Regexp.last_match[:bdy].clean
         
     | 
| 
       646 
706 
     | 
    
         
             
                    fmt = fmt.sub(Regexp.last_match[0], '')
         
     | 
| 
         @@ -656,7 +716,7 @@ module FatTable 
     | 
|
| 
       656 
716 
     | 
    
         
             
                  # We parse the more complex formatting constructs first, and after each
         
     | 
| 
       657 
717 
     | 
    
         
             
                  # parse, we remove the matched construct from fmt.  At the end, any
         
     | 
| 
       658 
718 
     | 
    
         
             
                  # remaining characters in fmt should be invalid.
         
     | 
| 
       659 
     | 
    
         
            -
                  fmt_hash, fmt =  
     | 
| 
      
 719 
     | 
    
         
            +
                  fmt_hash, fmt = parse_string_fmt(fmt)
         
     | 
| 
       660 
720 
     | 
    
         
             
                  if fmt =~ /(?<pre>\d+).(?<post>\d+)/
         
     | 
| 
       661 
721 
     | 
    
         
             
                    fmt_hash[:pre_digits] = Regexp.last_match[:pre].to_i
         
     | 
| 
       662 
722 
     | 
    
         
             
                    fmt_hash[:post_digits] = Regexp.last_match[:post].to_i
         
     | 
| 
         @@ -678,7 +738,7 @@ module FatTable 
     | 
|
| 
       678 
738 
     | 
    
         
             
                    raise UserError, "unrecognized numeric formatting instructions '#{fmt}'"
         
     | 
| 
       679 
739 
     | 
    
         
             
                  end
         
     | 
| 
       680 
740 
     | 
    
         | 
| 
       681 
     | 
    
         
            -
                  fmt_hash
         
     | 
| 
      
 741 
     | 
    
         
            +
                  [fmt_hash, fmt]
         
     | 
| 
       682 
742 
     | 
    
         
             
                end
         
     | 
| 
       683 
743 
     | 
    
         | 
| 
       684 
744 
     | 
    
         
             
                # Return a hash that reflects the datetime or string formatting instructions
         
     | 
| 
         @@ -689,20 +749,20 @@ module FatTable 
     | 
|
| 
       689 
749 
     | 
    
         
             
                  # We parse the more complex formatting constructs first, and after each
         
     | 
| 
       690 
750 
     | 
    
         
             
                  # parse, we remove the matched construct from fmt.  At the end, any
         
     | 
| 
       691 
751 
     | 
    
         
             
                  # remaining characters in fmt should be invalid.
         
     | 
| 
       692 
     | 
    
         
            -
                  fmt_hash, fmt =  
     | 
| 
      
 752 
     | 
    
         
            +
                  fmt_hash, fmt = parse_string_fmt(fmt)
         
     | 
| 
       693 
753 
     | 
    
         
             
                  if fmt =~ /d\[(?<bdy>[^\]]*)\]/
         
     | 
| 
       694 
754 
     | 
    
         
             
                    fmt_hash[:date_fmt] = Regexp.last_match[:bdy]
         
     | 
| 
       695 
755 
     | 
    
         
             
                    fmt = fmt.sub(Regexp.last_match[0], '')
         
     | 
| 
       696 
756 
     | 
    
         
             
                  end
         
     | 
| 
       697 
757 
     | 
    
         
             
                  if fmt =~ /D\[(?<bdy>[^\]]*)\]/
         
     | 
| 
       698 
     | 
    
         
            -
                    fmt_hash[: 
     | 
| 
      
 758 
     | 
    
         
            +
                    fmt_hash[:datetime_fmt] = Regexp.last_match[:bdy]
         
     | 
| 
       699 
759 
     | 
    
         
             
                    fmt = fmt.sub(Regexp.last_match[0], '')
         
     | 
| 
       700 
760 
     | 
    
         
             
                  end
         
     | 
| 
       701 
761 
     | 
    
         
             
                  unless fmt.blank? || !strict
         
     | 
| 
       702 
762 
     | 
    
         
             
                    msg = "unrecognized datetime formatting instructions '#{fmt}'"
         
     | 
| 
       703 
763 
     | 
    
         
             
                    raise UserError, msg
         
     | 
| 
       704 
764 
     | 
    
         
             
                  end
         
     | 
| 
       705 
     | 
    
         
            -
                  fmt_hash
         
     | 
| 
      
 765 
     | 
    
         
            +
                  [fmt_hash, fmt]
         
     | 
| 
       706 
766 
     | 
    
         
             
                end
         
     | 
| 
       707 
767 
     | 
    
         | 
| 
       708 
768 
     | 
    
         
             
                # Return a hash that reflects the boolean or string formatting instructions
         
     | 
| 
         @@ -730,7 +790,7 @@ module FatTable 
     | 
|
| 
       730 
790 
     | 
    
         
             
                    fmt_hash[:false_bgcolor] = fbg unless fbg.blank?
         
     | 
| 
       731 
791 
     | 
    
         
             
                    fmt = fmt.sub(Regexp.last_match[0], '')
         
     | 
| 
       732 
792 
     | 
    
         
             
                  end
         
     | 
| 
       733 
     | 
    
         
            -
                  str_fmt_hash, fmt =  
     | 
| 
      
 793 
     | 
    
         
            +
                  str_fmt_hash, fmt = parse_string_fmt(fmt)
         
     | 
| 
       734 
794 
     | 
    
         
             
                  fmt_hash = fmt_hash.merge(str_fmt_hash)
         
     | 
| 
       735 
795 
     | 
    
         
             
                  if fmt =~ /Y/
         
     | 
| 
       736 
796 
     | 
    
         
             
                    fmt_hash[:true_text] = 'Y'
         
     | 
| 
         @@ -751,7 +811,7 @@ module FatTable 
     | 
|
| 
       751 
811 
     | 
    
         
             
                    raise UserError, "unrecognized boolean formatting instructions '#{fmt}'"
         
     | 
| 
       752 
812 
     | 
    
         
             
                  end
         
     | 
| 
       753 
813 
     | 
    
         | 
| 
       754 
     | 
    
         
            -
                  fmt_hash
         
     | 
| 
      
 814 
     | 
    
         
            +
                  [fmt_hash, fmt]
         
     | 
| 
       755 
815 
     | 
    
         
             
                end
         
     | 
| 
       756 
816 
     | 
    
         | 
| 
       757 
817 
     | 
    
         
             
                ############################################################################
         
     | 
| 
         @@ -855,11 +915,16 @@ module FatTable 
     | 
|
| 
       855 
915 
     | 
    
         
             
                    result = val.secs_to_hms
         
     | 
| 
       856 
916 
     | 
    
         
             
                    istruct.commas = false
         
     | 
| 
       857 
917 
     | 
    
         
             
                  elsif istruct.currency
         
     | 
| 
       858 
     | 
    
         
            -
                    prec = istruct.post_digits.zero? ? 2 : istruct.post_digits
         
     | 
| 
       859 
918 
     | 
    
         
             
                    delim = istruct.commas ? ',' : ''
         
     | 
| 
       860 
     | 
    
         
            -
                    result = 
     | 
| 
      
 919 
     | 
    
         
            +
                    result =
         
     | 
| 
      
 920 
     | 
    
         
            +
                    if istruct.post_digits < 0
         
     | 
| 
      
 921 
     | 
    
         
            +
                      val.to_s(:currency, delimiter: delim,
         
     | 
| 
       861 
922 
     | 
    
         
             
                                      unit: FatTable.currency_symbol)
         
     | 
| 
       862 
     | 
    
         
            -
                     
     | 
| 
      
 923 
     | 
    
         
            +
                    else
         
     | 
| 
      
 924 
     | 
    
         
            +
                      val.to_s(:currency, precision: istruct.post_digits, delimiter: delim,
         
     | 
| 
      
 925 
     | 
    
         
            +
                                      unit: FatTable.currency_symbol)
         
     | 
| 
      
 926 
     | 
    
         
            +
                    end
         
     | 
| 
      
 927 
     | 
    
         
            +
                    # istruct.commas = false
         
     | 
| 
       863 
928 
     | 
    
         
             
                  elsif istruct.pre_digits.positive?
         
     | 
| 
       864 
929 
     | 
    
         
             
                    if val.whole?
         
     | 
| 
       865 
930 
     | 
    
         
             
                      # No fractional part, ignore post_digits
         
     | 
| 
         @@ -932,9 +997,10 @@ module FatTable 
     | 
|
| 
       932 
997 
     | 
    
         | 
| 
       933 
998 
     | 
    
         
             
                # :category: Output
         
     | 
| 
       934 
999 
     | 
    
         | 
| 
       935 
     | 
    
         
            -
                # Return  
     | 
| 
       936 
     | 
    
         
            -
                #  
     | 
| 
       937 
     | 
    
         
            -
                #  
     | 
| 
      
 1000 
     | 
    
         
            +
                # Return a representation of the +table+, along with all footers and group
         
     | 
| 
      
 1001 
     | 
    
         
            +
                # footers, as either a string in the target format or as a Ruby data
         
     | 
| 
      
 1002 
     | 
    
         
            +
                # structure if that is the target.  In the latter case, all the cells are
         
     | 
| 
      
 1003 
     | 
    
         
            +
                # converted to strings formatted according to the Formatter's formatting
         
     | 
| 
       938 
1004 
     | 
    
         
             
                # directives given in Formatter.format_for or Formatter.format.
         
     | 
| 
       939 
1005 
     | 
    
         
             
                def output
         
     | 
| 
       940 
1006 
     | 
    
         
             
                  # This results in a hash of two-element arrays. The key is the header and
         
     | 
| 
         @@ -1056,16 +1122,16 @@ module FatTable 
     | 
|
| 
       1056 
1122 
     | 
    
         
             
                  # Don't decorate if this Formatter calls for alignment.  It will be done
         
     | 
| 
       1057 
1123 
     | 
    
         
             
                  # in the second pass.
         
     | 
| 
       1058 
1124 
     | 
    
         
             
                  decorate = !aligned?
         
     | 
| 
       1059 
     | 
    
         
            -
                   
     | 
| 
      
 1125 
     | 
    
         
            +
                  out_rows = []
         
     | 
| 
       1060 
1126 
     | 
    
         
             
                  tbl_row_k = 0
         
     | 
| 
       1061 
1127 
     | 
    
         
             
                  table.groups.each_with_index do |grp, grp_k|
         
     | 
| 
      
 1128 
     | 
    
         
            +
                    # NB: grp is an array of hashes, one for each row in the group.
         
     | 
| 
      
 1129 
     | 
    
         
            +
                    #
         
     | 
| 
       1062 
1130 
     | 
    
         
             
                    # Mark the beginning of a group if this is the first group after the
         
     | 
| 
       1063 
1131 
     | 
    
         
             
                    # header or the second or later group.
         
     | 
| 
       1064 
     | 
    
         
            -
                     
     | 
| 
       1065 
     | 
    
         
            -
                    #  
     | 
| 
       1066 
     | 
    
         
            -
                    grp_col = {}
         
     | 
| 
      
 1132 
     | 
    
         
            +
                    out_rows << nil if include_header_row? || grp_k.positive?
         
     | 
| 
      
 1133 
     | 
    
         
            +
                    # Format group body rows
         
     | 
| 
       1067 
1134 
     | 
    
         
             
                    grp.each_with_index do |row, grp_row_k|
         
     | 
| 
       1068 
     | 
    
         
            -
                      new_row = {}
         
     | 
| 
       1069 
1135 
     | 
    
         
             
                      location =
         
     | 
| 
       1070 
1136 
     | 
    
         
             
                        if tbl_row_k.zero?
         
     | 
| 
       1071 
1137 
     | 
    
         
             
                          :bfirst
         
     | 
| 
         @@ -1074,80 +1140,51 @@ module FatTable 
     | 
|
| 
       1074 
1140 
     | 
    
         
             
                        else
         
     | 
| 
       1075 
1141 
     | 
    
         
             
                          :body
         
     | 
| 
       1076 
1142 
     | 
    
         
             
                        end
         
     | 
| 
       1077 
     | 
    
         
            -
             
     | 
| 
       1078 
     | 
    
         
            -
             
     | 
| 
       1079 
     | 
    
         
            -
             
     | 
| 
      
 1143 
     | 
    
         
            +
             
     | 
| 
      
 1144 
     | 
    
         
            +
                      out_row = {}
         
     | 
| 
      
 1145 
     | 
    
         
            +
                      row.each_pair do |h, v|
         
     | 
| 
       1080 
1146 
     | 
    
         
             
                        istruct = format_at[location][h]
         
     | 
| 
       1081 
     | 
    
         
            -
                         
     | 
| 
       1082 
     | 
    
         
            -
                                                          decorate: decorate)]
         
     | 
| 
      
 1147 
     | 
    
         
            +
                        out_row[h] = [row[h], format_cell(row[h], istruct, decorate: decorate)]
         
     | 
| 
       1083 
1148 
     | 
    
         
             
                      end
         
     | 
| 
       1084 
     | 
    
         
            -
                       
     | 
| 
      
 1149 
     | 
    
         
            +
                      out_rows << [location, out_row]
         
     | 
| 
       1085 
1150 
     | 
    
         
             
                      tbl_row_k += 1
         
     | 
| 
       1086 
1151 
     | 
    
         
             
                    end
         
     | 
| 
       1087 
     | 
    
         
            -
                    #  
     | 
| 
      
 1152 
     | 
    
         
            +
                    # Format group footers
         
     | 
| 
       1088 
1153 
     | 
    
         
             
                    gfooters.each_pair do |label, gfooter|
         
     | 
| 
       1089 
     | 
    
         
            -
                       
     | 
| 
       1090 
     | 
    
         
            -
                       
     | 
| 
       1091 
     | 
    
         
            -
                       
     | 
| 
       1092 
     | 
    
         
            -
             
     | 
| 
       1093 
     | 
    
         
            -
             
     | 
| 
       1094 
     | 
    
         
            -
                        first_h ||= h
         
     | 
| 
       1095 
     | 
    
         
            -
                        gfoot_row[h] =
         
     | 
| 
       1096 
     | 
    
         
            -
                          if gfooter[h]
         
     | 
| 
       1097 
     | 
    
         
            -
                            val = col.send(gfooter[h])
         
     | 
| 
       1098 
     | 
    
         
            -
                            istruct = format_at[:gfooter][h]
         
     | 
| 
       1099 
     | 
    
         
            -
                            [val, format_cell(val, istruct, decorate: decorate)]
         
     | 
| 
       1100 
     | 
    
         
            -
                          else
         
     | 
| 
       1101 
     | 
    
         
            -
                            [nil, '']
         
     | 
| 
       1102 
     | 
    
         
            -
                          end
         
     | 
| 
       1103 
     | 
    
         
            -
                      end
         
     | 
| 
       1104 
     | 
    
         
            -
                      if gfoot_row[first_h].last.blank?
         
     | 
| 
       1105 
     | 
    
         
            -
                        istruct = format_at[:gfooter][first_h]
         
     | 
| 
       1106 
     | 
    
         
            -
                        gfoot_row[first_h] =
         
     | 
| 
       1107 
     | 
    
         
            -
                          [label, format_cell(label, istruct, decorate: decorate)]
         
     | 
| 
      
 1154 
     | 
    
         
            +
                      out_rows << nil
         
     | 
| 
      
 1155 
     | 
    
         
            +
                      gfoot_row = Hash.new([nil, ''])
         
     | 
| 
      
 1156 
     | 
    
         
            +
                      gfooter.to_h(grp_k).each_pair do |h, v|
         
     | 
| 
      
 1157 
     | 
    
         
            +
                        istruct = format_at[:gfooter][h]
         
     | 
| 
      
 1158 
     | 
    
         
            +
                        gfoot_row[h] = [v, format_cell(v, istruct, decorate: decorate)]
         
     | 
| 
       1108 
1159 
     | 
    
         
             
                      end
         
     | 
| 
       1109 
     | 
    
         
            -
                       
     | 
| 
      
 1160 
     | 
    
         
            +
                      out_rows << [:gfooter, gfoot_row]
         
     | 
| 
       1110 
1161 
     | 
    
         
             
                    end
         
     | 
| 
       1111 
1162 
     | 
    
         
             
                  end
         
     | 
| 
       1112 
     | 
    
         
            -
                   
     | 
| 
      
 1163 
     | 
    
         
            +
                  out_rows
         
     | 
| 
       1113 
1164 
     | 
    
         
             
                end
         
     | 
| 
       1114 
1165 
     | 
    
         | 
| 
       1115 
1166 
     | 
    
         
             
                def build_formatted_footers
         
     | 
| 
       1116 
1167 
     | 
    
         
             
                  # Don't decorate if this Formatter calls for alignment.  It will be done
         
     | 
| 
       1117 
1168 
     | 
    
         
             
                  # in the second pass.
         
     | 
| 
       1118 
1169 
     | 
    
         
             
                  decorate = !aligned?
         
     | 
| 
       1119 
     | 
    
         
            -
                   
     | 
| 
       1120 
     | 
    
         
            -
             
     | 
| 
       1121 
     | 
    
         
            -
                  footers.each_pair do |label,  
     | 
| 
       1122 
     | 
    
         
            -
                     
     | 
| 
       1123 
     | 
    
         
            -
                     
     | 
| 
       1124 
     | 
    
         
            -
                     
     | 
| 
       1125 
     | 
    
         
            -
             
     | 
| 
       1126 
     | 
    
         
            -
             
     | 
| 
       1127 
     | 
    
         
            -
                      h = col.header
         
     | 
| 
       1128 
     | 
    
         
            -
                      first_h ||= h
         
     | 
| 
       1129 
     | 
    
         
            -
                      foot_row[h] =
         
     | 
| 
       1130 
     | 
    
         
            -
                        if footer[h]
         
     | 
| 
       1131 
     | 
    
         
            -
                          val = col.send(footer[h])
         
     | 
| 
       1132 
     | 
    
         
            -
                          istruct = format_at[:footer][h]
         
     | 
| 
       1133 
     | 
    
         
            -
                          [val, format_cell(val, istruct, decorate: decorate)]
         
     | 
| 
       1134 
     | 
    
         
            -
                        else
         
     | 
| 
       1135 
     | 
    
         
            -
                          [nil, '']
         
     | 
| 
       1136 
     | 
    
         
            -
                        end
         
     | 
| 
       1137 
     | 
    
         
            -
                    end
         
     | 
| 
       1138 
     | 
    
         
            -
                    # Put the label in the first column of footer unless it has been
         
     | 
| 
       1139 
     | 
    
         
            -
                    # formatted as part of footer.
         
     | 
| 
       1140 
     | 
    
         
            -
                    if foot_row[first_h].last.blank?
         
     | 
| 
       1141 
     | 
    
         
            -
                      istruct = format_at[:footer][first_h]
         
     | 
| 
       1142 
     | 
    
         
            -
                      foot_row[first_h] =
         
     | 
| 
       1143 
     | 
    
         
            -
                        [label, format_cell(label, istruct, decorate: decorate)]
         
     | 
| 
      
 1170 
     | 
    
         
            +
                  out_rows = []
         
     | 
| 
      
 1171 
     | 
    
         
            +
             
     | 
| 
      
 1172 
     | 
    
         
            +
                  footers.each_pair do |label, foot|
         
     | 
| 
      
 1173 
     | 
    
         
            +
                    out_rows << nil
         
     | 
| 
      
 1174 
     | 
    
         
            +
                    foot_row = Hash.new([nil, ''])
         
     | 
| 
      
 1175 
     | 
    
         
            +
                    foot.to_h.each_pair do |h, v|
         
     | 
| 
      
 1176 
     | 
    
         
            +
                      istruct = format_at[:gfooter][h]
         
     | 
| 
      
 1177 
     | 
    
         
            +
                      foot_row[h] = [v, format_cell(v, istruct, decorate: decorate)]
         
     | 
| 
       1144 
1178 
     | 
    
         
             
                    end
         
     | 
| 
       1145 
     | 
    
         
            -
                     
     | 
| 
      
 1179 
     | 
    
         
            +
                    out_rows << [:footer, foot_row]
         
     | 
| 
       1146 
1180 
     | 
    
         
             
                  end
         
     | 
| 
       1147 
     | 
    
         
            -
                   
     | 
| 
      
 1181 
     | 
    
         
            +
                  out_rows
         
     | 
| 
       1148 
1182 
     | 
    
         
             
                end
         
     | 
| 
       1149 
1183 
     | 
    
         | 
| 
       1150 
     | 
    
         
            -
                # Return a hash of the maximum widths of all the given headers and  
     | 
| 
      
 1184 
     | 
    
         
            +
                # Return a hash of the maximum widths of all the given headers and out
         
     | 
| 
      
 1185 
     | 
    
         
            +
                # rows, represented as hashes keyed by column symbol, h, and with a value
         
     | 
| 
      
 1186 
     | 
    
         
            +
                # being a 2-element array of the raw value for the cell and the formatted
         
     | 
| 
      
 1187 
     | 
    
         
            +
                # string representation of the cell.
         
     | 
| 
       1151 
1188 
     | 
    
         
             
                def width_map(formatted_headers, rows)
         
     | 
| 
       1152 
1189 
     | 
    
         
             
                  widths = {}
         
     | 
| 
       1153 
1190 
     | 
    
         
             
                  formatted_headers.each_pair do |h, (_v, fmt_v)|
         
     |