fat_table 0.7.0 → 0.8.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/README.org +90 -115
- data/lib/fat_table/convert.rb +0 -13
- data/lib/fat_table/table.rb +110 -74
- data/lib/fat_table/version.rb +1 -1
- data/lib/fat_table.rb +16 -16
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c16c57274c9f0f67abf924eeae6bce192f82844366199361ac68a7d9e1fdcf4
|
4
|
+
data.tar.gz: 42ea485003f76a56f66988a2bf4df159580dbbed022b1f637260ff8e1cd5888b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32a6d03a5effa045ef6db54654aef86d96e90846cd0002ada80eaf8218c573f8a430067119562b8369226f11bc45d1bd0c144ddb8c1947c7fc9256fca55389b0
|
7
|
+
data.tar.gz: f0a033ff19d04160b4137f1204df322174926be2a0d7f2c442388a358a8ddc4321f5edcc1670346b9b366aa8a55acbe6a23b6b44f0fa9a69bb038e9eee7d24ac
|
data/README.org
CHANGED
@@ -445,27 +445,49 @@ or nil. There are only five permissible types for a ~Column~:
|
|
445
445
|
4. *String* (for ruby ~String~ objects), or
|
446
446
|
5. *NilClass* (for the undetermined column type).
|
447
447
|
|
448
|
-
|
449
|
-
having a type of ~NilClass~, that is, their type is as yet
|
450
|
-
a string or object
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
448
|
+
By default, when a ~Table~ is constructed from an external source, all
|
449
|
+
~Columns~ start out having a type of ~NilClass~, that is, their type is as yet
|
450
|
+
undetermined. When a string or object is added to a ~Column~ and it can be
|
451
|
+
converted into one of the permissible types, it fixes the type of the column,
|
452
|
+
and all further items added to the ~Column~ must either be ~nil~ (indicating
|
453
|
+
no value) or be capable of being coerced to the column's type. Otherwise,
|
454
|
+
~FatTable~ raises an ~IncompatibleTypeError~ exception.
|
455
|
+
|
456
|
+
*** Type Keywords Arguments
|
457
|
+
All of the table constructors allow you to set the type for a column in
|
458
|
+
advance by adding keyword arguments to the end of the contructor arguments
|
459
|
+
where the keyword is a header symbol and the value is a string designating one
|
460
|
+
of the types. For example, suppose we are constructing a table from a CSV
|
461
|
+
file, and we know that one of the columns is labeled 'Start' and another
|
462
|
+
'Price'. We want to require the items in the 'Start' column to be a valid
|
463
|
+
date and the items in the 'Price' column to be valid numbers:
|
464
|
+
|
465
|
+
#+begin_example
|
466
|
+
FatTable.from_csv_file('data.csv', start: 'date', price: 'num')
|
467
|
+
#+end_example
|
468
|
+
|
469
|
+
The type string can be anything that starts with 'dat', 'num', 'boo', or
|
470
|
+
'str', regardless of case, to designate ~DateTime~, ~Numeric~, ~Boolean~, or
|
471
|
+
~String~ types, respectively. Any other string keeps the type as NilClass,
|
472
|
+
that is, it remains open for automatic typing.
|
455
473
|
|
456
474
|
The strictness of requiring all items to be of the same type can be relaxed by
|
457
|
-
declaring a column to be "tolerant." You can do so
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
475
|
+
declaring a column to be "tolerant." You can do so by adding a '~' to the end
|
476
|
+
of a keyword type specifier in the table constructor. In the above example,
|
477
|
+
if we wanted to allow strings to be mixed up with the numeric prices, we would
|
478
|
+
use the following:
|
479
|
+
|
480
|
+
#+begin_example
|
481
|
+
FatTable.from_csv_file('data.csv', start: 'date', price: 'num~')
|
482
|
+
#+end_example
|
483
|
+
|
484
|
+
If a Column is tolerant, ~FatTable~ tries to convert new items into the
|
485
|
+
column's specified type, or if the type is still open, to one of ~DateTime~,
|
486
|
+
~Numeric~, or ~Boolean~ and then fixing the column's type, or, if it cannot do
|
487
|
+
so converts the item into a ~String~ but does not raise an
|
488
|
+
~IncompatibleTypeError~ exception. These interloper strings are treated like
|
489
|
+
nils for purposes of sorting and evaluation, but are displayed according to
|
490
|
+
any string formatting on output. See [[*Designating "Tolerant" Columns][Designating "Tolerant" Columns]] below.
|
469
491
|
|
470
492
|
Items of input must be either one of the permissible ruby objects or strings. If
|
471
493
|
they are strings, ~FatTable~ attempts to parse them as one of the permissible
|
@@ -626,14 +648,32 @@ columns to be created:
|
|
626
648
|
|
627
649
|
Occasionally, ~FatTable~'s automatic type detection can get in the way and you
|
628
650
|
just want it to treat one or more columns as Strings regardless of their
|
629
|
-
appearance. Think, for example, of zip codes.
|
630
|
-
table is contructed, you can designate a
|
631
|
-
|
632
|
-
just mark it as a forced-string Column.
|
651
|
+
appearance. Think, for example, of zip codes. As mentioned above, when a
|
652
|
+
table is contructed, you can designate a 'String' type for a column by
|
653
|
+
using a keyword parameter.
|
633
654
|
|
634
|
-
#+
|
635
|
-
|
636
|
-
|
655
|
+
#+begin_src ruby :wrap EXAMPLE
|
656
|
+
require 'fat_table'
|
657
|
+
tab = FatTable.new(:a, 'b!', 'C', :d, :zip, zip: 'str')
|
658
|
+
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
659
|
+
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
660
|
+
tab << { zip: '01879--7884' }
|
661
|
+
tab << { zip: '66210', b: 'Not a Number' }
|
662
|
+
tab << { zip: '90210' }
|
663
|
+
tab.to_text
|
664
|
+
#+end_src
|
665
|
+
|
666
|
+
#+begin_EXAMPLE
|
667
|
+
+===+===+============+===+=============+===+
|
668
|
+
| A | B | C | D | Zip | E |
|
669
|
+
+---+---+------------+---+-------------+---+
|
670
|
+
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
671
|
+
| 3 | 2 | 2016-01-21 | T | | |
|
672
|
+
| | | | | 01879--7884 | |
|
673
|
+
| | | | | 90210 | |
|
674
|
+
| | | | | | |
|
675
|
+
+===+===+============+===+=============+===+
|
676
|
+
#+end_EXAMPLE
|
637
677
|
|
638
678
|
In addition, at any time after creating a table, you can force the String type
|
639
679
|
on any number of columns with the ~force_string!~ method. When you do so, all
|
@@ -662,84 +702,9 @@ exisiting items in the column are converted to strings with the #to_s method.
|
|
662
702
|
+======+======+============+===+=======+===+
|
663
703
|
#+end_EXAMPLE
|
664
704
|
|
665
|
-
**** Designating "Tolerant" Columns
|
666
|
-
|
667
|
-
Related to the problem just discussed is the problem of reading files in from
|
668
|
-
the wild where a column may get typed as, say Numeric, but then contain
|
669
|
-
something that can't be parsed as a Numeric. ~FatTable~ raises an exception
|
670
|
-
is such cases, and that may be what you want if you can control the input.
|
671
|
-
But, especially when you cannot do so, it can be helpful to designate one or
|
672
|
-
more columns as "tolerant." This means that when a conversion problem occurs,
|
673
|
-
the column item is retained as a string type in a column that is otherwise of
|
674
|
-
one of the types Numeric, DateTime, or Boolean. Those string items are
|
675
|
-
treated as nils for purposes of sorting or evaluation in a ~select~ method.
|
676
|
-
When formatted, they participate in string formatting directive, but not those
|
677
|
-
for other types.
|
678
|
-
|
679
|
-
All of the table construction methods, allow a keyword parameter,
|
680
|
-
~tolerant_columns~, where you can designate what columns should be convert to
|
681
|
-
String type when conversion to the auto-typed column type is not possible.
|
682
|
-
The parameter should be an array of headers, in either string or symbol form,
|
683
|
-
for which this behavior is desired. In addition, it can be set to the special
|
684
|
-
string '*' or symbol ~:*~ to indicate that all the columns should be made
|
685
|
-
tolerant.
|
686
|
-
|
687
|
-
#+begin_src ruby :wrap EXAMPLE
|
688
|
-
require 'fat_table'
|
689
|
-
tab = FatTable.new(:a, 'b', 'C', :d, :zip, tolerant_columns: [:zip])
|
690
|
-
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
691
|
-
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
692
|
-
tab << { zip: '01879--7884' }
|
693
|
-
tab << { zip: '66210' }
|
694
|
-
tab << { zip: '90210' }
|
695
|
-
tab.to_text
|
696
|
-
#+end_src
|
697
|
-
|
698
|
-
#+RESULTS:
|
699
|
-
#+begin_EXAMPLE
|
700
|
-
+======+======+============+===+=============+===+
|
701
|
-
| A | B | C | D | Zip | E |
|
702
|
-
+------+------+------------+---+-------------+---+
|
703
|
-
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
704
|
-
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
705
|
-
| | | | | 01879--7884 | |
|
706
|
-
| | | | | 66210 | |
|
707
|
-
| | | | | 90210 | |
|
708
|
-
+======+======+============+===+=============+===+
|
709
|
-
#+end_EXAMPLE
|
710
|
-
|
711
|
-
Another way to designate a column as tolerant is to end a column you want to
|
712
|
-
designate as tolerant with a ~!~. The ~!~ will be stripped from the header,
|
713
|
-
but it will be marked as tolerant.
|
714
|
-
#+begin_src ruby :wrap EXAMPLE
|
715
|
-
require 'fat_table'
|
716
|
-
tab = FatTable.new(:a, 'b!', 'C', :d, :zip!)
|
717
|
-
tab << { a: 1, b: 2, c: "<2017-01-21>", d: 'f', e: '', zip: 18552 }
|
718
|
-
tab << { a: 3.14, b: 2.17, c: '[2016-01-21 Thu]', d: 'Y', e: nil }
|
719
|
-
tab << { zip: '01879--7884' }
|
720
|
-
tab << { zip: '66210', b: 'Not a Number' }
|
721
|
-
tab << { zip: '90210' }
|
722
|
-
tab.to_text
|
723
|
-
#+end_src
|
724
|
-
|
725
|
-
#+RESULTS:
|
726
|
-
#+begin_EXAMPLE
|
727
|
-
+======+==============+============+===+=============+===+
|
728
|
-
| A | B | C | D | Zip | E |
|
729
|
-
+------+--------------+------------+---+-------------+---+
|
730
|
-
| 1 | 2 | 2017-01-21 | F | 18552 | |
|
731
|
-
| 3.14 | 2.17 | 2016-01-21 | T | | |
|
732
|
-
| | | | | 01879--7884 | |
|
733
|
-
| | Not a Number | | | 66210 | |
|
734
|
-
| | | | | 90210 | |
|
735
|
-
+======+==============+============+===+=============+===+
|
736
|
-
#+end_EXAMPLE
|
737
|
-
|
738
705
|
*** From CSV or Org Mode files or strings
|
739
706
|
Tables can also be read from ~.csv~ files or files containing ~org-mode~
|
740
|
-
tables.
|
741
|
-
~tolerant_columns:~ keyword argument or make them all tolerant by designating
|
742
|
-
the pseudo-column ~:*~ as tolerant.
|
707
|
+
tables.
|
743
708
|
|
744
709
|
In the case of org-mode files, ~FatTable~ skips through the file until it finds
|
745
710
|
a line that look like a table, that is, it begins with any number of spaces
|
@@ -799,10 +764,10 @@ header row, and the headers are converted to symbols as described above.
|
|
799
764
|
|
800
765
|
You can also initialize a table directly from ruby data structures. You can,
|
801
766
|
for example, build a table from an array of arrays. Remember that you can
|
802
|
-
make any column tolerant with a
|
803
|
-
|
767
|
+
make any column tolerant with a keyword argument for the column symbol and
|
768
|
+
ending it with a '~'.
|
804
769
|
|
805
|
-
#+BEGIN_SRC ruby
|
770
|
+
#+BEGIN_SRC ruby
|
806
771
|
aoa = [
|
807
772
|
['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
|
808
773
|
[1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ENTITY1', 'T'],
|
@@ -816,11 +781,27 @@ them all tolerant by designating the pseudo-column ~:*~ as tolerant.
|
|
816
781
|
[13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ENTITY3', 'T'],
|
817
782
|
[14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ENTITY3', 'F'],
|
818
783
|
[15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ENTITY3', 'T'],
|
819
|
-
[16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ENTITY3', 'T']
|
820
|
-
|
821
|
-
tab = FatTable.from_aoa(aoa)
|
784
|
+
[16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ENTITY3', 'T'] ]
|
785
|
+
|
786
|
+
tab = FatTable.from_aoa(aoa).to_aoa
|
822
787
|
#+END_SRC
|
823
788
|
|
789
|
+
#+RESULTS:
|
790
|
+
| Ref | Date | Code | Raw | Shares | Price | Info | Bool |
|
791
|
+
|-----+------------+------+--------+--------+-------+---------+------|
|
792
|
+
| 1 | 2013-05-02 | P | 795546 | 795546 | 1 | ENTITY1 | T |
|
793
|
+
| 2 | 2013-05-02 | P | 118186 | 118186 | 12 | ENTITY1 | T |
|
794
|
+
| 7 | 2013-05-20 | S | 12000 | 5046 | 28 | ENTITY3 | F |
|
795
|
+
| 8 | 2013-05-20 | S | 85000 | 35743 | 28 | ENTITY3 | T |
|
796
|
+
| 9 | 2013-05-20 | S | 33302 | 14003 | 29 | ENTITY3 | T |
|
797
|
+
| 10 | 2013-05-23 | S | 8000 | 3364 | 27 | ENTITY3 | T |
|
798
|
+
| 11 | 2013-05-23 | S | 23054 | 9694 | 27 | ENTITY3 | F |
|
799
|
+
| 12 | 2013-05-23 | S | 39906 | 16780 | 25 | ENTITY3 | T |
|
800
|
+
| 13 | 2013-05-29 | S | 13459 | 5660 | 25 | ENTITY3 | T |
|
801
|
+
| 14 | 2013-05-29 | S | 15700 | 6602 | 25 | ENTITY3 | F |
|
802
|
+
| 15 | 2013-05-29 | S | 15900 | 6686 | 25 | ENTITY3 | T |
|
803
|
+
| 16 | 2013-05-30 | S | 6679 | 2809 | 25 | ENTITY3 | T |
|
804
|
+
|
824
805
|
Notice that the values can either be ruby objects, such as the Integer ~85_000~,
|
825
806
|
or strings that can be parsed into one of the permissible column types.
|
826
807
|
|
@@ -898,9 +879,7 @@ This example illustrates several things:
|
|
898
879
|
A second ruby data structure that can be used to initialize a ~FatTable~ table
|
899
880
|
is an array of ruby Hashes. Each hash represents a row of the table, and the
|
900
881
|
headers of the table are taken from the keys of the hashes. Accordingly, all
|
901
|
-
the hashes must have the same keys.
|
902
|
-
tolerant with a ~tolerant_columns:~ keyword argument or make them all tolerant
|
903
|
-
by designating the pseudo-column ~:*~ as tolerant.
|
882
|
+
the hashes must have the same keys.
|
904
883
|
|
905
884
|
This same method can in fact take an array of any objects that can be converted
|
906
885
|
to a Hash with the ~#to_h~ method, so you can use an array of your own objects
|
@@ -989,10 +968,6 @@ The ~.connect~ function need only be called once, and the database handle it
|
|
989
968
|
creates will be used for all subsequent ~.from_sql~ calls until ~.connect~ is
|
990
969
|
called again.
|
991
970
|
|
992
|
-
Remember that you can make any column tolerant with a ~tolerant_columns:~
|
993
|
-
keyword argument or make them all tolerant by designating the pseudo-column
|
994
|
-
~:*~ as tolerant.
|
995
|
-
|
996
971
|
*** Marking Groups in Input
|
997
972
|
**** Manually
|
998
973
|
|
data/lib/fat_table/convert.rb
CHANGED
@@ -63,19 +63,6 @@ module FatTable
|
|
63
63
|
when 'String'
|
64
64
|
if val.nil?
|
65
65
|
nil
|
66
|
-
elsif tolerant
|
67
|
-
# Allow String to upgrade to one of Numeric, DateTime, or Boolean if
|
68
|
-
# possible.
|
69
|
-
if (new_val = convert_to_numeric(val))
|
70
|
-
new_val
|
71
|
-
elsif (new_val = convert_to_date_time(val))
|
72
|
-
new_val
|
73
|
-
elsif (new_val = convert_to_boolean(val))
|
74
|
-
new_val
|
75
|
-
else
|
76
|
-
new_val = convert_to_string(val)
|
77
|
-
end
|
78
|
-
new_val
|
79
66
|
else
|
80
67
|
new_val = convert_to_string(val)
|
81
68
|
if new_val.nil?
|
data/lib/fat_table/table.rb
CHANGED
@@ -54,6 +54,10 @@ module FatTable
|
|
54
54
|
# An Array of FatTable::Columns that constitute the table.
|
55
55
|
attr_reader :columns
|
56
56
|
|
57
|
+
# Headers of columns that are to be tolerant when they are built.
|
58
|
+
attr_accessor :tolerant_cols
|
59
|
+
attr_reader :omni_typ, :omni_tol
|
60
|
+
|
57
61
|
# Record boundaries set explicitly with mark_boundaries or from reading
|
58
62
|
# hlines from input. When we want to access boundaries, however, we want
|
59
63
|
# to add an implict boundary at the last row of the table. Since, as the
|
@@ -62,55 +66,58 @@ module FatTable
|
|
62
66
|
# method call.
|
63
67
|
attr_accessor :explicit_boundaries
|
64
68
|
|
65
|
-
# An Array of FatTable::Columns that should be tolerant.
|
66
|
-
attr_reader :tolerant_columns
|
67
|
-
|
68
69
|
###########################################################################
|
69
70
|
# Constructors
|
70
|
-
|
71
|
-
|
71
|
+
#
|
72
|
+
#
|
72
73
|
# :category: Constructors
|
73
|
-
|
74
|
+
#
|
74
75
|
# Return an empty FatTable::Table object. Specifying headers is optional.
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
|
76
|
+
# By default, all columns start our as having an "open" type and get
|
77
|
+
# assigned a type based on their contents. For example, if a column
|
78
|
+
# contains items that can be interpreted as dates, the column gets
|
79
|
+
# assigned a DateTime type. Other types are Numeric, Boolean, and String.
|
80
|
+
# Once a type is assigned to a column, any non-conforming vaules in that
|
81
|
+
# column raise an IncompatibleType error. If a column is marked
|
82
|
+
# "tolerant", however, the incompatible item is converted to a string and
|
83
|
+
# allowed to remain in the column without raising an error. They count as
|
84
|
+
# nils when calculations are performed on the column and paricipate only
|
85
|
+
# in string formatting directives on output.
|
86
|
+
#
|
87
|
+
# Rather than have a column's type determined by content, you can also
|
88
|
+
# specify a column type by providing a type hash, where the key is the
|
89
|
+
# header's name and the value is the desired type. In that case, any
|
90
|
+
# incompatible type raises an an IncompatibleTypeError unless the column
|
91
|
+
# is also marked tolerant, in which case it gets converted to a string as
|
92
|
+
# discussed above. If the type name in the types hash ends in a '~', it
|
93
|
+
# is treated as a specifying the given type but marking it as tolerant as
|
94
|
+
# well. The values in the type hash can be any string or sybol that
|
95
|
+
# starts with 'num', 'dat', 'bool', or 'str' to specify Numeric,
|
96
|
+
# DateTime, Boolean, or String types respectively.
|
97
|
+
def initialize(*heads, **types)
|
84
98
|
@columns = []
|
85
|
-
@
|
86
|
-
@
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
unless heads.empty?
|
106
|
-
heads.each do |h|
|
107
|
-
if h.to_s.end_with?('!') || @tolerant_columns.include?(h)
|
108
|
-
@columns << Column.new(header: h.to_s.sub(/!\s*\z/, ''), type: 'String')
|
109
|
-
else
|
110
|
-
@columns << Column.new(header: h)
|
111
|
-
end
|
112
|
-
end
|
99
|
+
@tolerant_cols = []
|
100
|
+
@headers = []
|
101
|
+
# Check for the special 'omni' key
|
102
|
+
@omni_type = 'NilClass'
|
103
|
+
@omni_tol = false
|
104
|
+
if types.keys.map(&:to_s).include?('omni')
|
105
|
+
# All columns not otherwise included in types should have the type and
|
106
|
+
# tolerance of omni.
|
107
|
+
omni_val = (types['omni'] || types[:omni])
|
108
|
+
@omni_type, @omni_tol = Table.typ_tol(omni_val)
|
109
|
+
# Remove omni from types.
|
110
|
+
types.delete(:omni)
|
111
|
+
types.delete('omni')
|
112
|
+
end
|
113
|
+
heads += types.keys
|
114
|
+
heads.uniq.each do |h|
|
115
|
+
typ, tol = Table.typ_tol(types[h])
|
116
|
+
@tolerant_cols << h.to_s.as_sym if tol
|
117
|
+
@columns << Column.new(header: h.to_s.sub(/~\s*\z/, ''), type: typ,
|
118
|
+
tolerant: tol)
|
113
119
|
end
|
120
|
+
@explicit_boundaries = []
|
114
121
|
end
|
115
122
|
|
116
123
|
# :category: Constructors
|
@@ -133,9 +140,9 @@ module FatTable
|
|
133
140
|
|
134
141
|
# Construct a Table from the contents of a CSV file named +fname+. Headers
|
135
142
|
# will be taken from the first CSV row and converted to symbols.
|
136
|
-
def self.from_csv_file(fname,
|
143
|
+
def self.from_csv_file(fname, **types)
|
137
144
|
File.open(fname, 'r') do |io|
|
138
|
-
from_csv_io(io,
|
145
|
+
from_csv_io(io, **types)
|
139
146
|
end
|
140
147
|
end
|
141
148
|
|
@@ -143,8 +150,8 @@ module FatTable
|
|
143
150
|
|
144
151
|
# Construct a Table from a CSV string +str+, treated in the same manner as
|
145
152
|
# the input from a CSV file in ::from_org_file.
|
146
|
-
def self.from_csv_string(str,
|
147
|
-
from_csv_io(StringIO.new(str),
|
153
|
+
def self.from_csv_string(str, **types)
|
154
|
+
from_csv_io(StringIO.new(str), **types)
|
148
155
|
end
|
149
156
|
|
150
157
|
# :category: Constructors
|
@@ -153,9 +160,9 @@ module FatTable
|
|
153
160
|
# file named +fname+. Headers are taken from the first row if the second row
|
154
161
|
# is an hrule. Otherwise, synthetic headers of the form +:col_1+, +:col_2+,
|
155
162
|
# etc. are created.
|
156
|
-
def self.from_org_file(fname,
|
163
|
+
def self.from_org_file(fname, **types)
|
157
164
|
File.open(fname, 'r') do |io|
|
158
|
-
from_org_io(io,
|
165
|
+
from_org_io(io, **types)
|
159
166
|
end
|
160
167
|
end
|
161
168
|
|
@@ -163,8 +170,8 @@ module FatTable
|
|
163
170
|
|
164
171
|
# Construct a Table from a string +str+, treated in the same manner as the
|
165
172
|
# contents of an org-mode file in ::from_org_file.
|
166
|
-
def self.from_org_string(str,
|
167
|
-
from_org_io(StringIO.new(str),
|
173
|
+
def self.from_org_string(str, **types)
|
174
|
+
from_org_io(StringIO.new(str), **types)
|
168
175
|
end
|
169
176
|
|
170
177
|
# :category: Constructors
|
@@ -183,8 +190,8 @@ module FatTable
|
|
183
190
|
# :hlines no +) org-mode strips all hrules from the table; otherwise (+
|
184
191
|
# HEADER: :hlines yes +) they are indicated with nil elements in the outer
|
185
192
|
# array.
|
186
|
-
def self.from_aoa(aoa, hlines: false,
|
187
|
-
from_array_of_arrays(aoa, hlines: hlines,
|
193
|
+
def self.from_aoa(aoa, hlines: false, **types)
|
194
|
+
from_array_of_arrays(aoa, hlines: hlines, **types)
|
188
195
|
end
|
189
196
|
|
190
197
|
# :category: Constructors
|
@@ -194,9 +201,9 @@ module FatTable
|
|
194
201
|
# keys, which, when converted to symbols will become the headers for the
|
195
202
|
# Table. If hlines is set true, mark a group boundary whenever a nil, rather
|
196
203
|
# than a hash appears in the outer array.
|
197
|
-
def self.from_aoh(aoh, hlines: false,
|
204
|
+
def self.from_aoh(aoh, hlines: false, **types)
|
198
205
|
if aoh.first.respond_to?(:to_h)
|
199
|
-
from_array_of_hashes(aoh, hlines: hlines,
|
206
|
+
from_array_of_hashes(aoh, hlines: hlines, **types)
|
200
207
|
else
|
201
208
|
raise UserError,
|
202
209
|
"Cannot initialize Table with an array of #{input[0].class}"
|
@@ -215,7 +222,7 @@ module FatTable
|
|
215
222
|
|
216
223
|
# Construct a Table by running a SQL +query+ against the database set up
|
217
224
|
# with FatTable.connect, with the rows of the query result as rows.
|
218
|
-
def self.from_sql(query,
|
225
|
+
def self.from_sql(query, **types)
|
219
226
|
msg = 'FatTable.db must be set with FatTable.connect'
|
220
227
|
raise UserError, msg if FatTable.db.nil?
|
221
228
|
|
@@ -232,13 +239,32 @@ module FatTable
|
|
232
239
|
############################################################################
|
233
240
|
|
234
241
|
class << self
|
242
|
+
# Return [typ, tol] based on the type string, str.
|
243
|
+
def typ_tol(str)
|
244
|
+
tol = str ? str.match?(/~\s*\Z/) : false
|
245
|
+
typ =
|
246
|
+
case str
|
247
|
+
when /\A\s*num/i
|
248
|
+
'Numeric'
|
249
|
+
when /\A\s*boo/i
|
250
|
+
'Boolean'
|
251
|
+
when /\A\s*dat/i
|
252
|
+
'DateTime'
|
253
|
+
when /\A\s*str/i
|
254
|
+
'String'
|
255
|
+
else
|
256
|
+
'NilClass'
|
257
|
+
end
|
258
|
+
[typ, tol]
|
259
|
+
end
|
260
|
+
|
235
261
|
private
|
236
262
|
|
237
263
|
# Construct table from an array of hashes or an array of any object that
|
238
264
|
# can respond to #to_h. If an array element is a nil, mark it as a group
|
239
265
|
# boundary in the Table.
|
240
|
-
def from_array_of_hashes(hashes, hlines: false,
|
241
|
-
result = new(
|
266
|
+
def from_array_of_hashes(hashes, hlines: false, **types)
|
267
|
+
result = new(**types)
|
242
268
|
hashes.each do |hsh|
|
243
269
|
if hsh.nil?
|
244
270
|
unless hlines
|
@@ -266,8 +292,8 @@ module FatTable
|
|
266
292
|
# hlines are stripped from the table, otherwise (:hlines yes) they are
|
267
293
|
# indicated with nil elements in the outer array as expected by this
|
268
294
|
# method when hlines is set true.
|
269
|
-
def from_array_of_arrays(rows, hlines: false,
|
270
|
-
result = new(
|
295
|
+
def from_array_of_arrays(rows, hlines: false, **types)
|
296
|
+
result = new(**types)
|
271
297
|
headers = []
|
272
298
|
if !hlines
|
273
299
|
# Take the first row as headers
|
@@ -303,8 +329,8 @@ module FatTable
|
|
303
329
|
result
|
304
330
|
end
|
305
331
|
|
306
|
-
def from_csv_io(io,
|
307
|
-
result = new(
|
332
|
+
def from_csv_io(io, **types)
|
333
|
+
result = new(**types)
|
308
334
|
::CSV.new(io, headers: true, header_converters: :symbol,
|
309
335
|
skip_blanks: true).each do |row|
|
310
336
|
result << row.to_h
|
@@ -317,7 +343,7 @@ module FatTable
|
|
317
343
|
# header row must be marked with an hline (i.e, a row that looks like
|
318
344
|
# '|---+--...--|') and groups of rows may be marked with hlines to
|
319
345
|
# indicate group boundaries.
|
320
|
-
def from_org_io(io,
|
346
|
+
def from_org_io(io, **types)
|
321
347
|
table_re = /\A\s*\|/
|
322
348
|
hrule_re = /\A\s*\|[-+]+/
|
323
349
|
rows = []
|
@@ -352,7 +378,7 @@ module FatTable
|
|
352
378
|
rows << line.split('|').map(&:clean)
|
353
379
|
end
|
354
380
|
end
|
355
|
-
from_array_of_arrays(rows, hlines: true,
|
381
|
+
from_array_of_arrays(rows, hlines: true, **types)
|
356
382
|
end
|
357
383
|
end
|
358
384
|
|
@@ -377,6 +403,16 @@ module FatTable
|
|
377
403
|
column(key).type
|
378
404
|
end
|
379
405
|
|
406
|
+
# Return the type of the Column with the given +key+ as its
|
407
|
+
# header as a String.
|
408
|
+
def types
|
409
|
+
result = {}
|
410
|
+
headers.each do |h|
|
411
|
+
result[h] = type(h)
|
412
|
+
end
|
413
|
+
result
|
414
|
+
end
|
415
|
+
|
380
416
|
# :category: Attributes
|
381
417
|
|
382
418
|
# Set the column type for Column with the given +key+ as a String type.
|
@@ -428,7 +464,7 @@ module FatTable
|
|
428
464
|
# :category: Attributes
|
429
465
|
|
430
466
|
# Return a Hash of the Table's Column header symbols to type strings.
|
431
|
-
def
|
467
|
+
def col_types
|
432
468
|
result = {}
|
433
469
|
columns.each do |c|
|
434
470
|
result[c.header] = c.type
|
@@ -445,11 +481,11 @@ module FatTable
|
|
445
481
|
|
446
482
|
# :category: Attributes
|
447
483
|
|
448
|
-
# Return whether the column with the given head
|
484
|
+
# Return whether the column with the given head is supposed to be
|
485
|
+
# tolerant. We can't just look up the Column because it may not be build
|
486
|
+
# yet, as when we do a row-by-row add.
|
449
487
|
def tolerant_col?(h)
|
450
|
-
|
451
|
-
|
452
|
-
tolerant_columns.include?(h)
|
488
|
+
tolerant_cols.include?(h.to_s.as_sym) || self.omni_tol
|
453
489
|
end
|
454
490
|
|
455
491
|
# :category: Attributes
|
@@ -994,11 +1030,11 @@ module FatTable
|
|
994
1030
|
result = empty_dup
|
995
1031
|
headers.each do |h|
|
996
1032
|
col =
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1033
|
+
if tolerant_col?(h)
|
1034
|
+
Column.new(header: h, tolerant: true)
|
1035
|
+
else
|
1036
|
+
Column.new(header: h)
|
1037
|
+
end
|
1002
1038
|
result.add_column(col)
|
1003
1039
|
end
|
1004
1040
|
ev = Evaluator.new(ivars: { row: 0, group: 0 })
|
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, **types)
|
65
|
+
Table.new(*args, **types)
|
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, **types)
|
72
|
+
Table.from_csv_file(fname, **types)
|
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, **types)
|
79
|
+
Table.from_csv_string(str, **types)
|
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, **types)
|
88
|
+
Table.from_org_file(fname, **types)
|
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, **types)
|
97
|
+
Table.from_org_string(str, **types)
|
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, **types)
|
112
|
+
Table.from_aoa(aoa, hlines: hlines, **types)
|
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, **types)
|
121
|
+
Table.from_aoh(aoh, hlines: hlines, **types)
|
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, **types)
|
134
|
+
Table.from_sql(query, **types)
|
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.
|
4
|
+
version: 0.8.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: 2023-04-
|
11
|
+
date: 2023-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|