fat_table 0.2.6 → 0.3.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 +5 -5
- data/.gitignore +3 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +7 -2
- data/.yardopts +5 -1
- data/README.org +271 -251
- data/README.rdoc +4 -4
- data/TODO.org +7 -0
- data/bin/ft_console +82 -79
- data/fat_table.gemspec +47 -46
- data/lib/fat_table.rb +11 -2
- data/lib/fat_table/column.rb +41 -29
- data/lib/fat_table/db_handle.rb +31 -50
- data/lib/fat_table/evaluator.rb +26 -24
- data/lib/fat_table/formatters/aoa_formatter.rb +5 -6
- data/lib/fat_table/formatters/aoh_formatter.rb +6 -7
- data/lib/fat_table/formatters/formatter.rb +67 -48
- data/lib/fat_table/formatters/latex_formatter.rb +9 -7
- data/lib/fat_table/formatters/org_formatter.rb +8 -9
- data/lib/fat_table/formatters/term_formatter.rb +19 -18
- data/lib/fat_table/formatters/text_formatter.rb +6 -7
- data/lib/fat_table/patches.rb +30 -1
- data/lib/fat_table/table.rb +143 -117
- data/lib/fat_table/version.rb +1 -1
- data/md/README.md +2167 -0
- metadata +78 -84
data/lib/fat_table/db_handle.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
# Set and access a database by module-level methods.
|
1
2
|
module FatTable
|
2
|
-
class << self
|
3
|
+
class << self
|
3
4
|
# The +Sequel+ database handle to use in calls to +FatTable.from_sql+.
|
4
5
|
attr_accessor :handle
|
5
6
|
end
|
@@ -17,21 +18,23 @@ module FatTable
|
|
17
18
|
# Sequel's adapter-specific connection methods.
|
18
19
|
# http://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html
|
19
20
|
#
|
20
|
-
# +
|
21
|
-
# One of 'pg' (for Postgresql), 'mysql' or 'mysql2' (for Mysql), or
|
22
|
-
# SQLite3)
|
23
|
-
#
|
21
|
+
# +adapter+::
|
22
|
+
# One of 'pg' (for Postgresql), 'mysql' or 'mysql2' (for Mysql), or
|
23
|
+
# 'sqlite' (for SQLite3) (or any other adapter supported by the +Sequel+
|
24
|
+
# gem) to specify the driver to use. You may have to install the
|
25
|
+
# appropriate driver to make this work.
|
24
26
|
#
|
25
27
|
# +database+::
|
26
28
|
# The name of the database to access. There is no default for this.
|
27
29
|
#
|
28
30
|
# +user+::
|
29
|
-
# The user name to use for accessing the database.
|
30
|
-
# which may be interpreted as a default user by the Sequel driver being
|
31
|
+
# The user name to use for accessing the database. It defaults to nil,
|
32
|
+
# which may be interpreted as a default user by the Sequel driver being
|
33
|
+
# used.
|
31
34
|
#
|
32
35
|
# +password+::
|
33
|
-
# The password to use for accessing the database.
|
34
|
-
#
|
36
|
+
# The password to use for accessing the database. It defaults to nil, which
|
37
|
+
# may be interpreted as a default password by the Sequel driver being used.
|
35
38
|
#
|
36
39
|
# +host+::
|
37
40
|
# The name of the host on which to look for the database connection,
|
@@ -39,52 +42,30 @@ module FatTable
|
|
39
42
|
#
|
40
43
|
# +port+::
|
41
44
|
# The port number as a string or integer on which to access the database on
|
42
|
-
# the given host.
|
45
|
+
# the given host. Defaults to '5432'. Only used if host is not 'localhost'.
|
43
46
|
#
|
44
47
|
# +socket+::
|
45
48
|
# The socket to use to access the database if the host is 'localhost'.
|
46
49
|
# Defaults to the standard socket for the Pg driver, '/tmp/.s.PGSQL.5432'.
|
47
50
|
#
|
48
|
-
# If successful the database handle for Sequel is return.
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
def self.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
valid_drivers = ['postgres', 'mysql', 'mysql2', 'sqlite']
|
66
|
-
unless valid_drivers.include?(driver)
|
67
|
-
raise UserError, "'#{driver}' driver must be one of #{valid_drivers.join(' or ')}"
|
68
|
-
end
|
69
|
-
if database.blank?
|
70
|
-
raise UserError, "must supply database parameter to set_db"
|
71
|
-
end
|
72
|
-
|
73
|
-
if driver == 'sqlite'
|
74
|
-
dsn = "sqlite://#{database}"
|
51
|
+
# If successful the database handle for Sequel is return. Once called
|
52
|
+
# successfully, this establishes the database handle to use for all subsequent
|
53
|
+
# calls to FatTable.from_sql or FatTable::Table.from_sql. You can then access
|
54
|
+
# the handle if needed with FatTable.db.
|
55
|
+
def self.connect(args)
|
56
|
+
# Set the dsn for Sequel
|
57
|
+
begin
|
58
|
+
self.handle = Sequel.connect(args)
|
59
|
+
rescue Sequel::AdapterNotFound => ex
|
60
|
+
case ex.to_s
|
61
|
+
when /pg/
|
62
|
+
raise TransientError, 'You need to install the postgres adapter pg'
|
63
|
+
when /mysql/
|
64
|
+
raise TransientError, 'You need to install the mysql adapter'
|
65
|
+
when /sqlite/
|
66
|
+
raise TransientError, 'You need to install the sqlite adapter'
|
75
67
|
else
|
76
|
-
|
77
|
-
hst_part = host ? "@#{host}" : ''
|
78
|
-
prt_part = port ? ":#{port}" : ''
|
79
|
-
dsn = "#{driver}:://#{user}#{pw_part}#{hst_part}#{prt_part}/#{database}"
|
80
|
-
end
|
81
|
-
|
82
|
-
# Set the dsn for Sequel
|
83
|
-
begin
|
84
|
-
# DB = Sequel.connect(dsn)
|
85
|
-
self.handle = Sequel.connect(dsn)
|
86
|
-
rescue => ex
|
87
|
-
raise TransientError, "#{dsn}: #{ex}"
|
68
|
+
raise ex
|
88
69
|
end
|
89
70
|
end
|
90
71
|
handle
|
@@ -96,7 +77,7 @@ module FatTable
|
|
96
77
|
end
|
97
78
|
|
98
79
|
# Directly set the db handle to a Sequel connection formed without
|
99
|
-
# FatTable.
|
80
|
+
# FatTable.connect.
|
100
81
|
def self.db=(db)
|
101
82
|
self.handle = db
|
102
83
|
end
|
data/lib/fat_table/evaluator.rb
CHANGED
@@ -12,57 +12,59 @@ module FatTable
|
|
12
12
|
# update values of instance variables for use in subsequent calls to
|
13
13
|
# #evaluate.
|
14
14
|
class Evaluator
|
15
|
-
# Return a new Evaluator object in which the Hash +
|
15
|
+
# Return a new Evaluator object in which the Hash +ivars+ defines the
|
16
16
|
# bindings for instance variables to be available and maintained across all
|
17
17
|
# subsequent calls to Evaluator.evaluate. The strings +before+ and +after+
|
18
|
-
# are string expressions that will be evaluated before and after each
|
18
|
+
# are string Ruby expressions that will be evaluated before and after each
|
19
19
|
# subsequent call to Evaluator.evaluate.
|
20
20
|
def initialize(ivars: {}, before: nil, after: nil)
|
21
21
|
@before = before
|
22
22
|
@after = after
|
23
|
-
|
23
|
+
instance_vars(ivars)
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
def eval_before_hook(vars)
|
30
|
-
bdg = binding
|
31
|
-
set_local_vars(vars, bdg)
|
32
|
-
@group = vars[:__group]
|
33
|
-
eval(@before, bdg) if @before
|
26
|
+
# Set the @group instance variable to the given value.
|
27
|
+
def update_ivars(ivars)
|
28
|
+
instance_vars(ivars)
|
34
29
|
end
|
35
30
|
|
36
|
-
# Run
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
# Run any before hook in the context of the given local variables.
|
32
|
+
def eval_before_hook(locals: {})
|
33
|
+
return if @before.blank?
|
34
|
+
|
35
|
+
evaluate(@before, locals: locals)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Run any after hook in the context of the given local variables.
|
39
|
+
def eval_after_hook(locals: {})
|
40
|
+
return if @after.blank?
|
41
|
+
|
42
|
+
evaluate(@after, locals: locals)
|
42
43
|
end
|
43
44
|
|
44
45
|
# Return the result of evaluating +expr+ as a Ruby expression in which the
|
45
46
|
# instance variables set in Evaluator.new and any local variables set in the
|
46
|
-
# Hash parameter +
|
47
|
-
def evaluate(expr = '',
|
48
|
-
|
49
|
-
set_local_vars(vars, bdg)
|
50
|
-
eval(expr, bdg)
|
47
|
+
# Hash parameter +locals+ are available to the expression.
|
48
|
+
def evaluate(expr = '', locals: {})
|
49
|
+
eval(expr, local_vars(binding, locals))
|
51
50
|
end
|
52
51
|
|
53
52
|
private
|
54
53
|
|
55
|
-
|
54
|
+
# Set the instance variables according to Hash vars.
|
55
|
+
def instance_vars(vars = {})
|
56
56
|
vars.each_pair do |name, val|
|
57
57
|
name = "@#{name}" unless name.to_s.start_with?('@')
|
58
58
|
instance_variable_set(name, val)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
|
62
|
+
# Set the local variables within the binding bnd according to Hash vars.
|
63
|
+
def local_vars(bnd, vars = {})
|
63
64
|
vars.each_pair do |name, val|
|
64
65
|
bnd.local_variable_set(name, val)
|
65
66
|
end
|
67
|
+
bnd
|
66
68
|
end
|
67
69
|
end
|
68
70
|
end
|
@@ -4,7 +4,6 @@ module FatTable
|
|
4
4
|
# directives. All footers are included as extra Arrays of the output.
|
5
5
|
# AoaFormatter supports no +options+
|
6
6
|
class AoaFormatter < Formatter
|
7
|
-
|
8
7
|
private
|
9
8
|
|
10
9
|
def evaluate?
|
@@ -31,20 +30,20 @@ module FatTable
|
|
31
30
|
'['
|
32
31
|
end
|
33
32
|
|
34
|
-
def pre_cell(
|
33
|
+
def pre_cell(_val)
|
35
34
|
"'"
|
36
35
|
end
|
37
36
|
|
38
37
|
# Because the cell, after conversion to a single-quoted string will be
|
39
38
|
# eval'ed, we need to escape any single-quotes (') that appear in the
|
40
39
|
# string.
|
41
|
-
def quote_cell(
|
42
|
-
if
|
40
|
+
def quote_cell(val)
|
41
|
+
if val.match?(/'/)
|
43
42
|
# Use a negative look-behind to only quote single-quotes that are not
|
44
43
|
# already preceded by a backslash
|
45
|
-
|
44
|
+
val.gsub(/(?<!\\)'/, "'" => "\\'")
|
46
45
|
else
|
47
|
-
|
46
|
+
val
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
@@ -6,7 +6,6 @@ module FatTable
|
|
6
6
|
# footers are included as extra Hashes of the output. AoaFormatter supports no
|
7
7
|
# +options+
|
8
8
|
class AohFormatter < Formatter
|
9
|
-
|
10
9
|
private
|
11
10
|
|
12
11
|
def evaluate?
|
@@ -31,20 +30,20 @@ module FatTable
|
|
31
30
|
'{'
|
32
31
|
end
|
33
32
|
|
34
|
-
def pre_cell(
|
35
|
-
":#{
|
33
|
+
def pre_cell(head)
|
34
|
+
":#{head.as_sym} => '"
|
36
35
|
end
|
37
36
|
|
38
37
|
# Because the cell, after conversion to a single-quoted string will be
|
39
38
|
# eval'ed, we need to escape any single-quotes (') that appear in the
|
40
39
|
# string.
|
41
|
-
def quote_cell(
|
42
|
-
if
|
40
|
+
def quote_cell(val)
|
41
|
+
if val.match?(/'/)
|
43
42
|
# Use a negative look-behind to only quote single-quotes that are not
|
44
43
|
# already preceded by a backslash
|
45
|
-
|
44
|
+
val.gsub(/(?<!\\)'/, "'" => "\\'")
|
46
45
|
else
|
47
|
-
|
46
|
+
val
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
@@ -12,7 +12,7 @@ module FatTable
|
|
12
12
|
# provided by subclasses will override these for different output targets.
|
13
13
|
class Formatter
|
14
14
|
# Valid locations in a Table as an array of symbols.
|
15
|
-
LOCATIONS = [
|
15
|
+
LOCATIONS = %i[header body bfirst gfirst gfooter footer].freeze
|
16
16
|
|
17
17
|
# The table that is the subject of the Formatter.
|
18
18
|
attr_reader :table
|
@@ -45,8 +45,9 @@ module FatTable
|
|
45
45
|
# footer content. The value is Hash in which the keys are column symbols and
|
46
46
|
# the values are symbols for the aggregate method to be applied to the
|
47
47
|
# group's column to provide a value in the group footer for that column.
|
48
|
-
# Thus, +gfooters['Average'][:shares]+ might be set to +:avg+ to indicate
|
49
|
-
# the +:shares+ column is to be averaged in the group footer labeled
|
48
|
+
# Thus, +gfooters['Average'][:shares]+ might be set to +:avg+ to indicate
|
49
|
+
# that the +:shares+ column is to be averaged in the group footer labeled
|
50
|
+
# 'Average'.
|
50
51
|
attr_reader :gfooters
|
51
52
|
|
52
53
|
class_attribute :default_format
|
@@ -91,20 +92,21 @@ module FatTable
|
|
91
92
|
# Formatter is yielded to the block so that methods for formatting and
|
92
93
|
# adding footers can be called on it.
|
93
94
|
def initialize(table = Table.new, **options)
|
94
|
-
unless table
|
95
|
+
unless table&.is_a?(Table)
|
95
96
|
raise UserError, 'must initialize Formatter with a Table'
|
96
97
|
end
|
98
|
+
|
97
99
|
@table = table
|
98
100
|
@options = options
|
99
101
|
@footers = {}
|
100
102
|
@gfooters = {}
|
101
|
-
# Formatting instructions for various "locations" within the Table, as
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# :datetime, :boolean, or :nil.
|
103
|
+
# Formatting instructions for various "locations" within the Table, as a
|
104
|
+
# hash of hashes. The outer hash is keyed on the location, and each inner
|
105
|
+
# hash is keyed on either a column sym or a type sym, :string, :numeric,
|
106
|
+
# :datetime, :boolean, or :nil. The value of the inner hashes are
|
105
107
|
# OpenStruct structs.
|
106
108
|
@format_at = {}
|
107
|
-
[
|
109
|
+
%i[header bfirst gfirst body footer gfooter].each do |loc|
|
108
110
|
@format_at[loc] = {}
|
109
111
|
table.headers.each do |h|
|
110
112
|
fmt_hash = self.class.default_format
|
@@ -169,12 +171,14 @@ module FatTable
|
|
169
171
|
unless table.headers.include?(h)
|
170
172
|
raise UserError, "No '#{h}' column in table to sum in the footer"
|
171
173
|
end
|
174
|
+
|
172
175
|
foot[h] = :sum
|
173
176
|
end
|
174
177
|
agg_cols.each do |h, agg|
|
175
178
|
unless table.headers.include?(h)
|
176
179
|
raise UserError, "No '#{h}' column in table to #{agg} in the footer"
|
177
180
|
end
|
181
|
+
|
178
182
|
foot[h] = agg
|
179
183
|
end
|
180
184
|
@footers[label] = foot
|
@@ -205,14 +209,16 @@ module FatTable
|
|
205
209
|
foot = {}
|
206
210
|
sum_cols.each do |h|
|
207
211
|
unless table.headers.include?(h)
|
208
|
-
raise UserError, "No '#{h}' column in table
|
212
|
+
raise UserError, "No '#{h}' column in table for group sum footer"
|
209
213
|
end
|
214
|
+
|
210
215
|
foot[h] = :sum
|
211
216
|
end
|
212
217
|
agg_cols.each do |h, agg|
|
213
218
|
unless table.headers.include?(h)
|
214
|
-
raise UserError, "No '#{h}' column in table
|
219
|
+
raise UserError, "No '#{h}' column in table for #{agg} group footer"
|
215
220
|
end
|
221
|
+
|
216
222
|
foot[h] = agg
|
217
223
|
end
|
218
224
|
@gfooters[label] = foot
|
@@ -241,7 +247,7 @@ module FatTable
|
|
241
247
|
cols.each do |c|
|
242
248
|
hsh[c] = :avg
|
243
249
|
end
|
244
|
-
footer('Average', hsh)
|
250
|
+
footer('Average', **hsh)
|
245
251
|
end
|
246
252
|
|
247
253
|
# :category: Footers
|
@@ -252,7 +258,7 @@ module FatTable
|
|
252
258
|
cols.each do |c|
|
253
259
|
hsh[c] = :avg
|
254
260
|
end
|
255
|
-
gfooter('Group Average', hsh)
|
261
|
+
gfooter('Group Average', **hsh)
|
256
262
|
end
|
257
263
|
|
258
264
|
# :category: Footers
|
@@ -264,7 +270,7 @@ module FatTable
|
|
264
270
|
cols.each do |c|
|
265
271
|
hsh[c] = :min
|
266
272
|
end
|
267
|
-
footer('Minimum', hsh)
|
273
|
+
footer('Minimum', **hsh)
|
268
274
|
end
|
269
275
|
|
270
276
|
# :category: Footers
|
@@ -276,7 +282,7 @@ module FatTable
|
|
276
282
|
cols.each do |c|
|
277
283
|
hsh[c] = :min
|
278
284
|
end
|
279
|
-
gfooter('Group Minimum', hsh)
|
285
|
+
gfooter('Group Minimum', **hsh)
|
280
286
|
end
|
281
287
|
|
282
288
|
# :category: Footers
|
@@ -288,7 +294,7 @@ module FatTable
|
|
288
294
|
cols.each do |c|
|
289
295
|
hsh[c] = :max
|
290
296
|
end
|
291
|
-
footer('Maximum', hsh)
|
297
|
+
footer('Maximum', **hsh)
|
292
298
|
end
|
293
299
|
|
294
300
|
# :category: Footers
|
@@ -300,7 +306,7 @@ module FatTable
|
|
300
306
|
cols.each do |c|
|
301
307
|
hsh[c] = :max
|
302
308
|
end
|
303
|
-
gfooter('Group Maximum', hsh)
|
309
|
+
gfooter('Group Maximum', **hsh)
|
304
310
|
end
|
305
311
|
|
306
312
|
# :category: Formatting
|
@@ -411,8 +417,8 @@ module FatTable
|
|
411
417
|
#
|
412
418
|
# \n\[niltext\]:: render a nil item with the given text.
|
413
419
|
def format(**fmts)
|
414
|
-
[
|
415
|
-
format_for(loc, fmts)
|
420
|
+
%i[header bfirst gfirst body footer gfooter].each do |loc|
|
421
|
+
format_for(loc, **fmts)
|
416
422
|
end
|
417
423
|
self
|
418
424
|
end
|
@@ -465,7 +471,8 @@ module FatTable
|
|
465
471
|
unless LOCATIONS.include?(location)
|
466
472
|
raise UserError, "unknown format location '#{location}'"
|
467
473
|
end
|
468
|
-
|
474
|
+
|
475
|
+
valid_keys = table.headers + %i[string numeric datetime boolean nil]
|
469
476
|
invalid_keys = (fmts.keys - valid_keys).uniq
|
470
477
|
unless invalid_keys.empty?
|
471
478
|
msg = "invalid #{location} column or type: #{invalid_keys.join(',')}"
|
@@ -486,32 +493,33 @@ module FatTable
|
|
486
493
|
# Merge in string and nil formatting, but not in header. Header is
|
487
494
|
# always typed a string, so it will get formatted in type-based
|
488
495
|
# formatting below. And headers are never nil.
|
489
|
-
if fmts.
|
496
|
+
if fmts.key?(:string)
|
490
497
|
typ_fmt = parse_string_fmt(fmts[:string])
|
491
498
|
format_h = format_h.merge(typ_fmt)
|
492
499
|
end
|
493
|
-
if fmts.
|
500
|
+
if fmts.key?(:nil)
|
494
501
|
typ_fmt = parse_nil_fmt(fmts[:nil]).first
|
495
502
|
format_h = format_h.merge(typ_fmt)
|
496
503
|
end
|
497
504
|
end
|
498
505
|
typ = location == :header ? :string : table.type(h).as_sym
|
499
506
|
parse_typ_method_name = 'parse_' + typ.to_s + '_fmt'
|
500
|
-
if fmts.
|
507
|
+
if fmts.key?(typ)
|
501
508
|
# Merge in type-based formatting
|
502
509
|
typ_fmt = send(parse_typ_method_name, fmts[typ])
|
503
510
|
format_h = format_h.merge(typ_fmt)
|
504
511
|
end
|
505
512
|
if fmts[h]
|
506
513
|
# Merge in column formatting
|
507
|
-
col_fmt = send(parse_typ_method_name, fmts[h],
|
514
|
+
col_fmt = send(parse_typ_method_name, fmts[h],
|
515
|
+
strict: location != :header)
|
508
516
|
format_h = format_h.merge(col_fmt)
|
509
517
|
end
|
510
518
|
|
511
519
|
if location == :body
|
512
520
|
# Copy :body formatting for column h to :bfirst and :gfirst if they
|
513
|
-
# still have the default formatting. Can be overridden with a
|
514
|
-
# call with those locations.
|
521
|
+
# still have the default formatting. Can be overridden with a
|
522
|
+
# format_for call with those locations.
|
515
523
|
format_h.each_pair do |k, v|
|
516
524
|
if format_at[:bfirst][h].send(k) == default_format[k]
|
517
525
|
format_at[:bfirst][h].send("#{k}=", v)
|
@@ -538,14 +546,14 @@ module FatTable
|
|
538
546
|
self
|
539
547
|
end
|
540
548
|
|
541
|
-
|
549
|
+
############################################################################
|
542
550
|
# Parsing and validation routines
|
543
|
-
|
551
|
+
############################################################################
|
544
552
|
|
545
553
|
private
|
546
554
|
|
547
555
|
# Re to match a color name
|
548
|
-
CLR_RE = /(?:[-_a-zA-Z0-9 ]*)
|
556
|
+
CLR_RE = /(?:[-_a-zA-Z0-9 ]*)/.freeze
|
549
557
|
|
550
558
|
# Return a hash that reflects the formatting instructions given in the
|
551
559
|
# string fmt. Raise an error if it contains invalid formatting instructions.
|
@@ -556,6 +564,7 @@ module FatTable
|
|
556
564
|
unless fmt.blank? || !strict
|
557
565
|
raise UserError, "unrecognized string formatting instructions '#{fmt}'"
|
558
566
|
end
|
567
|
+
|
559
568
|
format
|
560
569
|
end
|
561
570
|
|
@@ -591,11 +600,11 @@ module FatTable
|
|
591
600
|
fmt = fmt.sub($&, '')
|
592
601
|
end
|
593
602
|
if fmt =~ /(~\s*)?B/
|
594
|
-
fmt_hash[:bold] =
|
603
|
+
fmt_hash[:bold] = !$1
|
595
604
|
fmt = fmt.sub($&, '')
|
596
605
|
end
|
597
606
|
if fmt =~ /(~\s*)?I/
|
598
|
-
fmt_hash[:italic] =
|
607
|
+
fmt_hash[:italic] = !$1
|
599
608
|
fmt = fmt.sub($&, '')
|
600
609
|
end
|
601
610
|
if fmt =~ /R/
|
@@ -611,11 +620,11 @@ module FatTable
|
|
611
620
|
fmt = fmt.sub($&, '')
|
612
621
|
end
|
613
622
|
if fmt =~ /(~\s*)?_/
|
614
|
-
fmt_hash[:underline] =
|
623
|
+
fmt_hash[:underline] = !$1
|
615
624
|
fmt = fmt.sub($&, '')
|
616
625
|
end
|
617
626
|
if fmt =~ /(~\s*)?\*/
|
618
|
-
fmt_hash[:blink] =
|
627
|
+
fmt_hash[:blink] = !$1
|
619
628
|
fmt = fmt.sub($&, '')
|
620
629
|
end
|
621
630
|
[fmt_hash, fmt]
|
@@ -652,20 +661,21 @@ module FatTable
|
|
652
661
|
fmt = fmt.sub($&, '')
|
653
662
|
end
|
654
663
|
if fmt =~ /(~\s*)?,/
|
655
|
-
fmt_hash[:commas] =
|
664
|
+
fmt_hash[:commas] = !$1
|
656
665
|
fmt = fmt.sub($&, '')
|
657
666
|
end
|
658
667
|
if fmt =~ /(~\s*)?\$/
|
659
|
-
fmt_hash[:currency] =
|
668
|
+
fmt_hash[:currency] = !$1
|
660
669
|
fmt = fmt.sub($&, '')
|
661
670
|
end
|
662
671
|
if fmt =~ /(~\s*)?H/
|
663
|
-
fmt_hash[:hms] =
|
672
|
+
fmt_hash[:hms] = !$1
|
664
673
|
fmt = fmt.sub($&, '')
|
665
674
|
end
|
666
675
|
unless fmt.blank? || !strict
|
667
676
|
raise UserError, "unrecognized numeric formatting instructions '#{fmt}'"
|
668
677
|
end
|
678
|
+
|
669
679
|
fmt_hash
|
670
680
|
end
|
671
681
|
|
@@ -687,7 +697,8 @@ module FatTable
|
|
687
697
|
fmt = fmt.sub($&, '')
|
688
698
|
end
|
689
699
|
unless fmt.blank? || !strict
|
690
|
-
|
700
|
+
msg = "unrecognized datetime formatting instructions '#{fmt}'"
|
701
|
+
raise UserError, msg
|
691
702
|
end
|
692
703
|
fmt_hash
|
693
704
|
end
|
@@ -708,7 +719,8 @@ module FatTable
|
|
708
719
|
end
|
709
720
|
# Since true_text, false_text and nil_text may want to have internal
|
710
721
|
# spaces, defer removing extraneous spaces until after they are parsed.
|
711
|
-
if fmt =~ /c\[(#{CLR_RE})(\.(#{CLR_RE}))
|
722
|
+
if fmt =~ /c\[(#{CLR_RE})(\.(#{CLR_RE}))?,
|
723
|
+
\s*(#{CLR_RE})(\.(#{CLR_RE}))?\]/x
|
712
724
|
fmt_hash[:true_color] = $1 unless $1.blank?
|
713
725
|
fmt_hash[:true_bgcolor] = $3 unless $3.blank?
|
714
726
|
fmt_hash[:false_color] = $4 unless $4.blank?
|
@@ -735,12 +747,13 @@ module FatTable
|
|
735
747
|
unless fmt.blank? || !strict
|
736
748
|
raise UserError, "unrecognized boolean formatting instructions '#{fmt}'"
|
737
749
|
end
|
750
|
+
|
738
751
|
fmt_hash
|
739
752
|
end
|
740
753
|
|
741
|
-
|
754
|
+
############################################################################
|
742
755
|
# Applying formatting
|
743
|
-
|
756
|
+
############################################################################
|
744
757
|
|
745
758
|
public
|
746
759
|
|
@@ -806,6 +819,7 @@ module FatTable
|
|
806
819
|
# specializing this method.
|
807
820
|
def format_boolean(val, istruct)
|
808
821
|
return istruct.nil_text if val.nil?
|
822
|
+
|
809
823
|
val ? istruct.true_text : istruct.false_text
|
810
824
|
end
|
811
825
|
|
@@ -816,6 +830,7 @@ module FatTable
|
|
816
830
|
# specializing this method.
|
817
831
|
def format_datetime(val, istruct)
|
818
832
|
return istruct.nil_text if val.nil?
|
833
|
+
|
819
834
|
if val.to_date == val
|
820
835
|
# It is a Date, with no time component.
|
821
836
|
val.strftime(istruct.date_fmt)
|
@@ -831,12 +846,13 @@ module FatTable
|
|
831
846
|
# specializing this method.
|
832
847
|
def format_numeric(val, istruct)
|
833
848
|
return istruct.nil_text if val.nil?
|
849
|
+
|
834
850
|
val = val.round(istruct.post_digits) if istruct.post_digits >= 0
|
835
851
|
if istruct.hms
|
836
852
|
result = val.secs_to_hms
|
837
853
|
istruct.commas = false
|
838
854
|
elsif istruct.currency
|
839
|
-
prec = istruct.post_digits
|
855
|
+
prec = istruct.post_digits.zero? ? 2 : istruct.post_digits
|
840
856
|
delim = istruct.commas ? ',' : ''
|
841
857
|
result = val.to_s(:currency, precision: prec, delimiter: delim,
|
842
858
|
unit: FatTable.currency_symbol)
|
@@ -903,9 +919,9 @@ module FatTable
|
|
903
919
|
val
|
904
920
|
end
|
905
921
|
|
906
|
-
|
922
|
+
############################################################################
|
907
923
|
# Output routines
|
908
|
-
|
924
|
+
############################################################################
|
909
925
|
|
910
926
|
public
|
911
927
|
|
@@ -994,6 +1010,7 @@ module FatTable
|
|
994
1010
|
new_rows.each do |loc_row|
|
995
1011
|
result += hline(widths) if loc_row.nil?
|
996
1012
|
next if loc_row.nil?
|
1013
|
+
|
997
1014
|
_loc, row = *loc_row
|
998
1015
|
result += pre_row
|
999
1016
|
cells = []
|
@@ -1016,7 +1033,7 @@ module FatTable
|
|
1016
1033
|
# Return a hash mapping the table's headers to their formatted versions. If
|
1017
1034
|
# a hash of column widths is given, perform alignment within the given field
|
1018
1035
|
# widths.
|
1019
|
-
def build_formatted_headers(
|
1036
|
+
def build_formatted_headers(_widths = {})
|
1020
1037
|
# Don't decorate if this Formatter calls for alignment. It will be done
|
1021
1038
|
# in the second pass.
|
1022
1039
|
decorate = !aligned?
|
@@ -1058,7 +1075,8 @@ module FatTable
|
|
1058
1075
|
grp_col[h] ||= Column.new(header: h)
|
1059
1076
|
grp_col[h] << row[h]
|
1060
1077
|
istruct = format_at[location][h]
|
1061
|
-
new_row[h] = [row[h], format_cell(row[h], istruct,
|
1078
|
+
new_row[h] = [row[h], format_cell(row[h], istruct,
|
1079
|
+
decorate: decorate)]
|
1062
1080
|
end
|
1063
1081
|
new_rows << [location, new_row]
|
1064
1082
|
tbl_row_k += 1
|
@@ -1135,6 +1153,7 @@ module FatTable
|
|
1135
1153
|
end
|
1136
1154
|
rows.each do |loc_row|
|
1137
1155
|
next if loc_row.nil?
|
1156
|
+
|
1138
1157
|
_loc, row = *loc_row
|
1139
1158
|
row.each_pair do |h, (_v, fmt_v)|
|
1140
1159
|
widths[h] ||= 0
|
@@ -1217,12 +1236,12 @@ module FatTable
|
|
1217
1236
|
''
|
1218
1237
|
end
|
1219
1238
|
|
1220
|
-
def pre_cell(
|
1239
|
+
def pre_cell(_head)
|
1221
1240
|
''
|
1222
1241
|
end
|
1223
1242
|
|
1224
|
-
def quote_cell(
|
1225
|
-
|
1243
|
+
def quote_cell(val)
|
1244
|
+
val
|
1226
1245
|
end
|
1227
1246
|
|
1228
1247
|
def post_cell
|