fat_table 0.5.2 → 0.5.3
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/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: []
|