fat_table 0.5.5 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +18 -0
- data/README.org +94 -53
- data/fat_table.gemspec +0 -1
- data/lib/fat_table/column.rb +6 -1
- data/lib/fat_table/convert.rb +10 -1
- data/lib/fat_table/footer.rb +80 -32
- data/lib/fat_table/formatters/formatter.rb +136 -20
- data/lib/fat_table/table.rb +7 -2
- data/lib/fat_table/version.rb +1 -1
- metadata +3 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9cce93aafbc687a599cb04bb11ead038af02ecd433b64168f904ea09707a298
|
4
|
+
data.tar.gz: d61de9b052e89b47c13afa517f1e229d05e42903ba96bb272ddb14ce81c1c563
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f5418bad20549df9794fe7c3c69226d77e017433926b220282d094b66dc5b130de88f93e62d3045763aad065a5e1fb9ffbf626ee7b20e745774ee4653bad649
|
7
|
+
data.tar.gz: 31f4eee0b70bbcef9cba988dc4a2b7b00f1372fe23dd38732b34f520e6a1e2244d73f05abc9963f8b97cb5bd294e4c2ec406dbc0709d59a88355ce4f3676acd7
|
data/.simplecov
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
|
3
|
+
SimpleCov.start do
|
4
|
+
# any custom configs like groups and filters can be here at a central place
|
5
|
+
add_filter '/spec/'
|
6
|
+
add_filter '/tmp/'
|
7
|
+
add_group "Models", "lib/fat_table"
|
8
|
+
add_group "Core Extension", "lib/ext"
|
9
|
+
# After this many seconds between runs, old coverage stats are thrown out,
|
10
|
+
# so 3600 => 1 hour
|
11
|
+
merge_timeout 3600
|
12
|
+
# Make this true to merge rspec and cucumber coverage together
|
13
|
+
use_merging false
|
14
|
+
command_name 'Rspec'
|
15
|
+
nocov_token 'no_cover'
|
16
|
+
# Branch coverage
|
17
|
+
enable_coverage :branch
|
18
|
+
end
|
data/README.org
CHANGED
@@ -32,7 +32,7 @@ The following is for org.
|
|
32
32
|
#+end_src
|
33
33
|
|
34
34
|
#+begin_EXAMPLE
|
35
|
-
Current version is: 0.
|
35
|
+
Current version is: 0.6.0
|
36
36
|
#+end_EXAMPLE
|
37
37
|
|
38
38
|
* Introduction
|
@@ -148,6 +148,7 @@ org-mode buffer as an org-table, ready for processing by other code blocks.
|
|
148
148
|
- [[#type-and-column-priority][Type and Column priority]]
|
149
149
|
- [[#footers][Footers]]
|
150
150
|
- [[#adding-footers][Adding Footers]]
|
151
|
+
- [[#dynamic-labels][Dynamic Labels]]
|
151
152
|
- [[#aggregators][Aggregators]]
|
152
153
|
- [[#footer-objects][Footer objects]]
|
153
154
|
- [[#footer-examples][Footer Examples]]
|
@@ -534,6 +535,7 @@ You can create an empty table with ~FatTable::Table.new~ or, the shorter form,
|
|
534
535
|
in the added rows determine the names of the headers:
|
535
536
|
|
536
537
|
#+BEGIN_SRC ruby :results silent
|
538
|
+
require 'fat_table'
|
537
539
|
tab = FatTable.new
|
538
540
|
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '' }
|
539
541
|
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
@@ -1754,8 +1756,6 @@ available in ~ft_console~ as ~@tab_a~ and ~@tab_b~:
|
|
1754
1756
|
| 2 | Engineering | 2 |
|
1755
1757
|
| 3 | Finance | 7 |
|
1756
1758
|
EOS
|
1757
|
-
|
1758
|
-
tab_b = FatTable.from_org_string(tab_b_str)
|
1759
1759
|
#+END_SRC
|
1760
1760
|
|
1761
1761
|
Here is ~tab_a~:
|
@@ -2465,7 +2465,6 @@ Finance&
|
|
2465
2465
|
{:id=>"3", :dept=>"Finance", :emp_id=>"7"}]
|
2466
2466
|
#+end_EXAMPLE
|
2467
2467
|
|
2468
|
-
|
2469
2468
|
*** Formatting Directives
|
2470
2469
|
The formatting methods explained in the next section all take formatting
|
2471
2470
|
directives as strings in which letters and other characters signify what
|
@@ -2706,12 +2705,27 @@ but not for other nils, such as in the last row of the ~:join_date~ column.
|
|
2706
2705
|
|
2707
2706
|
*** Footers
|
2708
2707
|
**** Adding Footers
|
2709
|
-
You can call the ~
|
2710
|
-
objects to add footers and group footers. Note that all of these
|
2711
|
-
return a ~Footer~ object that can be accessed to extract the computed
|
2712
|
-
All of these methods return the ~FatTable::Footer~ object so
|
2713
|
-
can be used to access the values and other attributes of the
|
2714
|
-
computed. Their signatures are:
|
2708
|
+
You can call the ~foot~, ~gfoot~, ~footer,~ or ~gfooter~, methods on
|
2709
|
+
~Formatter~ objects to add footers and group footers. Note that all of these
|
2710
|
+
methods return a ~Footer~ object that can be accessed to extract the computed
|
2711
|
+
values. All of these methods return the ~FatTable::Footer~ object so
|
2712
|
+
constructed. It can be used to access the values and other attributes of the
|
2713
|
+
footer computed. Their signatures are:
|
2714
|
+
|
2715
|
+
- ~foot(label: label, label_col: nil, **agg_cols)~ :: where ~label~ is a label
|
2716
|
+
to be placed in the column with header ~label_col~, or, if ommitted, in the
|
2717
|
+
first cell of the footer (unless that column is named as one of the
|
2718
|
+
~agg_cols~, in which case the label is ignored), and ~**agg_cols~ is zero or
|
2719
|
+
more hash-like parameters with a column symbol as a key and a valid
|
2720
|
+
aggregate as the value. This causes a table-wide header to be added at the
|
2721
|
+
bottom of the table applying ~agg~, to the ~agg_cols~. A table can have any
|
2722
|
+
number of footers attached, and they will appear at the bottom of the output
|
2723
|
+
table in the order they are given.
|
2724
|
+
|
2725
|
+
- ~gfoot(label: 'Group Total', label_col: nil, **agg_cols)~ :: where the
|
2726
|
+
parameters have the same meaning as for the ~foot~ method, but results in a
|
2727
|
+
footer for each group in the table rather than the table as a whole. These
|
2728
|
+
will appear in the output table just below each group.
|
2715
2729
|
|
2716
2730
|
- ~footer(label, *sum_cols, **agg_cols)~ :: where ~label~ is a label to be
|
2717
2731
|
placed in the first cell of the footer (unless that column is named as one
|
@@ -2724,26 +2738,11 @@ computed. Their signatures are:
|
|
2724
2738
|
number of footers attached, and they will appear at the bottom of the output
|
2725
2739
|
table in the order they are given.
|
2726
2740
|
|
2727
|
-
- ~foot(label, label_col, **agg_cols)~ :: where ~label~ is a label to be
|
2728
|
-
placed in the column with header ~label_col~, or, if ommitted, in the first
|
2729
|
-
cell of the footer (unless that column is named as one of the ~agg_cols~, in
|
2730
|
-
which case the label is ignored), and ~**agg_cols~ is zero or more hash-like
|
2731
|
-
parameters with a column symbol as a key and a valid aggregate as the
|
2732
|
-
value. This causes a table-wide header to be added at the bottom of the
|
2733
|
-
table applying ~agg~, to the ~agg_cols~. A table can have any number of
|
2734
|
-
footers attached, and they will appear at the bottom of the output table in
|
2735
|
-
the order they are given.
|
2736
|
-
|
2737
2741
|
- ~gfooter(label, *sum_cols, **agg_cols)~ :: where the parameters have the
|
2738
2742
|
same meaning as for the ~footer~ method, but results in a footer for each
|
2739
2743
|
group in the table rather than the table as a whole. These will appear in
|
2740
2744
|
the output table just below each group.
|
2741
2745
|
|
2742
|
-
- ~gfoot(label, label_col, **agg_cols)~ :: where the parameters have the same
|
2743
|
-
meaning as for the ~foot~ method, but results in a footer for each group in
|
2744
|
-
the table rather than the table as a whole. These will appear in the output
|
2745
|
-
table just below each group.
|
2746
|
-
|
2747
2746
|
There are also a number of convenience methods for adding common footers:
|
2748
2747
|
- ~sum_footer(*cols)~ :: Add a footer summing the given columns with the label
|
2749
2748
|
'Total'.
|
@@ -2762,6 +2761,39 @@ There are also a number of convenience methods for adding common footers:
|
|
2762
2761
|
- ~max_gfooter(*cols)~ :: Add a group footer showing the maximum for the given
|
2763
2762
|
columns with the label 'Group Maximum'.
|
2764
2763
|
|
2764
|
+
**** Dynamic Labels
|
2765
|
+
Most of the time, you will want a fixed string as the label. However,
|
2766
|
+
especially in the case of a group footer, you might want a dynamically
|
2767
|
+
contructed label. You can use a proc or lambda for a label, and it will be
|
2768
|
+
computed for you. In the case of non-group footers, the proc takes a single
|
2769
|
+
parameter, the footer object itself. This allows you to make the label a
|
2770
|
+
function of other footer values, for example, you could make the label
|
2771
|
+
include the most recent year from the date column:
|
2772
|
+
|
2773
|
+
#+begin_src ruby
|
2774
|
+
fmtr.foot(label: -> (f) { "Average (latest year #{f.column(:date).max.year})" },
|
2775
|
+
temp: :avg)
|
2776
|
+
#+end_src
|
2777
|
+
|
2778
|
+
In the case of a group footer, the lambda or proc may take either one or qtwo parameters.
|
2779
|
+
If it takes one, the parameter is simply the 0-based number of the group:
|
2780
|
+
|
2781
|
+
#+begin_src ruby
|
2782
|
+
fmtr.gfoot(label: -> (k) { "Group #{(k+1).to_roman} Average" }, temp: :avg)
|
2783
|
+
#+end_src
|
2784
|
+
This would format the label with a roman numeral (assuming you defined a
|
2785
|
+
method to do so) for the group number.
|
2786
|
+
|
2787
|
+
If it takes two arguments, the second argument is the footer itself, as with
|
2788
|
+
non-group footers:
|
2789
|
+
|
2790
|
+
#+begin_src ruby
|
2791
|
+
fmtr.gfoot(label: -> (k, f) { "Year #{f.column(:date, k).max.year} Group #{(k+1).to_roman} Average" },
|
2792
|
+
temp: :avg)
|
2793
|
+
#+end_src
|
2794
|
+
This would add the group's year to label, assuming the :date column of the
|
2795
|
+
footer's table had the same year for each item in the group.
|
2796
|
+
|
2765
2797
|
**** Aggregators
|
2766
2798
|
When adding a footer with the above methods, you can specify an aggregator for
|
2767
2799
|
each column named in the ~agg_cols~ parameter. There are several candidates
|
@@ -2778,6 +2810,9 @@ for what you can use for an aggregator:
|
|
2778
2810
|
In the case of datetime columns, these aggrgators convert the dates to
|
2779
2811
|
julian date numbers, perform the calculation, then convert the result back
|
2780
2812
|
to a datetime object.
|
2813
|
+
Apart from the built-in aggrgators, you could define your own by opening the
|
2814
|
+
FatTable::Column class and adding a suitable instance method. In that
|
2815
|
+
case, the symbol could also refer to the method you defined.
|
2781
2816
|
- String :: using a string as an aggrgegator can result in:
|
2782
2817
|
+ the string being converted to an object matching the type of the column
|
2783
2818
|
(for example, using '$1,888' in a numeric column puts the constant number
|
@@ -2792,11 +2827,16 @@ for what you can use for an aggregator:
|
|
2792
2827
|
- A Lambda :: finally, you can provide a lambda for performing arbitrary
|
2793
2828
|
calculations and placing the result in the footer field. The number of
|
2794
2829
|
arguments the lambda takes can vary:
|
2795
|
-
* If the lambda is used in
|
2796
|
-
|
2797
|
-
|
2798
|
-
|
2799
|
-
|
2830
|
+
* If the lambda is used in an ordinary footer column, it can take 0, 1, or 2
|
2831
|
+
arguments: (1) the first argument, if given, will be set to the
|
2832
|
+
FatTable::Column object for that column and (2) the second argument, if
|
2833
|
+
given, will be set to the Footer object itself.
|
2834
|
+
* If the lambda is used in a group footer column, it can 0, 1, 2, or 3
|
2835
|
+
arguments: (1) the first argument, if given, will be set to the group's
|
2836
|
+
0-based index number, (2) the second argument, if given, will be set to a
|
2837
|
+
FatTable::Column object consisting of those items in the group's column,
|
2838
|
+
and (3) the third argument, if given, will be set to the Footer object
|
2839
|
+
itself.
|
2800
2840
|
|
2801
2841
|
**** Footer objects
|
2802
2842
|
Each of the methods for adding a footer to a ~Formatter~ returns a ~Footer~ object
|
@@ -2820,7 +2860,6 @@ their computed values. Here are the accessors available on a
|
|
2820
2860
|
to the footer value for that column, nil for unused columns. Use the index
|
2821
2861
|
~k~ to specify which group to access in the case of a group footer.
|
2822
2862
|
|
2823
|
-
|
2824
2863
|
**** Footer Examples
|
2825
2864
|
As a reminder, here is the table, ~tab_a~ defined earlier:
|
2826
2865
|
|
@@ -2981,18 +3020,20 @@ But it can be any type. Here we pick a lottery winner from the employee ids.
|
|
2981
3020
|
#+end_EXAMPLE
|
2982
3021
|
|
2983
3022
|
***** Lambdas
|
2984
|
-
Perhaps the most flexible form of aggregator is a lambda form. They
|
2985
|
-
or 3 parameters in non-group and group footers, respectively:
|
2986
|
-
|
2987
|
-
- ~->(
|
2988
|
-
paramters: the first, ~
|
2989
|
-
|
2990
|
-
lambda
|
2991
|
-
|
2992
|
-
|
2993
|
-
|
2994
|
-
|
2995
|
-
|
3023
|
+
Perhaps the most flexible form of aggregator is a lambda form. They can take
|
3024
|
+
up to 2 or up to 3 parameters in non-group and group footers, respectively:
|
3025
|
+
|
3026
|
+
- ~->(c, f) {...}~ :: in a normal, non-group footer, you may provide for up to
|
3027
|
+
two paramters: the first, ~c~, if given, will be bound to the column header
|
3028
|
+
to which the lambda is attached and and the second, ~f~, if given will be
|
3029
|
+
bound to the footer in which the lambda appears. A lambda with no
|
3030
|
+
parameters can be provided as well if none are needed.
|
3031
|
+
- ~->(k, c, f)~ :: in a group footer, you may provide for up to three
|
3032
|
+
paramters: the the first, ~k~, if provided, will be bound to the group
|
3033
|
+
number of the group being evaluated, the second, ~c~, if provided, will be
|
3034
|
+
bound to the column header to which the lambda is attached, and the third,
|
3035
|
+
~f~, will be bound to the footer in which the lambda appears. A lambda with
|
3036
|
+
no parameters can be provided as well if none are needed.
|
2996
3037
|
|
2997
3038
|
With the first argument, the footer itself becomes available and with it all
|
2998
3039
|
the things accessible with the footers, including the items in the current
|
@@ -3003,7 +3044,7 @@ Compute the summ of the squares if the items in the ~:age~ column:
|
|
3003
3044
|
tab_a.to_text do |f|
|
3004
3045
|
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]')
|
3005
3046
|
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
3006
|
-
f.footer('SSQ', age: ->(
|
3047
|
+
f.footer('SSQ', age: ->(c) { sa = c.items.map {|x| x * x}.sum; Math.sqrt(sa) })
|
3007
3048
|
end
|
3008
3049
|
#+END_SRC
|
3009
3050
|
|
@@ -3032,8 +3073,8 @@ summ of the squares if the ages in each group:
|
|
3032
3073
|
tab_a.order_with('join_date.year').to_text do |f|
|
3033
3074
|
f.format(numeric: '0.0R,', datetime: 'd[%v]D[%v]', sort_key: '0.0~,')
|
3034
3075
|
f.footer('Average', age: :avg, salary: :avg, join_date: :avg)
|
3035
|
-
f.gfooter('Group SSQ', age: ->(
|
3036
|
-
f.footer('Total SSQ', age: ->(
|
3076
|
+
f.gfooter('Group SSQ', age: ->(k, c, f) { sa = c.items.map {|x| x * x}.sum; Math.sqrt(sa) })
|
3077
|
+
f.footer('Total SSQ', age: ->(c, f) { sa = c.items.map {|x| x * x}.sum; Math.sqrt(sa) })
|
3037
3078
|
end
|
3038
3079
|
#+END_SRC
|
3039
3080
|
|
@@ -3045,19 +3086,19 @@ summ of the squares if the ages in each group:
|
|
3045
3086
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3046
3087
|
| Group SSQ | | 45 | | | | |
|
3047
3088
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3048
|
-
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 |
|
3089
|
+
| 1 | Paul | 32 | California | 20,000 | 13-JUL-2001 | 2001 |
|
3049
3090
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3050
3091
|
| Group SSQ | | 32 | | | | |
|
3051
3092
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3052
|
-
| 2 | Allen | 25 | Texas | | 13-JUL-2005 |
|
3053
|
-
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 |
|
3054
|
-
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 |
|
3093
|
+
| 2 | Allen | 25 | Texas | | 13-JUL-2005 | 2005 |
|
3094
|
+
| 8 | Paul | 24 | Houston | 20,000 | 13-JUL-2005 | 2005 |
|
3095
|
+
| 9 | James | 44 | Norway | 5,000 | 13-JUL-2005 | 2005 |
|
3055
3096
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3056
3097
|
| Group SSQ | | 56 | | | | |
|
3057
3098
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3058
|
-
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 |
|
3059
|
-
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 |
|
3060
|
-
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 |
|
3099
|
+
| 3 | Teddy | 23 | Norway | 20,000 | 13-DEC-2007 | 2007 |
|
3100
|
+
| 4 | Mark | 25 | Rich-Mond | 65,000 | 13-DEC-2007 | 2007 |
|
3101
|
+
| 5 | David | 27 | Texas | 85,000 | 13-DEC-2007 | 2007 |
|
3061
3102
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
3062
3103
|
| Group SSQ | | 43 | | | | |
|
3063
3104
|
+-----------+-------+-----+------------+--------+-------------+----------+
|
data/fat_table.gemspec
CHANGED
@@ -76,7 +76,6 @@ Gem::Specification.new do |spec|
|
|
76
76
|
spec.add_development_dependency 'rubocop-performance'
|
77
77
|
spec.add_development_dependency 'simplecov'
|
78
78
|
|
79
|
-
spec.add_runtime_dependency 'activesupport', '>3.0'
|
80
79
|
spec.add_runtime_dependency 'fat_core', '>= 4.9.0'
|
81
80
|
spec.add_runtime_dependency 'rainbow'
|
82
81
|
spec.add_runtime_dependency 'sequel'
|
data/lib/fat_table/column.rb
CHANGED
@@ -173,7 +173,12 @@ module FatTable
|
|
173
173
|
# Column. This makes Columns Enumerable, so all the Enumerable methods are
|
174
174
|
# available on a Column.
|
175
175
|
def each
|
176
|
-
|
176
|
+
if block_given?
|
177
|
+
items.each { |itm| yield itm }
|
178
|
+
self
|
179
|
+
else
|
180
|
+
to_enum(:each)
|
181
|
+
end
|
177
182
|
end
|
178
183
|
|
179
184
|
##########################################################################
|
data/lib/fat_table/convert.rb
CHANGED
@@ -161,13 +161,22 @@ module FatTable
|
|
161
161
|
def self.convert_to_numeric(val)
|
162
162
|
return BigDecimal(val, Float::DIG) if val.is_a?(Float)
|
163
163
|
return val if val.is_a?(Numeric)
|
164
|
+
|
164
165
|
# Eliminate any commas, $'s (or other currency symbol), or _'s.
|
165
166
|
cursym = Regexp.quote(FatTable.currency_symbol)
|
166
167
|
clean_re = /[,_#{cursym}]/
|
167
168
|
val = val.to_s.clean.gsub(clean_re, '')
|
168
169
|
return nil if val.blank?
|
170
|
+
|
169
171
|
case val
|
170
|
-
when
|
172
|
+
when /\A[-+]?\d+\.\z/
|
173
|
+
# Catch quirk in Ruby's BigDecimal converter where a number ending in
|
174
|
+
# a decimal but with no digits after the decimal throws an
|
175
|
+
# ArgumentError as an invalid value for BigDecimal(). Just append a
|
176
|
+
# '0'.
|
177
|
+
val += '0'
|
178
|
+
BigDecimal(val.to_s.clean)
|
179
|
+
when /(\A[-+]?\d+\.\d+\z)|(\A[-+]?\d*\.\d+\z)/
|
171
180
|
BigDecimal(val.to_s.clean)
|
172
181
|
when /\A[-+]?[\d]+\z/
|
173
182
|
val.to_i
|
data/lib/fat_table/footer.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module FatTable
|
4
4
|
class Footer
|
5
|
-
attr_reader :table, :
|
5
|
+
attr_reader :table, :label_col, :values, :group
|
6
6
|
|
7
7
|
###########################################################################
|
8
8
|
# Constructors
|
@@ -15,6 +15,7 @@ module FatTable
|
|
15
15
|
# for the footer are added later with the #add_value method.
|
16
16
|
def initialize(label = 'Total', table, label_col: nil, group: false)
|
17
17
|
@label = label
|
18
|
+
|
18
19
|
unless table.is_a?(Table)
|
19
20
|
raise ArgumentError, 'Footer.new needs a table argument'
|
20
21
|
end
|
@@ -30,14 +31,7 @@ module FatTable
|
|
30
31
|
@group = group
|
31
32
|
@group_cols = {}
|
32
33
|
@values = {}
|
33
|
-
|
34
|
-
@values[@label_col] = []
|
35
|
-
table.number_of_groups.times do
|
36
|
-
@values[@label_col] << @label
|
37
|
-
end
|
38
|
-
else
|
39
|
-
@values[@label_col] = [@label]
|
40
|
-
end
|
34
|
+
insert_labels_in_label_col
|
41
35
|
make_accessor_methods
|
42
36
|
end
|
43
37
|
|
@@ -71,6 +65,11 @@ module FatTable
|
|
71
65
|
end
|
72
66
|
end
|
73
67
|
|
68
|
+
# Return the value of the label, for the kth group if grouped.
|
69
|
+
def label(k = 0)
|
70
|
+
@values[@label_col][k]
|
71
|
+
end
|
72
|
+
|
74
73
|
# :category: Accessors
|
75
74
|
|
76
75
|
# Return the value of under the +key+ header, or if this is a group
|
@@ -108,8 +107,10 @@ module FatTable
|
|
108
107
|
if group && k.nil?
|
109
108
|
raise ArgumentError, 'Footer#column(h, k) missing the group number argument k'
|
110
109
|
end
|
110
|
+
|
111
111
|
if group
|
112
|
-
|
112
|
+
@group_cols[h] ||= table.group_cols(h)
|
113
|
+
@group_cols[h][k]
|
113
114
|
else
|
114
115
|
table.column(h)
|
115
116
|
end
|
@@ -151,14 +152,13 @@ module FatTable
|
|
151
152
|
|
152
153
|
# Evaluate the given agg for the header col and, in the case of a group
|
153
154
|
# footer, the group k.
|
154
|
-
def calc_val(agg,
|
155
|
-
column =
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
end
|
155
|
+
def calc_val(agg, h, k = nil)
|
156
|
+
column = column(h, k)
|
157
|
+
|
158
|
+
# Convert Date and Time objects to DateTime
|
159
|
+
if [Date, Time].include?(agg.class)
|
160
|
+
agg = agg.to_datetime
|
161
|
+
end
|
162
162
|
|
163
163
|
case agg
|
164
164
|
when Symbol
|
@@ -179,29 +179,77 @@ module FatTable
|
|
179
179
|
when Proc
|
180
180
|
result =
|
181
181
|
if group
|
182
|
-
|
183
|
-
|
182
|
+
case agg.arity
|
183
|
+
when 0
|
184
|
+
agg.call
|
185
|
+
when 1
|
186
|
+
agg.call(k)
|
187
|
+
when 2
|
188
|
+
agg.call(k, column)
|
189
|
+
when 3
|
190
|
+
agg.call(k, column, self)
|
191
|
+
else
|
192
|
+
msg = 'a lambda used in a group footer may have 0 to 3 three arguments: (k, c, f)'
|
184
193
|
raise ArgumentError, msg
|
185
194
|
end
|
186
|
-
agg.call(self, col, k)
|
187
195
|
else
|
188
|
-
|
189
|
-
|
196
|
+
case agg.arity
|
197
|
+
when 0
|
198
|
+
agg.call
|
199
|
+
when 1
|
200
|
+
agg.call(column)
|
201
|
+
when 2
|
202
|
+
agg.call(column, self)
|
203
|
+
else
|
204
|
+
msg = 'a lambda used in a non-group footer may have 0 to 2 arguments: (c, f)'
|
190
205
|
raise ArgumentError, msg
|
191
206
|
end
|
192
|
-
agg.call(self, col)
|
193
207
|
end
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
208
|
+
# Pass the result back into this method as the new agg
|
209
|
+
calc_val(result, h, k)
|
210
|
+
else
|
211
|
+
agg.to_s
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Insert a possibly calculated value for the label in the appropriate
|
216
|
+
# @values column.
|
217
|
+
def insert_labels_in_label_col
|
218
|
+
if group
|
219
|
+
@values[@label_col] = []
|
220
|
+
table.number_of_groups.times do |k|
|
221
|
+
@values[@label_col] << calc_label(k)
|
222
|
+
end
|
223
|
+
else
|
224
|
+
@values[@label_col] = [calc_label]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Calculate the label for the kth group, using k = nil for non-group
|
229
|
+
# footers. If the label is a proc, call it with the group number.
|
230
|
+
def calc_label(k = nil)
|
231
|
+
case @label
|
232
|
+
when Proc
|
233
|
+
case @label.arity
|
234
|
+
when 0
|
235
|
+
@label.call
|
236
|
+
when 1
|
237
|
+
k ? @label.call(k) : @label.call(self)
|
238
|
+
when 2
|
239
|
+
if k
|
240
|
+
@label.call(k, self)
|
241
|
+
else
|
242
|
+
raise ArgumentError, "a non-group footer label proc may only have 1 argument for the containing footer f"
|
243
|
+
end
|
200
244
|
else
|
201
|
-
|
245
|
+
if k
|
246
|
+
raise ArgumentError, "group footer label proc may only have 0, 1, or 2 arguments for group number k and containing footer f"
|
247
|
+
else
|
248
|
+
raise ArgumentError, "a non-group footer label proc may only have 0 or 1 arguments for the containing footer f"
|
249
|
+
end
|
202
250
|
end
|
203
251
|
else
|
204
|
-
|
252
|
+
@label.to_s
|
205
253
|
end
|
206
254
|
end
|
207
255
|
|
@@ -181,41 +181,74 @@ module FatTable
|
|
181
181
|
end
|
182
182
|
|
183
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
184
|
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
185
|
+
# A keyword method for adding a footer to the formatted output having the
|
186
|
+
# label +label:+ (default 'Total') placed in the column with the header
|
187
|
+
# +label_col:+ or in the first column if +label_col+ is ommitted.
|
188
|
+
# This assigns a fixed group label to be placed in the :date column:
|
189
|
+
# #+begin_src ruby
|
190
|
+
# fmtr.foot(label: "Year's Average", label_col: :date, temp: avg)
|
191
|
+
# #+end_src
|
192
|
+
#
|
193
|
+
# Besides being a fixed string, the +label:+ can also be a proc or lambda
|
194
|
+
# taking one argument, the foooter itself.
|
195
|
+
# Thus, a label such as:
|
196
|
+
#
|
197
|
+
# #+begin_src ruby
|
198
|
+
# fmtr.foot(label: -> (f) { "Average (latest year #{f.column(:date).max.year})" },
|
199
|
+
# temp: :avg)
|
200
|
+
# #+end_src
|
201
|
+
# And this would add the highest number to label, assuming the :date column
|
202
|
+
# of the footer's table had the year for each item.
|
203
|
+
#
|
204
|
+
# The remaining hash arguments apply an aggregate to the values of the
|
205
|
+
# column, which can be:
|
206
|
+
#
|
207
|
+
# 1. a symbol representing one of the builtin aggregates, i.e., :first,
|
208
|
+
# :last, :range, :sum, :count, :min, :max, :avg, :var, :pvar, :dev,
|
209
|
+
# :pdev, :any?, :all?, :none?, and :one?, or a symbol for your own
|
210
|
+
# aggregate defined as an instance method on FatTable::Column.
|
211
|
+
# 2. a fixed string, but it the string can be converted into the Column's
|
212
|
+
# type, it will be converted, so the string '3.14159' will be converted
|
213
|
+
# to 3.14159 in a Numeric column.
|
214
|
+
# 3. a value of the Column's type, so Date.today would simply be evaluated
|
215
|
+
# for a Numeric column.
|
216
|
+
# 4. most flexibly of all, a proc or lambda taking arguments: f, the
|
217
|
+
# footer object itself; c, the column (or in the case of a group
|
218
|
+
# footer, the sub-column) corresponding to the current header, and in
|
219
|
+
# the case of a group footer, k, the number of the group (0-based).
|
220
|
+
# 5. Any other value is converted to a string with #to_s.
|
198
221
|
#
|
199
222
|
# Examples:
|
200
223
|
#
|
201
224
|
# Put the label in the :dickens column of the footer and the maximum value
|
202
225
|
# from the :alpha column in the :alpha column of the footer.
|
203
226
|
#
|
204
|
-
# fmtr.foot('Best', :dickens, alpha: :max)
|
227
|
+
# fmtr.foot(label: 'Best', label_col: :dickens, alpha: :max)
|
205
228
|
#
|
206
229
|
# Put the label 'Today' in the first column of the footer and today's date
|
207
230
|
# in the :beta column.
|
208
231
|
#
|
209
|
-
# fmtr.foot('Today', beta: Date.today)
|
232
|
+
# fmtr.foot(label: 'Today', beta: Date.today)
|
210
233
|
#
|
211
234
|
# Put the label 'Best' in the :dickens column of the footer and the string
|
212
235
|
# 'Tale of Two Cities' in the :alpha column of the footer. Since it can't
|
213
236
|
# be interpreted as Boolean, Numeric, or DateTime, it is placed in the
|
214
237
|
# footer literally.
|
215
238
|
#
|
216
|
-
# fmtr.foot('Best', :dickens, alpha: 'A Tale of Two
|
239
|
+
# fmtr.foot(label: 'Best', label_col: :dickens, alpha: 'A Tale of Two
|
240
|
+
# Cities')
|
241
|
+
#
|
242
|
+
# Use a lambda to calculate the value to be placed in the column :gamma.
|
243
|
+
#
|
244
|
+
# fmtr.foot(label: 'Gamma', beta: :avg, gamma: ->(f, c) {
|
245
|
+
# (Math.gamma(c.count) + f[:beta] } )
|
217
246
|
#
|
218
|
-
|
247
|
+
# Note that this way a footer can be made a function of the other footer
|
248
|
+
# values (using f[:other_col]) as well as the Column object corresponding
|
249
|
+
# to the lamda's column.
|
250
|
+
#
|
251
|
+
def foot(label: 'Total', label_col: nil, **agg_cols)
|
219
252
|
foot = Footer.new(label, table, label_col: label_col)
|
220
253
|
agg_cols.each_pair do |h, agg|
|
221
254
|
foot.add_value(h, agg)
|
@@ -255,9 +288,92 @@ module FatTable
|
|
255
288
|
foot
|
256
289
|
end
|
257
290
|
|
258
|
-
# Add
|
259
|
-
#
|
260
|
-
|
291
|
+
# :category: Add Footers
|
292
|
+
#
|
293
|
+
# A keyword method for adding a group footer to the formatted output
|
294
|
+
# having the label +label:+ (default 'Total') placed in the column with
|
295
|
+
# the header +label_col:+ or in the first column if +label_col+ is
|
296
|
+
# ommitted.
|
297
|
+
#
|
298
|
+
# This assigns a fixed group label to be placed in the :date column:
|
299
|
+
# #+begin_src ruby
|
300
|
+
# fmtr.gfoot(label: "Year's Average", label_col: :date, temp: avg)
|
301
|
+
# #+end_src
|
302
|
+
#
|
303
|
+
# Besides being a fixed string, the +label:+ can also be a proc or lambda
|
304
|
+
# taking one or two arguments. In the one argument form, the argument is
|
305
|
+
# the group number k. If a second argument is specified, the foooter
|
306
|
+
# itself is passed as the argument. Thus, a label such as:
|
307
|
+
#
|
308
|
+
# #+begin_src ruby
|
309
|
+
# fmtr.gfoot(label: -> (k) { "Group #{(k+1).to_roman} Average" }, temp: :avg)
|
310
|
+
# #+end_src
|
311
|
+
# This would format the label with a roman numeral (assuming you defined a
|
312
|
+
# method to do so) for the group number.
|
313
|
+
#
|
314
|
+
# #+begin_src ruby
|
315
|
+
# fmtr.gfoot(label: -> (k, f) { "Year #{f.column(:date, k).max.year} Group #{(k+1).to_roman} Average" },
|
316
|
+
# temp: :avg)
|
317
|
+
# #+end_src
|
318
|
+
# And this would add the group's year to label, assuming the :date column
|
319
|
+
# of the footer's table had the same year for each item in the group.
|
320
|
+
#
|
321
|
+
#
|
322
|
+
# The remaining hash arguments apply an aggregate to the values
|
323
|
+
# of the column, which can be:
|
324
|
+
#
|
325
|
+
# 1. a symbol representing one of the builtin aggregates, i.e., :first,
|
326
|
+
# :last, :range, :sum, :count, :min, :max, :avg, :var, :pvar, :dev,
|
327
|
+
# :pdev, :any?, :all?, :none?, and :one?, or a symbol for your own
|
328
|
+
# aggregate defined as an instance method on FatTable::Column.
|
329
|
+
# 2. a fixed string, but it the string can be converted into the Column's
|
330
|
+
# type, it will be converted, so the string '3.14159' will be converted
|
331
|
+
# to 3.14159 in a Numeric column.
|
332
|
+
# 3. a value of the Column's type, so Date.today would simply be evaluated
|
333
|
+
# for a Numeric column.
|
334
|
+
# 4. most flexibly of all, a proc or lambda taking arguments: f, the
|
335
|
+
# footer object itself; c, the column (or in the case of a group
|
336
|
+
# footer, the sub-column) corresponding to the current header, and k,
|
337
|
+
# this group's group number (0-based).
|
338
|
+
# 5. Any other value is converted to a string with #to_s.
|
339
|
+
#
|
340
|
+
# Examples:
|
341
|
+
#
|
342
|
+
# Put the label in the :dickens column of the footer and the maximum value
|
343
|
+
# from the :alpha column in the :alpha column of the footer.
|
344
|
+
#
|
345
|
+
# #+begin_src ruby
|
346
|
+
# fmtr.gfoot(label: 'Best', label_col: :dickens, alpha: :max)
|
347
|
+
# #+end_src
|
348
|
+
#
|
349
|
+
# Put the label 'Today' in the first column of the footer and today's date
|
350
|
+
# in the :beta column.
|
351
|
+
#
|
352
|
+
# #+begin_src ruby
|
353
|
+
# fmtr.gfoot(label: 'Today', beta: Date.today)
|
354
|
+
# #+end_src
|
355
|
+
#
|
356
|
+
# Put the label 'Best' in the :dickens column of the footer and the string
|
357
|
+
# 'Tale of Two Cities' in the :alpha column of the footer. Since it can't
|
358
|
+
# be interpreted as Boolean, Numeric, or DateTime, it is placed in the
|
359
|
+
# footer literally.
|
360
|
+
#
|
361
|
+
# #+begin_src ruby
|
362
|
+
# fmtr.gfoot(label: 'Best', label_col: :dickens, alpha: 'A Tale of Two
|
363
|
+
# Cities')
|
364
|
+
# #+end_src
|
365
|
+
#
|
366
|
+
# Use a lambda to calculate the value to be placed in the column :gamma.
|
367
|
+
#
|
368
|
+
# #+begin_src ruby
|
369
|
+
# fmtr.gfoot(label: 'Gamma', beta: :avg, gamma: ->(f, c) {
|
370
|
+
# (Math.gamma(c.count) + f[:beta] } )
|
371
|
+
# #+end_src
|
372
|
+
#
|
373
|
+
# Note that this way a footer can be made a function of the other footer
|
374
|
+
# values (using f[:other_col]) as well as the Column object corresponding
|
375
|
+
# to the lamda's column.
|
376
|
+
def gfoot(label: 'Group Total', label_col: nil, **agg_cols)
|
261
377
|
foot = Footer.new(label, table, label_col: label_col, group: true)
|
262
378
|
agg_cols.each_pair do |h, agg|
|
263
379
|
foot.add_value(h, agg)
|
data/lib/fat_table/table.rb
CHANGED
@@ -530,8 +530,13 @@ module FatTable
|
|
530
530
|
|
531
531
|
# Yield each row of the table as a Hash with the column symbols as keys.
|
532
532
|
def each
|
533
|
-
|
534
|
-
|
533
|
+
if block_given?
|
534
|
+
rows.each do |row|
|
535
|
+
yield row
|
536
|
+
end
|
537
|
+
self
|
538
|
+
else
|
539
|
+
to_enum(:each)
|
535
540
|
end
|
536
541
|
end
|
537
542
|
|
data/lib/fat_table/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fat_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel E. Doherty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -178,20 +178,6 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: activesupport
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - ">"
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: '3.0'
|
188
|
-
type: :runtime
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - ">"
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: '3.0'
|
195
181
|
- !ruby/object:Gem::Dependency
|
196
182
|
name: fat_core
|
197
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -287,6 +273,7 @@ files:
|
|
287
273
|
- ".gitignore"
|
288
274
|
- ".rspec"
|
289
275
|
- ".rubocop.yml"
|
276
|
+
- ".simplecov"
|
290
277
|
- ".travis.yml"
|
291
278
|
- ".yardopts"
|
292
279
|
- Gemfile
|