fat_core 1.7.1 → 2.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/TODO.org +0 -19
- data/lib/fat_core/string.rb +23 -11
- data/lib/fat_core/version.rb +3 -3
- data/lib/fat_core.rb +0 -4
- data/spec/lib/string_spec.rb +41 -37
- metadata +2 -33
- data/lib/fat_core/column.rb +0 -345
- data/lib/fat_core/evaluator.rb +0 -43
- data/lib/fat_core/formatters/aoa_formatter.rb +0 -84
- data/lib/fat_core/formatters/aoh_formatter.rb +0 -82
- data/lib/fat_core/formatters/formatter.rb +0 -973
- data/lib/fat_core/formatters/org_formatter.rb +0 -72
- data/lib/fat_core/formatters/text_formatter.rb +0 -91
- data/lib/fat_core/formatters.rb +0 -5
- data/lib/fat_core/table.rb +0 -988
- data/spec/example_files/datawatch.org +0 -471
- data/spec/example_files/goldberg.org +0 -199
- data/spec/example_files/wpcs.csv +0 -92
- data/spec/lib/column_spec.rb +0 -224
- data/spec/lib/evaluator_spec.rb +0 -34
- data/spec/lib/formatters/aoa_formatter_spec.rb +0 -62
- data/spec/lib/formatters/aoh_formatter_spec.rb +0 -61
- data/spec/lib/formatters/formatter_spec.rb +0 -371
- data/spec/lib/formatters/org_formatter_spec.rb +0 -60
- data/spec/lib/formatters/text_formatter_spec.rb +0 -60
- data/spec/lib/table_spec.rb +0 -990
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c3d2ea26248f15906e7885b3efeaa09ffa10554
|
4
|
+
data.tar.gz: 0d14aa12596198d1add2e0ae2922a89411da65fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a68397f420d5a775818d7071e12e4bf15aadbe05f77e8fe1a34066bab5c3ab853040be1399d5bd08be587a33bade93cd7bbade821806a18df5f6f5c4508bb12
|
7
|
+
data.tar.gz: e1e0c5b69ba7d652c17cf41a788c2d5291c725e9c5fb80cbef1b2edd016146ab67bd4f68b767828bea4d5c5ce9779a47b01bfd6a0299cd3480b9d6bd5ff628c1
|
data/TODO.org
CHANGED
@@ -1,19 +0,0 @@
|
|
1
|
-
* Conversion to Spreadsheets
|
2
|
-
This is a [[https://github.com/westonganger/spreadsheet_architect][gem]] that I can include into the Table model to convert a table into
|
3
|
-
a spread-sheet, or even a sheet in a multi-sheet spreadsheet file.
|
4
|
-
|
5
|
-
* Formatters
|
6
|
-
Need to think about ways to define formatters for Table for different output
|
7
|
-
types, including tty, color-tty, latex, csv, spreadsheet?
|
8
|
-
|
9
|
-
* Add a Group Boundary concept
|
10
|
-
If I want a table to perform sub-totals at various break points, need to have a
|
11
|
-
way for a table to record its grouping boundaries. Maybe an array of row
|
12
|
-
numbers? Automatically injected by the group-by method?
|
13
|
-
|
14
|
-
* DONE Add uniq method and set operations
|
15
|
-
CLOSED: [2017-03-02 Thu 15:54]
|
16
|
-
- State "WAIT" from "TODO" [2017-03-02 Thu 15:54]
|
17
|
-
- State "TODO" from [2017-03-02 Thu 15:54]
|
18
|
-
For tables, add a method that eliminates any duplicate rows. Perhaps just
|
19
|
-
apply Array#uniq to the columns?
|
data/lib/fat_core/string.rb
CHANGED
@@ -109,20 +109,28 @@ class String
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def wrap(width = 70, hang = 0)
|
112
|
-
offset = 0
|
113
|
-
trip = 1
|
114
112
|
result = ''
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
113
|
+
first_line = true
|
114
|
+
first_word_on_line = true
|
115
|
+
line_width_so_far = 0
|
116
|
+
words = split(' ')
|
117
|
+
words.each do |w|
|
118
|
+
if !first_line && first_word_on_line
|
119
|
+
w = ' ' * hang + w
|
120
|
+
end
|
121
|
+
unless first_word_on_line
|
122
|
+
w = ' ' + w
|
123
|
+
end
|
124
|
+
result << w
|
125
|
+
first_word_on_line = false
|
126
|
+
line_width_so_far += 1 + w.length
|
127
|
+
if line_width_so_far >= width
|
128
|
+
result << "\n"
|
129
|
+
line_width_so_far = 0
|
130
|
+
first_line = false
|
131
|
+
first_word_on_line = true
|
121
132
|
end
|
122
|
-
result << s + "\n"
|
123
|
-
trip += 1
|
124
133
|
end
|
125
|
-
# Remove the final newline before exiting
|
126
134
|
result.strip
|
127
135
|
end
|
128
136
|
|
@@ -254,6 +262,10 @@ class String
|
|
254
262
|
colorize(self, "\001\e[1m\e[35m\002")
|
255
263
|
end
|
256
264
|
|
265
|
+
def console_cyan
|
266
|
+
colorize(self, "\001\e[1m\e[36m\002")
|
267
|
+
end
|
268
|
+
|
257
269
|
def console_def
|
258
270
|
colorize(self, "\001\e[1m\002")
|
259
271
|
end
|
data/lib/fat_core/version.rb
CHANGED
data/lib/fat_core.rb
CHANGED
data/spec/lib/string_spec.rb
CHANGED
@@ -25,34 +25,36 @@ the last full measure of devotion--that we here highly resolve that th\
|
|
25
25
|
ese dead shall not have died in vain--that this nation, under God, sha\
|
26
26
|
ll have a new birth of freedom--and that government of the people, by \
|
27
27
|
the people, for the people, shall not perish from the earth."
|
28
|
-
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
28
|
+
|
29
|
+
# 0000000000111111111122222222223333333333444444444455555555556666666666
|
30
|
+
# 0123456789012345678901234567890123456789012345678901234567890123456789|
|
31
|
+
# Four score and seven years ago our fathers brought forth on
|
32
|
+
# this continent a new nation, conceived in liberty, and
|
33
|
+
# dedicated to the proposition that all men are created
|
34
|
+
# equal. Now we are engaged in a great civil war, testing
|
35
|
+
# whether that nation, or any nation, so conceived and
|
36
|
+
# so dedicated, can long endure. We are met on a great
|
37
|
+
# battle-field of that war. We have come to dedicate a
|
38
|
+
# portion of that field, as a final resting place for
|
39
|
+
# those who here gave their lives that that nation might
|
40
|
+
# live. It is altogether fitting and proper that we should
|
41
|
+
# do this. But, in a larger sense, we can not dedicate,
|
42
|
+
# we can not consecrate, we can not hallow this ground.
|
43
|
+
# The brave men, living and dead, who struggled here,
|
44
|
+
# have consecrated it, far above our poor power to add
|
45
|
+
# or detract. The world will little note, nor long remember
|
46
|
+
# what we say here, but it can never forget what they
|
47
|
+
# did here. It is for us the living, rather, to be dedicated
|
48
|
+
# here to the unfinished work which they who fought here
|
49
|
+
# have thus far so nobly advanced. It is rather for us
|
50
|
+
# to be here dedicated to the great task remaining before
|
51
|
+
# us--that from these honored dead we take increased devotion
|
52
|
+
# to that cause for which they gave the last full measure
|
53
|
+
# of devotion--that we here highly resolve that these dead
|
54
|
+
# shall not have died in vain--that this nation, under
|
55
|
+
# God, shall have a new birth of freedom--and that government
|
56
|
+
# of the people, by the people, for the people, shall
|
57
|
+
# not perish from the earth.
|
56
58
|
end
|
57
59
|
|
58
60
|
describe 'class methods' do
|
@@ -77,20 +79,22 @@ the people, for the people, shall not perish from the earth."
|
|
77
79
|
end
|
78
80
|
|
79
81
|
it 'should wrap a long string' do
|
80
|
-
@getty.wrap
|
82
|
+
str = @getty.wrap
|
83
|
+
str.split("\n").each { |l| expect(l.length).to be <= 70 }
|
81
84
|
end
|
82
85
|
|
83
86
|
it 'should wrap a long string with a hangining indent' do
|
84
|
-
@getty.wrap(70, 10)
|
85
|
-
|
87
|
+
str = @getty.wrap(70, 10)
|
88
|
+
str.split("\n").each { |l| expect(l.length).to be <= 70 }
|
89
|
+
str.split("\n")[1..-1].each do |l|
|
86
90
|
expect(l).to match(/^ /)
|
87
91
|
end
|
88
|
-
second_line = ' ' * 10 + '
|
89
|
-
third_line = ' ' * 10 + '
|
90
|
-
twenty_fourth_line = ' ' * 10 + '
|
91
|
-
expect(
|
92
|
-
expect(
|
93
|
-
expect(
|
92
|
+
second_line = ' ' * 10 + 'this continent a new nation'
|
93
|
+
third_line = ' ' * 10 + 'dedicated to the proposition'
|
94
|
+
twenty_fourth_line = ' ' * 10 + 'shall not have died in vain'
|
95
|
+
expect(str.split("\n")[1]).to match(/^#{second_line}/)
|
96
|
+
expect(str.split("\n")[2]).to match(/^#{third_line}/)
|
97
|
+
expect(str.split("\n")[23]).to match(/^#{twenty_fourth_line}/)
|
94
98
|
end
|
95
99
|
|
96
100
|
it 'should be able to quote special TeX characters' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fat_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel E. Doherty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simplecov
|
@@ -204,16 +204,8 @@ files:
|
|
204
204
|
- lib/fat_core/ChangeLog
|
205
205
|
- lib/fat_core/array.rb
|
206
206
|
- lib/fat_core/boolean.rb
|
207
|
-
- lib/fat_core/column.rb
|
208
207
|
- lib/fat_core/date.rb
|
209
208
|
- lib/fat_core/enumerable.rb
|
210
|
-
- lib/fat_core/evaluator.rb
|
211
|
-
- lib/fat_core/formatters.rb
|
212
|
-
- lib/fat_core/formatters/aoa_formatter.rb
|
213
|
-
- lib/fat_core/formatters/aoh_formatter.rb
|
214
|
-
- lib/fat_core/formatters/formatter.rb
|
215
|
-
- lib/fat_core/formatters/org_formatter.rb
|
216
|
-
- lib/fat_core/formatters/text_formatter.rb
|
217
209
|
- lib/fat_core/hash.rb
|
218
210
|
- lib/fat_core/kernel.rb
|
219
211
|
- lib/fat_core/latex_eruby.rb
|
@@ -223,21 +215,10 @@ files:
|
|
223
215
|
- lib/fat_core/range.rb
|
224
216
|
- lib/fat_core/string.rb
|
225
217
|
- lib/fat_core/symbol.rb
|
226
|
-
- lib/fat_core/table.rb
|
227
218
|
- lib/fat_core/version.rb
|
228
|
-
- spec/example_files/datawatch.org
|
229
|
-
- spec/example_files/goldberg.org
|
230
|
-
- spec/example_files/wpcs.csv
|
231
219
|
- spec/lib/array_spec.rb
|
232
|
-
- spec/lib/column_spec.rb
|
233
220
|
- spec/lib/date_spec.rb
|
234
221
|
- spec/lib/enumerable_spec.rb
|
235
|
-
- spec/lib/evaluator_spec.rb
|
236
|
-
- spec/lib/formatters/aoa_formatter_spec.rb
|
237
|
-
- spec/lib/formatters/aoh_formatter_spec.rb
|
238
|
-
- spec/lib/formatters/formatter_spec.rb
|
239
|
-
- spec/lib/formatters/org_formatter_spec.rb
|
240
|
-
- spec/lib/formatters/text_formatter_spec.rb
|
241
222
|
- spec/lib/hash_spec.rb
|
242
223
|
- spec/lib/kernel_spec.rb
|
243
224
|
- spec/lib/nil_spec.rb
|
@@ -246,7 +227,6 @@ files:
|
|
246
227
|
- spec/lib/range_spec.rb
|
247
228
|
- spec/lib/string_spec.rb
|
248
229
|
- spec/lib/symbol_spec.rb
|
249
|
-
- spec/lib/table_spec.rb
|
250
230
|
- spec/spec_helper.rb
|
251
231
|
homepage: ''
|
252
232
|
licenses:
|
@@ -273,19 +253,9 @@ signing_key:
|
|
273
253
|
specification_version: 4
|
274
254
|
summary: fat_core provides some useful core extensions
|
275
255
|
test_files:
|
276
|
-
- spec/example_files/datawatch.org
|
277
|
-
- spec/example_files/goldberg.org
|
278
|
-
- spec/example_files/wpcs.csv
|
279
256
|
- spec/lib/array_spec.rb
|
280
|
-
- spec/lib/column_spec.rb
|
281
257
|
- spec/lib/date_spec.rb
|
282
258
|
- spec/lib/enumerable_spec.rb
|
283
|
-
- spec/lib/evaluator_spec.rb
|
284
|
-
- spec/lib/formatters/aoa_formatter_spec.rb
|
285
|
-
- spec/lib/formatters/aoh_formatter_spec.rb
|
286
|
-
- spec/lib/formatters/formatter_spec.rb
|
287
|
-
- spec/lib/formatters/org_formatter_spec.rb
|
288
|
-
- spec/lib/formatters/text_formatter_spec.rb
|
289
259
|
- spec/lib/hash_spec.rb
|
290
260
|
- spec/lib/kernel_spec.rb
|
291
261
|
- spec/lib/nil_spec.rb
|
@@ -294,5 +264,4 @@ test_files:
|
|
294
264
|
- spec/lib/range_spec.rb
|
295
265
|
- spec/lib/string_spec.rb
|
296
266
|
- spec/lib/symbol_spec.rb
|
297
|
-
- spec/lib/table_spec.rb
|
298
267
|
- spec/spec_helper.rb
|
data/lib/fat_core/column.rb
DELETED
@@ -1,345 +0,0 @@
|
|
1
|
-
module FatCore
|
2
|
-
# Column objects are just a thin wrapper around an Array to allow columns to
|
3
|
-
# be summed and have other operations performed on them, but compacting out
|
4
|
-
# nils before proceeding. My original attempt to do this by monkey-patching
|
5
|
-
# Array turned out badly. This works much nicer.
|
6
|
-
class Column
|
7
|
-
attr_reader :header, :raw_header, :type, :items
|
8
|
-
|
9
|
-
TYPES = %w(NilClass Boolean DateTime Numeric String).freeze
|
10
|
-
|
11
|
-
def initialize(header:, items: [])
|
12
|
-
@raw_header = header
|
13
|
-
@header =
|
14
|
-
if @raw_header.is_a?(Symbol)
|
15
|
-
@raw_header
|
16
|
-
else
|
17
|
-
@raw_header.gsub(/[^A-Za-z0-9 ]/, '').as_sym
|
18
|
-
end
|
19
|
-
@type = 'NilClass'
|
20
|
-
raise "Unknown column type '#{type}" unless TYPES.include?(@type.to_s)
|
21
|
-
@items = []
|
22
|
-
items.each { |i| self << i }
|
23
|
-
end
|
24
|
-
|
25
|
-
##########################################################################
|
26
|
-
# Attributes
|
27
|
-
##########################################################################
|
28
|
-
|
29
|
-
def [](k)
|
30
|
-
items[k]
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_a
|
34
|
-
items
|
35
|
-
end
|
36
|
-
|
37
|
-
def size
|
38
|
-
items.size
|
39
|
-
end
|
40
|
-
|
41
|
-
def empty?
|
42
|
-
items.empty?
|
43
|
-
end
|
44
|
-
|
45
|
-
def last_i
|
46
|
-
size - 1
|
47
|
-
end
|
48
|
-
|
49
|
-
##########################################################################
|
50
|
-
# Aggregates
|
51
|
-
##########################################################################
|
52
|
-
|
53
|
-
VALID_AGGREGATES = %s(first last rng
|
54
|
-
sum count min max avg var dev
|
55
|
-
any? all? none? one?)
|
56
|
-
|
57
|
-
# Return the first non-nil item in the column. Works with any column type.
|
58
|
-
def first
|
59
|
-
items.compact.first
|
60
|
-
end
|
61
|
-
|
62
|
-
# Return the last non-nil item in the column. Works with any column type.
|
63
|
-
def last
|
64
|
-
items.compact.last
|
65
|
-
end
|
66
|
-
|
67
|
-
# Return a string of the first and last non-nil values. Works with any
|
68
|
-
# column type.
|
69
|
-
def rng
|
70
|
-
"#{first}..#{last}"
|
71
|
-
end
|
72
|
-
|
73
|
-
# Return the sum of the non-nil items in the column. Works with numeric and
|
74
|
-
# string columns. For a string column, it will return the concatenation of
|
75
|
-
# the non-nil items.
|
76
|
-
def sum
|
77
|
-
only_with('sum', 'Numeric', 'String')
|
78
|
-
items.compact.sum
|
79
|
-
end
|
80
|
-
|
81
|
-
# Return a count of the non-nil items in the column. Works with any column
|
82
|
-
# type.
|
83
|
-
def count
|
84
|
-
items.compact.count
|
85
|
-
end
|
86
|
-
|
87
|
-
# Return the smallest non-nil item in the column. Works with numeric,
|
88
|
-
# string, and datetime columns.
|
89
|
-
def min
|
90
|
-
only_with('min', 'NilClass', 'Numeric', 'String', 'DateTime')
|
91
|
-
items.compact.min
|
92
|
-
end
|
93
|
-
|
94
|
-
# Return the largest non-nil item in the column. Works with numeric,
|
95
|
-
# string, and datetime columns.
|
96
|
-
def max
|
97
|
-
only_with('max', 'NilClass', 'Numeric', 'String', 'DateTime')
|
98
|
-
items.compact.max
|
99
|
-
end
|
100
|
-
|
101
|
-
# Return the average value of the non-nil items in the column. Works with
|
102
|
-
# numeric and datetime columns. For datetime columns, it converts each date
|
103
|
-
# to its Julian day number, computes the average, and then converts the
|
104
|
-
# average back to a DateTime.
|
105
|
-
def avg
|
106
|
-
only_with('avg', 'DateTime', 'Numeric')
|
107
|
-
if type == 'DateTime'
|
108
|
-
avg_jd = items.compact.map(&:jd).sum / items.compact.size.to_d
|
109
|
-
DateTime.jd(avg_jd)
|
110
|
-
else
|
111
|
-
sum / items.compact.size.to_d
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Return the variance, the average squared deviation from the mean, of the
|
116
|
-
# non-nil items in the column. Works with numeric and datetime columns.
|
117
|
-
# For datetime columns, it converts each date to its Julian day number and
|
118
|
-
# computes the variance of those numbers.
|
119
|
-
def var
|
120
|
-
only_with('var', 'DateTime', 'Numeric')
|
121
|
-
all_items =
|
122
|
-
if type == 'DateTime'
|
123
|
-
items.compact.map(&:jd)
|
124
|
-
else
|
125
|
-
items.compact
|
126
|
-
end
|
127
|
-
mu = Column.new(header: :mu, items: all_items).avg
|
128
|
-
sq_dev = 0.0
|
129
|
-
all_items.compact.each do |itm|
|
130
|
-
sq_dev += (itm - mu) * (itm - mu)
|
131
|
-
end
|
132
|
-
sq_dev / items.compact.size.to_d
|
133
|
-
end
|
134
|
-
|
135
|
-
# Return the standard deviation, the square root of the variance, of the
|
136
|
-
# non-nil items in the column. Works with numeric and datetime columns.
|
137
|
-
# For datetime columns, it converts each date to its Julian day number and
|
138
|
-
# computes the standard deviation of those numbers.
|
139
|
-
def dev
|
140
|
-
only_with('dev', 'DateTime', 'Numeric')
|
141
|
-
Math.sqrt(var)
|
142
|
-
end
|
143
|
-
|
144
|
-
# Return true if any of the items in the column are true; otherwise return
|
145
|
-
# false. Works only with boolean columns.
|
146
|
-
def any?
|
147
|
-
only_with('any?', 'Boolean')
|
148
|
-
items.compact.any?
|
149
|
-
end
|
150
|
-
|
151
|
-
# Return true if all of the items in the column are true; otherwise return
|
152
|
-
# false. Works only with boolean columns.
|
153
|
-
def all?
|
154
|
-
only_with('all?', 'Boolean')
|
155
|
-
items.compact.all?
|
156
|
-
end
|
157
|
-
|
158
|
-
# Return true if none of the items in the column are true; otherwise return
|
159
|
-
# false. Works only with boolean columns.
|
160
|
-
def none?
|
161
|
-
only_with('any?', 'Boolean')
|
162
|
-
items.compact.none?
|
163
|
-
end
|
164
|
-
|
165
|
-
# Return true if precisely one of the items in the column is true;
|
166
|
-
# otherwise return false. Works only with boolean columns.
|
167
|
-
def one?
|
168
|
-
only_with('any?', 'Boolean')
|
169
|
-
items.compact.one?
|
170
|
-
end
|
171
|
-
|
172
|
-
private
|
173
|
-
|
174
|
-
def only_with(agg, *valid_types)
|
175
|
-
return self if valid_types.include?(type)
|
176
|
-
raise "Aggregate '#{agg}' cannot be applied to a #{type} column"
|
177
|
-
end
|
178
|
-
|
179
|
-
public
|
180
|
-
|
181
|
-
##########################################################################
|
182
|
-
# Construction
|
183
|
-
##########################################################################
|
184
|
-
|
185
|
-
# Append item to end of the column
|
186
|
-
def <<(itm)
|
187
|
-
items << convert_to_type(itm)
|
188
|
-
end
|
189
|
-
|
190
|
-
# Return a new Column appending the items of other to our items, checking
|
191
|
-
# for type compatibility.
|
192
|
-
def +(other)
|
193
|
-
raise 'Cannot combine columns with different types' unless type == other.type
|
194
|
-
Column.new(header: header, items: items + other.items)
|
195
|
-
end
|
196
|
-
|
197
|
-
private
|
198
|
-
|
199
|
-
# Convert val to the type of key, a ruby class constant, such as Date,
|
200
|
-
# Numeric, etc. If type is NilClass, the type is open, and a non-blank val
|
201
|
-
# will attempt conversion to one of the allowed types, typing it as a String
|
202
|
-
# if no other type is recognized. If the val is blank, and the type is nil,
|
203
|
-
# the column type remains open. If the val is nil or a blank and the type is
|
204
|
-
# already determined, the val is set to nil, and should be filtered from any
|
205
|
-
# column computations. If the val is non-blank and the column type
|
206
|
-
# determined, raise an error if the val cannot be converted to the column
|
207
|
-
# type. Otherwise, returns the converted val as an object of the correct
|
208
|
-
# class.
|
209
|
-
def convert_to_type(val)
|
210
|
-
case type
|
211
|
-
when 'NilClass'
|
212
|
-
if val != false && val.blank?
|
213
|
-
# Leave the type of the column open. Unfortunately, false counts as
|
214
|
-
# blank and we don't want it to. It should be classified as a boolean.
|
215
|
-
new_val = nil
|
216
|
-
else
|
217
|
-
# Only non-blank values are allowed to set the type of the column
|
218
|
-
bool_val = convert_to_boolean(val)
|
219
|
-
new_val =
|
220
|
-
if bool_val.nil?
|
221
|
-
convert_to_date_time(val) ||
|
222
|
-
convert_to_numeric(val) ||
|
223
|
-
convert_to_string(val)
|
224
|
-
else
|
225
|
-
bool_val
|
226
|
-
end
|
227
|
-
@type =
|
228
|
-
if new_val == true || new_val == false
|
229
|
-
'Boolean'
|
230
|
-
elsif new_val.is_a?(Date) || new_val.is_a?(DateTime)
|
231
|
-
'DateTime'
|
232
|
-
elsif new_val.is_a?(Numeric)
|
233
|
-
'Numeric'
|
234
|
-
elsif new_val.is_a?(String)
|
235
|
-
'String'
|
236
|
-
else
|
237
|
-
raise "Cannot add #{val} of type #{new_val.class.name} to a column"
|
238
|
-
end
|
239
|
-
end
|
240
|
-
new_val
|
241
|
-
when 'Boolean'
|
242
|
-
if val.nil?
|
243
|
-
nil
|
244
|
-
else
|
245
|
-
new_val = convert_to_boolean(val)
|
246
|
-
if new_val.nil?
|
247
|
-
raise "Attempt to add '#{val}' to a column already typed as #{type}"
|
248
|
-
end
|
249
|
-
new_val
|
250
|
-
end
|
251
|
-
when 'DateTime'
|
252
|
-
if val.nil?
|
253
|
-
nil
|
254
|
-
else
|
255
|
-
new_val = convert_to_date_time(val)
|
256
|
-
if new_val.nil?
|
257
|
-
raise "Attempt to add '#{val}' to a column already typed as #{type}"
|
258
|
-
end
|
259
|
-
new_val
|
260
|
-
end
|
261
|
-
when 'Numeric'
|
262
|
-
if val.nil?
|
263
|
-
nil
|
264
|
-
else
|
265
|
-
new_val = convert_to_numeric(val)
|
266
|
-
if new_val.nil?
|
267
|
-
raise "Attempt to add '#{val}' to a column already typed as #{type}"
|
268
|
-
end
|
269
|
-
new_val
|
270
|
-
end
|
271
|
-
when 'String'
|
272
|
-
if val.nil?
|
273
|
-
nil
|
274
|
-
else
|
275
|
-
new_val = convert_to_string(val)
|
276
|
-
if new_val.nil?
|
277
|
-
raise "Attempt to add '#{val}' to a column already typed as #{type}"
|
278
|
-
end
|
279
|
-
new_val
|
280
|
-
end
|
281
|
-
else
|
282
|
-
raise "Mysteriously, column has unknown type '#{type}'"
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
# Convert the val to a boolean if it looks like one, otherwise return nil.
|
287
|
-
# Any boolean or a string of t, f, true, false, y, n, yes, or no, regardless
|
288
|
-
# of case is assumed to be a boolean.
|
289
|
-
def convert_to_boolean(val)
|
290
|
-
return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
291
|
-
val = val.to_s.clean
|
292
|
-
return nil if val.blank?
|
293
|
-
if val =~ /\A(false|f|n|no)\z/i
|
294
|
-
false
|
295
|
-
elsif val =~ /\A(true|t|y|yes)\z/i
|
296
|
-
true
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# Convert the val to a DateTime if it is either a DateTime, a Date, or a
|
301
|
-
# String that can be parsed as a DateTime, otherwise return nil. It only
|
302
|
-
# recognizes strings that contain a something like '2016-01-14' or
|
303
|
-
# '2/12/1985' within them, otherwise DateTime.parse would treat many bare
|
304
|
-
# numbers as dates, such as '2841381', which it would recognize as a valid
|
305
|
-
# date, but the user probably does not intend it to be so treated.
|
306
|
-
def convert_to_date_time(val)
|
307
|
-
return val if val.is_a?(DateTime)
|
308
|
-
return val.to_datetime if val.is_a?(Date) && type == 'DateTime'
|
309
|
-
return val if val.is_a?(Date)
|
310
|
-
begin
|
311
|
-
val = val.to_s.clean
|
312
|
-
return nil if val.blank?
|
313
|
-
return nil unless val =~ %r{\b\d\d\d\d[-/]\d\d?[-/]\d\d?\b}
|
314
|
-
val = DateTime.parse(val.to_s.clean)
|
315
|
-
val = val.to_date if val.seconds_since_midnight.zero?
|
316
|
-
val
|
317
|
-
rescue ArgumentError
|
318
|
-
return nil
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
# Convert the val to a Numeric if is already a Numberic or is a String that
|
323
|
-
# looks like one. Any Float is promoted to a BigDecimal. Otherwise return
|
324
|
-
# nil.
|
325
|
-
def convert_to_numeric(val)
|
326
|
-
return BigDecimal.new(val, Float::DIG) if val.is_a?(Float)
|
327
|
-
return val if val.is_a?(Numeric)
|
328
|
-
# Eliminate any commas, $'s, or _'s.
|
329
|
-
val = val.to_s.clean.gsub(/[,_$]/, '')
|
330
|
-
return nil if val.blank?
|
331
|
-
case val
|
332
|
-
when /\A(\d+\.\d*)|(\d*\.\d+)\z/
|
333
|
-
BigDecimal.new(val.to_s.clean)
|
334
|
-
when /\A[\d]+\z/
|
335
|
-
val.to_i
|
336
|
-
when %r{\A(\d+)\s*[:/]\s*(\d+)\z}
|
337
|
-
Rational($1, $2)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
def convert_to_string(val)
|
342
|
-
val.to_s
|
343
|
-
end
|
344
|
-
end
|
345
|
-
end
|