fat_table 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.org +2106 -0
- data/README.rdoc +1965 -0
- data/Rakefile +12 -0
- data/TODO.org +31 -0
- data/bin/ft_console +119 -0
- data/bin/setup +8 -0
- data/fat_table.gemspec +80 -0
- data/lib/fat_table.rb +225 -0
- data/lib/fat_table/column.rb +522 -0
- data/lib/fat_table/db_handle.rb +81 -0
- data/lib/fat_table/errors.rb +13 -0
- data/lib/fat_table/evaluator.rb +55 -0
- data/lib/fat_table/formatters.rb +7 -0
- data/lib/fat_table/formatters/aoa_formatter.rb +91 -0
- data/lib/fat_table/formatters/aoh_formatter.rb +91 -0
- data/lib/fat_table/formatters/formatter.rb +1248 -0
- data/lib/fat_table/formatters/latex_formatter.rb +208 -0
- data/lib/fat_table/formatters/org_formatter.rb +72 -0
- data/lib/fat_table/formatters/term_formatter.rb +297 -0
- data/lib/fat_table/formatters/text_formatter.rb +92 -0
- data/lib/fat_table/table.rb +1322 -0
- data/lib/fat_table/version.rb +4 -0
- metadata +331 -0
@@ -0,0 +1,522 @@
|
|
1
|
+
module FatTable
|
2
|
+
# Column objects are a thin wrapper around an Array to allow columns to be
|
3
|
+
# summed and have other aggregate operations performed on them, but compacting
|
4
|
+
# out nils before proceeding. They are characterized by a header, which gives
|
5
|
+
# the Column a name, a type, which limits the kinds of items that can be
|
6
|
+
# stored in the Column, and the items themselves, which all must either be nil
|
7
|
+
# or objects compatible with the Column's type. The valid types are Boolean,
|
8
|
+
# DateTime, Numeric, String, and NilClass, the last of which is used as the
|
9
|
+
# initial type until items added to the Column fix its type as one of the
|
10
|
+
# others.
|
11
|
+
class Column
|
12
|
+
# The symbol representing this Column.
|
13
|
+
attr_reader :header
|
14
|
+
|
15
|
+
# The header as provided by the caller before its conversion to a symbol.
|
16
|
+
# You can use this to recover the original string form of the header.
|
17
|
+
attr_reader :raw_header
|
18
|
+
|
19
|
+
# A string representing the deduced type of this Column. One of
|
20
|
+
# Column::TYPES.
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
# An Array of the items of this Column, all of which must be values of the
|
24
|
+
# Columns type or a nil. This Array contains the value of the item after
|
25
|
+
# conversion to a native Ruby type, such as TrueClass, Date, DateTime,
|
26
|
+
# Integer, String, etc. Thus, you can perform operations on the items,
|
27
|
+
# perhaps after removing nils with +.items.compact+.
|
28
|
+
attr_reader :items
|
29
|
+
|
30
|
+
# Valid Column types as strings.
|
31
|
+
TYPES = %w(NilClass Boolean DateTime Numeric String).freeze
|
32
|
+
|
33
|
+
# :category: Constructors
|
34
|
+
|
35
|
+
# Create a new Column with the given +header+ and initialized with the given
|
36
|
+
# +items+, as an array of either strings or ruby objects that are one of the
|
37
|
+
# permissible types or strings parsable as one of the permissible types. If
|
38
|
+
# no +items+ are passed, returns an empty Column to which items may be added
|
39
|
+
# with the Column#<< method. The item types must be one of the following types or
|
40
|
+
# strings parseable as one of them:
|
41
|
+
#
|
42
|
+
# Boolean::
|
43
|
+
# an object of type TrueClass or FalseClass or a string that is either
|
44
|
+
# 't', 'true', 'y', 'yes', 'f', 'false', 'n', or 'no', in each case,
|
45
|
+
# regardless of case.
|
46
|
+
#
|
47
|
+
# DateTime::
|
48
|
+
# an object of class Date, DateTime, or a string that matches
|
49
|
+
# +/\d\d\d\d[-\/]\d\d?[-\/]\d\d?/+ and is parseable by DateTime.parse.
|
50
|
+
#
|
51
|
+
# Numeric::
|
52
|
+
# on object that is of class Numeric, or a string that looks
|
53
|
+
# like a number after removing '+$+', '+,+', and '+_+' as well as Rationals
|
54
|
+
# in the form /<number>:<number>/ or <number>/<number>, where <number>
|
55
|
+
# is an integer.
|
56
|
+
#
|
57
|
+
# String::
|
58
|
+
# if the object is a non-blank string that does not parse as any
|
59
|
+
# of the foregoing, it its treated as a Sting type, and once a column
|
60
|
+
# is typed as such, blank strings represent blank strings rather than
|
61
|
+
# nil values.
|
62
|
+
#
|
63
|
+
# NilClass::
|
64
|
+
# until a Column sees an item that qualifies as one of the
|
65
|
+
# foregoing, it is typed as NilClass, meaning that the type is
|
66
|
+
# undetermined. Until a column obtains a type, blank strings are
|
67
|
+
# treated as nils and do not affect the type of the column. After a
|
68
|
+
# column acquires a type, blank strings are treated as nil values
|
69
|
+
# except in the case of String columns, which retain them a blank
|
70
|
+
# strings.
|
71
|
+
#
|
72
|
+
# Examples:
|
73
|
+
#
|
74
|
+
# require 'fat_table'
|
75
|
+
# col = FatTable::Column.new(header: 'date')
|
76
|
+
# col << Date.today - 30
|
77
|
+
# col << '2017-05-04'
|
78
|
+
# col.type #=> 'DateTime'
|
79
|
+
# col.header #=> :date
|
80
|
+
# nums = [35.25, 18, '35:14', '$18_321']
|
81
|
+
# col = FatTable::Column.new(header: :prices, items: nums)
|
82
|
+
# col.type #=> 'Numeric'
|
83
|
+
# col.header #=> :prices
|
84
|
+
# col.sum #=> 18376.75
|
85
|
+
def initialize(header:, items: [])
|
86
|
+
@raw_header = header
|
87
|
+
@header =
|
88
|
+
if @raw_header.is_a?(Symbol)
|
89
|
+
@raw_header
|
90
|
+
else
|
91
|
+
@raw_header.to_s.as_sym
|
92
|
+
end
|
93
|
+
@type = 'NilClass'
|
94
|
+
raise UserError, "Unknown column type '#{type}" unless TYPES.include?(@type.to_s)
|
95
|
+
@items = []
|
96
|
+
items.each { |i| self << i }
|
97
|
+
end
|
98
|
+
|
99
|
+
##########################################################################
|
100
|
+
# Attributes
|
101
|
+
##########################################################################
|
102
|
+
|
103
|
+
# :category: Attributes
|
104
|
+
|
105
|
+
# Return the item of the Column at the given index.
|
106
|
+
def [](k)
|
107
|
+
items[k]
|
108
|
+
end
|
109
|
+
|
110
|
+
# :category: Attributes
|
111
|
+
|
112
|
+
# Return a dupped Array of this Column's items. To get the non-dupped items,
|
113
|
+
# just use the .items accessor.
|
114
|
+
def to_a
|
115
|
+
items.deep_dup
|
116
|
+
end
|
117
|
+
|
118
|
+
# :category: Attributes
|
119
|
+
|
120
|
+
# Return the size of the Column, including any nils.
|
121
|
+
def size
|
122
|
+
items.size
|
123
|
+
end
|
124
|
+
|
125
|
+
# :category: Attributes
|
126
|
+
|
127
|
+
# Return true if there are no items in the Column.
|
128
|
+
def empty?
|
129
|
+
items.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
# :category: Attributes
|
133
|
+
|
134
|
+
# Return the index of the last item in the Column.
|
135
|
+
def last_i
|
136
|
+
size - 1
|
137
|
+
end
|
138
|
+
|
139
|
+
##########################################################################
|
140
|
+
# Enumerable
|
141
|
+
##########################################################################
|
142
|
+
|
143
|
+
include Enumerable
|
144
|
+
|
145
|
+
# :category: Attributes
|
146
|
+
|
147
|
+
# Yield each item in the Column in the order in which they appear in the
|
148
|
+
# Column. This makes Columns Enumerable, so all the Enumerable methods are
|
149
|
+
# available on a Column.
|
150
|
+
def each
|
151
|
+
items.each { |itm| yield itm }
|
152
|
+
end
|
153
|
+
|
154
|
+
##########################################################################
|
155
|
+
# Aggregates
|
156
|
+
##########################################################################
|
157
|
+
|
158
|
+
# :category: Aggregates
|
159
|
+
|
160
|
+
# The names of the known aggregate operations that can be performed on a
|
161
|
+
# Column.
|
162
|
+
VALID_AGGREGATES = %s(first last rng
|
163
|
+
sum count min max avg var dev
|
164
|
+
any? all? none? one?)
|
165
|
+
|
166
|
+
# :category: Aggregates
|
167
|
+
|
168
|
+
# Return the first non-nil item in the Column. Works with any Column type.
|
169
|
+
def first
|
170
|
+
items.compact.first
|
171
|
+
end
|
172
|
+
|
173
|
+
# :category: Aggregates
|
174
|
+
|
175
|
+
# Return the last non-nil item in the Column. Works with any Column type.
|
176
|
+
def last
|
177
|
+
items.compact.last
|
178
|
+
end
|
179
|
+
|
180
|
+
# :category: Aggregates
|
181
|
+
|
182
|
+
# Return a string of the #first and #last non-nil values in the Column.
|
183
|
+
# Works with any Column type.
|
184
|
+
def rng
|
185
|
+
"#{first}..#{last}"
|
186
|
+
end
|
187
|
+
|
188
|
+
# :category: Aggregates
|
189
|
+
|
190
|
+
# Return the sum of the non-nil items in the Column. Works with numeric and
|
191
|
+
# string Columns. For a string Column, it will return the concatenation of
|
192
|
+
# the non-nil items.
|
193
|
+
def sum
|
194
|
+
only_with('sum', 'Numeric', 'String')
|
195
|
+
items.compact.sum
|
196
|
+
end
|
197
|
+
|
198
|
+
# :category: Aggregates
|
199
|
+
|
200
|
+
# Return a count of the non-nil items in the Column. Works with any Column
|
201
|
+
# type.
|
202
|
+
def count
|
203
|
+
items.compact.count.to_d
|
204
|
+
end
|
205
|
+
|
206
|
+
# :category: Aggregates
|
207
|
+
|
208
|
+
# Return the smallest non-nil item in the Column. Works with numeric,
|
209
|
+
# string, and datetime Columns.
|
210
|
+
def min
|
211
|
+
only_with('min', 'NilClass', 'Numeric', 'String', 'DateTime')
|
212
|
+
items.compact.min
|
213
|
+
end
|
214
|
+
|
215
|
+
# :category: Aggregates
|
216
|
+
|
217
|
+
# Return the largest non-nil item in the Column. Works with numeric,
|
218
|
+
# string, and datetime Columns.
|
219
|
+
def max
|
220
|
+
only_with('max', 'NilClass', 'Numeric', 'String', 'DateTime')
|
221
|
+
items.compact.max
|
222
|
+
end
|
223
|
+
|
224
|
+
# :category: Aggregates
|
225
|
+
|
226
|
+
# Return the average value of the non-nil items in the Column. Works with
|
227
|
+
# numeric and datetime Columns. For datetime Columns, it converts each date
|
228
|
+
# to its Julian day number, computes the average, and then converts the
|
229
|
+
# average back to a DateTime.
|
230
|
+
def avg
|
231
|
+
only_with('avg', 'DateTime', 'Numeric')
|
232
|
+
if type == 'DateTime'
|
233
|
+
avg_jd = items.compact.map(&:jd).sum / items.compact.size.to_d
|
234
|
+
DateTime.jd(avg_jd)
|
235
|
+
else
|
236
|
+
sum / items.compact.size.to_d
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# :category: Aggregates
|
241
|
+
|
242
|
+
# Return the sample variance (the unbiased estimator of the population
|
243
|
+
# variance using a divisor of N-1) as the average squared deviation from the
|
244
|
+
# mean, of the non-nil items in the Column. Works with numeric and datetime
|
245
|
+
# Columns. For datetime Columns, it converts each date to its Julian day
|
246
|
+
# number and computes the variance of those numbers.
|
247
|
+
def var
|
248
|
+
only_with('var', 'DateTime', 'Numeric')
|
249
|
+
all_items =
|
250
|
+
if type == 'DateTime'
|
251
|
+
items.compact.map(&:jd)
|
252
|
+
else
|
253
|
+
items.compact
|
254
|
+
end
|
255
|
+
n = count
|
256
|
+
return BigDecimal('0.0') if n <= 1
|
257
|
+
mu = Column.new(header: :mu, items: all_items).avg
|
258
|
+
sq_dev = BigDecimal('0.0')
|
259
|
+
all_items.each do |itm|
|
260
|
+
sq_dev += (itm - mu) * (itm - mu)
|
261
|
+
end
|
262
|
+
sq_dev / (n - 1)
|
263
|
+
end
|
264
|
+
|
265
|
+
# :category: Aggregates
|
266
|
+
|
267
|
+
# Return the population variance (the biased estimator of the population
|
268
|
+
# variance using a divisor of N) as the average squared deviation from the
|
269
|
+
# mean, of the non-nil items in the Column. Works with numeric and datetime
|
270
|
+
# Columns. For datetime Columns, it converts each date to its Julian day
|
271
|
+
# number and computes the variance of those numbers.
|
272
|
+
def pvar
|
273
|
+
only_with('var', 'DateTime', 'Numeric')
|
274
|
+
n = items.compact.size.to_d
|
275
|
+
return BigDecimal('0.0') if n <= 1
|
276
|
+
var * ((n - 1) / n)
|
277
|
+
end
|
278
|
+
|
279
|
+
# :category: Aggregates
|
280
|
+
|
281
|
+
# Return the sample standard deviation (the unbiased estimator of the
|
282
|
+
# population standard deviation using a divisor of N-1) as the square root
|
283
|
+
# of the sample variance, of the non-nil items in the Column. Works with
|
284
|
+
# numeric and datetime Columns. For datetime Columns, it converts each date
|
285
|
+
# to its Julian day number and computes the standard deviation of those
|
286
|
+
# numbers.
|
287
|
+
def dev
|
288
|
+
only_with('dev', 'DateTime', 'Numeric')
|
289
|
+
var.sqrt(20)
|
290
|
+
end
|
291
|
+
|
292
|
+
# :category: Aggregates
|
293
|
+
|
294
|
+
# Return the population standard deviation (the biased estimator of the
|
295
|
+
# population standard deviation using a divisor of N) as the square root of
|
296
|
+
# the population variance, of the non-nil items in the Column. Works with
|
297
|
+
# numeric and datetime Columns. For datetime Columns, it converts each date
|
298
|
+
# to its Julian day number and computes the standard deviation of those
|
299
|
+
# numbers.
|
300
|
+
def pdev
|
301
|
+
only_with('dev', 'DateTime', 'Numeric')
|
302
|
+
Math.sqrt(pvar)
|
303
|
+
end
|
304
|
+
|
305
|
+
# :category: Aggregates
|
306
|
+
|
307
|
+
# Return true if any of the items in the Column are true; otherwise return
|
308
|
+
# false. Works only with boolean Columns.
|
309
|
+
def any?
|
310
|
+
only_with('any?', 'Boolean')
|
311
|
+
items.compact.any?
|
312
|
+
end
|
313
|
+
|
314
|
+
# :category: Aggregates
|
315
|
+
|
316
|
+
# Return true if all of the items in the Column are true; otherwise return
|
317
|
+
# false. Works only with boolean Columns.
|
318
|
+
def all?
|
319
|
+
only_with('all?', 'Boolean')
|
320
|
+
items.compact.all?
|
321
|
+
end
|
322
|
+
|
323
|
+
# :category: Aggregates
|
324
|
+
|
325
|
+
# Return true if none of the items in the Column are true; otherwise return
|
326
|
+
# false. Works only with boolean Columns.
|
327
|
+
def none?
|
328
|
+
only_with('none?', 'Boolean')
|
329
|
+
items.compact.none?
|
330
|
+
end
|
331
|
+
|
332
|
+
# :category: Aggregates
|
333
|
+
|
334
|
+
# Return true if precisely one of the items in the Column is true;
|
335
|
+
# otherwise return false. Works only with boolean Columns.
|
336
|
+
def one?
|
337
|
+
only_with('one?', 'Boolean')
|
338
|
+
items.compact.one?
|
339
|
+
end
|
340
|
+
|
341
|
+
private
|
342
|
+
|
343
|
+
def only_with(agg, *valid_types)
|
344
|
+
return self if valid_types.include?(type)
|
345
|
+
raise UserError, "Aggregate '#{agg}' cannot be applied to a #{type} column"
|
346
|
+
end
|
347
|
+
|
348
|
+
public
|
349
|
+
|
350
|
+
##########################################################################
|
351
|
+
# Construction
|
352
|
+
##########################################################################
|
353
|
+
|
354
|
+
# :category: Constructors
|
355
|
+
|
356
|
+
# Append +itm+ to end of the Column after converting it to the Column's
|
357
|
+
# type. If the Column's type is still open, i.e. NilClass, attempt to fix
|
358
|
+
# the Column's type based on the type of +itm+ as with Column.new.
|
359
|
+
def <<(itm)
|
360
|
+
items << convert_to_type(itm)
|
361
|
+
end
|
362
|
+
|
363
|
+
# :category: Constructors
|
364
|
+
|
365
|
+
# Return a new Column appending the items of other to this Column's items,
|
366
|
+
# checking for type compatibility. Use the header of this Column as the
|
367
|
+
# header of the new Column.
|
368
|
+
def +(other)
|
369
|
+
raise UserError, 'Cannot combine columns with different types' unless type == other.type
|
370
|
+
Column.new(header: header, items: items + other.items)
|
371
|
+
end
|
372
|
+
|
373
|
+
private
|
374
|
+
|
375
|
+
# Convert val to the type of key, a ruby class constant, such as Date,
|
376
|
+
# Numeric, etc. If type is NilClass, the type is open, and a non-blank val
|
377
|
+
# will attempt conversion to one of the allowed types, typing it as a String
|
378
|
+
# if no other type is recognized. If the val is blank, and the type is nil,
|
379
|
+
# the Column type remains open. If the val is nil or a blank and the type is
|
380
|
+
# already determined, the val is set to nil, and should be filtered from any
|
381
|
+
# Column computations. If the val is non-blank and the Column type
|
382
|
+
# determined, raise an error if the val cannot be converted to the Column
|
383
|
+
# type. Otherwise, returns the converted val as an object of the correct
|
384
|
+
# class.
|
385
|
+
def convert_to_type(val)
|
386
|
+
case type
|
387
|
+
when 'NilClass'
|
388
|
+
if val != false && val.blank?
|
389
|
+
# Leave the type of the Column open. Unfortunately, false counts as
|
390
|
+
# blank and we don't want it to. It should be classified as a boolean.
|
391
|
+
new_val = nil
|
392
|
+
else
|
393
|
+
# Only non-blank values are allowed to set the type of the Column
|
394
|
+
bool_val = convert_to_boolean(val)
|
395
|
+
new_val =
|
396
|
+
if bool_val.nil?
|
397
|
+
convert_to_date_time(val) ||
|
398
|
+
convert_to_numeric(val) ||
|
399
|
+
convert_to_string(val)
|
400
|
+
else
|
401
|
+
bool_val
|
402
|
+
end
|
403
|
+
@type =
|
404
|
+
if new_val == true || new_val == false
|
405
|
+
'Boolean'
|
406
|
+
elsif new_val.is_a?(Date) || new_val.is_a?(DateTime)
|
407
|
+
'DateTime'
|
408
|
+
elsif new_val.is_a?(Numeric)
|
409
|
+
'Numeric'
|
410
|
+
elsif new_val.is_a?(String)
|
411
|
+
'String'
|
412
|
+
else
|
413
|
+
raise UserError, "Cannot add #{val} of type #{new_val.class.name} to a column"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
new_val
|
417
|
+
when 'Boolean'
|
418
|
+
if (val.is_a?(String) && val.blank? || val.nil?)
|
419
|
+
nil
|
420
|
+
else
|
421
|
+
new_val = convert_to_boolean(val)
|
422
|
+
if new_val.nil?
|
423
|
+
raise UserError, "Attempt to add '#{val}' to a column already typed as #{type}"
|
424
|
+
end
|
425
|
+
new_val
|
426
|
+
end
|
427
|
+
when 'DateTime'
|
428
|
+
if val.blank?
|
429
|
+
nil
|
430
|
+
else
|
431
|
+
new_val = convert_to_date_time(val)
|
432
|
+
if new_val.nil?
|
433
|
+
raise UserError, "Attempt to add '#{val}' to a column already typed as #{type}"
|
434
|
+
end
|
435
|
+
new_val
|
436
|
+
end
|
437
|
+
when 'Numeric'
|
438
|
+
if val.blank?
|
439
|
+
nil
|
440
|
+
else
|
441
|
+
new_val = convert_to_numeric(val)
|
442
|
+
if new_val.nil?
|
443
|
+
raise UserError, "Attempt to add '#{val}' to a column already typed as #{type}"
|
444
|
+
end
|
445
|
+
new_val
|
446
|
+
end
|
447
|
+
when 'String'
|
448
|
+
if val.nil?
|
449
|
+
nil
|
450
|
+
else
|
451
|
+
new_val = convert_to_string(val)
|
452
|
+
if new_val.nil?
|
453
|
+
raise UserError, "Attempt to add '#{val}' to a column already typed as #{type}"
|
454
|
+
end
|
455
|
+
new_val
|
456
|
+
end
|
457
|
+
else
|
458
|
+
raise UserError, "Mysteriously, column has unknown type '#{type}'"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
# Convert the val to a boolean if it looks like one, otherwise return nil.
|
463
|
+
# Any boolean or a string of t, f, true, false, y, n, yes, or no, regardless
|
464
|
+
# of case is assumed to be a boolean.
|
465
|
+
def convert_to_boolean(val)
|
466
|
+
return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
467
|
+
val = val.to_s.clean
|
468
|
+
return nil if val.blank?
|
469
|
+
if val =~ /\A(false|f|n|no)\z/i
|
470
|
+
false
|
471
|
+
elsif val =~ /\A(true|t|y|yes)\z/i
|
472
|
+
true
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
# Convert the val to a DateTime if it is either a DateTime, a Date, or a
|
477
|
+
# String that can be parsed as a DateTime, otherwise return nil. It only
|
478
|
+
# recognizes strings that contain a something like '2016-01-14' or
|
479
|
+
# '2/12/1985' within them, otherwise DateTime.parse would treat many bare
|
480
|
+
# numbers as dates, such as '2841381', which it would recognize as a valid
|
481
|
+
# date, but the user probably does not intend it to be so treated.
|
482
|
+
def convert_to_date_time(val)
|
483
|
+
return val if val.is_a?(DateTime)
|
484
|
+
return val if val.is_a?(Date)
|
485
|
+
begin
|
486
|
+
val = val.to_s.clean
|
487
|
+
return nil if val.blank?
|
488
|
+
return nil unless val =~ %r{\b\d\d\d\d[-/]\d\d?[-/]\d\d?\b}
|
489
|
+
val = DateTime.parse(val.to_s.clean)
|
490
|
+
val = val.to_date if val.seconds_since_midnight.zero?
|
491
|
+
val
|
492
|
+
rescue ArgumentError
|
493
|
+
return nil
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
# Convert the val to a Numeric if is already a Numberic or is a String that
|
498
|
+
# looks like one. Any Float is promoted to a BigDecimal. Otherwise return
|
499
|
+
# nil.
|
500
|
+
def convert_to_numeric(val)
|
501
|
+
return BigDecimal.new(val, Float::DIG) if val.is_a?(Float)
|
502
|
+
return val if val.is_a?(Numeric)
|
503
|
+
# Eliminate any commas, $'s (or other currency symbol), or _'s.
|
504
|
+
cursym = Regexp.quote(FatTable.currency_symbol)
|
505
|
+
clean_re = /[,_#{cursym}]/
|
506
|
+
val = val.to_s.clean.gsub(clean_re, '')
|
507
|
+
return nil if val.blank?
|
508
|
+
case val
|
509
|
+
when /\A(\d+\.\d*)|(\d*\.\d+)\z/
|
510
|
+
BigDecimal.new(val.to_s.clean)
|
511
|
+
when /\A[\d]+\z/
|
512
|
+
val.to_i
|
513
|
+
when %r{\A(\d+)\s*[:/]\s*(\d+)\z}
|
514
|
+
Rational($1, $2)
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
def convert_to_string(val)
|
519
|
+
val.to_s
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|