fat_table 0.2.6 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|