fat_table 0.2.9 → 0.3.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/.gitignore +2 -0
- data/.rubocop.yml +18 -0
- data/.travis.yml +6 -5
- data/Gemfile +2 -0
- data/README.org +48 -46
- data/fat_table.gemspec +7 -8
- data/lib/fat_table/column.rb +57 -21
- data/lib/fat_table/db_handle.rb +16 -4
- data/lib/fat_table/errors.rb +2 -0
- data/lib/fat_table/evaluator.rb +2 -0
- data/lib/fat_table/formatters/aoa_formatter.rb +2 -0
- data/lib/fat_table/formatters/aoh_formatter.rb +2 -0
- data/lib/fat_table/formatters/formatter.rb +56 -53
- data/lib/fat_table/formatters/org_formatter.rb +2 -0
- data/lib/fat_table/formatters/term_formatter.rb +17 -15
- data/lib/fat_table/formatters/text_formatter.rb +2 -0
- data/lib/fat_table/formatters.rb +2 -0
- data/lib/fat_table/patches.rb +2 -0
- data/lib/fat_table/table.rb +50 -25
- data/lib/fat_table/version.rb +3 -1
- data/lib/fat_table.rb +13 -2
- metadata +45 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d95bf4d370e8c501b679aba77975ee9404940383a0058718d4c906b491ef195d
|
4
|
+
data.tar.gz: fc39721ae44a5e656bfb8d6225164aa74cf0bdf21246b1eec4c247572b73bf30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a532d371acf8e0a8fe36e83410c646a12125af501638749bf69f6c85fedcabf7eabf6b6edd5579eb0b32df42220cdac2f7ce018447b2734749e735815ac53ff2
|
7
|
+
data.tar.gz: 555e412df95d31e035debb960c5dc86bfba4ec5b4c9827ea7ec19ef2458a18b01f54d4c620e69c621800ddb31bde7ab9e4d7034c9c53380ee301322260647c49
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-shopify: rubocop.yml
|
3
|
+
require: rubocop-rspec
|
4
|
+
require: rubocop-performance
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 2.7
|
8
|
+
Exclude:
|
9
|
+
- 'test/tmp/**/*'
|
10
|
+
- 'vendor/bundle/**/*'
|
11
|
+
|
12
|
+
Style/MethodCallWithArgsParentheses:
|
13
|
+
Exclude:
|
14
|
+
- '**/Gemfile'
|
15
|
+
|
16
|
+
Style/ClassAndModuleChildren:
|
17
|
+
Exclude:
|
18
|
+
- 'test/**/*'
|
data/.travis.yml
CHANGED
@@ -2,8 +2,9 @@ language: ruby
|
|
2
2
|
before_install:
|
3
3
|
- sudo apt-get -qq update
|
4
4
|
- sudo apt-get install -y texlive-latex-base texlive-latex-recommended
|
5
|
-
|
6
|
-
|
5
|
+
- gem install pg
|
6
|
+
# before_script:
|
7
|
+
# - createdb -U travis travis
|
7
8
|
after_failure:
|
8
9
|
- "pwd"
|
9
10
|
- "cat ./spec/tmp/latex.err"
|
@@ -13,9 +14,9 @@ bundler_args: --without debug
|
|
13
14
|
services:
|
14
15
|
- postgresql
|
15
16
|
rvm:
|
16
|
-
- 2.2.2
|
17
|
-
- 2.3
|
18
17
|
- 2.4
|
19
18
|
- 2.5
|
20
19
|
- 2.6
|
21
|
-
-
|
20
|
+
- 2.7
|
21
|
+
- 3.0
|
22
|
+
- truffleruby
|
data/Gemfile
CHANGED
data/README.org
CHANGED
@@ -44,17 +44,6 @@ buffer as an org-table, ready for processing by other code blocks.
|
|
44
44
|
|
45
45
|
* Installation
|
46
46
|
|
47
|
-
** Prerequisites
|
48
|
-
The ~fat_table~ gem depends on several libraries being available for building,
|
49
|
-
mostly those concerned with accessing databases. On an ubuntu system, the
|
50
|
-
following packages should be installed before you install the ~fat_table~ gem:
|
51
|
-
|
52
|
-
- ruby-dev
|
53
|
-
- build-essential
|
54
|
-
- libsqlite3-dev
|
55
|
-
- libpq-dev
|
56
|
-
- libmysqlclient-dev
|
57
|
-
|
58
47
|
** Installing the gem
|
59
48
|
|
60
49
|
Add this line to your application's Gemfile:
|
@@ -492,7 +481,7 @@ This example illustrates several things:
|
|
492
481
|
A second ruby data structure that can be used to initialize a ~FatTable~ table
|
493
482
|
is an array of ruby Hashes. Each hash represents a row of the table, and the
|
494
483
|
headers of the table are taken from the keys of the hashes. Accordingly, all the
|
495
|
-
hashes
|
484
|
+
hashes must have the same keys.
|
496
485
|
|
497
486
|
This same method can in fact take an array of any objects that can be converted
|
498
487
|
to a Hash with the ~#to_h~ method, so you can use an array of your own objects
|
@@ -526,9 +515,17 @@ or strings that can be parsed into one of the permissible column types.
|
|
526
515
|
|
527
516
|
*** From SQL queries
|
528
517
|
|
529
|
-
Another way to initialize a ~FatTable~ table is with the results of a SQL
|
530
|
-
|
531
|
-
|
518
|
+
Another way to initialize a ~FatTable~ table is with the results of a SQL
|
519
|
+
query. Before you can connect to a database, you need to make sure that the required
|
520
|
+
adapter for your database is installed. ~FatTable~ uses the ~sequel~ gem
|
521
|
+
under the hood, so any database that it supports can be used. For example, if
|
522
|
+
you are accessing a Postgres database, you must install the ~pg~ gem with
|
523
|
+
|
524
|
+
#+begin_src sh
|
525
|
+
$ gem install pg
|
526
|
+
#+end_src
|
527
|
+
|
528
|
+
You must first set the database parameters to be used for the queries.
|
532
529
|
|
533
530
|
#+BEGIN_SRC ruby
|
534
531
|
# This automatically requires sequel.
|
@@ -771,17 +768,18 @@ symbol representing an existing column, which has the effect of renaming an
|
|
771
768
|
existing column, or (2) a string representing a ruby expression for the value of
|
772
769
|
a new column.
|
773
770
|
|
774
|
-
Within the string expression, the names of existing or already-specified
|
775
|
-
are available as local variables
|
776
|
-
'@
|
777
|
-
|
771
|
+
Within the string expression, the names of existing or already-specified
|
772
|
+
columns are available as local variables. In addition the instance variables
|
773
|
+
'@row' and '@group' are available as the row number and group number of the
|
774
|
+
new value. So for our example table, the string expressions for new columns
|
775
|
+
have access to local variables ~ref~, ~date~, ~code~, ~price~, ~g10~, ~qp10~,
|
778
776
|
~shares~, ~lp~, ~qp~, ~iplp~, and ~ipqp~ as well as the instance variables
|
779
777
|
~@row~ and ~@group~. The local variables are set to the values of the cell in
|
780
|
-
their respective columns for each row in the input table and the instance
|
781
|
-
variables are set the number of the current row and group respectively.
|
778
|
+
their respective columns for each row in the input table, and the instance
|
779
|
+
variables are set the number of the current row and group number respectively.
|
782
780
|
|
783
|
-
For example, if we want to rename the ~:date~
|
784
|
-
compute the cost of shares, we could do the following:
|
781
|
+
For example, if we want to rename the ~traded_on~ column to ~:date~ and add a
|
782
|
+
new column to compute the cost of shares, we could do the following:
|
785
783
|
|
786
784
|
#+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
|
787
785
|
#+BEGIN_SRC ruby
|
@@ -963,28 +961,21 @@ You can sort a table on any number of columns with ~order_by~. The ~order_by~
|
|
963
961
|
method takes any number of symbol arguments for the columns to sort on. If you
|
964
962
|
specify more than one column, the sort is performed on the first column, then
|
965
963
|
all columns that are equal with respect to the first column are sorted by the
|
966
|
-
second column, and so on.
|
967
|
-
|
964
|
+
second column, and so on. Ordering is done is ascending order for each of the
|
965
|
+
columns, but can be reversed by adding a '!' to the end a symbol argument.
|
966
|
+
All columns of the input table are included in the output.
|
968
967
|
|
969
|
-
Let's sort our table first by ~:code~, then
|
968
|
+
Let's sort our table first by ~:code~, then in reverse order of ~:date~.
|
970
969
|
|
971
970
|
#+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
|
972
971
|
#+BEGIN_SRC ruby
|
973
|
-
tab1.order_by(:code, :date) \
|
972
|
+
tab1.order_by(:code, :date!) \
|
974
973
|
.to_aoa
|
975
974
|
#+END_SRC
|
976
975
|
|
977
|
-
#+
|
976
|
+
#+begin_EXAMPLE
|
978
977
|
| Ref | Date | Code | Price | G10 | QP10 | Shares | Lp | Qp | Iplp | Ipqp |
|
979
978
|
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
|
980
|
-
| T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
981
|
-
| T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
|
982
|
-
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
|
983
|
-
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
|
984
|
-
| T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
|
985
|
-
| T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
|
986
|
-
| T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
|
987
|
-
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
|
988
979
|
| T011 | 2016-11-02 | P | 7.425 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
989
980
|
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
|
990
981
|
| T012 | 2016-11-02 | P | 7.55 | F | F | 4700 | 677 | 4023 | 0.2453 | 0.1924 |
|
@@ -993,12 +984,20 @@ Let's sort our table first by ~:code~, then by ~:date~.
|
|
993
984
|
| T015 | 2016-11-02 | P | 7.75 | F | F | 500 | 72 | 428 | 0.2453 | 0.1924 |
|
994
985
|
| T016 | 2016-11-02 | P | 8.25 | T | T | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
995
986
|
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
|
987
|
+
| T001 | 2016-11-01 | P | 7.7 | T | F | 100 | 14 | 86 | 0.2453 | 0.1924 |
|
988
|
+
| T002 | 2016-11-01 | P | 7.75 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
|
989
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
|
990
|
+
| T003 | 2016-11-01 | P | 7.5 | F | T | 800 | 112 | 688 | 0.2453 | 0.1924 |
|
991
|
+
| T008 | 2016-11-01 | P | 7.65 | F | F | 2771 | 393 | 2378 | 0.2453 | 0.1924 |
|
992
|
+
| T009 | 2016-11-01 | P | 7.6 | F | F | 9550 | 1363 | 8187 | 0.2453 | 0.1924 |
|
993
|
+
| T010 | 2016-11-01 | P | 7.55 | F | T | 3175 | 451 | 2724 | 0.2453 | 0.1924 |
|
994
|
+
|------+------------+------+-------+-----+------+--------+------+-------+--------+--------|
|
996
995
|
| T004 | 2016-11-01 | S | 7.55 | T | F | 6811 | 966 | 5845 | 0.2453 | 0.1924 |
|
997
996
|
| T005 | 2016-11-01 | S | 7.5 | F | F | 4000 | 572 | 3428 | 0.2453 | 0.1924 |
|
998
997
|
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
|
999
998
|
| T006 | 2016-11-01 | S | 7.6 | F | T | 1000 | 143 | 857 | 0.2453 | 0.1924 |
|
1000
999
|
| T007 | 2016-11-01 | S | 7.65 | T | F | 200 | 28 | 172 | 0.2453 | 0.1924 |
|
1001
|
-
#+
|
1000
|
+
#+end_EXAMPLE
|
1002
1001
|
|
1003
1002
|
The interesting thing about ~order_by~ is that, while it ignores groups in its
|
1004
1003
|
input, it adds group boundaries in the output table at those rows where the sort
|
@@ -1043,8 +1042,10 @@ the value is a symbol for one of several aggregating methods that
|
|
1043
1042
|
to the :price column so that the output shows the average price in each group.
|
1044
1043
|
The ~:shares~, ~:lp~, and ~:qp~ columns are summed, and the ~:any?~ aggregate is
|
1045
1044
|
applied to one of the boolean fields, that is, it is ~true~ if any of the values
|
1046
|
-
in that column are ~true~.
|
1047
|
-
|
1045
|
+
in that column are ~true~.
|
1046
|
+
|
1047
|
+
Note that the column names in the output of the aggregated columns have the
|
1048
|
+
name of the aggregating method pre-pended to the column name.
|
1048
1049
|
|
1049
1050
|
Here is a list of all the aggregate methods available. If the description
|
1050
1051
|
restricts the aggregate to particular column types, applying it to other types
|
@@ -1313,6 +1314,7 @@ table where the join expression is satisfied and augmented with nils otherwise.
|
|
1313
1314
|
Finally, a cross join outputs every row of ~tab_a~ augmented with every row of
|
1314
1315
|
~tab_b~, in other words, the Cartesian product of the two tables. If ~tab_a~ has
|
1315
1316
|
~N~ rows and ~tab_b~ has ~M~ rows, the output table will have ~N * M~ rows.
|
1317
|
+
So be careful lest you consume all your computer's memory.
|
1316
1318
|
|
1317
1319
|
#+HEADER: :colnames no :session readme :hlines yes :wrap EXAMPLE :exports both
|
1318
1320
|
#+BEGIN_SRC ruby
|
@@ -1350,10 +1352,10 @@ Finally, a cross join outputs every row of ~tab_a~ augmented with every row of
|
|
1350
1352
|
|
1351
1353
|
*** Set Operations
|
1352
1354
|
|
1353
|
-
~FatTable~ can perform several set operations on tables. In order for
|
1354
|
-
to be used this way, they must have the same number of columns with
|
1355
|
-
types or an exception will be raised. We'll call two tables that
|
1356
|
-
combining with set operations "set-compatible."
|
1355
|
+
~FatTable~ can perform several set operations on pairs of tables. In order for
|
1356
|
+
two tables to be used this way, they must have the same number of columns with
|
1357
|
+
the same types or an exception will be raised. We'll call two tables that
|
1358
|
+
qualify for combining with set operations "set-compatible."
|
1357
1359
|
|
1358
1360
|
We'll use the following two set-compatible tables in the examples. They each
|
1359
1361
|
have some duplicates and some group boundaries so you can see the effect of the
|
@@ -1765,9 +1767,9 @@ but ruby data structures, and for them, things such as alignment are irrelevant.
|
|
1765
1767
|
array of array,
|
1766
1768
|
|
1767
1769
|
These are all implemented by classes that inherit from ~FatTable::Formatter~
|
1768
|
-
class by defining about a dozen methods that get called at various places
|
1769
|
-
the construction of the output table. The idea is that more
|
1770
|
-
defined by adding additional classes.
|
1770
|
+
class by defining about a dozen methods that get called at various places
|
1771
|
+
during the construction of the output table. The idea is that more output
|
1772
|
+
formats can be defined by adding additional classes.
|
1771
1773
|
|
1772
1774
|
*** Table Locations
|
1773
1775
|
|
data/fat_table.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
lib = File.expand_path('../lib', __FILE__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'fat_table/version'
|
4
|
-
require 'fat_table/patches'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = 'fat_table'
|
@@ -10,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
10
9
|
spec.email = ['ded-law@ddoherty.net']
|
11
10
|
|
12
11
|
spec.summary = 'Provides tools for working with tables as a data type.'
|
13
|
-
spec.description = <<-DESC
|
12
|
+
spec.description = <<-DESC
|
14
13
|
FatTable is a gem that treats tables as a data type. It provides methods for
|
15
14
|
constructing tables from a variety of sources, building them row-by-row,
|
16
15
|
extracting rows, columns, and cells, and performing aggregate operations on
|
@@ -64,22 +63,22 @@ Gem::Specification.new do |spec|
|
|
64
63
|
spec.require_paths = ['lib']
|
65
64
|
spec.metadata['yard.run'] = 'yri' # use "yard" to build full HTML docs.
|
66
65
|
|
67
|
-
spec.add_development_dependency 'bundler'
|
66
|
+
spec.add_development_dependency 'bundler'
|
68
67
|
spec.add_development_dependency 'byebug'
|
69
68
|
spec.add_development_dependency 'pry'
|
70
69
|
spec.add_development_dependency 'pry-byebug'
|
71
70
|
spec.add_development_dependency 'pry-doc'
|
72
|
-
spec.add_development_dependency 'rake', '~>
|
71
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
73
72
|
spec.add_development_dependency 'redcarpet'
|
73
|
+
spec.add_development_dependency 'pg'
|
74
74
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
75
75
|
spec.add_development_dependency 'rubocop-rspec'
|
76
|
+
spec.add_development_dependency 'rubocop-performance'
|
76
77
|
spec.add_development_dependency 'simplecov'
|
77
78
|
|
78
79
|
spec.add_runtime_dependency 'activesupport', '>3.0'
|
79
|
-
spec.add_runtime_dependency 'fat_core', '
|
80
|
-
spec.add_runtime_dependency 'mysql2'
|
81
|
-
spec.add_runtime_dependency 'pg'
|
80
|
+
spec.add_runtime_dependency 'fat_core', '>= 4.9.0'
|
82
81
|
spec.add_runtime_dependency 'rainbow'
|
83
82
|
spec.add_runtime_dependency 'sequel'
|
84
|
-
spec.add_runtime_dependency '
|
83
|
+
spec.add_runtime_dependency 'gem-path'
|
85
84
|
end
|
data/lib/fat_table/column.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FatTable
|
2
4
|
# Column objects are a thin wrapper around an Array to allow columns to be
|
3
5
|
# summed and have other aggregate operations performed on them, but compacting
|
@@ -137,6 +139,19 @@ module FatTable
|
|
137
139
|
size - 1
|
138
140
|
end
|
139
141
|
|
142
|
+
# :category: Attributes
|
143
|
+
|
144
|
+
# Force the column to have String type and then convert all items to
|
145
|
+
# strings.
|
146
|
+
def force_to_string_type
|
147
|
+
# msg = "Can only force an empty column to String type"
|
148
|
+
# raise UserError, msg unless empty?
|
149
|
+
@type = 'String'
|
150
|
+
unless empty?
|
151
|
+
@items = items.map(&:to_s)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
140
155
|
##########################################################################
|
141
156
|
# Enumerable
|
142
157
|
##########################################################################
|
@@ -483,34 +498,55 @@ module FatTable
|
|
483
498
|
end
|
484
499
|
end
|
485
500
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
501
|
+
ISO_DATE_RE = %r{(?<yr>\d\d\d\d)[-\/]
|
502
|
+
(?<mo>\d\d?)[-\/]
|
503
|
+
(?<dy>\d\d?)\s*
|
504
|
+
(T?\s*\d\d:\d\d(:\d\d)?
|
505
|
+
([-+](\d\d?)(:\d\d?))?)?}x
|
506
|
+
|
507
|
+
AMR_DATE_RE = %r{(?<dy>\d\d?)[-/](?<mo>\d\d?)[-/](?<yr>\d\d\d\d)\s*
|
508
|
+
(?<tm>T\d\d:\d\d:\d\d(\+\d\d:\d\d)?)?}x
|
509
|
+
|
510
|
+
# A Date like 'Tue, 01 Nov 2016' or 'Tue 01 Nov 2016' or '01 Nov 2016'.
|
511
|
+
# These are emitted by Postgresql, so it makes from_sql constructor
|
512
|
+
# possible without special formatting of the dates.
|
513
|
+
INV_DATE_RE = %r{((mon|tue|wed|thu|fri|sat|sun)[a-zA-z]*,?)?\s+ # looks like dow
|
514
|
+
(?<dy>\d\d?)\s+ # one or two-digit day
|
515
|
+
(?<mo_name>[jfmasondJFMASOND][A-Za-z]{2,})\s+ # looks like a month name
|
516
|
+
(?<yr>\d\d\d\d) # and a 4-digit year
|
517
|
+
}xi
|
518
|
+
|
519
|
+
# Convert the val to a DateTime if it is either a DateTime, a Date, a Time, or a
|
492
520
|
# String that can be parsed as a DateTime, otherwise return nil. It only
|
493
|
-
# recognizes strings that contain a something like '2016-01-14' or
|
494
|
-
#
|
495
|
-
#
|
496
|
-
#
|
521
|
+
# recognizes strings that contain a something like '2016-01-14' or '2/12/1985'
|
522
|
+
# within them, otherwise DateTime.parse would treat many bare numbers as dates,
|
523
|
+
# such as '2841381', which it would recognize as a valid date, but the user
|
524
|
+
# probably does not intend it to be so treated.
|
497
525
|
def convert_to_date_time(val)
|
498
526
|
return val if val.is_a?(DateTime)
|
499
527
|
return val if val.is_a?(Date)
|
528
|
+
return val.to_datetime if val.is_a?(Time)
|
500
529
|
begin
|
501
|
-
|
502
|
-
return nil if
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
530
|
+
str = val.to_s.clean
|
531
|
+
return nil if str.blank?
|
532
|
+
|
533
|
+
if str.match(ISO_DATE_RE)
|
534
|
+
date = DateTime.parse(val)
|
535
|
+
elsif str =~ AMR_DATE_RE
|
536
|
+
date = DateTime.new(Regexp.last_match[:yr].to_i,
|
537
|
+
Regexp.last_match[:mo].to_i,
|
538
|
+
Regexp.last_match[:dy].to_i)
|
539
|
+
elsif str =~ INV_DATE_RE
|
540
|
+
mo = Date.mo_name_to_num(last_match[:mo_name])
|
541
|
+
date = DateTime.new(Regexp.last_match[:yr].to_i, mo,
|
542
|
+
Regexp.last_match[:dy].to_i)
|
507
543
|
else
|
508
544
|
return nil
|
509
545
|
end
|
510
|
-
val = val.to_date if
|
511
|
-
|
546
|
+
# val = val.to_date if
|
547
|
+
date.seconds_since_midnight.zero? ? date.to_date : date
|
512
548
|
rescue ArgumentError
|
513
|
-
|
549
|
+
nil
|
514
550
|
end
|
515
551
|
end
|
516
552
|
|
@@ -530,8 +566,8 @@ module FatTable
|
|
530
566
|
BigDecimal(val.to_s.clean)
|
531
567
|
when /\A[-+]?[\d]+\z/
|
532
568
|
val.to_i
|
533
|
-
when %r{\A([-+]?\d+)\s*[:/]\s*([-+]?\d+)\z}
|
534
|
-
Rational(
|
569
|
+
when %r{\A(?<nm>[-+]?\d+)\s*[:/]\s*(?<dn>[-+]?\d+)\z}
|
570
|
+
Rational(Regexp.last_match[:nm], Regexp.last_match[:dn])
|
535
571
|
end
|
536
572
|
end
|
537
573
|
|
data/lib/fat_table/db_handle.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Set and access a database by module-level methods.
|
2
4
|
module FatTable
|
3
5
|
class << self
|
@@ -20,8 +22,9 @@ module FatTable
|
|
20
22
|
#
|
21
23
|
# +adapter+::
|
22
24
|
# One of 'pg' (for Postgresql), 'mysql' or 'mysql2' (for Mysql), or
|
23
|
-
# 'sqlite' (for SQLite3)
|
24
|
-
#
|
25
|
+
# 'sqlite' (for SQLite3) (or any other adapter supported by the +Sequel+
|
26
|
+
# gem) to specify the driver to use. You may have to install the
|
27
|
+
# appropriate driver to make this work.
|
25
28
|
#
|
26
29
|
# +database+::
|
27
30
|
# The name of the database to access. There is no default for this.
|
@@ -55,8 +58,17 @@ module FatTable
|
|
55
58
|
# Set the dsn for Sequel
|
56
59
|
begin
|
57
60
|
self.handle = Sequel.connect(args)
|
58
|
-
rescue Sequel::
|
59
|
-
|
61
|
+
rescue Sequel::AdapterNotFound => ex
|
62
|
+
case ex.to_s
|
63
|
+
when /pg/
|
64
|
+
raise TransientError, 'You need to install the postgres adapter pg'
|
65
|
+
when /mysql/
|
66
|
+
raise TransientError, 'You need to install the mysql adapter'
|
67
|
+
when /sqlite/
|
68
|
+
raise TransientError, 'You need to install the sqlite adapter'
|
69
|
+
else
|
70
|
+
raise ex
|
71
|
+
end
|
60
72
|
end
|
61
73
|
handle
|
62
74
|
end
|
data/lib/fat_table/errors.rb
CHANGED
data/lib/fat_table/evaluator.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FatTable
|
2
4
|
# A Formatter is for use in Table output routines, and provides methods for
|
3
5
|
# adding group and table footers to the output and instructions for how the
|
@@ -73,7 +75,7 @@ module FatTable
|
|
73
75
|
false_color: 'none',
|
74
76
|
false_bgcolor: 'none',
|
75
77
|
underline: false,
|
76
|
-
blink: false
|
78
|
+
blink: false,
|
77
79
|
}
|
78
80
|
|
79
81
|
class_attribute :valid_colors
|
@@ -247,7 +249,7 @@ module FatTable
|
|
247
249
|
cols.each do |c|
|
248
250
|
hsh[c] = :avg
|
249
251
|
end
|
250
|
-
footer('Average', hsh)
|
252
|
+
footer('Average', **hsh)
|
251
253
|
end
|
252
254
|
|
253
255
|
# :category: Footers
|
@@ -258,7 +260,7 @@ module FatTable
|
|
258
260
|
cols.each do |c|
|
259
261
|
hsh[c] = :avg
|
260
262
|
end
|
261
|
-
gfooter('Group Average', hsh)
|
263
|
+
gfooter('Group Average', **hsh)
|
262
264
|
end
|
263
265
|
|
264
266
|
# :category: Footers
|
@@ -270,7 +272,7 @@ module FatTable
|
|
270
272
|
cols.each do |c|
|
271
273
|
hsh[c] = :min
|
272
274
|
end
|
273
|
-
footer('Minimum', hsh)
|
275
|
+
footer('Minimum', **hsh)
|
274
276
|
end
|
275
277
|
|
276
278
|
# :category: Footers
|
@@ -282,7 +284,7 @@ module FatTable
|
|
282
284
|
cols.each do |c|
|
283
285
|
hsh[c] = :min
|
284
286
|
end
|
285
|
-
gfooter('Group Minimum', hsh)
|
287
|
+
gfooter('Group Minimum', **hsh)
|
286
288
|
end
|
287
289
|
|
288
290
|
# :category: Footers
|
@@ -294,7 +296,7 @@ module FatTable
|
|
294
296
|
cols.each do |c|
|
295
297
|
hsh[c] = :max
|
296
298
|
end
|
297
|
-
footer('Maximum', hsh)
|
299
|
+
footer('Maximum', **hsh)
|
298
300
|
end
|
299
301
|
|
300
302
|
# :category: Footers
|
@@ -306,7 +308,7 @@ module FatTable
|
|
306
308
|
cols.each do |c|
|
307
309
|
hsh[c] = :max
|
308
310
|
end
|
309
|
-
gfooter('Group Maximum', hsh)
|
311
|
+
gfooter('Group Maximum', **hsh)
|
310
312
|
end
|
311
313
|
|
312
314
|
# :category: Formatting
|
@@ -418,7 +420,7 @@ module FatTable
|
|
418
420
|
# \n\[niltext\]:: render a nil item with the given text.
|
419
421
|
def format(**fmts)
|
420
422
|
%i[header bfirst gfirst body footer gfooter].each do |loc|
|
421
|
-
format_for(loc, fmts)
|
423
|
+
format_for(loc, **fmts)
|
422
424
|
end
|
423
425
|
self
|
424
426
|
end
|
@@ -577,9 +579,9 @@ module FatTable
|
|
577
579
|
# parse, we remove the matched construct from fmt. At the end, any
|
578
580
|
# remaining characters in fmt should be invalid.
|
579
581
|
fmt_hash = {}
|
580
|
-
if fmt =~ /c\[(
|
581
|
-
fmt_hash[:color] =
|
582
|
-
fmt_hash[:bgcolor] =
|
582
|
+
if fmt =~ /c\[(?<co>#{CLR_RE})(\.(?<bg>#{CLR_RE}))?\]/
|
583
|
+
fmt_hash[:color] = Regexp.last_match[:co] unless Regexp.last_match[:co].blank?
|
584
|
+
fmt_hash[:bgcolor] = Regexp.last_match[:bg] unless Regexp.last_match[:bg].blank?
|
583
585
|
validate_color(fmt_hash[:color])
|
584
586
|
validate_color(fmt_hash[:bgcolor])
|
585
587
|
fmt = fmt.sub($&, '')
|
@@ -599,12 +601,12 @@ module FatTable
|
|
599
601
|
fmt_hash[:case] = :title
|
600
602
|
fmt = fmt.sub($&, '')
|
601
603
|
end
|
602
|
-
if fmt =~ /(
|
603
|
-
fmt_hash[:bold] =
|
604
|
+
if fmt =~ /(?<neg>~\s*)?B/
|
605
|
+
fmt_hash[:bold] = !Regexp.last_match[:neg]
|
604
606
|
fmt = fmt.sub($&, '')
|
605
607
|
end
|
606
|
-
if fmt =~ /(
|
607
|
-
fmt_hash[:italic] =
|
608
|
+
if fmt =~ /(?<neg>~\s*)?I/
|
609
|
+
fmt_hash[:italic] = !Regexp.last_match[:neg]
|
608
610
|
fmt = fmt.sub($&, '')
|
609
611
|
end
|
610
612
|
if fmt =~ /R/
|
@@ -619,12 +621,12 @@ module FatTable
|
|
619
621
|
fmt_hash[:alignment] = :left
|
620
622
|
fmt = fmt.sub($&, '')
|
621
623
|
end
|
622
|
-
if fmt =~ /(
|
623
|
-
fmt_hash[:underline] =
|
624
|
+
if fmt =~ /(?<neg>~\s*)?_/
|
625
|
+
fmt_hash[:underline] = !Regexp.last_match[:neg]
|
624
626
|
fmt = fmt.sub($&, '')
|
625
627
|
end
|
626
|
-
if fmt =~ /(
|
627
|
-
fmt_hash[:blink] =
|
628
|
+
if fmt =~ /(?<neg>~\s*)?\*/
|
629
|
+
fmt_hash[:blink] = !Regexp.last_match[:neg]
|
628
630
|
fmt = fmt.sub($&, '')
|
629
631
|
end
|
630
632
|
[fmt_hash, fmt]
|
@@ -639,9 +641,9 @@ module FatTable
|
|
639
641
|
# parse, we remove the matched construct from fmt. At the end, any
|
640
642
|
# remaining characters in fmt should be invalid.
|
641
643
|
fmt_hash = {}
|
642
|
-
if fmt =~ /n\[\s*([^\]]*)\s*\]/
|
643
|
-
fmt_hash[:nil_text] =
|
644
|
-
fmt = fmt.sub(
|
644
|
+
if fmt =~ /n\[\s*(?<bdy>[^\]]*)\s*\]/
|
645
|
+
fmt_hash[:nil_text] = Regexp.last_match[:bdy].clean
|
646
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
645
647
|
end
|
646
648
|
[fmt_hash, fmt]
|
647
649
|
end
|
@@ -655,22 +657,22 @@ module FatTable
|
|
655
657
|
# parse, we remove the matched construct from fmt. At the end, any
|
656
658
|
# remaining characters in fmt should be invalid.
|
657
659
|
fmt_hash, fmt = parse_str_fmt(fmt)
|
658
|
-
if fmt =~ /(
|
659
|
-
fmt_hash[:pre_digits] =
|
660
|
-
fmt_hash[:post_digits] =
|
661
|
-
fmt = fmt.sub(
|
660
|
+
if fmt =~ /(?<pre>\d+).(?<post>\d+)/
|
661
|
+
fmt_hash[:pre_digits] = Regexp.last_match[:pre].to_i
|
662
|
+
fmt_hash[:post_digits] = Regexp.last_match[:post].to_i
|
663
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
662
664
|
end
|
663
|
-
if fmt =~ /(
|
664
|
-
fmt_hash[:commas] =
|
665
|
-
fmt = fmt.sub(
|
665
|
+
if fmt =~ /(?<neg>~\s*)?,/
|
666
|
+
fmt_hash[:commas] = !Regexp.last_match[:neg]
|
667
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
666
668
|
end
|
667
|
-
if fmt =~ /(
|
668
|
-
fmt_hash[:currency] =
|
669
|
-
fmt = fmt.sub(
|
669
|
+
if fmt =~ /(?<neg>~\s*)?\$/
|
670
|
+
fmt_hash[:currency] = !Regexp.last_match[:neg]
|
671
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
670
672
|
end
|
671
|
-
if fmt =~ /(
|
672
|
-
fmt_hash[:hms] =
|
673
|
-
fmt = fmt.sub(
|
673
|
+
if fmt =~ /(?<neg>~\s*)?H/
|
674
|
+
fmt_hash[:hms] = !Regexp.last_match[:neg]
|
675
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
674
676
|
end
|
675
677
|
unless fmt.blank? || !strict
|
676
678
|
raise UserError, "unrecognized numeric formatting instructions '#{fmt}'"
|
@@ -688,13 +690,13 @@ module FatTable
|
|
688
690
|
# parse, we remove the matched construct from fmt. At the end, any
|
689
691
|
# remaining characters in fmt should be invalid.
|
690
692
|
fmt_hash, fmt = parse_str_fmt(fmt)
|
691
|
-
if fmt =~ /d\[([^\]]*)\]/
|
692
|
-
fmt_hash[:date_fmt] =
|
693
|
-
fmt = fmt.sub(
|
693
|
+
if fmt =~ /d\[(?<bdy>[^\]]*)\]/
|
694
|
+
fmt_hash[:date_fmt] = Regexp.last_match[:bdy]
|
695
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
694
696
|
end
|
695
|
-
if fmt =~ /D\[([^\]]*)\]/
|
696
|
-
fmt_hash[:date_fmt] =
|
697
|
-
fmt = fmt.sub(
|
697
|
+
if fmt =~ /D\[(?<bdy>[^\]]*)\]/
|
698
|
+
fmt_hash[:date_fmt] = Regexp.last_match[:bdy]
|
699
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
698
700
|
end
|
699
701
|
unless fmt.blank? || !strict
|
700
702
|
msg = "unrecognized datetime formatting instructions '#{fmt}'"
|
@@ -712,37 +714,38 @@ module FatTable
|
|
712
714
|
# parse, we remove the matched construct from fmt. At the end, any
|
713
715
|
# remaining characters in fmt should be invalid.
|
714
716
|
fmt_hash = {}
|
715
|
-
if fmt =~ /b\[\s*([^\],]*),([^\]]*)\s*\]/
|
716
|
-
fmt_hash[:true_text] =
|
717
|
-
fmt_hash[:false_text] =
|
718
|
-
fmt = fmt.sub(
|
717
|
+
if fmt =~ /b\[\s*(?<t>[^\],]*),(?<f>[^\]]*)\s*\]/
|
718
|
+
fmt_hash[:true_text] = Regexp.last_match[:t].clean
|
719
|
+
fmt_hash[:false_text] = Regexp.last_match[:f].clean
|
720
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
719
721
|
end
|
720
722
|
# Since true_text, false_text and nil_text may want to have internal
|
721
723
|
# spaces, defer removing extraneous spaces until after they are parsed.
|
722
724
|
if fmt =~ /c\[(#{CLR_RE})(\.(#{CLR_RE}))?,
|
723
725
|
\s*(#{CLR_RE})(\.(#{CLR_RE}))?\]/x
|
724
|
-
|
725
|
-
fmt_hash[:
|
726
|
-
fmt_hash[:
|
727
|
-
fmt_hash[:
|
728
|
-
|
726
|
+
tco, _, tbg, fco, _, fbg = Regexp.last_match.captures
|
727
|
+
fmt_hash[:true_color] = tco unless tco.blank?
|
728
|
+
fmt_hash[:true_bgcolor] = tbg unless tbg.blank?
|
729
|
+
fmt_hash[:false_color] = fco unless fco.blank?
|
730
|
+
fmt_hash[:false_bgcolor] = fbg unless fbg.blank?
|
731
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
729
732
|
end
|
730
733
|
str_fmt_hash, fmt = parse_str_fmt(fmt)
|
731
734
|
fmt_hash = fmt_hash.merge(str_fmt_hash)
|
732
735
|
if fmt =~ /Y/
|
733
736
|
fmt_hash[:true_text] = 'Y'
|
734
737
|
fmt_hash[:false_text] = 'N'
|
735
|
-
fmt = fmt.sub(
|
738
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
736
739
|
end
|
737
740
|
if fmt =~ /T/
|
738
741
|
fmt_hash[:true_text] = 'T'
|
739
742
|
fmt_hash[:false_text] = 'F'
|
740
|
-
fmt = fmt.sub(
|
743
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
741
744
|
end
|
742
745
|
if fmt =~ /X/
|
743
746
|
fmt_hash[:true_text] = 'X'
|
744
747
|
fmt_hash[:false_text] = ''
|
745
|
-
fmt = fmt.sub(
|
748
|
+
fmt = fmt.sub(Regexp.last_match[0], '')
|
746
749
|
end
|
747
750
|
unless fmt.blank? || !strict
|
748
751
|
raise UserError, "unrecognized boolean formatting instructions '#{fmt}'"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rainbow'
|
2
4
|
|
3
5
|
module FatTable
|
@@ -26,10 +28,10 @@ module FatTable
|
|
26
28
|
super
|
27
29
|
@options[:unicode] = options.fetch(:unicode, true)
|
28
30
|
@options[:framecolor] = options.fetch(:framecolor, 'none.none')
|
29
|
-
return unless @options[:framecolor] =~ /([-_a-zA-Z]*)(\.([-_a-zA-Z]*))/
|
31
|
+
return unless @options[:framecolor] =~ /(?<co>[-_a-zA-Z]*)(\.(?<bg>[-_a-zA-Z]*))/
|
30
32
|
|
31
|
-
@options[:frame_fg] =
|
32
|
-
@options[:frame_bg] =
|
33
|
+
@options[:frame_fg] = Regexp.last_match[:co].downcase unless Regexp.last_match[:co].blank?
|
34
|
+
@options[:frame_bg] = Regexp.last_match[:bg].downcase unless Regexp.last_match[:bg].blank?
|
33
35
|
end
|
34
36
|
|
35
37
|
# Valid colors for ANSI terminal using the rainbow gem's X11ColorNames.
|
@@ -101,18 +103,18 @@ module FatTable
|
|
101
103
|
# Unicode line-drawing characters. We use double lines before and after the
|
102
104
|
# table and single lines for the sides and hlines between groups and
|
103
105
|
# footers.
|
104
|
-
UPPER_LEFT = "\u2552"
|
105
|
-
UPPER_RIGHT = "\u2555"
|
106
|
-
DOUBLE_RULE = "\u2550"
|
107
|
-
UPPER_TEE = "\u2564"
|
108
|
-
VERTICAL_RULE = "\u2502"
|
109
|
-
LEFT_TEE = "\u251C"
|
110
|
-
HORIZONTAL_RULE = "\u2500"
|
111
|
-
SINGLE_CROSS = "\u253C"
|
112
|
-
RIGHT_TEE = "\u2524"
|
113
|
-
LOWER_LEFT = "\u2558"
|
114
|
-
LOWER_RIGHT = "\u255B"
|
115
|
-
LOWER_TEE = "\u2567"
|
106
|
+
UPPER_LEFT = "\u2552"
|
107
|
+
UPPER_RIGHT = "\u2555"
|
108
|
+
DOUBLE_RULE = "\u2550"
|
109
|
+
UPPER_TEE = "\u2564"
|
110
|
+
VERTICAL_RULE = "\u2502"
|
111
|
+
LEFT_TEE = "\u251C"
|
112
|
+
HORIZONTAL_RULE = "\u2500"
|
113
|
+
SINGLE_CROSS = "\u253C"
|
114
|
+
RIGHT_TEE = "\u2524"
|
115
|
+
LOWER_LEFT = "\u2558"
|
116
|
+
LOWER_RIGHT = "\u255B"
|
117
|
+
LOWER_TEE = "\u2567"
|
116
118
|
# :startdoc:
|
117
119
|
|
118
120
|
def upper_left
|
data/lib/fat_table/formatters.rb
CHANGED
data/lib/fat_table/patches.rb
CHANGED
data/lib/fat_table/table.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module FatTable
|
2
4
|
# A container for a two-dimensional table. All cells in the table must be a
|
3
5
|
# String, a DateTime (or Date), a Numeric (Bignum, Integer, or BigDecimal), or
|
@@ -156,7 +158,8 @@ module FatTable
|
|
156
158
|
raise UserError, msg if FatTable.db.nil?
|
157
159
|
|
158
160
|
result = Table.new
|
159
|
-
FatTable.db[query]
|
161
|
+
rows = FatTable.db[query]
|
162
|
+
rows.each do |h|
|
160
163
|
result << h
|
161
164
|
end
|
162
165
|
result
|
@@ -295,6 +298,8 @@ module FatTable
|
|
295
298
|
# :category: Attributes
|
296
299
|
|
297
300
|
# Return the table's Column with the given +key+ as its header.
|
301
|
+
# @param key [Symbol] symbol for header of column to return
|
302
|
+
# @return [FatTable::Column]
|
298
303
|
def column(key)
|
299
304
|
columns.detect { |c| c.header == key.as_sym }
|
300
305
|
end
|
@@ -309,6 +314,15 @@ module FatTable
|
|
309
314
|
|
310
315
|
# :category: Attributes
|
311
316
|
|
317
|
+
# Set the column type for Column with the given +key+ as a String type,
|
318
|
+
# but only if empty. Otherwise, we would have to worry about converting
|
319
|
+
# existing items in the column to String. Perhaps that's a TODO.
|
320
|
+
def set_column_to_string_type(key)
|
321
|
+
column(key).force_to_string_type
|
322
|
+
end
|
323
|
+
|
324
|
+
# :category: Attributes
|
325
|
+
|
312
326
|
# Return the array of items of the column with the given header symbol
|
313
327
|
# +key+, or if +key+ is an Integer, return that row at that index. So a
|
314
328
|
# table's rows can be accessed by number, and its columns can be accessed by
|
@@ -880,10 +894,7 @@ module FatTable
|
|
880
894
|
# Apply the set operation given by ~oper~ between this table and the other
|
881
895
|
# table given in the first argument. If distinct is true, eliminate
|
882
896
|
# duplicates from the result.
|
883
|
-
def set_operation(other, oper = :+,
|
884
|
-
distinct: true,
|
885
|
-
add_boundaries: true,
|
886
|
-
inherit_boundaries: false)
|
897
|
+
def set_operation(other, oper = :+, distinct: true, add_boundaries: true, inherit_boundaries: false)
|
887
898
|
unless columns.size == other.columns.size
|
888
899
|
msg = "can't apply set ops to tables with a different number of columns"
|
889
900
|
raise UserError, msg
|
@@ -981,6 +992,11 @@ module FatTable
|
|
981
992
|
#
|
982
993
|
# Any groups present in either Table are eliminated in the output Table. See
|
983
994
|
# the README for examples.
|
995
|
+
# @param other [FatTable::Table] table to join with self
|
996
|
+
# @param exps [Array<String>, Array<Symbol>] table to join with self
|
997
|
+
# @param join_type [Array<String>, Array<Symbol>] type of join :inner, :left, :right, :full, :cross
|
998
|
+
# @return [FatTable::Table] result of joining self to other
|
999
|
+
#
|
984
1000
|
def join(other, *exps, join_type: :inner)
|
985
1001
|
unless other.is_a?(Table)
|
986
1002
|
raise UserError, 'need other table as first argument to join'
|
@@ -1083,10 +1099,10 @@ module FatTable
|
|
1083
1099
|
# Translate any remaining row_b heads to append '_b' if they have the
|
1084
1100
|
# same name as a row_a key.
|
1085
1101
|
a_heads = row_a.keys
|
1086
|
-
row_b = row_b.to_a.each.map
|
1102
|
+
row_b = row_b.to_a.each.map do |k, v|
|
1087
1103
|
[a_heads.include?(k) ? "#{k}_b".to_sym : k, v]
|
1088
|
-
|
1089
|
-
row_a.merge(row_b)
|
1104
|
+
end
|
1105
|
+
row_a.merge(row_b.to_h)
|
1090
1106
|
end
|
1091
1107
|
|
1092
1108
|
# Return a hash for the local variables of a join expression in which all
|
@@ -1125,7 +1141,7 @@ module FatTable
|
|
1125
1141
|
[nat_exp, common_heads]
|
1126
1142
|
end
|
1127
1143
|
else
|
1128
|
-
# We have expressions to evaluate
|
1144
|
+
# We have join expressions to evaluate
|
1129
1145
|
and_conds = []
|
1130
1146
|
partial_result = nil
|
1131
1147
|
last_sym = nil
|
@@ -1133,8 +1149,8 @@ module FatTable
|
|
1133
1149
|
case exp
|
1134
1150
|
when Symbol
|
1135
1151
|
case exp.to_s.clean
|
1136
|
-
when /\A(
|
1137
|
-
a_head =
|
1152
|
+
when /\A(?<sy>.*)_a\z/
|
1153
|
+
a_head = Regexp.last_match[:sy].to_sym
|
1138
1154
|
unless a_heads.include?(a_head)
|
1139
1155
|
raise UserError, "no column '#{a_head}' in table"
|
1140
1156
|
end
|
@@ -1149,11 +1165,11 @@ module FatTable
|
|
1149
1165
|
partial_result = nil
|
1150
1166
|
else
|
1151
1167
|
# First of a pair of _a or _b
|
1152
|
-
partial_result = "(#{a_head}_a == "
|
1168
|
+
partial_result = String.new("(#{a_head}_a == ")
|
1153
1169
|
end
|
1154
1170
|
last_sym = a_head
|
1155
|
-
when /\A(
|
1156
|
-
b_head =
|
1171
|
+
when /\A(?<sy>.*)_b\z/
|
1172
|
+
b_head = Regexp.last_match[:sy].to_sym
|
1157
1173
|
unless b_heads.include?(b_head)
|
1158
1174
|
raise UserError, "no column '#{b_head}' in second table"
|
1159
1175
|
end
|
@@ -1168,7 +1184,7 @@ module FatTable
|
|
1168
1184
|
partial_result = nil
|
1169
1185
|
else
|
1170
1186
|
# First of a pair of _a or _b
|
1171
|
-
partial_result = "(#{b_head}_b == "
|
1187
|
+
partial_result = String.new("(#{b_head}_b == ")
|
1172
1188
|
end
|
1173
1189
|
b_common_heads << b_head
|
1174
1190
|
last_sym = b_head
|
@@ -1275,6 +1291,15 @@ module FatTable
|
|
1275
1291
|
|
1276
1292
|
# :category: Constructors
|
1277
1293
|
|
1294
|
+
# Add a group boundary mark at the given row, or at the end of the table
|
1295
|
+
# by default.
|
1296
|
+
def add_boundary(at_row = nil)
|
1297
|
+
row = at_row || (size - 1)
|
1298
|
+
@boundaries << row
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
# :category: Constructors
|
1302
|
+
|
1278
1303
|
# Add a +row+ represented by a Hash having the headers as keys. If +mark:+
|
1279
1304
|
# is set true, mark this row as a boundary. All tables should be built
|
1280
1305
|
# ultimately using this method as a primitive.
|
@@ -1284,7 +1309,7 @@ module FatTable
|
|
1284
1309
|
columns << Column.new(header: k) unless column?(k)
|
1285
1310
|
column(key) << v
|
1286
1311
|
end
|
1287
|
-
|
1312
|
+
add_boundary if mark
|
1288
1313
|
self
|
1289
1314
|
end
|
1290
1315
|
|
@@ -1353,9 +1378,9 @@ module FatTable
|
|
1353
1378
|
|
1354
1379
|
method = "to_#{fmt}"
|
1355
1380
|
if block_given?
|
1356
|
-
send
|
1381
|
+
send(method, options, &Proc.new)
|
1357
1382
|
else
|
1358
|
-
send
|
1383
|
+
send(method, options)
|
1359
1384
|
end
|
1360
1385
|
end
|
1361
1386
|
|
@@ -1369,7 +1394,7 @@ module FatTable
|
|
1369
1394
|
# default format for Formatter, there is no class PsvFormatter as you might
|
1370
1395
|
# expect.
|
1371
1396
|
def to_psv(options = {})
|
1372
|
-
fmt = Formatter.new(self, options)
|
1397
|
+
fmt = Formatter.new(self, **options)
|
1373
1398
|
yield fmt if block_given?
|
1374
1399
|
fmt.output
|
1375
1400
|
end
|
@@ -1382,7 +1407,7 @@ module FatTable
|
|
1382
1407
|
# the block to which formatting instructions and footers can be added by
|
1383
1408
|
# calling methods on it.
|
1384
1409
|
def to_aoa(options = {})
|
1385
|
-
fmt = FatTable::AoaFormatter.new(self, options)
|
1410
|
+
fmt = FatTable::AoaFormatter.new(self, **options)
|
1386
1411
|
yield fmt if block_given?
|
1387
1412
|
fmt.output
|
1388
1413
|
end
|
@@ -1396,7 +1421,7 @@ module FatTable
|
|
1396
1421
|
# given, it yields an AohFormatter to the block to which formatting
|
1397
1422
|
# instructions and footers can be added by calling methods on it.
|
1398
1423
|
def to_aoh(options = {})
|
1399
|
-
fmt = AohFormatter.new(self, options)
|
1424
|
+
fmt = AohFormatter.new(self, **options)
|
1400
1425
|
yield fmt if block_given?
|
1401
1426
|
fmt.output
|
1402
1427
|
end
|
@@ -1409,7 +1434,7 @@ module FatTable
|
|
1409
1434
|
# LaTeXFormatter to the block to which formatting instructions and footers
|
1410
1435
|
# can be added by calling methods on it.
|
1411
1436
|
def to_latex(options = {})
|
1412
|
-
fmt = LaTeXFormatter.new(self, options)
|
1437
|
+
fmt = LaTeXFormatter.new(self, **options)
|
1413
1438
|
yield fmt if block_given?
|
1414
1439
|
fmt.output
|
1415
1440
|
end
|
@@ -1422,7 +1447,7 @@ module FatTable
|
|
1422
1447
|
# OrgFormatter to the block to which formatting instructions and footers can
|
1423
1448
|
# be added by calling methods on it.
|
1424
1449
|
def to_org(options = {})
|
1425
|
-
fmt = OrgFormatter.new(self, options)
|
1450
|
+
fmt = OrgFormatter.new(self, **options)
|
1426
1451
|
yield fmt if block_given?
|
1427
1452
|
fmt.output
|
1428
1453
|
end
|
@@ -1435,7 +1460,7 @@ module FatTable
|
|
1435
1460
|
# given, it yields a TermFormatter to the block to which formatting
|
1436
1461
|
# instructions and footers can be added by calling methods on it.
|
1437
1462
|
def to_term(options = {})
|
1438
|
-
fmt = TermFormatter.new(self, options)
|
1463
|
+
fmt = TermFormatter.new(self, **options)
|
1439
1464
|
yield fmt if block_given?
|
1440
1465
|
fmt.output
|
1441
1466
|
end
|
@@ -1449,7 +1474,7 @@ module FatTable
|
|
1449
1474
|
# footers can be added by calling methods on it.
|
1450
1475
|
# @return [String]
|
1451
1476
|
def to_text(options = {})
|
1452
|
-
fmt = TextFormatter.new(self, options)
|
1477
|
+
fmt = TextFormatter.new(self, **options)
|
1453
1478
|
yield fmt if block_given?
|
1454
1479
|
fmt.output
|
1455
1480
|
end
|
data/lib/fat_table/version.rb
CHANGED
data/lib/fat_table.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This module provides objects for treating tables as a data type on which you
|
2
4
|
# can (1) perform operations, such as select, where, join, and others and (2)
|
3
5
|
# output the tables in several formats, including text, ANSI terminal, LaTeX,
|
@@ -24,6 +26,15 @@ module FatTable
|
|
24
26
|
require 'fat_table/db_handle'
|
25
27
|
require 'fat_table/errors'
|
26
28
|
|
29
|
+
# Add paths for common db gems to the load paths
|
30
|
+
%w[pg mysql2 sqlite].each do |gem_name|
|
31
|
+
path = Dir.glob("#{ENV['GEM_HOME']}/gems/#{gem_name}*").sort.last
|
32
|
+
if path
|
33
|
+
path = File.join(path, 'lib')
|
34
|
+
$LOAD_PATH << path unless $LOAD_PATH.include?(path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
27
38
|
# Valid output formats as symbols.
|
28
39
|
FORMATS = %i[psv aoa aoh latex org term text].freeze
|
29
40
|
|
@@ -149,9 +160,9 @@ module FatTable
|
|
149
160
|
raise UserError, "unknown format '#{fmt}'" unless FORMATS.include?(fmt)
|
150
161
|
method = "to_#{fmt}"
|
151
162
|
if block_given?
|
152
|
-
send
|
163
|
+
send(method, table, options, &Proc.new)
|
153
164
|
else
|
154
|
-
send
|
165
|
+
send(method, table, options)
|
155
166
|
end
|
156
167
|
end
|
157
168
|
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fat_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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:
|
11
|
+
date: 2022-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: byebug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '13.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '13.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: redcarpet
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pg
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rspec
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,7 +151,7 @@ dependencies:
|
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
154
|
+
name: rubocop-performance
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - ">="
|
@@ -151,67 +165,47 @@ dependencies:
|
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: '3.0'
|
160
|
-
type: :runtime
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - ">"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: '3.0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: fat_core
|
168
|
+
name: simplecov
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
|
-
- - "~>"
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: '4.0'
|
174
171
|
- - ">="
|
175
172
|
- !ruby/object:Gem::Version
|
176
|
-
version: '
|
177
|
-
type: :
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
178
175
|
prerelease: false
|
179
176
|
version_requirements: !ruby/object:Gem::Requirement
|
180
177
|
requirements:
|
181
|
-
- - "~>"
|
182
|
-
- !ruby/object:Gem::Version
|
183
|
-
version: '4.0'
|
184
178
|
- - ">="
|
185
179
|
- !ruby/object:Gem::Version
|
186
|
-
version: '
|
180
|
+
version: '0'
|
187
181
|
- !ruby/object:Gem::Dependency
|
188
|
-
name:
|
182
|
+
name: activesupport
|
189
183
|
requirement: !ruby/object:Gem::Requirement
|
190
184
|
requirements:
|
191
|
-
- - "
|
185
|
+
- - ">"
|
192
186
|
- !ruby/object:Gem::Version
|
193
|
-
version: '0'
|
187
|
+
version: '3.0'
|
194
188
|
type: :runtime
|
195
189
|
prerelease: false
|
196
190
|
version_requirements: !ruby/object:Gem::Requirement
|
197
191
|
requirements:
|
198
|
-
- - "
|
192
|
+
- - ">"
|
199
193
|
- !ruby/object:Gem::Version
|
200
|
-
version: '0'
|
194
|
+
version: '3.0'
|
201
195
|
- !ruby/object:Gem::Dependency
|
202
|
-
name:
|
196
|
+
name: fat_core
|
203
197
|
requirement: !ruby/object:Gem::Requirement
|
204
198
|
requirements:
|
205
199
|
- - ">="
|
206
200
|
- !ruby/object:Gem::Version
|
207
|
-
version:
|
201
|
+
version: 4.9.0
|
208
202
|
type: :runtime
|
209
203
|
prerelease: false
|
210
204
|
version_requirements: !ruby/object:Gem::Requirement
|
211
205
|
requirements:
|
212
206
|
- - ">="
|
213
207
|
- !ruby/object:Gem::Version
|
214
|
-
version:
|
208
|
+
version: 4.9.0
|
215
209
|
- !ruby/object:Gem::Dependency
|
216
210
|
name: rainbow
|
217
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,7 +235,7 @@ dependencies:
|
|
241
235
|
- !ruby/object:Gem::Version
|
242
236
|
version: '0'
|
243
237
|
- !ruby/object:Gem::Dependency
|
244
|
-
name:
|
238
|
+
name: gem-path
|
245
239
|
requirement: !ruby/object:Gem::Requirement
|
246
240
|
requirements:
|
247
241
|
- - ">="
|
@@ -264,7 +258,8 @@ description: |2
|
|
264
258
|
duplicate rows, group_by for aggregating multiple rows into single rows and
|
265
259
|
applying column aggregate methods to ungrouped columns, a collection of join
|
266
260
|
methods for combining tables, and more.
|
267
|
-
|
261
|
+
|
262
|
+
Furthermore, FatTable provides methods for formatting tables and producing
|
268
263
|
output that targets various output media: text, ANSI terminals, ruby data
|
269
264
|
structures, LaTeX tables, Emacs org-mode tables, and more. The formatting
|
270
265
|
methods can specify cell formatting in a way that is uniform across all the
|
@@ -272,7 +267,8 @@ description: |2
|
|
272
267
|
including group footers. FatTable applies formatting directives to the extent
|
273
268
|
they makes sense for the output medium and treats other formatting directives as
|
274
269
|
no-ops.
|
275
|
-
|
270
|
+
|
271
|
+
FatTable can be used to perform operations on data that are naturally best
|
276
272
|
conceived of as tables, which in my experience is quite often. It can also serve
|
277
273
|
as a foundation for providing reporting functions where flexibility about the
|
278
274
|
output medium can be quite useful. Finally FatTable can be used within Emacs
|
@@ -290,6 +286,7 @@ extra_rdoc_files: []
|
|
290
286
|
files:
|
291
287
|
- ".gitignore"
|
292
288
|
- ".rspec"
|
289
|
+
- ".rubocop.yml"
|
293
290
|
- ".travis.yml"
|
294
291
|
- ".yardopts"
|
295
292
|
- Gemfile
|
@@ -323,7 +320,7 @@ licenses: []
|
|
323
320
|
metadata:
|
324
321
|
allowed_push_host: https://rubygems.org
|
325
322
|
yard.run: yri
|
326
|
-
post_install_message:
|
323
|
+
post_install_message:
|
327
324
|
rdoc_options: []
|
328
325
|
require_paths:
|
329
326
|
- lib
|
@@ -338,8 +335,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
338
335
|
- !ruby/object:Gem::Version
|
339
336
|
version: '0'
|
340
337
|
requirements: []
|
341
|
-
rubygems_version: 3.
|
342
|
-
signing_key:
|
338
|
+
rubygems_version: 3.3.3
|
339
|
+
signing_key:
|
343
340
|
specification_version: 4
|
344
341
|
summary: Provides tools for working with tables as a data type.
|
345
342
|
test_files: []
|