fat_table 0.9.8 → 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 +31 -57
- data/lib/fat_table/formatters/latex_formatter.rb +2 -2
- data/lib/fat_table/formatters/term_formatter.rb +18 -18
- data/lib/fat_table/table.rb +10 -10
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +51 -26
- metadata +10 -13
- 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',
|
|
@@ -729,11 +729,11 @@ module FatTable
|
|
|
729
729
|
# Parsing and validation routines
|
|
730
730
|
############################################################################
|
|
731
731
|
|
|
732
|
-
private
|
|
733
|
-
|
|
734
732
|
# Re to match a color name
|
|
735
733
|
CLR_RE = /(?:[-_a-zA-Z0-9 ]*)/
|
|
736
734
|
|
|
735
|
+
private
|
|
736
|
+
|
|
737
737
|
# Return a hash that reflects the formatting instructions given in the
|
|
738
738
|
# string fmt. Raise an error if it contains invalid formatting instructions.
|
|
739
739
|
# If fmt contains conflicting instructions, say C and L, there is no
|
|
@@ -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.
|
|
@@ -1251,7 +1225,7 @@ module FatTable
|
|
|
1251
1225
|
map = {}
|
|
1252
1226
|
table.headers.each do |h|
|
|
1253
1227
|
istruct = format_at[:header][h]
|
|
1254
|
-
map[h] = [h, format_cell(h.
|
|
1228
|
+
map[h] = [h, format_cell(h.entitle, istruct, decorate: decorate)]
|
|
1255
1229
|
end
|
|
1256
1230
|
map
|
|
1257
1231
|
end
|
|
@@ -1294,7 +1268,7 @@ module FatTable
|
|
|
1294
1268
|
# Format group footers
|
|
1295
1269
|
gfooters.each_pair do |_label, gfooter|
|
|
1296
1270
|
out_rows << nil
|
|
1297
|
-
gfoot_row = Hash.new([nil, ''])
|
|
1271
|
+
gfoot_row = Hash.new([nil, ''].freeze)
|
|
1298
1272
|
gfooter.to_h(grp_k).each_pair do |h, v|
|
|
1299
1273
|
istruct = format_at[:gfooter][h]
|
|
1300
1274
|
gfoot_row[h] = [v, format_cell(v, istruct, decorate: decorate)]
|
|
@@ -1313,7 +1287,7 @@ module FatTable
|
|
|
1313
1287
|
|
|
1314
1288
|
footers.each_pair do |_label, foot|
|
|
1315
1289
|
out_rows << nil
|
|
1316
|
-
foot_row = Hash.new([nil, ''])
|
|
1290
|
+
foot_row = Hash.new([nil, ''].freeze)
|
|
1317
1291
|
foot.to_h.each_pair do |h, v|
|
|
1318
1292
|
istruct = format_at[:gfooter][h]
|
|
1319
1293
|
foot_row[h] = [v, format_cell(v, istruct, decorate: decorate)]
|
|
@@ -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
|
|
@@ -49,6 +49,24 @@ module FatTable
|
|
|
49
49
|
result
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# :stopdoc:
|
|
53
|
+
# Unicode line-drawing characters. We use double lines before and after the
|
|
54
|
+
# table and single lines for the sides and hlines between groups and
|
|
55
|
+
# footers.
|
|
56
|
+
UPPER_LEFT = "\u2552"
|
|
57
|
+
UPPER_RIGHT = "\u2555"
|
|
58
|
+
DOUBLE_RULE = "\u2550"
|
|
59
|
+
UPPER_TEE = "\u2564"
|
|
60
|
+
VERTICAL_RULE = "\u2502"
|
|
61
|
+
LEFT_TEE = "\u251C"
|
|
62
|
+
HORIZONTAL_RULE = "\u2500"
|
|
63
|
+
SINGLE_CROSS = "\u253C"
|
|
64
|
+
RIGHT_TEE = "\u2524"
|
|
65
|
+
LOWER_LEFT = "\u2558"
|
|
66
|
+
LOWER_RIGHT = "\u255B"
|
|
67
|
+
LOWER_TEE = "\u2567"
|
|
68
|
+
# :startdoc:
|
|
69
|
+
|
|
52
70
|
private
|
|
53
71
|
|
|
54
72
|
def color_valid?(clr)
|
|
@@ -99,24 +117,6 @@ module FatTable
|
|
|
99
117
|
colorize(str, @options[:frame_fg], @options[:frame_bg])
|
|
100
118
|
end
|
|
101
119
|
|
|
102
|
-
# :stopdoc:
|
|
103
|
-
# Unicode line-drawing characters. We use double lines before and after the
|
|
104
|
-
# table and single lines for the sides and hlines between groups and
|
|
105
|
-
# footers.
|
|
106
|
-
UPPER_LEFT = "\u2552"
|
|
107
|
-
UPPER_RIGHT = "\u2555"
|
|
108
|
-
DOUBLE_RULE = "\u2550"
|
|
109
|
-
UPPER_TEE = "\u2564"
|
|
110
|
-
VERTICAL_RULE = "\u2502"
|
|
111
|
-
LEFT_TEE = "\u251C"
|
|
112
|
-
HORIZONTAL_RULE = "\u2500"
|
|
113
|
-
SINGLE_CROSS = "\u253C"
|
|
114
|
-
RIGHT_TEE = "\u2524"
|
|
115
|
-
LOWER_LEFT = "\u2558"
|
|
116
|
-
LOWER_RIGHT = "\u255B"
|
|
117
|
-
LOWER_TEE = "\u2567"
|
|
118
|
-
# :startdoc:
|
|
119
|
-
|
|
120
120
|
def upper_left
|
|
121
121
|
if options[:unicode]
|
|
122
122
|
UPPER_LEFT
|
data/lib/fat_table/table.rb
CHANGED
|
@@ -135,7 +135,7 @@ module FatTable
|
|
|
135
135
|
# might have been set by a subclass instance.
|
|
136
136
|
def empty_dup(result_cols = nil)
|
|
137
137
|
result_cols ||= heads
|
|
138
|
-
result_types = types.
|
|
138
|
+
result_types = types.slice(*result_cols)
|
|
139
139
|
result = self.class.new(result_cols, **result_types)
|
|
140
140
|
tolerant_cols.each do |h|
|
|
141
141
|
result.tolerant_cols << h
|
|
@@ -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.
|
|
@@ -1521,7 +1521,7 @@ module FatTable
|
|
|
1521
1521
|
groups = sorted_tab.rows.group_by do |r|
|
|
1522
1522
|
group_cols.map { |k| r[k] }
|
|
1523
1523
|
end
|
|
1524
|
-
grp_types = types.
|
|
1524
|
+
grp_types = types.slice(*group_cols)
|
|
1525
1525
|
result = Table.new(*group_cols, **grp_types)
|
|
1526
1526
|
groups.each_pair do |_vals, grp_rows|
|
|
1527
1527
|
result << row_from_group(grp_rows, group_cols, agg_cols)
|
data/lib/fat_table/version.rb
CHANGED