fat_table 0.9.9 → 1.0.0
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/.github/workflows/ruby-with-dbs.yml +180 -0
- data/.gitignore +4 -1
- data/.rubocop.yml +9 -2
- data/.simplecov +2 -0
- data/.yardopts +4 -5
- data/CHANGELOG.md +303 -0
- data/CHANGELOG.org +145 -0
- data/Gemfile +5 -7
- data/README.md +2961 -0
- data/README.org +1234 -914
- data/Rakefile +5 -0
- data/bin/ft_console +1 -0
- data/fat_table.gemspec +2 -0
- data/lib/{ext → core_ext}/array.rb +2 -0
- data/lib/core_ext/numeric_string.rb +196 -0
- data/lib/core_ext.rb +4 -0
- data/lib/fat_table/column.rb +5 -1
- data/lib/fat_table/convert.rb +2 -0
- data/lib/fat_table/footer.rb +1 -1
- data/lib/fat_table/formatters/formatter.rb +26 -52
- data/lib/fat_table/formatters/latex_formatter.rb +2 -2
- data/lib/fat_table/table.rb +8 -8
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +51 -26
- metadata +11 -14
- data/examples/create_trans.sql +0 -14
- data/examples/quick.pdf +0 -0
- data/examples/quick.png +0 -0
- data/examples/quick.ppm +0 -0
- data/examples/quick.tex +0 -8
- data/examples/quick_small.png +0 -0
- data/examples/quicktable.tex +0 -123
- data/examples/trades.db +0 -0
- data/examples/trans.csv +0 -13
- /data/{lib/fat_table/formatters → data}/xcolors.txt +0 -0
data/Rakefile
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'bundler/gem_tasks'
|
|
2
4
|
require 'rspec/core/rake_task'
|
|
3
5
|
require 'rdoc/task'
|
|
@@ -13,4 +15,7 @@ end
|
|
|
13
15
|
|
|
14
16
|
RSpec::Core::RakeTask.new(:spec)
|
|
15
17
|
|
|
18
|
+
require "gem_docs"
|
|
19
|
+
GemDocs.install
|
|
20
|
+
|
|
16
21
|
task :default => [:spec, :rubocop]
|
data/bin/ft_console
CHANGED
data/fat_table.gemspec
CHANGED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'debug'
|
|
4
|
+
|
|
5
|
+
# This refinement contains methods that extend the String class to massage
|
|
6
|
+
# strings that represent numbers, such as adding commas, pre- and
|
|
7
|
+
# post-padding, etc.
|
|
8
|
+
module NumericString
|
|
9
|
+
# You can control how NumericString behaves by supplying a new
|
|
10
|
+
# NumericString::Config to the config: parameter of these methods. Calling
|
|
11
|
+
# `NumericString::Config.build` gives you the deafult configuration with
|
|
12
|
+
# whatever overrides you give it. You can also modify an existing config
|
|
13
|
+
# with some overrides. For example:
|
|
14
|
+
#
|
|
15
|
+
# #+begin_src ruby
|
|
16
|
+
# cfg = NumericString::Config.build(group_size: 4)
|
|
17
|
+
# cfg2 = cfg.with(group_char: '_')
|
|
18
|
+
#
|
|
19
|
+
# "1234567.89".add_grouping(config: cfg)
|
|
20
|
+
# => "123,4567.89"
|
|
21
|
+
# "1234567.89".add_grouping(config: cfg2)
|
|
22
|
+
# => '123_4567.89'
|
|
23
|
+
# #+end_src
|
|
24
|
+
Config = Struct.new(
|
|
25
|
+
:group_char,
|
|
26
|
+
:group_size,
|
|
27
|
+
:decimal_char,
|
|
28
|
+
:currency_symbol,
|
|
29
|
+
:pre_pad_char,
|
|
30
|
+
:post_pad_char,
|
|
31
|
+
keyword_init: true,
|
|
32
|
+
) do
|
|
33
|
+
DEFAULTS = {
|
|
34
|
+
group_char: ',',
|
|
35
|
+
group_size: 3,
|
|
36
|
+
decimal_char: '.',
|
|
37
|
+
currency_symbol: '$',
|
|
38
|
+
pre_pad_char: '0',
|
|
39
|
+
post_pad_char: '0',
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
def self.default
|
|
43
|
+
@default ||= new(**DEFAULTS).freeze
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Build from defaults, overriding selectively
|
|
47
|
+
def self.build(**overrides)
|
|
48
|
+
new(**DEFAULTS.merge(overrides)).freeze
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Clone-with-changes (very Ruby, very nice)
|
|
52
|
+
def with(**overrides)
|
|
53
|
+
self.class.build(
|
|
54
|
+
group_char: overrides.fetch(:group_char, group_char),
|
|
55
|
+
group_size: overrides.fetch(:group_size, group_size),
|
|
56
|
+
decimal_char: overrides.fetch(:decimal_char, decimal_char),
|
|
57
|
+
currency_symbol: overrides.fetch(:currency_symbol, currency_symbol),
|
|
58
|
+
pre_pad_char: overrides.fetch(:pre_pad_char, pre_pad_char),
|
|
59
|
+
post_pad_char: overrides.fetch(:post_pad_char, post_pad_char),
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
refine String do
|
|
65
|
+
# If self is a valid decimal number, add grouping commas to the whole
|
|
66
|
+
# part, retaining any fractional part and currency symbol undisturbed.
|
|
67
|
+
# The optional cond: parameter can contain a test to determine if the
|
|
68
|
+
# grouping ought to be performed. If (1) self is not a valid decimal
|
|
69
|
+
# number string, (2) the whole part already contains grouping characters,
|
|
70
|
+
# or (3) cond: is falsey, return self.
|
|
71
|
+
def add_grouping(cond: true, config: Config.default)
|
|
72
|
+
return self unless cond
|
|
73
|
+
return self unless valid_num?(config:)
|
|
74
|
+
|
|
75
|
+
cur, whole, frac = cur_whole_frac(config:)
|
|
76
|
+
return self if whole.include?(config.group_char)
|
|
77
|
+
|
|
78
|
+
whole = whole.split('').reverse
|
|
79
|
+
.each_slice(config.group_size).to_a
|
|
80
|
+
.map { |a| a.reverse.join }
|
|
81
|
+
.reverse
|
|
82
|
+
.join(config.group_char)
|
|
83
|
+
cur + whole + frac
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
alias_method :add_commas, :add_grouping
|
|
87
|
+
|
|
88
|
+
# If self is a valid decimal number, add the currency symbol (per
|
|
89
|
+
# NumericString::Config.currency_symbol) to the front of the number
|
|
90
|
+
# string, retaining any grouping characters undisturbed. The optional
|
|
91
|
+
# cond: parameter can contain a test to determine if the currency symbol
|
|
92
|
+
# ought to be pre-pended. If (1) self is not a valid decimal number
|
|
93
|
+
# string, (2) the currency symbol is already present, or (3) cond: is
|
|
94
|
+
# falsey, return self.
|
|
95
|
+
def add_currency(cond: true, config: Config.default)
|
|
96
|
+
return self unless cond
|
|
97
|
+
return self unless valid_num?(config:)
|
|
98
|
+
|
|
99
|
+
md = match(num_re(config:))
|
|
100
|
+
return self unless md[:cur].blank?
|
|
101
|
+
|
|
102
|
+
config.currency_symbol + self
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def add_pre_digits(n, cond: true, config: Config.default)
|
|
106
|
+
return self unless cond
|
|
107
|
+
return self if n <= 0
|
|
108
|
+
return self unless valid_num?(config:)
|
|
109
|
+
|
|
110
|
+
cur, whole, frac = cur_whole_frac(config:)
|
|
111
|
+
n_pads = [n - whole.delete(config.group_char).size, 0].max
|
|
112
|
+
padding = config.pre_pad_char * n_pads
|
|
113
|
+
"#{cur}#{padding}#{whole}#{frac}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def add_post_digits(n, cond: true, config: Config.default)
|
|
117
|
+
return self unless cond
|
|
118
|
+
return self unless valid_num?(config:)
|
|
119
|
+
|
|
120
|
+
cur, whole, frac = cur_whole_frac(config:)
|
|
121
|
+
frac_digs = frac.size - 1 # frac includes the decimal character
|
|
122
|
+
if n >= frac_digs
|
|
123
|
+
n_pads = [n - frac_digs, 0].max
|
|
124
|
+
padding = config.post_pad_char * n_pads
|
|
125
|
+
"#{cur}#{whole}#{frac}#{padding}"
|
|
126
|
+
elsif n.zero?
|
|
127
|
+
# Round up last digit of whole if first digit of frac >= 5
|
|
128
|
+
if frac[1].to_i >= 5
|
|
129
|
+
whole = whole[0..-2] + (whole[-1].to_i + 1).to_s
|
|
130
|
+
end
|
|
131
|
+
# No fractional part
|
|
132
|
+
"#{cur}#{whole}"
|
|
133
|
+
elsif n.negative?
|
|
134
|
+
# This calls for rounding the whole part to nearest 10^n.abs and
|
|
135
|
+
# dropping the frac part.
|
|
136
|
+
ndigs_in_whole = whole.delete(config.group_char).size
|
|
137
|
+
nplaces = [ndigs_in_whole - 1, n.abs].min
|
|
138
|
+
# Replace the right-most nplaces digs with the pre-pad character.
|
|
139
|
+
replace_count = 0
|
|
140
|
+
new_whole = +''
|
|
141
|
+
round_char = whole.delete(config.group_char)[-1]
|
|
142
|
+
rounded = false
|
|
143
|
+
whole.split('').reverse_each do |c|
|
|
144
|
+
if c == config.group_char
|
|
145
|
+
new_whole << c
|
|
146
|
+
elsif replace_count < nplaces
|
|
147
|
+
new_whole << config.pre_pad_char
|
|
148
|
+
round_char = c
|
|
149
|
+
replace_count += 1
|
|
150
|
+
elsif !rounded
|
|
151
|
+
new_whole <<
|
|
152
|
+
if round_char.to_i >= 5
|
|
153
|
+
(c.to_i + 1).to_s
|
|
154
|
+
else
|
|
155
|
+
c
|
|
156
|
+
end
|
|
157
|
+
rounded = true
|
|
158
|
+
else
|
|
159
|
+
new_whole << c
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
"#{cur}#{new_whole.reverse}"
|
|
163
|
+
else
|
|
164
|
+
# We have to shorten the fractional part, which required rounding.
|
|
165
|
+
last_frac_dig = frac[n]
|
|
166
|
+
following_frac_dig = frac[n + 1]
|
|
167
|
+
if following_frac_dig.to_i >= 5
|
|
168
|
+
last_frac_dig = (last_frac_dig.to_i + 1).to_s
|
|
169
|
+
end
|
|
170
|
+
frac = frac[0..(n - 1)] + last_frac_dig
|
|
171
|
+
padding = ''
|
|
172
|
+
"#{cur}#{whole}#{frac}#{padding}"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
private
|
|
177
|
+
|
|
178
|
+
def num_re(config: Config.default)
|
|
179
|
+
cur_sym = Regexp.quote(config.currency_symbol)
|
|
180
|
+
grp_char = Regexp.quote(config.group_char)
|
|
181
|
+
dec_char = Regexp.quote(config.decimal_char)
|
|
182
|
+
/\A(?<cur>#{cur_sym})?(?<whole>[0-9#{grp_char}]+)(?<frac>#{dec_char}[0-9]*)?\z/
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Return the currency, whole and fractional parts of a string with a possible
|
|
186
|
+
# decimal point attached to the frac part if present.
|
|
187
|
+
def cur_whole_frac(config: Config.default)
|
|
188
|
+
match = match(num_re(config:))
|
|
189
|
+
[match[:cur].to_s, match[:whole].to_s, match[:frac].to_s]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def valid_num?(config: Config.default)
|
|
193
|
+
match?(num_re(config:))
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
data/lib/core_ext.rb
ADDED
data/lib/fat_table/column.rb
CHANGED
|
@@ -86,7 +86,11 @@ module FatTable
|
|
|
86
86
|
# col.header #=> :prices
|
|
87
87
|
# col.sum #=> 18376.75
|
|
88
88
|
#
|
|
89
|
-
# @param
|
|
89
|
+
# @param header [String, Symbol] the name of the column header
|
|
90
|
+
# @param items [Array<String>, Array<DateTime>, Array<Numeric>, Array<Boolean>] the initial data items in column
|
|
91
|
+
# @param type [String] the column type: 'String', 'Numeric', 'DateTime', 'Boolean', or 'NilClass'
|
|
92
|
+
# @param tolerant [Boolean] whether the column accepts unconvertable items not of its type as Strings
|
|
93
|
+
# @return [Column] the new Column
|
|
90
94
|
def initialize(header:, items: [], type: 'NilClass', tolerant: false)
|
|
91
95
|
@raw_header = header
|
|
92
96
|
@header =
|
data/lib/fat_table/convert.rb
CHANGED
data/lib/fat_table/footer.rb
CHANGED
|
@@ -67,7 +67,7 @@ module FatTable
|
|
|
67
67
|
bgcolor: 'none',
|
|
68
68
|
hms: false,
|
|
69
69
|
pre_digits: 0,
|
|
70
|
-
post_digits:
|
|
70
|
+
post_digits: 4,
|
|
71
71
|
commas: false,
|
|
72
72
|
currency: false,
|
|
73
73
|
datetime_fmt: '%F %H:%M:%S',
|
|
@@ -1026,63 +1026,37 @@ module FatTable
|
|
|
1026
1026
|
# above. Only device-independent formatting is done here. Device dependent
|
|
1027
1027
|
# formatting (e.g., color) can be done in a subclass of Formatter by
|
|
1028
1028
|
# specializing this method.
|
|
1029
|
+
|
|
1030
|
+
using NumericString
|
|
1031
|
+
|
|
1029
1032
|
def format_numeric(val, istruct)
|
|
1030
1033
|
return istruct[:nil_text] if val.nil?
|
|
1031
1034
|
return val.secs_to_hms if istruct[:hms]
|
|
1032
1035
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if n_zeros.positive?
|
|
1055
|
-
if istruct[:commas]
|
|
1056
|
-
# Insert leading zeros with commas
|
|
1057
|
-
pre_comma_match = whole_part.match(/\A(\d+),/)
|
|
1058
|
-
if pre_comma_match
|
|
1059
|
-
n_partial_zeros = 3 - pre_comma_match[1].size
|
|
1060
|
-
whole_part = "0" * n_partial_zeros + whole_part
|
|
1061
|
-
n_zeros -= n_partial_zeros
|
|
1062
|
-
end
|
|
1063
|
-
zeros = ''
|
|
1064
|
-
if n_zeros.positive?
|
|
1065
|
-
zeros = "0" * n_zeros
|
|
1066
|
-
if n_zeros > 3 && istruct[:commas]
|
|
1067
|
-
zeros = zeros.reverse.gsub!(/([0-9]{3})/, "\\1,").reverse
|
|
1068
|
-
end
|
|
1069
|
-
end
|
|
1070
|
-
"#{zeros},#{whole_part}#{frac_part}"
|
|
1071
|
-
else
|
|
1072
|
-
# Insert leading zeros without commas
|
|
1073
|
-
zeros = "0" * n_zeros
|
|
1074
|
-
"#{zeros}#{whole_part}#{frac_part}"
|
|
1075
|
-
end
|
|
1076
|
-
else
|
|
1077
|
-
"#{whole_part}#{frac_part}"
|
|
1078
|
-
end
|
|
1036
|
+
case val
|
|
1037
|
+
when Integer
|
|
1038
|
+
val.to_s
|
|
1039
|
+
.add_pre_digits(istruct[:pre_digits], cond: istruct[:pre_digits].positive?)
|
|
1040
|
+
.add_commas(cond: istruct[:commas])
|
|
1041
|
+
.add_currency(cond: istruct[:currency])
|
|
1042
|
+
when Rational
|
|
1043
|
+
num = val.numerator.to_s
|
|
1044
|
+
.add_pre_digits(istruct[:pre_digits], cond: istruct[:pre_digits].positive?)
|
|
1045
|
+
.add_commas(cond: istruct[:commas])
|
|
1046
|
+
.add_currency(cond: istruct[:currency])
|
|
1047
|
+
den = val.denominator.to_s
|
|
1048
|
+
.add_pre_digits(istruct[:pre_digits], cond: istruct[:pre_digits].positive?)
|
|
1049
|
+
.add_commas(cond: istruct[:commas])
|
|
1050
|
+
"#{num}/#{den}"
|
|
1051
|
+
when Float, BigDecimal
|
|
1052
|
+
val.to_s
|
|
1053
|
+
.add_pre_digits(istruct[:pre_digits], cond: istruct[:pre_digits].positive?)
|
|
1054
|
+
.add_post_digits(istruct[:post_digits])
|
|
1055
|
+
.add_commas(cond: istruct[:commas])
|
|
1056
|
+
.add_currency(cond: istruct[:currency])
|
|
1079
1057
|
else
|
|
1080
|
-
|
|
1081
|
-
end
|
|
1082
|
-
if istruct[:currency]
|
|
1083
|
-
result = "#{FatTable.currency_symbol}#{result}"
|
|
1058
|
+
raise ArgumentError, "cannot apply format_numeric to #{val} of class #{val.class}"
|
|
1084
1059
|
end
|
|
1085
|
-
result
|
|
1086
1060
|
end
|
|
1087
1061
|
|
|
1088
1062
|
# Apply non-device-dependent string formatting instructions.
|
|
@@ -26,8 +26,8 @@ module FatTable
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Taken from the Rainbow gem's list of valid colors.
|
|
29
|
-
|
|
30
|
-
self.valid_colors = File.readlines(
|
|
29
|
+
color_path = File.expand_path("../../../data/xcolors.txt", __dir__)
|
|
30
|
+
self.valid_colors = File.readlines(color_path, chomp: true)
|
|
31
31
|
|
|
32
32
|
# LaTeX commands to load the needed packages based on the :environement
|
|
33
33
|
# option. For now, just handles the default 'longtable' :environment. The
|
data/lib/fat_table/table.rb
CHANGED
|
@@ -721,7 +721,7 @@ module FatTable
|
|
|
721
721
|
# row in the table as a group boundary. An attempt to add a boundary to
|
|
722
722
|
# an empty table has no effect. We adopt the convention that the last row
|
|
723
723
|
# of the table always marks an implicit boundary even if it is not in the
|
|
724
|
-
#
|
|
724
|
+
# `@explicit_boundaries` array. When we "mark" a boundary, we intend it to
|
|
725
725
|
# be an explicit boundary, even if it marks the last row of the table.
|
|
726
726
|
def mark_boundary(row_num = nil)
|
|
727
727
|
return self if empty?
|
|
@@ -774,7 +774,7 @@ module FatTable
|
|
|
774
774
|
# user's point of view are indexed starting at 0.
|
|
775
775
|
def row_index_to_group_index(row_num)
|
|
776
776
|
boundaries.each_with_index do |b_last, g_num|
|
|
777
|
-
return
|
|
777
|
+
return g_num + 1 if row_num <= b_last
|
|
778
778
|
end
|
|
779
779
|
0
|
|
780
780
|
end
|
|
@@ -918,8 +918,8 @@ module FatTable
|
|
|
918
918
|
# access the instance variable @row, as the row number of the row being
|
|
919
919
|
# evaluated, and @group, as the group number of the row being evaluated.
|
|
920
920
|
#
|
|
921
|
-
# 4. a hash in +new_cols+ with one of the special keys,
|
|
922
|
-
# hash}
|
|
921
|
+
# 4. a hash in +new_cols+ with one of the special keys, `+ivars: {literal
|
|
922
|
+
# hash}+`, +before_hook: 'ruby-code'+, or +after_hook: 'ruby-code'+ for
|
|
923
923
|
# defining custom instance variables to be used during evaluation of
|
|
924
924
|
# parameters described in point 3 and hooks of ruby code snippets to be
|
|
925
925
|
# evaluated before and after processing each row.
|
|
@@ -1150,7 +1150,7 @@ module FatTable
|
|
|
1150
1150
|
# exception will be thrown. Duplicates are eliminated from the result. Any
|
|
1151
1151
|
# groups present in either Table are eliminated in the output Table.
|
|
1152
1152
|
def intersect(other)
|
|
1153
|
-
set_operation(other,
|
|
1153
|
+
set_operation(other, :&, distinct: true)
|
|
1154
1154
|
end
|
|
1155
1155
|
|
|
1156
1156
|
# :category: Operators
|
|
@@ -1163,7 +1163,7 @@ module FatTable
|
|
|
1163
1163
|
# will be thrown. Duplicates are not eliminated from the result. Resets
|
|
1164
1164
|
# groups.
|
|
1165
1165
|
def intersect_all(other)
|
|
1166
|
-
set_operation(other,
|
|
1166
|
+
set_operation(other, :&, distinct: false)
|
|
1167
1167
|
end
|
|
1168
1168
|
|
|
1169
1169
|
# :category: Operators
|
|
@@ -1176,7 +1176,7 @@ module FatTable
|
|
|
1176
1176
|
# are eliminated from the result. Any groups present in either Table are
|
|
1177
1177
|
# eliminated in the output Table.
|
|
1178
1178
|
def except(other)
|
|
1179
|
-
set_operation(other,
|
|
1179
|
+
set_operation(other, :-, distinct: true)
|
|
1180
1180
|
end
|
|
1181
1181
|
|
|
1182
1182
|
# :category: Operators
|
|
@@ -1189,7 +1189,7 @@ module FatTable
|
|
|
1189
1189
|
# are /not/ eliminated from the result. Any groups present in either Table
|
|
1190
1190
|
# are eliminated in the output Table.
|
|
1191
1191
|
def except_all(other)
|
|
1192
|
-
set_operation(other,
|
|
1192
|
+
set_operation(other, :-, distinct: false)
|
|
1193
1193
|
end
|
|
1194
1194
|
|
|
1195
1195
|
# An Array of symbols for the valid join types.
|
data/lib/fat_table/version.rb
CHANGED
data/lib/fat_table.rb
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# variety of
|
|
8
|
-
#
|
|
3
|
+
# Gem Overview (extracted from README.org by gem_docs)
|
|
4
|
+
#
|
|
5
|
+
# * Introduction
|
|
6
|
+
# ~FatTable~ is a gem that treats tables as a data type. It provides methods for
|
|
7
|
+
# constructing tables from a variety of sources, building them row-by-row,
|
|
8
|
+
# extracting rows, columns, and cells, and performing aggregate operations on
|
|
9
|
+
# columns. It also provides a set of SQL-esque methods for manipulating table
|
|
10
|
+
# objects: ~select~ for filtering by columns or for creating new columns, ~where~
|
|
11
|
+
# for filtering by rows, ~order_by~ for sorting rows, ~distinct~ for eliminating
|
|
12
|
+
# duplicate rows, ~group_by~ for aggregating multiple rows into single rows and
|
|
13
|
+
# applying column aggregate methods to ungrouped columns, a collection of ~join~
|
|
14
|
+
# methods for combining tables, and more.
|
|
15
|
+
#
|
|
16
|
+
# Furthermore, ~FatTable~ provides methods for formatting tables and producing
|
|
17
|
+
# output that targets various output media: text, ANSI terminals, ruby data
|
|
18
|
+
# structures, LaTeX tables, Emacs org-mode tables, and more. The formatting
|
|
19
|
+
# methods can specify cell formatting in a way that is uniform across all the
|
|
20
|
+
# output methods and can also decorate the output with any number of footers,
|
|
21
|
+
# including group footers. ~FatTable~ applies formatting directives to the extent
|
|
22
|
+
# they makes sense for the output medium and treats other formatting directives as
|
|
23
|
+
# no-ops.
|
|
24
|
+
#
|
|
25
|
+
# ~FatTable~ can be used to perform operations on data that are naturally best
|
|
26
|
+
# conceived of as tables, which in my experience is quite often. It can also
|
|
27
|
+
# serve as a foundation for providing reporting functions where flexibility
|
|
28
|
+
# about the output medium can be useful. Finally ~FatTable~ can be used within
|
|
29
|
+
# Emacs ~org-mode~ files in code blocks targeting the Ruby language. Org mode
|
|
30
|
+
# tables are presented to a ruby code block as an array of arrays, so ~FatTable~
|
|
31
|
+
# can read them in with its ~.from_aoa~ constructor. A ~FatTable~ table output as an
|
|
32
|
+
# array of arrays with its ~.to_aoa~ output function will be rendered in an
|
|
33
|
+
# org-mode buffer as an org-table, ready for processing by other code blocks.
|
|
9
34
|
module FatTable
|
|
10
35
|
require 'fat_core/symbol'
|
|
11
36
|
require 'fat_core/array'
|
|
@@ -18,9 +43,9 @@ module FatTable
|
|
|
18
43
|
require 'active_support/core_ext'
|
|
19
44
|
require 'active_support/number_helper'
|
|
20
45
|
|
|
46
|
+
require 'core_ext'
|
|
21
47
|
require 'fat_table/version'
|
|
22
48
|
require 'fat_table/patches'
|
|
23
|
-
require 'ext/array'
|
|
24
49
|
require 'fat_table/evaluator'
|
|
25
50
|
require 'fat_table/convert'
|
|
26
51
|
require 'fat_table/column'
|
|
@@ -145,11 +170,11 @@ module FatTable
|
|
|
145
170
|
# methods can be called. If no block is given, the default format for the type
|
|
146
171
|
# will be used. The +options+ are passed along to the FatTable::Formatter
|
|
147
172
|
# created to process the output.
|
|
148
|
-
def self.to_format(table, options
|
|
173
|
+
def self.to_format(table, **options) # :yields: formatter
|
|
149
174
|
if block_given?
|
|
150
|
-
to_any(format, table, options, &Proc.new)
|
|
175
|
+
to_any(format, table, **options, &Proc.new)
|
|
151
176
|
else
|
|
152
|
-
to_any(format, table, options)
|
|
177
|
+
to_any(format, table, **options)
|
|
153
178
|
end
|
|
154
179
|
end
|
|
155
180
|
|
|
@@ -159,15 +184,15 @@ module FatTable
|
|
|
159
184
|
# +FatTable::Formatter+ of the appropriate type on which formatting and footer
|
|
160
185
|
# methods can be called. If no block is given, the default format for the
|
|
161
186
|
# +fmt+ type will be used.
|
|
162
|
-
def self.to_any(fmt, table, options
|
|
187
|
+
def self.to_any(fmt, table, **options)
|
|
163
188
|
fmt = fmt.as_sym
|
|
164
189
|
raise UserError, "unknown format '#{fmt}'" unless FORMATS.include?(fmt)
|
|
165
190
|
|
|
166
191
|
method = "to_#{fmt}"
|
|
167
192
|
if block_given?
|
|
168
|
-
send(method, table, options, &Proc.new)
|
|
193
|
+
send(method, table, **options, &Proc.new)
|
|
169
194
|
else
|
|
170
|
-
send(method, table, options)
|
|
195
|
+
send(method, table, **options)
|
|
171
196
|
end
|
|
172
197
|
end
|
|
173
198
|
|
|
@@ -176,7 +201,7 @@ module FatTable
|
|
|
176
201
|
# default formatting is applied to the +table+'s cells. If a block is given,
|
|
177
202
|
# it yields the +FatTable::Formatter+ to the block on which formatting
|
|
178
203
|
# and footer methods can be called.
|
|
179
|
-
def self.to_psv(table, options
|
|
204
|
+
def self.to_psv(table, **options)
|
|
180
205
|
fmt = Formatter.new(table, options)
|
|
181
206
|
yield fmt if block_given?
|
|
182
207
|
fmt.output
|
|
@@ -186,8 +211,8 @@ module FatTable
|
|
|
186
211
|
# default formatting is applies to the table's cells. If a block is given, it
|
|
187
212
|
# yields an AoaFormatter to the block to which formatting instructions and
|
|
188
213
|
# footers can be added by calling methods on it.
|
|
189
|
-
def self.to_aoa(table, options
|
|
190
|
-
fmt = AoaFormatter.new(table, options)
|
|
214
|
+
def self.to_aoa(table, **options)
|
|
215
|
+
fmt = AoaFormatter.new(table, **options)
|
|
191
216
|
yield fmt if block_given?
|
|
192
217
|
fmt.output
|
|
193
218
|
end
|
|
@@ -197,8 +222,8 @@ module FatTable
|
|
|
197
222
|
# table. If no block is given, default formatting is applies to the table's
|
|
198
223
|
# cells. If a block is given, it yields an AohFormatter to the block to which
|
|
199
224
|
# formatting instructions and footers can be added by calling methods on it.
|
|
200
|
-
def self.to_aoh(table, options
|
|
201
|
-
fmt = AohFormatter.new(table, options)
|
|
225
|
+
def self.to_aoh(table, **options)
|
|
226
|
+
fmt = AohFormatter.new(table, **options)
|
|
202
227
|
yield fmt if block_given?
|
|
203
228
|
fmt.output
|
|
204
229
|
end
|
|
@@ -207,8 +232,8 @@ module FatTable
|
|
|
207
232
|
# default formatting applies to the table's cells. If a block is given, it
|
|
208
233
|
# yields a LaTeXFormatter to the block to which formatting instructions and
|
|
209
234
|
# footers can be added by calling methods on it.
|
|
210
|
-
def self.to_latex(table, options
|
|
211
|
-
fmt = LaTeXFormatter.new(table, options)
|
|
235
|
+
def self.to_latex(table, **options)
|
|
236
|
+
fmt = LaTeXFormatter.new(table, **options)
|
|
212
237
|
yield fmt if block_given?
|
|
213
238
|
fmt.output
|
|
214
239
|
end
|
|
@@ -217,8 +242,8 @@ module FatTable
|
|
|
217
242
|
# is given, default formatting applies to the table's cells. If a block is
|
|
218
243
|
# given, it yields a OrgFormatter to the block to which formatting
|
|
219
244
|
# instructions and footers can be added by calling methods on it.
|
|
220
|
-
def self.to_org(table, options
|
|
221
|
-
fmt = OrgFormatter.new(table, options)
|
|
245
|
+
def self.to_org(table, **options)
|
|
246
|
+
fmt = OrgFormatter.new(table, **options)
|
|
222
247
|
yield fmt if block_given?
|
|
223
248
|
fmt.output
|
|
224
249
|
end
|
|
@@ -227,8 +252,8 @@ module FatTable
|
|
|
227
252
|
# table. If no block is given, default formatting applies to the table's
|
|
228
253
|
# cells. If a block is given, it yields a TermFormatter to the block to which
|
|
229
254
|
# formatting instructions and footers can be added by calling methods on it.
|
|
230
|
-
def self.to_term(table, options
|
|
231
|
-
fmt = TermFormatter.new(table, options)
|
|
255
|
+
def self.to_term(table, **options)
|
|
256
|
+
fmt = TermFormatter.new(table, **options)
|
|
232
257
|
yield fmt if block_given?
|
|
233
258
|
fmt.output
|
|
234
259
|
end
|
|
@@ -237,8 +262,8 @@ module FatTable
|
|
|
237
262
|
# no block is given, default formatting applies to the table's cells. If a
|
|
238
263
|
# block is given, it yields a TextFormatter to the block to which formatting
|
|
239
264
|
# instructions and footers can be added by calling methods on it.
|
|
240
|
-
def self.to_text(table, options
|
|
241
|
-
fmt = TextFormatter.new(table, options)
|
|
265
|
+
def self.to_text(table, **options)
|
|
266
|
+
fmt = TextFormatter.new(table, **options)
|
|
242
267
|
yield fmt if block_given?
|
|
243
268
|
fmt.output
|
|
244
269
|
end
|