fat_table 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.org +97 -5
- data/lib/fat_table/column.rb +20 -2
- data/lib/fat_table/convert.rb +6 -5
- data/lib/fat_table/errors.rb +4 -0
- data/lib/fat_table/table.rb +79 -27
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +16 -16
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa339300269582eedb57d26138bd3fa2de68d526ed4cc94e4c3a369a67989ddb
|
4
|
+
data.tar.gz: b35cc4785b208b670d6f039de8ef12351ea0533c37d8802fbf89f57763e63330
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49fbab273c609035f9c2a179b0b97cdca067a8a30db754916e7b17d03a435b4d3a4eefb89ddb326ef07c6e5b91632f764ce101de0acbc79fcf6bd876741da4cf
|
7
|
+
data.tar.gz: 76f87e5b342da743ea3e0c2000273de35d081ad4835c2423d6342278ebbced67d3a34bd11526c7f32185ac19e0b225356baa02fbcb8b7b630868739a944f0ecb
|
data/README.org
CHANGED
@@ -25,6 +25,16 @@ The following is for org.
|
|
25
25
|
|
26
26
|
[[https://travis-ci.org/ddoherty03/fat_table.svg?branch=master]]
|
27
27
|
|
28
|
+
* Version
|
29
|
+
#+begin_src ruby :wrap EXAMPLE
|
30
|
+
require 'fat_table'
|
31
|
+
"Current version is: #{FatTable::VERSION}"
|
32
|
+
#+end_src
|
33
|
+
|
34
|
+
#+begin_EXAMPLE
|
35
|
+
Current version is: 0.5.3
|
36
|
+
#+end_EXAMPLE
|
37
|
+
|
28
38
|
* Introduction
|
29
39
|
|
30
40
|
~FatTable~ is a gem that treats tables as a data type. It provides methods for
|
@@ -57,6 +67,7 @@ array of arrays with its ~.to_aoa~ output function will be rendered in an
|
|
57
67
|
org-mode buffer as an org-table, ready for processing by other code blocks.
|
58
68
|
|
59
69
|
* Table of Contents :toc:noexport:
|
70
|
+
- [[#version][Version]]
|
60
71
|
- [[#introduction][Introduction]]
|
61
72
|
- [[#installation][Installation]]
|
62
73
|
- [[#using-in-a-gem][Using in a gem]]
|
@@ -74,6 +85,7 @@ org-mode buffer as an org-table, ready for processing by other code blocks.
|
|
74
85
|
- [[#without-headers][Without Headers]]
|
75
86
|
- [[#with-headers][With Headers]]
|
76
87
|
- [[#forcing-string-type][Forcing String Type]]
|
88
|
+
- [[#designating-tolerant-columns][Designating "Tolerant" Columns]]
|
77
89
|
- [[#from-csv-or-org-mode-files-or-strings][From CSV or Org Mode files or strings]]
|
78
90
|
- [[#from-arrays-of-arrays][From Arrays of Arrays]]
|
79
91
|
- [[#in-ruby-code][In Ruby Code]]
|
@@ -619,8 +631,80 @@ converted to strings with the #to_s method.
|
|
619
631
|
+======+======+============+===+=======+===+
|
620
632
|
#+end_EXAMPLE
|
621
633
|
|
634
|
+
**** Designating "Tolerant" Columns
|
635
|
+
Related to the problem just discussed is the problem of reading files in from
|
636
|
+
the wild where a column may get typed as, say Numeric, but then contain
|
637
|
+
something that can't be parsed as a Numeric. ~FatTable~ raises an exception
|
638
|
+
is such cases, and that may be what you want if you can control the input.
|
639
|
+
But, especially when you cannot do so, it can be helpful to designate one or
|
640
|
+
more columns as "tolerant." This means that when a conversion problem occurs,
|
641
|
+
the column is forced to String type instead of throwing an exception, and the
|
642
|
+
table can continue to be read.
|
643
|
+
|
644
|
+
All of the table construction methods, allow a keyword parameter,
|
645
|
+
~tolerant_columns~, where you can designate what columns should be convert to
|
646
|
+
String type when conversion to the auto-typed column type is not possible.
|
647
|
+
The parameter should be an array of headers, in either string or symbol form,
|
648
|
+
for which this behavior is desired. In addition, it can be set to the special
|
649
|
+
string '*' or symbol ~:*~ to indicate that all the columns should be made
|
650
|
+
tolerant.
|
651
|
+
|
652
|
+
#+begin_src ruby :wrap EXAMPLE
|
653
|
+
require 'fat_table'
|
654
|
+
tab = FatTable.new(:a, 'b', 'C', :d, :zip, tolerant_columns: [:zip])
|
655
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
656
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
657
|
+
tab << { zip: '01879--7884' }
|
658
|
+
tab << { zip: '66210' }
|
659
|
+
tab << { zip: '90210' }
|
660
|
+
tab.to_text
|
661
|
+
#+end_src
|
662
|
+
|
663
|
+
#+RESULTS:
|
664
|
+
#+begin_EXAMPLE
|
665
|
+
+======+======+============+===+=============+===+
|
666
|
+
| A | B | C | D | Zip | E |
|
667
|
+
+------+------+------------+---+-------------+---+
|
668
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
669
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
670
|
+
| | | | | 01879--7884 | |
|
671
|
+
| | | | | 66210 | |
|
672
|
+
| | | | | 90210 | |
|
673
|
+
+======+======+============+===+=============+===+
|
674
|
+
#+end_EXAMPLE
|
675
|
+
|
676
|
+
Another way to designate a column as tolerant is to end a column you want to
|
677
|
+
designate as tolerant with a ~!~. The ~!~ will be stripped from the header,
|
678
|
+
but it will be marked as tolerant.
|
679
|
+
#+begin_src ruby :wrap EXAMPLE
|
680
|
+
require 'fat_table'
|
681
|
+
tab = FatTable.new(:a, 'b!', 'C', :d, :zip!)
|
682
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
683
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
684
|
+
tab << { zip: '01879--7884' }
|
685
|
+
tab << { zip: '66210', b: 'Not a Number' }
|
686
|
+
tab << { zip: '90210' }
|
687
|
+
tab.to_text
|
688
|
+
#+end_src
|
689
|
+
|
690
|
+
#+RESULTS:
|
691
|
+
#+begin_EXAMPLE
|
692
|
+
+======+==============+============+===+=============+===+
|
693
|
+
| A | B | C | D | Zip | E |
|
694
|
+
+------+--------------+------------+---+-------------+---+
|
695
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
696
|
+
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
697
|
+
| | | | | 01879--7884 | |
|
698
|
+
| | Not a Number | | | 66210 | |
|
699
|
+
| | | | | 90210 | |
|
700
|
+
+======+==============+============+===+=============+===+
|
701
|
+
#+end_EXAMPLE
|
702
|
+
|
622
703
|
*** From CSV or Org Mode files or strings
|
623
|
-
Tables can also be read from ~.csv~ files or files containing ~org-mode~
|
704
|
+
Tables can also be read from ~.csv~ files or files containing ~org-mode~
|
705
|
+
tables. Remember that you can make any column tolerant with a
|
706
|
+
~tolerant_columns:~ keyword argument or make them all tolerant by designating
|
707
|
+
the pseudo-column ~:*~ as tolerant.
|
624
708
|
|
625
709
|
In the case of org-mode files, ~FatTable~ skips through the file until it finds
|
626
710
|
a line that look like a table, that is, it begins with any number of spaces
|
@@ -677,8 +761,10 @@ header row, and the headers are converted to symbols as described above.
|
|
677
761
|
|
678
762
|
*** From Arrays of Arrays
|
679
763
|
**** In Ruby Code
|
680
|
-
You can also initialize a table directly from ruby data structures. You can,
|
681
|
-
example, build a table from an array of arrays
|
764
|
+
You can also initialize a table directly from ruby data structures. You can,
|
765
|
+
for example, build a table from an array of arrays. Remember that you can
|
766
|
+
make any column tolerant with a ~tolerant_columns:~ keyword argument or make
|
767
|
+
them all tolerant by designating the pseudo-column ~:*~ as tolerant.
|
682
768
|
|
683
769
|
#+BEGIN_SRC ruby
|
684
770
|
aoa = [
|
@@ -772,8 +858,10 @@ This example illustrates several things:
|
|
772
858
|
|
773
859
|
A second ruby data structure that can be used to initialize a ~FatTable~ table
|
774
860
|
is an array of ruby Hashes. Each hash represents a row of the table, and the
|
775
|
-
headers of the table are taken from the keys of the hashes. Accordingly, all
|
776
|
-
hashes must have the same keys.
|
861
|
+
headers of the table are taken from the keys of the hashes. Accordingly, all
|
862
|
+
the hashes must have the same keys. Remember that you can make any column
|
863
|
+
tolerant with a ~tolerant_columns:~ keyword argument or make them all tolerant
|
864
|
+
by designating the pseudo-column ~:*~ as tolerant.
|
777
865
|
|
778
866
|
This same method can in fact take an array of any objects that can be converted
|
779
867
|
to a Hash with the ~#to_h~ method, so you can use an array of your own objects
|
@@ -862,6 +950,10 @@ The ~.connect~ function need only be called once, and the database handle it
|
|
862
950
|
creates will be used for all subsequent ~.from_sql~ calls until ~.connect~ is
|
863
951
|
called again.
|
864
952
|
|
953
|
+
Remember that you can make any column tolerant with a ~tolerant_columns:~
|
954
|
+
keyword argument or make them all tolerant by designating the pseudo-column
|
955
|
+
~:*~ as tolerant.
|
956
|
+
|
865
957
|
*** Marking Groups in Input
|
866
958
|
**** Manually
|
867
959
|
At any point, you can add a boundary to a table by invokong the
|
data/lib/fat_table/column.rb
CHANGED
@@ -83,7 +83,7 @@ module FatTable
|
|
83
83
|
# col.type #=> 'Numeric'
|
84
84
|
# col.header #=> :prices
|
85
85
|
# col.sum #=> 18376.75
|
86
|
-
def initialize(header:, items: [], type: 'NilClass')
|
86
|
+
def initialize(header:, items: [], type: 'NilClass', tolerant: false)
|
87
87
|
@raw_header = header
|
88
88
|
@header =
|
89
89
|
if @raw_header.is_a?(Symbol)
|
@@ -92,6 +92,7 @@ module FatTable
|
|
92
92
|
@raw_header.to_s.as_sym
|
93
93
|
end
|
94
94
|
@type = type
|
95
|
+
@tolerant = tolerant
|
95
96
|
msg = "unknown column type '#{type}"
|
96
97
|
raise UserError, msg unless TYPES.include?(@type.to_s)
|
97
98
|
|
@@ -141,6 +142,14 @@ module FatTable
|
|
141
142
|
|
142
143
|
# :category: Attributes
|
143
144
|
|
145
|
+
# Is this column tolerant of type incompatibilities? If so, the Column
|
146
|
+
# type will be forced to String if an incompatible type is found.
|
147
|
+
def tolerant?
|
148
|
+
@tolerant
|
149
|
+
end
|
150
|
+
|
151
|
+
# :category: Attributes
|
152
|
+
|
144
153
|
# Force the column to have String type and then convert all items to
|
145
154
|
# strings.
|
146
155
|
def force_string!
|
@@ -400,9 +409,18 @@ module FatTable
|
|
400
409
|
|
401
410
|
# Append +itm+ to end of the Column after converting it to the Column's
|
402
411
|
# type. If the Column's type is still open, i.e. NilClass, attempt to fix
|
403
|
-
# the Column's type based on the type of +itm+ as with Column.new.
|
412
|
+
# the Column's type based on the type of +itm+ as with Column.new. If its
|
413
|
+
# a tolerant column, respond to type errors by converting the column to a
|
414
|
+
# String type.
|
404
415
|
def <<(itm)
|
405
416
|
items << convert_to_type(itm)
|
417
|
+
rescue IncompatibleTypeError => ex
|
418
|
+
if tolerant?
|
419
|
+
force_string!
|
420
|
+
retry
|
421
|
+
else
|
422
|
+
raise ex
|
423
|
+
end
|
406
424
|
end
|
407
425
|
|
408
426
|
# :category: Constructors
|
data/lib/fat_table/convert.rb
CHANGED
@@ -37,7 +37,7 @@ module FatTable
|
|
37
37
|
new_val = convert_to_boolean(val)
|
38
38
|
if new_val.nil?
|
39
39
|
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
40
|
-
raise
|
40
|
+
raise IncompatibleTypeError, msg
|
41
41
|
end
|
42
42
|
new_val
|
43
43
|
end
|
@@ -48,7 +48,7 @@ module FatTable
|
|
48
48
|
new_val = convert_to_date_time(val)
|
49
49
|
if new_val.nil?
|
50
50
|
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
51
|
-
raise
|
51
|
+
raise IncompatibleTypeError, msg
|
52
52
|
end
|
53
53
|
new_val
|
54
54
|
end
|
@@ -59,7 +59,7 @@ module FatTable
|
|
59
59
|
new_val = convert_to_numeric(val)
|
60
60
|
if new_val.nil?
|
61
61
|
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
62
|
-
raise
|
62
|
+
raise IncompatibleTypeError, msg
|
63
63
|
end
|
64
64
|
new_val
|
65
65
|
end
|
@@ -70,12 +70,12 @@ module FatTable
|
|
70
70
|
new_val = convert_to_string(val)
|
71
71
|
if new_val.nil?
|
72
72
|
msg = "attempt to add '#{val}' to a column already typed as #{type}"
|
73
|
-
raise
|
73
|
+
raise IncompatibleTypeError, msg
|
74
74
|
end
|
75
75
|
new_val
|
76
76
|
end
|
77
77
|
else
|
78
|
-
raise
|
78
|
+
raise LogicError, "Mysteriously, column has unknown type '#{type}'"
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -121,6 +121,7 @@ module FatTable
|
|
121
121
|
return val if val.is_a?(DateTime)
|
122
122
|
return val if val.is_a?(Date)
|
123
123
|
return val.to_datetime if val.is_a?(Time)
|
124
|
+
|
124
125
|
begin
|
125
126
|
str = val.to_s.clean
|
126
127
|
return nil if str.blank?
|
data/lib/fat_table/errors.rb
CHANGED
@@ -9,6 +9,10 @@ module FatTable
|
|
9
9
|
# cannot correct.
|
10
10
|
class LogicError < StandardError; end
|
11
11
|
|
12
|
+
# Raised when attempting to add an incompatible type to an already-typed
|
13
|
+
# Column.
|
14
|
+
class IncompatibleTypeError < UserError; end
|
15
|
+
|
12
16
|
# Raised when an external resource is not available due to caller or
|
13
17
|
# programmer error or some failure of the external resource to be available.
|
14
18
|
class TransientError < StandardError; end
|
data/lib/fat_table/table.rb
CHANGED
@@ -62,19 +62,53 @@ module FatTable
|
|
62
62
|
# method call.
|
63
63
|
attr_accessor :explicit_boundaries
|
64
64
|
|
65
|
+
# An Array of FatTable::Columns that should be tolerant.
|
66
|
+
attr_reader :tolerant_columns
|
67
|
+
|
65
68
|
###########################################################################
|
66
69
|
# Constructors
|
67
70
|
###########################################################################
|
68
71
|
|
69
72
|
# :category: Constructors
|
70
73
|
|
71
|
-
# Return an empty FatTable::Table object.
|
72
|
-
|
74
|
+
# Return an empty FatTable::Table object. Specifying headers is optional.
|
75
|
+
# Any headers ending with a ! are marked as tolerant, in that, if an
|
76
|
+
# incompatible type is added to it, the column is re-typed as a String
|
77
|
+
# column, and construction proceeds. The ! is stripped from the header to
|
78
|
+
# form the column key, though. You can also provide the names of columns
|
79
|
+
# that should be tolerant by using the +tolerant_columns key-word to
|
80
|
+
# provide an array of headers that should be tolerant. The special string
|
81
|
+
# '*' or the symbol :* indicates that all columns should be created
|
82
|
+
# tolerant.
|
83
|
+
def initialize(*heads, tolerant_columns: [])
|
73
84
|
@columns = []
|
74
85
|
@explicit_boundaries = []
|
86
|
+
@tolerant_columns =
|
87
|
+
case tolerant_columns
|
88
|
+
when Array
|
89
|
+
tolerant_columns.map { |h| h.to_s.as_sym }
|
90
|
+
when String
|
91
|
+
if tolerant_columns.strip == '*'
|
92
|
+
['*'.to_sym]
|
93
|
+
else
|
94
|
+
[tolerant_columns.as_sym]
|
95
|
+
end
|
96
|
+
when Symbol
|
97
|
+
if tolerant_columns.to_s.strip == '*'
|
98
|
+
['*'.to_sym]
|
99
|
+
else
|
100
|
+
[tolerant_columns.to_s.as_sym]
|
101
|
+
end
|
102
|
+
else
|
103
|
+
raise ArgumentError, "set tolerant_columns to String, Symbol, or an Array of either"
|
104
|
+
end
|
75
105
|
unless heads.empty?
|
76
106
|
heads.each do |h|
|
77
|
-
|
107
|
+
if h.to_s.end_with?('!') || @tolerant_columns.include?(h)
|
108
|
+
@columns << Column.new(header: h.to_s.sub(/!\s*\z/, ''), tolerant: true)
|
109
|
+
else
|
110
|
+
@columns << Column.new(header: h)
|
111
|
+
end
|
78
112
|
end
|
79
113
|
end
|
80
114
|
end
|
@@ -99,9 +133,9 @@ module FatTable
|
|
99
133
|
|
100
134
|
# Construct a Table from the contents of a CSV file named +fname+. Headers
|
101
135
|
# will be taken from the first CSV row and converted to symbols.
|
102
|
-
def self.from_csv_file(fname)
|
136
|
+
def self.from_csv_file(fname, tolerant_columns: [])
|
103
137
|
File.open(fname, 'r') do |io|
|
104
|
-
from_csv_io(io)
|
138
|
+
from_csv_io(io, tolerant_columns: tolerant_columns)
|
105
139
|
end
|
106
140
|
end
|
107
141
|
|
@@ -109,8 +143,8 @@ module FatTable
|
|
109
143
|
|
110
144
|
# Construct a Table from a CSV string +str+, treated in the same manner as
|
111
145
|
# the input from a CSV file in ::from_org_file.
|
112
|
-
def self.from_csv_string(str)
|
113
|
-
from_csv_io(StringIO.new(str))
|
146
|
+
def self.from_csv_string(str, tolerant_columns: [])
|
147
|
+
from_csv_io(StringIO.new(str), tolerant_columns: tolerant_columns)
|
114
148
|
end
|
115
149
|
|
116
150
|
# :category: Constructors
|
@@ -119,9 +153,9 @@ module FatTable
|
|
119
153
|
# file named +fname+. Headers are taken from the first row if the second row
|
120
154
|
# is an hrule. Otherwise, synthetic headers of the form +:col_1+, +:col_2+,
|
121
155
|
# etc. are created.
|
122
|
-
def self.from_org_file(fname)
|
156
|
+
def self.from_org_file(fname, tolerant_columns: [])
|
123
157
|
File.open(fname, 'r') do |io|
|
124
|
-
from_org_io(io)
|
158
|
+
from_org_io(io, tolerant_columns: tolerant_columns)
|
125
159
|
end
|
126
160
|
end
|
127
161
|
|
@@ -129,8 +163,8 @@ module FatTable
|
|
129
163
|
|
130
164
|
# Construct a Table from a string +str+, treated in the same manner as the
|
131
165
|
# contents of an org-mode file in ::from_org_file.
|
132
|
-
def self.from_org_string(str)
|
133
|
-
from_org_io(StringIO.new(str))
|
166
|
+
def self.from_org_string(str, tolerant_columns: [])
|
167
|
+
from_org_io(StringIO.new(str), tolerant_columns: tolerant_columns)
|
134
168
|
end
|
135
169
|
|
136
170
|
# :category: Constructors
|
@@ -149,8 +183,8 @@ module FatTable
|
|
149
183
|
# :hlines no +) org-mode strips all hrules from the table; otherwise (+
|
150
184
|
# HEADER: :hlines yes +) they are indicated with nil elements in the outer
|
151
185
|
# array.
|
152
|
-
def self.from_aoa(aoa, hlines: false)
|
153
|
-
from_array_of_arrays(aoa, hlines: hlines)
|
186
|
+
def self.from_aoa(aoa, hlines: false, tolerant_columns: [])
|
187
|
+
from_array_of_arrays(aoa, hlines: hlines, tolerant_columns: tolerant_columns)
|
154
188
|
end
|
155
189
|
|
156
190
|
# :category: Constructors
|
@@ -160,9 +194,9 @@ module FatTable
|
|
160
194
|
# keys, which, when converted to symbols will become the headers for the
|
161
195
|
# Table. If hlines is set true, mark a group boundary whenever a nil, rather
|
162
196
|
# than a hash appears in the outer array.
|
163
|
-
def self.from_aoh(aoh, hlines: false)
|
197
|
+
def self.from_aoh(aoh, hlines: false, tolerant_columns: [])
|
164
198
|
if aoh.first.respond_to?(:to_h)
|
165
|
-
from_array_of_hashes(aoh, hlines: hlines)
|
199
|
+
from_array_of_hashes(aoh, hlines: hlines, tolerant_columns: tolerant_columns)
|
166
200
|
else
|
167
201
|
raise UserError,
|
168
202
|
"Cannot initialize Table with an array of #{input[0].class}"
|
@@ -181,7 +215,7 @@ module FatTable
|
|
181
215
|
|
182
216
|
# Construct a Table by running a SQL +query+ against the database set up
|
183
217
|
# with FatTable.connect, with the rows of the query result as rows.
|
184
|
-
def self.from_sql(query)
|
218
|
+
def self.from_sql(query, tolerant_columns: [])
|
185
219
|
msg = 'FatTable.db must be set with FatTable.connect'
|
186
220
|
raise UserError, msg if FatTable.db.nil?
|
187
221
|
|
@@ -203,8 +237,8 @@ module FatTable
|
|
203
237
|
# Construct table from an array of hashes or an array of any object that
|
204
238
|
# can respond to #to_h. If an array element is a nil, mark it as a group
|
205
239
|
# boundary in the Table.
|
206
|
-
def from_array_of_hashes(hashes, hlines: false)
|
207
|
-
result = new
|
240
|
+
def from_array_of_hashes(hashes, hlines: false, tolerant_columns: [])
|
241
|
+
result = new(tolerant_columns: tolerant_columns)
|
208
242
|
hashes.each do |hsh|
|
209
243
|
if hsh.nil?
|
210
244
|
unless hlines
|
@@ -232,8 +266,8 @@ module FatTable
|
|
232
266
|
# hlines are stripped from the table, otherwise (:hlines yes) they are
|
233
267
|
# indicated with nil elements in the outer array as expected by this
|
234
268
|
# method when hlines is set true.
|
235
|
-
def from_array_of_arrays(rows, hlines: false)
|
236
|
-
result = new
|
269
|
+
def from_array_of_arrays(rows, hlines: false, tolerant_columns: [])
|
270
|
+
result = new(tolerant_columns: tolerant_columns)
|
237
271
|
headers = []
|
238
272
|
if !hlines
|
239
273
|
# Take the first row as headers
|
@@ -269,8 +303,8 @@ module FatTable
|
|
269
303
|
result
|
270
304
|
end
|
271
305
|
|
272
|
-
def from_csv_io(io)
|
273
|
-
result = new
|
306
|
+
def from_csv_io(io, tolerant_columns: [])
|
307
|
+
result = new(tolerant_columns: tolerant_columns)
|
274
308
|
::CSV.new(io, headers: true, header_converters: :symbol,
|
275
309
|
skip_blanks: true).each do |row|
|
276
310
|
result << row.to_h
|
@@ -283,7 +317,7 @@ module FatTable
|
|
283
317
|
# header row must be marked with an hline (i.e, a row that looks like
|
284
318
|
# '|---+--...--|') and groups of rows may be marked with hlines to
|
285
319
|
# indicate group boundaries.
|
286
|
-
def from_org_io(io)
|
320
|
+
def from_org_io(io, tolerant_columns: [])
|
287
321
|
table_re = /\A\s*\|/
|
288
322
|
hrule_re = /\A\s*\|[-+]+/
|
289
323
|
rows = []
|
@@ -318,7 +352,7 @@ module FatTable
|
|
318
352
|
rows << line.split('|').map(&:clean)
|
319
353
|
end
|
320
354
|
end
|
321
|
-
from_array_of_arrays(rows, hlines: true)
|
355
|
+
from_array_of_arrays(rows, hlines: true, tolerant_columns: tolerant_columns)
|
322
356
|
end
|
323
357
|
end
|
324
358
|
|
@@ -412,6 +446,15 @@ module FatTable
|
|
412
446
|
|
413
447
|
# :category: Attributes
|
414
448
|
|
449
|
+
# Return whether the column with the given head should be made tolerant.
|
450
|
+
def tolerant_col?(h)
|
451
|
+
return true if tolerant_columns.include?(:'*')
|
452
|
+
|
453
|
+
tolerant_columns.include?(h)
|
454
|
+
end
|
455
|
+
|
456
|
+
# :category: Attributes
|
457
|
+
|
415
458
|
# Return the number of rows in the Table.
|
416
459
|
def size
|
417
460
|
return 0 if columns.empty?
|
@@ -571,7 +614,8 @@ module FatTable
|
|
571
614
|
range = group_row_range(k)
|
572
615
|
tab_col = column(col)
|
573
616
|
gitems = tab_col.items[range]
|
574
|
-
cols << Column.new(header: col, items: gitems,
|
617
|
+
cols << Column.new(header: col, items: gitems,
|
618
|
+
type: tab_col.type, tolerant: tab_col.tolerant?)
|
575
619
|
end
|
576
620
|
cols
|
577
621
|
end
|
@@ -941,7 +985,12 @@ module FatTable
|
|
941
985
|
expr = expr.to_s
|
942
986
|
result = empty_dup
|
943
987
|
headers.each do |h|
|
944
|
-
col =
|
988
|
+
col =
|
989
|
+
if tolerant_col?(h)
|
990
|
+
Column.new(header: h, tolerant: true)
|
991
|
+
else
|
992
|
+
Column.new(header: h)
|
993
|
+
end
|
945
994
|
result.add_column(col)
|
946
995
|
end
|
947
996
|
ev = Evaluator.new(ivars: { row: 0, group: 0 })
|
@@ -1406,6 +1455,9 @@ module FatTable
|
|
1406
1455
|
|
1407
1456
|
private
|
1408
1457
|
|
1458
|
+
# Collapse a group of rows to a single row by applying the aggregator from
|
1459
|
+
# the +agg_cols+ to the items in that column and the presumably identical
|
1460
|
+
# value in the +grp_cols to those columns.
|
1409
1461
|
def row_from_group(rows, grp_cols, agg_cols)
|
1410
1462
|
new_row = {}
|
1411
1463
|
grp_cols.each do |h|
|
@@ -1440,7 +1492,7 @@ module FatTable
|
|
1440
1492
|
# This column is new, so it needs nil items for all prior rows lest
|
1441
1493
|
# the value be added to a prior row.
|
1442
1494
|
items = Array.new(size, nil)
|
1443
|
-
columns << Column.new(header: h, items: items)
|
1495
|
+
columns << Column.new(header: h, items: items, tolerant: tolerant_col?(h))
|
1444
1496
|
end
|
1445
1497
|
headers.each do |h|
|
1446
1498
|
# NB: This adds a nil if h is not in row.
|
data/lib/fat_table/version.rb
CHANGED
data/lib/fat_table.rb
CHANGED
@@ -61,22 +61,22 @@ module FatTable
|
|
61
61
|
|
62
62
|
# Return an empty FatTable::Table object. You can use FatTable::Table#add_row
|
63
63
|
# or FatTable::Table#add_column to populate the table with data.
|
64
|
-
def self.new(*args)
|
65
|
-
Table.new(*args)
|
64
|
+
def self.new(*args, tolerant_columns: [])
|
65
|
+
Table.new(*args, tolerant_columns: tolerant_columns)
|
66
66
|
end
|
67
67
|
|
68
68
|
# Construct a FatTable::Table from the contents of a CSV file given by the
|
69
69
|
# file name +fname+. Headers will be taken from the first row and converted to
|
70
70
|
# symbols.
|
71
|
-
def self.from_csv_file(fname)
|
72
|
-
Table.from_csv_file(fname)
|
71
|
+
def self.from_csv_file(fname, tolerant_columns: [])
|
72
|
+
Table.from_csv_file(fname, tolerant_columns: tolerant_columns)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Construct a FatTable::Table from the string +str+, treated in the same
|
76
76
|
# manner as if read the input from a CSV file. Headers will be taken from the
|
77
77
|
# first row and converted to symbols.
|
78
|
-
def self.from_csv_string(str)
|
79
|
-
Table.from_csv_string(str)
|
78
|
+
def self.from_csv_string(str, tolerant_columns: [])
|
79
|
+
Table.from_csv_string(str, tolerant_columns: tolerant_columns)
|
80
80
|
end
|
81
81
|
|
82
82
|
# Construct a FatTable::Table from the first table found in the Emacs org-mode
|
@@ -84,8 +84,8 @@ module FatTable
|
|
84
84
|
# is an hline. Otherwise, synthetic headers of the form +:col_1+, +:col_2+,
|
85
85
|
# etc. are created. Any other hlines will be treated as marking a boundary in
|
86
86
|
# the table.
|
87
|
-
def self.from_org_file(fname)
|
88
|
-
Table.from_org_file(fname)
|
87
|
+
def self.from_org_file(fname, tolerant_columns: [])
|
88
|
+
Table.from_org_file(fname, tolerant_columns: tolerant_columns)
|
89
89
|
end
|
90
90
|
|
91
91
|
# Construct a FatTable::Table from the first table found in the string +str+,
|
@@ -93,8 +93,8 @@ module FatTable
|
|
93
93
|
# are taken from the first row if the second row is an hrule. Otherwise,
|
94
94
|
# synthetic headers of the form :col_1, :col_2, etc. are created. Any other
|
95
95
|
# hlines will be treated as marking a boundary in the table.
|
96
|
-
def self.from_org_string(str)
|
97
|
-
Table.from_org_string(str)
|
96
|
+
def self.from_org_string(str, tolerant_columns: [])
|
97
|
+
Table.from_org_string(str, tolerant_columns: tolerant_columns)
|
98
98
|
end
|
99
99
|
|
100
100
|
# Construct a FatTable::Table from the array of arrays +aoa+. By default, with
|
@@ -108,8 +108,8 @@ module FatTable
|
|
108
108
|
# org-mode code blocks, by default (+:hlines no+) all hlines are stripped from
|
109
109
|
# the table, otherwise (+:hlines yes+) they are indicated with nil elements in
|
110
110
|
# the outer array.
|
111
|
-
def self.from_aoa(aoa, hlines: false)
|
112
|
-
Table.from_aoa(aoa, hlines: hlines)
|
111
|
+
def self.from_aoa(aoa, hlines: false, tolerant_columns: [])
|
112
|
+
Table.from_aoa(aoa, hlines: hlines, tolerant_columns: tolerant_columns)
|
113
113
|
end
|
114
114
|
|
115
115
|
# Construct a FatTable::Table from the array of hashes +aoh+, which can be an
|
@@ -117,8 +117,8 @@ module FatTable
|
|
117
117
|
# interpret nil separators as marking boundaries in the new Table. All hashes
|
118
118
|
# must have the same keys, which, converted to symbols, become the headers for
|
119
119
|
# the new Table.
|
120
|
-
def self.from_aoh(aoh, hlines: false)
|
121
|
-
Table.from_aoh(aoh, hlines: hlines)
|
120
|
+
def self.from_aoh(aoh, hlines: false, tolerant_columns: [])
|
121
|
+
Table.from_aoh(aoh, hlines: hlines, tolerant_columns: tolerant_columns)
|
122
122
|
end
|
123
123
|
|
124
124
|
# Construct a FatTable::Table from another FatTable::Table. Inherit any group
|
@@ -130,8 +130,8 @@ module FatTable
|
|
130
130
|
# Construct a Table by running a SQL query against the database set up with
|
131
131
|
# FatTable.connect. Return the Table with the query results as rows and the
|
132
132
|
# headers from the query, converted to symbols, as headers.
|
133
|
-
def self.from_sql(query)
|
134
|
-
Table.from_sql(query)
|
133
|
+
def self.from_sql(query, tolerant_columns: [])
|
134
|
+
Table.from_sql(query, tolerant_columns: tolerant_columns)
|
135
135
|
end
|
136
136
|
|
137
137
|
########################################################################
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fat_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel E. Doherty
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -332,7 +332,7 @@ licenses: []
|
|
332
332
|
metadata:
|
333
333
|
allowed_push_host: https://rubygems.org
|
334
334
|
yard.run: yri
|
335
|
-
post_install_message:
|
335
|
+
post_install_message:
|
336
336
|
rdoc_options: []
|
337
337
|
require_paths:
|
338
338
|
- lib
|
@@ -348,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
348
348
|
version: '0'
|
349
349
|
requirements: []
|
350
350
|
rubygems_version: 3.3.3
|
351
|
-
signing_key:
|
351
|
+
signing_key:
|
352
352
|
specification_version: 4
|
353
353
|
summary: Provides tools for working with tables as a data type.
|
354
354
|
test_files: []
|