fat_table 0.3.4 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/.rubocop.yml +3 -5
- data/README.org +1343 -456
- data/README.rdoc +1 -2
- data/TODO.org +17 -10
- data/examples/create_trans.sql +14 -0
- data/examples/quick.pdf +0 -0
- data/examples/quick.png +0 -0
- data/examples/quick.ppm +0 -0
- data/examples/quick.tex +8 -0
- data/examples/quick_small.png +0 -0
- data/examples/quicktable.tex +123 -0
- data/examples/trades.db +0 -0
- data/examples/trans.csv +13 -0
- data/fat_table.gemspec +1 -0
- data/lib/ext/array.rb +15 -0
- data/lib/fat_table/column.rb +69 -206
- data/lib/fat_table/convert.rb +173 -0
- data/lib/fat_table/evaluator.rb +7 -0
- data/lib/fat_table/footer.rb +228 -0
- data/lib/fat_table/formatters/formatter.rb +200 -166
- data/lib/fat_table/formatters/latex_formatter.rb +9 -7
- data/lib/fat_table/table.rb +303 -98
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +5 -2
- data/md/README.md +1 -2
- metadata +28 -2
data/README.rdoc
CHANGED
@@ -888,8 +888,7 @@ will raise an exception.
|
|
888
888
|
|
889
889
|
+last+:: the last non-nil item in the column,
|
890
890
|
|
891
|
-
+
|
892
|
-
values in the column,
|
891
|
+
+range+- :: form a Range ~~{min}..{max}~ to show the range of values in the column,
|
893
892
|
|
894
893
|
+sum+:: for Numeric and String columns, apply '+' to all the non-nil values,
|
895
894
|
|
data/TODO.org
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
* TODO Conversion to Spreadsheets
|
2
|
+
- State "TODO" from [2017-04-21 Fri 10:36]
|
3
|
+
This is a [[https://github.com/westonganger/spreadsheet_architect][gem]] that I can include into the Table model to convert a table into
|
4
|
+
a spread-sheet, or even a sheet in a multi-sheet spreadsheet file.
|
5
|
+
|
6
|
+
* TODO Add from_yql for fetching from Yahoo
|
7
|
+
- State "TODO" from [2017-04-21 Fri 10:35]
|
8
|
+
Add a constructor to allow fetching stock data from yql. Perhaps grab all
|
9
|
+
available fields, then allow a select of those of interest.
|
10
|
+
|
11
|
+
* DONE Allow sorting by expression
|
12
|
+
CLOSED: [2022-01-20 Thu 12:47]
|
13
|
+
Either by a single string argument as the sole argument to order_by, or use
|
14
|
+
another method, such a order_with. Note that this can be done now by creating
|
15
|
+
a new column having the sort expression with select and then just order_by
|
16
|
+
that column. Perhaps that is an easy way to implement it.
|
17
|
+
|
1
18
|
* DONE Ensure that columns resulting from aggregates have proper type
|
2
19
|
CLOSED: [2017-12-29 Fri 05:34]
|
3
20
|
- State "WAIT" from "TODO" [2017-12-29 Fri 05:34]
|
@@ -5,11 +22,6 @@ CLOSED: [2017-12-29 Fri 05:34]
|
|
5
22
|
After applying avg, does the column have the proper Numeric or Date, or DateTime
|
6
23
|
type. How about Boolean aggregates?
|
7
24
|
|
8
|
-
* TODO Conversion to Spreadsheets
|
9
|
-
- State "TODO" from [2017-04-21 Fri 10:36]
|
10
|
-
This is a [[https://github.com/westonganger/spreadsheet_architect][gem]] that I can include into the Table model to convert a table into
|
11
|
-
a spread-sheet, or even a sheet in a multi-sheet spreadsheet file.
|
12
|
-
|
13
25
|
* DONE Formatters
|
14
26
|
CLOSED: [2017-04-21 Fri 10:36]
|
15
27
|
- State "WAIT" from "TODO" [2017-04-21 Fri 10:36]
|
@@ -31,8 +43,3 @@ CLOSED: [2017-03-02 Thu 15:54]
|
|
31
43
|
- State "TODO" from [2017-03-02 Thu 15:54]
|
32
44
|
For tables, add a method that eliminates any duplicate rows. Perhaps just apply
|
33
45
|
Array#uniq to the columns?
|
34
|
-
|
35
|
-
* TODO Add from_yql for fetching from Yahoo
|
36
|
-
- State "TODO" from [2017-04-21 Fri 10:35]
|
37
|
-
Add a constructor to allow fetching stock data from yql. Perhaps grab all
|
38
|
-
available fields, then allow a select of those of interest.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
drop table trans;
|
2
|
+
create table trans(date text, code text, raw, shares, price, info text, ok text);
|
3
|
+
insert into trans values('2013-05-29', 'S', 15700.00, 6601.85, 24.7790, 'ENTITY3', 'F');
|
4
|
+
insert into trans values('2013-05-02', 'P', 118186.40, 118186.4, 11.8500, 'ENTITY1', 'T');
|
5
|
+
insert into trans values('2013-05-20', 'S', 12000.00, 5046.00, 28.2804, 'ENTITY3', 'F');
|
6
|
+
insert into trans values('2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ENTITY3', 'T');
|
7
|
+
insert into trans values('2013-05-23', 'S', 39906.00, 16780.47, 25.1749, 'ENTITY3', 'T');
|
8
|
+
insert into trans values('2013-05-20', 'S', 85000.00, 35742.50, 28.3224, 'ENTITY3', 'T');
|
9
|
+
insert into trans values('2013-05-02', 'P', 795546.20, 795546.2, 1.1850, 'ENTITY1', 'T');
|
10
|
+
insert into trans values('2013-05-29', 'S', 13459.00, 5659.51, 24.7464, 'ENTITY3', 'T');
|
11
|
+
insert into trans values('2013-05-20', 'S', 33302.00, 14003.49, 28.6383, 'ENTITY3', 'T');
|
12
|
+
insert into trans values('2013-05-29', 'S', 15900.00, 6685.95, 24.5802, 'ENTITY3', 'T');
|
13
|
+
insert into trans values('2013-05-30', 'S', 6679.00, 2808.52, 25.0471, 'ENTITY3', 'T');
|
14
|
+
insert into trans values('2013-05-23', 'S', 23054.00, 9694.21, 26.8015, 'ENTITY3', 'F');
|
data/examples/quick.pdf
ADDED
Binary file
|
data/examples/quick.png
ADDED
Binary file
|
data/examples/quick.ppm
ADDED
Binary file
|
data/examples/quick.tex
ADDED
Binary file
|
@@ -0,0 +1,123 @@
|
|
1
|
+
\begin{longtable}{clcrrc}
|
2
|
+
\bfseries{Ref}&
|
3
|
+
\multicolumn{1}{c}{\bfseries{Date}}&
|
4
|
+
\bfseries{Code}&
|
5
|
+
\multicolumn{1}{c}{\bfseries{Shares}}&
|
6
|
+
\multicolumn{1}{c}{\bfseries{Price}}&
|
7
|
+
\bfseries{Ok}\\
|
8
|
+
\endhead
|
9
|
+
\bfseries{1}&
|
10
|
+
2013-05-02&
|
11
|
+
P&
|
12
|
+
\cellcolor{lightgray}{\textcolor{blue}{118,186.4}}&
|
13
|
+
\$11.8500&
|
14
|
+
Y\\
|
15
|
+
\bfseries{2}&
|
16
|
+
2013-05-02&
|
17
|
+
P&
|
18
|
+
\cellcolor{lightgray}{\textcolor{blue}{795,546.2}}&
|
19
|
+
1.1850&
|
20
|
+
Y\\
|
21
|
+
\bfseries{Avg}&
|
22
|
+
&
|
23
|
+
\multicolumn{1}{l}{}&
|
24
|
+
\bfseries{456,866.3}&
|
25
|
+
\bfseries{6.5175}&
|
26
|
+
\\
|
27
|
+
\bfseries{3}&
|
28
|
+
2013-05-20&
|
29
|
+
S&
|
30
|
+
\cellcolor{lightgray}{\textcolor{blue}{5,046.0}}&
|
31
|
+
28.2804&
|
32
|
+
N\\
|
33
|
+
\bfseries{4}&
|
34
|
+
2013-05-20&
|
35
|
+
S&
|
36
|
+
\cellcolor{lightgray}{\textcolor{blue}{35,742.5}}&
|
37
|
+
28.3224&
|
38
|
+
Y\\
|
39
|
+
\bfseries{5}&
|
40
|
+
2013-05-20&
|
41
|
+
S&
|
42
|
+
\cellcolor{lightgray}{\textcolor{blue}{14,003.5}}&
|
43
|
+
28.6383&
|
44
|
+
Y\\
|
45
|
+
\bfseries{Avg}&
|
46
|
+
&
|
47
|
+
\multicolumn{1}{l}{}&
|
48
|
+
\bfseries{18,264.0}&
|
49
|
+
\bfseries{28.4137}&
|
50
|
+
\\
|
51
|
+
\bfseries{6}&
|
52
|
+
2013-05-23&
|
53
|
+
S&
|
54
|
+
\cellcolor{lightgray}{\textcolor{blue}{3,364.0}}&
|
55
|
+
27.1083&
|
56
|
+
Y\\
|
57
|
+
\bfseries{7}&
|
58
|
+
2013-05-23&
|
59
|
+
S&
|
60
|
+
\cellcolor{lightgray}{\textcolor{blue}{16,780.5}}&
|
61
|
+
25.1749&
|
62
|
+
Y\\
|
63
|
+
\bfseries{8}&
|
64
|
+
2013-05-23&
|
65
|
+
S&
|
66
|
+
\cellcolor{lightgray}{\textcolor{blue}{9,694.2}}&
|
67
|
+
26.8015&
|
68
|
+
N\\
|
69
|
+
\bfseries{Avg}&
|
70
|
+
&
|
71
|
+
\multicolumn{1}{l}{}&
|
72
|
+
\bfseries{9,946.2}&
|
73
|
+
\bfseries{26.3616}&
|
74
|
+
\\
|
75
|
+
\bfseries{9}&
|
76
|
+
2013-05-29&
|
77
|
+
S&
|
78
|
+
\cellcolor{lightgray}{\textcolor{blue}{6,601.9}}&
|
79
|
+
24.7790&
|
80
|
+
N\\
|
81
|
+
\bfseries{10}&
|
82
|
+
2013-05-29&
|
83
|
+
S&
|
84
|
+
\cellcolor{lightgray}{\textcolor{blue}{5,659.5}}&
|
85
|
+
24.7464&
|
86
|
+
Y\\
|
87
|
+
\bfseries{11}&
|
88
|
+
2013-05-29&
|
89
|
+
S&
|
90
|
+
\cellcolor{lightgray}{\textcolor{blue}{6,686.0}}&
|
91
|
+
24.5802&
|
92
|
+
Y\\
|
93
|
+
\bfseries{Avg}&
|
94
|
+
&
|
95
|
+
\multicolumn{1}{l}{}&
|
96
|
+
\bfseries{6,315.8}&
|
97
|
+
\bfseries{24.7019}&
|
98
|
+
\\
|
99
|
+
\bfseries{12}&
|
100
|
+
2013-05-30&
|
101
|
+
S&
|
102
|
+
\cellcolor{lightgray}{\textcolor{blue}{2,808.5}}&
|
103
|
+
25.0471&
|
104
|
+
Y\\
|
105
|
+
\bfseries{Avg}&
|
106
|
+
&
|
107
|
+
\multicolumn{1}{l}{}&
|
108
|
+
\bfseries{2,808.5}&
|
109
|
+
\bfseries{25.0471}&
|
110
|
+
\\
|
111
|
+
\bfseries{Average}&
|
112
|
+
&
|
113
|
+
\multicolumn{1}{l}{}&
|
114
|
+
\bfseries{85,009.9}&
|
115
|
+
\bfseries{23.0428}&
|
116
|
+
\\
|
117
|
+
\bfseries{Total}&
|
118
|
+
&
|
119
|
+
\multicolumn{1}{l}{}&
|
120
|
+
\bfseries{1,020,119.1}&
|
121
|
+
\bfseries{}&
|
122
|
+
\\
|
123
|
+
\end{longtable}
|
data/examples/trades.db
ADDED
Binary file
|
data/examples/trans.csv
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Ok',
|
2
|
+
'2013-05-29', 'S', 15700.00, 6601.85, 24.7790, 'ENTITY3', FALSE,
|
3
|
+
'2013-05-02', 'P', 118186.40, 118186.4, 11.8500, 'ENTITY1', TRUE,
|
4
|
+
'2013-05-20', 'S', 12000.00, 5046.00, 28.2804, 'ENTITY3', FALSE,
|
5
|
+
'2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ENTITY3', TRUE,
|
6
|
+
'2013-05-23', 'S', 39906.00, 16780.47, 25.1749, 'ENTITY3', TRUE,
|
7
|
+
'2013-05-20', 'S', 85000.00, 35742.50, 28.3224, 'ENTITY3', TRUE,
|
8
|
+
'2013-05-02', 'P', 795546.20, 795546.2, 1.1850, 'ENTITY1', TRUE,
|
9
|
+
'2013-05-29', 'S', 13459.00, 5659.51, 24.7464, 'ENTITY3', TRUE,
|
10
|
+
'2013-05-20', 'S', 33302.00, 14003.49, 28.6383, 'ENTITY3', TRUE,
|
11
|
+
'2013-05-29', 'S', 15900.00, 6685.95, 24.5802, 'ENTITY3', TRUE,
|
12
|
+
'2013-05-30', 'S', 6679.00, 2808.52, 25.0471, 'ENTITY3', TRUE,
|
13
|
+
'2013-05-23', 'S', 23054.00, 9694.21, 26.8015, 'ENTITY3', FALSE
|
data/fat_table.gemspec
CHANGED
@@ -70,6 +70,7 @@ Gem::Specification.new do |spec|
|
|
70
70
|
spec.add_development_dependency 'rake', '~> 13.0'
|
71
71
|
spec.add_development_dependency 'redcarpet'
|
72
72
|
spec.add_development_dependency 'pg'
|
73
|
+
spec.add_development_dependency 'sqlite3'
|
73
74
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
74
75
|
spec.add_development_dependency 'rubocop-rspec'
|
75
76
|
spec.add_development_dependency 'rubocop-performance'
|
data/lib/ext/array.rb
ADDED
data/lib/fat_table/column.rb
CHANGED
@@ -143,7 +143,7 @@ module FatTable
|
|
143
143
|
|
144
144
|
# Force the column to have String type and then convert all items to
|
145
145
|
# strings.
|
146
|
-
def
|
146
|
+
def force_string!
|
147
147
|
# msg = "Can only force an empty column to String type"
|
148
148
|
# raise UserError, msg unless empty?
|
149
149
|
@type = 'String'
|
@@ -175,66 +175,92 @@ module FatTable
|
|
175
175
|
|
176
176
|
# The names of the known aggregate operations that can be performed on a
|
177
177
|
# Column.
|
178
|
-
VALID_AGGREGATES = %s(first last
|
179
|
-
sum count min max
|
178
|
+
VALID_AGGREGATES = %s(first last range
|
179
|
+
sum count min max
|
180
|
+
avg var pvar dev pdev
|
180
181
|
any? all? none? one?)
|
181
182
|
|
182
183
|
# :category: Aggregates
|
183
184
|
|
184
185
|
# Return the first non-nil item in the Column. Works with any Column type.
|
185
186
|
def first
|
186
|
-
|
187
|
+
if type == 'String'
|
188
|
+
items.reject(&:blank?).first
|
189
|
+
else
|
190
|
+
items.compact.first
|
191
|
+
end
|
187
192
|
end
|
188
193
|
|
189
194
|
# :category: Aggregates
|
190
195
|
|
191
196
|
# Return the last non-nil item in the Column. Works with any Column type.
|
192
197
|
def last
|
193
|
-
|
198
|
+
if type == 'String'
|
199
|
+
items.reject(&:blank?).last
|
200
|
+
else
|
201
|
+
items.compact.last
|
202
|
+
end
|
194
203
|
end
|
195
204
|
|
196
205
|
# :category: Aggregates
|
197
206
|
|
198
|
-
# Return a
|
199
|
-
#
|
200
|
-
def
|
201
|
-
|
207
|
+
# Return a count of the non-nil items in the Column. Works with any Column
|
208
|
+
# type.
|
209
|
+
def count
|
210
|
+
if type == 'String'
|
211
|
+
items.reject(&:blank?).count.to_d
|
212
|
+
else
|
213
|
+
items.compact.count.to_d
|
214
|
+
end
|
202
215
|
end
|
203
216
|
|
204
217
|
# :category: Aggregates
|
205
218
|
|
206
|
-
# Return the
|
207
|
-
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
219
|
+
# Return the smallest non-nil, non-blank item in the Column. Works with
|
220
|
+
# numeric, string, and datetime Columns.
|
221
|
+
def min
|
222
|
+
only_with('min', 'NilClass', 'Numeric', 'String', 'DateTime')
|
223
|
+
if type == 'String'
|
224
|
+
items.reject(&:blank?).min
|
225
|
+
else
|
226
|
+
items.compact.min
|
227
|
+
end
|
212
228
|
end
|
213
229
|
|
214
230
|
# :category: Aggregates
|
215
231
|
|
216
|
-
# Return
|
217
|
-
#
|
218
|
-
def
|
219
|
-
|
232
|
+
# Return the largest non-nil, non-blank item in the Column. Works with
|
233
|
+
# numeric, string, and datetime Columns.
|
234
|
+
def max
|
235
|
+
only_with('max', 'NilClass', 'Numeric', 'String', 'DateTime')
|
236
|
+
if type == 'String'
|
237
|
+
items.reject(&:blank?).max
|
238
|
+
else
|
239
|
+
items.compact.max
|
240
|
+
end
|
220
241
|
end
|
221
242
|
|
222
243
|
# :category: Aggregates
|
223
244
|
|
224
|
-
# Return the smallest
|
225
|
-
# string, and datetime Columns.
|
226
|
-
def
|
227
|
-
only_with('
|
228
|
-
|
245
|
+
# Return a Range object for the smallest to largest value in the column.
|
246
|
+
# Works with numeric, string, and datetime Columns.
|
247
|
+
def range
|
248
|
+
only_with('range', 'NilClass', 'Numeric', 'String', 'DateTime')
|
249
|
+
Range.new(min, max)
|
229
250
|
end
|
230
251
|
|
231
252
|
# :category: Aggregates
|
232
253
|
|
233
|
-
# Return the
|
234
|
-
# string,
|
235
|
-
|
236
|
-
|
237
|
-
|
254
|
+
# Return the sum of the non-nil items in the Column. Works with numeric and
|
255
|
+
# string Columns. For a string Column, it will return the concatenation of
|
256
|
+
# the non-nil items.
|
257
|
+
def sum
|
258
|
+
only_with('sum', 'Numeric', 'String')
|
259
|
+
if type == 'String'
|
260
|
+
items.reject(&:blank?).join(' ')
|
261
|
+
else
|
262
|
+
items.compact.sum
|
263
|
+
end
|
238
264
|
end
|
239
265
|
|
240
266
|
# :category: Aggregates
|
@@ -392,187 +418,24 @@ module FatTable
|
|
392
418
|
|
393
419
|
private
|
394
420
|
|
395
|
-
# Convert val to the type of key, a ruby class constant, such as Date,
|
396
|
-
# Numeric, etc. If type is NilClass, the type is open, and a non-blank val
|
397
|
-
# will attempt conversion to one of the allowed types, typing it as a String
|
398
|
-
# if no other type is recognized. If the val is blank, and the type is nil,
|
399
|
-
# the Column type remains open. If the val is nil or a blank and the type is
|
400
|
-
# already determined, the val is set to nil, and should be filtered from any
|
401
|
-
# Column computations. If the val is non-blank and the Column type
|
402
|
-
# determined, raise an error if the val cannot be converted to the Column
|
403
|
-
# type. Otherwise, returns the converted val as an object of the correct
|
404
|
-
# class.
|
405
421
|
def convert_to_type(val)
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
new_val
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
new_val
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
convert_to_string(val)
|
420
|
-
else
|
421
|
-
bool_val
|
422
|
-
end
|
423
|
-
@type =
|
424
|
-
if [true, false].include?(new_val)
|
425
|
-
'Boolean'
|
426
|
-
elsif new_val.is_a?(Date) || new_val.is_a?(DateTime)
|
427
|
-
'DateTime'
|
428
|
-
elsif new_val.is_a?(Numeric)
|
429
|
-
'Numeric'
|
430
|
-
elsif new_val.is_a?(String)
|
431
|
-
'String'
|
432
|
-
else
|
433
|
-
msg = "can't add #{val} of type #{new_val.class.name} to a column"
|
434
|
-
raise UserError, msg
|
435
|
-
end
|
436
|
-
end
|
437
|
-
new_val
|
438
|
-
when 'Boolean'
|
439
|
-
if val.is_a?(String) && val.blank? || val.nil?
|
440
|
-
nil
|
441
|
-
else
|
442
|
-
new_val = convert_to_boolean(val)
|
443
|
-
if new_val.nil?
|
444
|
-
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
422
|
+
new_val = Convert.convert_to_type(val, type)
|
423
|
+
if new_val && type == 'NilClass'
|
424
|
+
@type =
|
425
|
+
if [true, false].include?(new_val)
|
426
|
+
'Boolean'
|
427
|
+
elsif new_val.is_a?(Date) || new_val.is_a?(DateTime)
|
428
|
+
'DateTime'
|
429
|
+
elsif new_val.is_a?(Numeric)
|
430
|
+
'Numeric'
|
431
|
+
elsif new_val.is_a?(String)
|
432
|
+
'String'
|
433
|
+
else
|
434
|
+
msg = "can't add value '#{val}' of type #{new_val.class.name} to a column"
|
445
435
|
raise UserError, msg
|
446
436
|
end
|
447
|
-
new_val
|
448
|
-
end
|
449
|
-
when 'DateTime'
|
450
|
-
if val.blank?
|
451
|
-
nil
|
452
|
-
else
|
453
|
-
new_val = convert_to_date_time(val)
|
454
|
-
if new_val.nil?
|
455
|
-
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
456
|
-
raise UserError, msg
|
457
|
-
end
|
458
|
-
new_val
|
459
|
-
end
|
460
|
-
when 'Numeric'
|
461
|
-
if val.blank?
|
462
|
-
nil
|
463
|
-
else
|
464
|
-
new_val = convert_to_numeric(val)
|
465
|
-
if new_val.nil?
|
466
|
-
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
467
|
-
raise UserError, msg
|
468
|
-
end
|
469
|
-
new_val
|
470
|
-
end
|
471
|
-
when 'String'
|
472
|
-
if val.nil?
|
473
|
-
nil
|
474
|
-
else
|
475
|
-
new_val = convert_to_string(val)
|
476
|
-
if new_val.nil?
|
477
|
-
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
478
|
-
raise UserError, msg
|
479
|
-
end
|
480
|
-
new_val
|
481
|
-
end
|
482
|
-
else
|
483
|
-
raise UserError, "Mysteriously, column has unknown type '#{type}'"
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
# Convert the val to a boolean if it looks like one, otherwise return nil.
|
488
|
-
# Any boolean or a string of t, f, true, false, y, n, yes, or no, regardless
|
489
|
-
# of case is assumed to be a boolean.
|
490
|
-
def convert_to_boolean(val)
|
491
|
-
return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
492
|
-
val = val.to_s.clean
|
493
|
-
return nil if val.blank?
|
494
|
-
if val.match?(/\A(false|f|n|no)\z/i)
|
495
|
-
false
|
496
|
-
elsif val.match?(/\A(true|t|y|yes)\z/i)
|
497
|
-
true
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
ISO_DATE_RE = %r{(?<yr>\d\d\d\d)[-\/]
|
502
|
-
(?<mo>\d\d?)[-\/]
|
503
|
-
(?<dy>\d\d?)\s*
|
504
|
-
(T?\s*\d\d:\d\d(:\d\d)?
|
505
|
-
([-+](\d\d?)(:\d\d?))?)?}x
|
506
|
-
|
507
|
-
AMR_DATE_RE = %r{(?<dy>\d\d?)[-/](?<mo>\d\d?)[-/](?<yr>\d\d\d\d)\s*
|
508
|
-
(?<tm>T\d\d:\d\d:\d\d(\+\d\d:\d\d)?)?}x
|
509
|
-
|
510
|
-
# A Date like 'Tue, 01 Nov 2016' or 'Tue 01 Nov 2016' or '01 Nov 2016'.
|
511
|
-
# These are emitted by Postgresql, so it makes from_sql constructor
|
512
|
-
# possible without special formatting of the dates.
|
513
|
-
INV_DATE_RE = %r{((mon|tue|wed|thu|fri|sat|sun)[a-zA-z]*,?)?\s+ # looks like dow
|
514
|
-
(?<dy>\d\d?)\s+ # one or two-digit day
|
515
|
-
(?<mo_name>[jfmasondJFMASOND][A-Za-z]{2,})\s+ # looks like a month name
|
516
|
-
(?<yr>\d\d\d\d) # and a 4-digit year
|
517
|
-
}xi
|
518
|
-
|
519
|
-
# Convert the val to a DateTime if it is either a DateTime, a Date, a Time, or a
|
520
|
-
# String that can be parsed as a DateTime, otherwise return nil. It only
|
521
|
-
# recognizes strings that contain a something like '2016-01-14' or '2/12/1985'
|
522
|
-
# within them, otherwise DateTime.parse would treat many bare numbers as dates,
|
523
|
-
# such as '2841381', which it would recognize as a valid date, but the user
|
524
|
-
# probably does not intend it to be so treated.
|
525
|
-
def convert_to_date_time(val)
|
526
|
-
return val if val.is_a?(DateTime)
|
527
|
-
return val if val.is_a?(Date)
|
528
|
-
return val.to_datetime if val.is_a?(Time)
|
529
|
-
begin
|
530
|
-
str = val.to_s.clean
|
531
|
-
return nil if str.blank?
|
532
|
-
|
533
|
-
if str.match(ISO_DATE_RE)
|
534
|
-
date = DateTime.parse(val)
|
535
|
-
elsif str =~ AMR_DATE_RE
|
536
|
-
date = DateTime.new(Regexp.last_match[:yr].to_i,
|
537
|
-
Regexp.last_match[:mo].to_i,
|
538
|
-
Regexp.last_match[:dy].to_i)
|
539
|
-
elsif str =~ INV_DATE_RE
|
540
|
-
mo = Date.mo_name_to_num(last_match[:mo_name])
|
541
|
-
date = DateTime.new(Regexp.last_match[:yr].to_i, mo,
|
542
|
-
Regexp.last_match[:dy].to_i)
|
543
|
-
else
|
544
|
-
return nil
|
545
|
-
end
|
546
|
-
# val = val.to_date if
|
547
|
-
date.seconds_since_midnight.zero? ? date.to_date : date
|
548
|
-
rescue ArgumentError
|
549
|
-
nil
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
|
-
# Convert the val to a Numeric if is already a Numeric or is a String that
|
554
|
-
# looks like one. Any Float is promoted to a BigDecimal. Otherwise return
|
555
|
-
# nil.
|
556
|
-
def convert_to_numeric(val)
|
557
|
-
return BigDecimal(val, Float::DIG) if val.is_a?(Float)
|
558
|
-
return val if val.is_a?(Numeric)
|
559
|
-
# Eliminate any commas, $'s (or other currency symbol), or _'s.
|
560
|
-
cursym = Regexp.quote(FatTable.currency_symbol)
|
561
|
-
clean_re = /[,_#{cursym}]/
|
562
|
-
val = val.to_s.clean.gsub(clean_re, '')
|
563
|
-
return nil if val.blank?
|
564
|
-
case val
|
565
|
-
when /(\A[-+]?\d+\.\d*\z)|(\A[-+]?\d*\.\d+\z)/
|
566
|
-
BigDecimal(val.to_s.clean)
|
567
|
-
when /\A[-+]?[\d]+\z/
|
568
|
-
val.to_i
|
569
|
-
when %r{\A(?<nm>[-+]?\d+)\s*[:/]\s*(?<dn>[-+]?\d+)\z}
|
570
|
-
Rational(Regexp.last_match[:nm], Regexp.last_match[:dn])
|
571
437
|
end
|
572
|
-
|
573
|
-
|
574
|
-
def convert_to_string(val)
|
575
|
-
val.to_s
|
438
|
+
new_val
|
576
439
|
end
|
577
440
|
end
|
578
441
|
end
|