fat_core 1.0.3 → 1.2.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/.gitignore +1 -0
- data/.rubocop.yml +3 -0
- data/Rakefile +2 -2
- data/fat_core.gemspec +19 -18
- data/lib/core_extensions/date/fat_core.rb +0 -1
- data/lib/fat_core.rb +5 -1
- data/lib/fat_core/column.rb +197 -0
- data/lib/fat_core/date.rb +192 -132
- data/lib/fat_core/enumerable.rb +1 -1
- data/lib/fat_core/evaluator.rb +43 -0
- data/lib/fat_core/hash.rb +1 -1
- data/lib/fat_core/nil.rb +4 -0
- data/lib/fat_core/numeric.rb +30 -17
- data/lib/fat_core/period.rb +58 -67
- data/lib/fat_core/range.rb +20 -25
- data/lib/fat_core/string.rb +95 -55
- data/lib/fat_core/symbol.rb +12 -14
- data/lib/fat_core/table.rb +515 -0
- data/lib/fat_core/version.rb +2 -2
- data/spec/example_files/goldberg.org +199 -0
- data/spec/example_files/wpcs.csv +92 -0
- data/spec/lib/array_spec.rb +1 -1
- data/spec/lib/date_spec.rb +5 -6
- data/spec/lib/enumerable_spec.rb +1 -1
- data/spec/lib/evaluator_spec.rb +34 -0
- data/spec/lib/hash_spec.rb +5 -5
- data/spec/lib/kernel_spec.rb +3 -3
- data/spec/lib/nil_spec.rb +2 -2
- data/spec/lib/numeric_spec.rb +22 -22
- data/spec/lib/period_spec.rb +11 -12
- data/spec/lib/range_spec.rb +50 -50
- data/spec/lib/string_spec.rb +71 -74
- data/spec/lib/symbol_spec.rb +3 -3
- data/spec/lib/table_spec.rb +659 -0
- data/spec/spec_helper.rb +1 -1
- metadata +28 -2
data/spec/lib/symbol_spec.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Symbol do
|
4
|
-
it
|
4
|
+
it 'should be able to convert to a capitalized string' do
|
5
5
|
expect(:i_am_a_symbol.entitle).to eq 'I Am a Symbol'
|
6
6
|
expect(:i_am_a_symbol.to_string).to eq 'I Am a Symbol'
|
7
7
|
end
|
8
8
|
|
9
|
-
it
|
9
|
+
it 'should respond to tex_quote' do
|
10
10
|
expect(:i_am_a_symbol.tex_quote).to eq 'i\\_am\\_a\\_symbol'
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
13
|
+
it 'should respond to :as_sym as identity' do
|
14
14
|
expect(:i_am_a_symbol.as_sym).to eq :i_am_a_symbol
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,659 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FatCore
|
4
|
+
describe Table do
|
5
|
+
before :all do
|
6
|
+
@csv_file_body = <<-EOS
|
7
|
+
Ref,Date,Code,RawShares,Shares,Price,Info
|
8
|
+
1,2006-05-02,P,5000,5000,8.6000,2006-08-09-1-I
|
9
|
+
2,2006-05-03,P,5000,5000,8.4200,2006-08-09-1-I
|
10
|
+
3,2006-05-04,P,5000,5000,8.4000,2006-08-09-1-I
|
11
|
+
4,2006-05-10,P,8600,8600,8.0200,2006-08-09-1-D
|
12
|
+
5,2006-05-12,P,10000,10000,7.2500,2006-08-09-1-D
|
13
|
+
6,2006-05-12,P,2000,2000,6.7400,2006-08-09-1-I
|
14
|
+
7,2006-05-16,P,5000,5000,7.0000,2006-08-09-1-D
|
15
|
+
8,2006-05-17,P,5000,5000,6.7000,2006-08-09-1-D
|
16
|
+
9,2006-05-17,P,2000,2000,6.7400,2006-08-09-1-I
|
17
|
+
10,2006-05-19,P,1000,1000,7.2500,2006-08-09-1-I
|
18
|
+
11,2006-05-19,P,1000,1000,7.2500,2006-08-09-1-I
|
19
|
+
12,2006-05-31,P,2000,2000,7.9200,2006-08-09-1-I
|
20
|
+
13,2006-06-05,P,1000,1000,7.9200,2006-08-09-1-I
|
21
|
+
14,2006-06-15,P,5000,5000,6.9800,2006-08-09-1-I
|
22
|
+
15,2006-06-16,P,1000,1000,6.9300,2006-08-09-1-I
|
23
|
+
16,2006-06-16,P,1000,1000,6.9400,2006-08-09-1-I
|
24
|
+
17,2006-06-29,P,4000,4000,7.0000,2006-08-09-1-I
|
25
|
+
18,2006-07-14,P,2000,2000,6.2000,2006-08-09-1-D
|
26
|
+
19,2006-08-03,P,1400,1400,4.3900,2006-08-09-1-D
|
27
|
+
20,2006-08-07,P,1100,1100,4.5900,2006-08-09-1-D
|
28
|
+
21,2006-08-08,P,16000,16000,4.7000,2006-08-21-1-D
|
29
|
+
22,2006-08-08,S,16000,16000,4.7000,2006-08-21-1-I
|
30
|
+
23,2006-08-15,P,16000,16000,4.8000,2006-08-21-1-D
|
31
|
+
24,2006-08-15,S,16000,16000,4.8000,2006-08-21-1-I
|
32
|
+
25,2006-08-16,P,2100,2100,5.2900,2006-08-21-1-I
|
33
|
+
26,2006-08-17,P,2900,2900,5.7000,2006-08-21-1-I
|
34
|
+
27,2006-08-23,P,8000,8000,5.2400,2006-08-25-1-D
|
35
|
+
28,2006-08-23,S,8000,8000,5.2400,2006-08-29-1-I
|
36
|
+
29,2006-08-28,P,1000,1000,5.4000,2006-08-29-1-D
|
37
|
+
30,2006-08-29,P,2000,2000,5.4000,2006-08-30-1-D
|
38
|
+
31,2006-08-29,S,2000,2000,5.4000,2006-08-30-1-I
|
39
|
+
32,2006-09-05,P,2700,2700,5.7500,2006-09-06-1-I
|
40
|
+
33,2006-09-11,P,4000,4000,5.7200,2006-09-15-1-D
|
41
|
+
34,2006-09-11,S,4000,4000,5.7200,2006-09-15-1-I
|
42
|
+
35,2006-09-12,P,3000,3000,5.4800,2006-09-15-2-I
|
43
|
+
36,2006-09-13,P,1700,1700,5.4100,2006-09-15-2-I
|
44
|
+
37,2006-09-20,P,7500,7500,5.4900,2006-09-21-1-I
|
45
|
+
38,2006-12-07,S,6000,6000,7.8900,2006-12-11-1-I
|
46
|
+
39,2006-12-11,S,100,100,8.0000,2006-12-11-1-I
|
47
|
+
40,2007-01-29,P,2500,2500,12.1000,2007-04-27-1-I
|
48
|
+
41,2007-01-31,P,2500,2500,13.7000,2007-04-27-1-I
|
49
|
+
42,2007-02-02,P,4000,4000,15.1500,2007-04-27-1-I
|
50
|
+
43,2007-02-06,P,5000,5000,14.9500,2007-04-27-1-I
|
51
|
+
44,2007-02-07,P,400,400,15.0000,2007-04-27-1-I
|
52
|
+
45,2007-02-08,P,4600,4600,15.0000,2007-04-27-1-I
|
53
|
+
46,2007-02-12,P,3500,3500,14.9100,2007-04-27-1-I
|
54
|
+
47,2007-02-13,P,1500,1500,14.6500,2007-04-27-1-D
|
55
|
+
48,2007-02-14,P,2000,2000,14.4900,2007-04-27-1-D
|
56
|
+
49,2007-02-15,P,3000,3000,14.3000,2007-04-27-1-I
|
57
|
+
50,2007-02-21,P,8500,8500,14.6500,2007-04-27-1-D
|
58
|
+
51,2007-02-21,S,8500,8500,14.6500,2007-04-27-1-I
|
59
|
+
52,2007-02-22,P,1500,1500,14.8800,2007-04-27-1-I
|
60
|
+
53,2007-02-23,P,3000,3000,14.9700,2007-04-27-1-I
|
61
|
+
54,2007-02-23,P,5000,5000,14.9700,2007-04-27-1-I
|
62
|
+
55,2007-02-27,P,5200,5200,13.8800,2007-04-27-1-I
|
63
|
+
56,2007-02-28,P,6700,6700,13.0000,2007-04-27-1-D
|
64
|
+
57,2007-02-28,P,800,800,13.0000,2007-04-27-1-I
|
65
|
+
58,2007-02-28,P,8400,8400,13.0000,2007-04-27-1-I
|
66
|
+
59,2007-03-01,P,2500,2500,12.2500,2007-04-27-1-D
|
67
|
+
60,2007-03-05,P,1800,1800,11.9700,2007-04-27-1-D
|
68
|
+
61,2007-03-06,P,500,500,12.1300,2007-04-27-1-D
|
69
|
+
62,2007-03-07,P,3000,3000,12.3700,2007-04-27-1-D
|
70
|
+
63,2007-03-08,P,2000,2000,12.6000,2007-04-27-1-I
|
71
|
+
64,2007-03-09,P,7700,7700,12.8100,2007-04-27-1-I
|
72
|
+
65,2007-03-12,P,4200,4200,12.4600,2007-04-27-1-I
|
73
|
+
66,2007-03-13,P,800,800,12.2500,2007-04-27-1-I
|
74
|
+
67,2007-03-19,P,2000,2000,14.5500,2007-04-27-2-I
|
75
|
+
68,2007-03-19,P,5000,5000,14.5500,2007-04-27-2-I
|
76
|
+
69,2007-03-19,P,2000,2000,14.3300,2007-04-27-2-I
|
77
|
+
70,2007-03-20,P,1000,1000,14.4600,2007-04-27-2-I
|
78
|
+
71,2007-03-20,P,1500,1500,14.4600,2007-04-27-2-I
|
79
|
+
72,2007-03-21,P,3900,3900,16.9000,2007-04-27-2-I
|
80
|
+
73,2007-03-23,P,8000,8000,14.9700,2007-04-27-1-D
|
81
|
+
74,2007-03-27,P,1000,1000,16.9300,2007-04-27-2-I
|
82
|
+
75,2007-03-28,P,1000,1000,16.5000,2007-04-27-2-D
|
83
|
+
76,2007-03-29,P,1000,1000,16.2500,2007-04-27-2-D
|
84
|
+
77,2007-04-04,P,200,200,17.8600,2007-04-27-2-I
|
85
|
+
78,2007-04-04,P,2000,2000,19.5000,2007-04-27-2-I
|
86
|
+
79,2007-04-04,P,3000,3000,19.1300,2007-04-27-2-I
|
87
|
+
80,2007-04-05,P,1000,1000,19.1500,2007-04-27-2-I
|
88
|
+
81,2007-04-10,P,2000,2000,20.7500,2007-04-27-2-I
|
89
|
+
82,2007-04-11,P,1000,1000,20.5000,2007-04-27-2-I
|
90
|
+
83,2007-04-12,P,600,600,21.5000,2007-04-27-2-I
|
91
|
+
84,2007-04-12,P,1000,1000,21.4500,2007-04-27-2-I
|
92
|
+
85,2007-04-13,P,2100,2100,21.5000,2007-04-27-2-I
|
93
|
+
86,2007-04-16,P,500,500,22.6000,2007-04-27-2-I
|
94
|
+
87,2007-04-17,P,3500,3500,23.5500,2007-04-27-2-D
|
95
|
+
88,2007-04-17,S,3500,3500,23.5500,2007-04-27-2-I
|
96
|
+
89,2007-04-23,P,5000,5000,23.4500,2007-04-27-2-I
|
97
|
+
90,2007-04-24,P,5000,5000,24.3000,2007-04-27-2-I
|
98
|
+
91,2007-04-25,S,10000,10000,25.7000,2007-04-27-2-I
|
99
|
+
EOS
|
100
|
+
|
101
|
+
@org_file_body = <<-EOS
|
102
|
+
|
103
|
+
* Morgan Transactions
|
104
|
+
:PROPERTIES:
|
105
|
+
:TABLE_EXPORT_FILE: morgan.csv
|
106
|
+
:END:
|
107
|
+
|
108
|
+
#+TBLNAME: morgan_tab
|
109
|
+
| Ref | Date | Code | Raw | Shares | Price | Info |
|
110
|
+
|-----+------------+------+---------+--------+----------+--------|
|
111
|
+
| 29 | 2013-05-02 | P | 795,546 | 2,609 | 1.18500 | ZMPEF1 |
|
112
|
+
| 30 | 2013-05-02 | P | 118,186 | 388 | 11.85000 | ZMPEF1 |
|
113
|
+
| 31 | 2013-05-02 | P | 340,948 | 1,926 | 1.18500 | ZMPEF2 |
|
114
|
+
| 32 | 2013-05-02 | P | 50,651 | 286 | 11.85000 | ZMPEF2 |
|
115
|
+
| 33 | 2013-05-20 | S | 12,000 | 32 | 28.28040 | ZMEAC |
|
116
|
+
| 34 | 2013-05-20 | S | 85,000 | 226 | 28.32240 | ZMEAC |
|
117
|
+
| 35 | 2013-05-20 | S | 33,302 | 88 | 28.63830 | ZMEAC |
|
118
|
+
| 36 | 2013-05-23 | S | 8,000 | 21 | 27.10830 | ZMEAC |
|
119
|
+
| 37 | 2013-05-23 | S | 23,054 | 61 | 26.80150 | ZMEAC |
|
120
|
+
| 38 | 2013-05-23 | S | 39,906 | 106 | 25.17490 | ZMEAC |
|
121
|
+
| 39 | 2013-05-29 | S | 13,459 | 36 | 24.74640 | ZMEAC |
|
122
|
+
| 40 | 2013-05-29 | S | 15,700 | 42 | 24.77900 | ZMEAC |
|
123
|
+
| 41 | 2013-05-29 | S | 15,900 | 42 | 24.58020 | ZMEAC |
|
124
|
+
| 42 | 2013-05-30 | S | 6,679 | 18 | 25.04710 | ZMEAC |
|
125
|
+
|
126
|
+
* Another Heading
|
127
|
+
EOS
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'construction' do
|
131
|
+
it 'should be create-able from a CSV IO object' do
|
132
|
+
tab = Table.new(StringIO.new(@csv_file_body), '.csv')
|
133
|
+
expect(tab.class).to eq(Table)
|
134
|
+
expect(tab.rows.size).to be > 20
|
135
|
+
expect(tab.headers.sort)
|
136
|
+
.to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
|
137
|
+
tab.rows.each do |row|
|
138
|
+
row.each_pair do |k, _v|
|
139
|
+
expect(k.class).to eq Symbol
|
140
|
+
end
|
141
|
+
expect(row[:code].class).to eq String
|
142
|
+
expect(row[:date].class).to eq Date
|
143
|
+
expect(row[:shares].is_a?(Numeric)).to be true
|
144
|
+
unless row[:rawshares].nil?
|
145
|
+
expect(row[:rawshares].is_a?(Numeric)).to be true
|
146
|
+
end
|
147
|
+
expect(row[:price].is_a?(Numeric)).to be true
|
148
|
+
expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should be create-able from an Org IO object' do
|
153
|
+
tab = Table.new(StringIO.new(@org_file_body), '.org')
|
154
|
+
expect(tab.class).to eq(Table)
|
155
|
+
expect(tab.rows.size).to be > 10
|
156
|
+
expect(tab.headers.sort)
|
157
|
+
.to eq [:code, :date, :info, :price, :raw, :ref, :shares]
|
158
|
+
tab.rows.each do |row|
|
159
|
+
row.each_pair do |k, _v|
|
160
|
+
expect(k.class).to eq Symbol
|
161
|
+
end
|
162
|
+
expect(row[:code].class).to eq String
|
163
|
+
expect(row[:date].class).to eq Date
|
164
|
+
expect(row[:shares].is_a?(Numeric)).to be true
|
165
|
+
unless row[:rawshares].nil?
|
166
|
+
expect(row[:rawshares].is_a?(Numeric)).to be true
|
167
|
+
end
|
168
|
+
expect(row[:price].is_a?(BigDecimal)).to be true
|
169
|
+
expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
|
170
|
+
expect(row[:info].class).to eq String
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should be create-able from a CSV file' do
|
175
|
+
File.open('/tmp/junk.csv', 'w') { |f| f.write(@csv_file_body) }
|
176
|
+
tab = Table.new('/tmp/junk.csv')
|
177
|
+
expect(tab.class).to eq(Table)
|
178
|
+
expect(tab.rows.size).to be > 20
|
179
|
+
expect(tab.headers.sort)
|
180
|
+
.to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
|
181
|
+
tab.rows.each do |row|
|
182
|
+
row.each_pair do |k, _v|
|
183
|
+
expect(k.class).to eq Symbol
|
184
|
+
end
|
185
|
+
expect(row[:code].class).to eq String
|
186
|
+
expect(row[:date].class).to eq Date
|
187
|
+
expect(row[:shares].is_a?(Numeric)).to be true
|
188
|
+
unless row[:rawshares].nil?
|
189
|
+
expect(row[:rawshares].is_a?(Numeric)).to be true
|
190
|
+
end
|
191
|
+
expect(row[:price].is_a?(BigDecimal)).to be true
|
192
|
+
expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
|
193
|
+
expect(row[:info].class).to eq Date
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should be create-able from an Org IO object' do
|
198
|
+
File.open('/tmp/junk.org', 'w') { |f| f.write(@org_file_body) }
|
199
|
+
tab = Table.new('/tmp/junk.org')
|
200
|
+
expect(tab.class).to eq(Table)
|
201
|
+
expect(tab.rows.size).to be > 10
|
202
|
+
expect(tab.rows[0].keys.sort)
|
203
|
+
.to eq [:code, :date, :info, :price, :raw, :ref, :shares]
|
204
|
+
tab.rows.each do |row|
|
205
|
+
row.each_pair do |k, _v|
|
206
|
+
expect(k.class).to eq Symbol
|
207
|
+
end
|
208
|
+
expect(row[:code].class).to eq String
|
209
|
+
expect(row[:date].class).to eq Date
|
210
|
+
expect(row[:shares].is_a?(Numeric)).to be true
|
211
|
+
unless row[:rawshares].nil?
|
212
|
+
expect(row[:rawshares].is_a?(Numeric)).to be true
|
213
|
+
end
|
214
|
+
expect(row[:price].is_a?(BigDecimal)).to be true
|
215
|
+
expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
|
216
|
+
expect(row[:info].class).to eq String
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'should be create-able from an Array of Arrays with header and hrule' do
|
221
|
+
# rubocop:disable Style/WordArray
|
222
|
+
aoa = [
|
223
|
+
['First', 'Second', 'Third'],
|
224
|
+
['|---------+----------+---------|', nil, nil],
|
225
|
+
['1', '2', '3.2'],
|
226
|
+
['4', '5', '6.4'],
|
227
|
+
['7', '8', '9.0'],
|
228
|
+
[10, 11, 12.1]
|
229
|
+
]
|
230
|
+
tab = Table.new(aoa)
|
231
|
+
expect(tab.class).to eq(Table)
|
232
|
+
expect(tab.rows.size).to eq(4)
|
233
|
+
expect(tab.rows[0].keys.sort).to eq [:first, :second, :third]
|
234
|
+
tab.rows.each do |row|
|
235
|
+
row.each_pair do |k, _v|
|
236
|
+
expect(k.class).to eq Symbol
|
237
|
+
end
|
238
|
+
expect(row[:first].is_a?(Numeric)).to be true
|
239
|
+
expect(row[:second].is_a?(Numeric)).to be true
|
240
|
+
expect(row[:third].is_a?(BigDecimal)).to be true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'should be create-able from an Array of Arrays with header no hrule' do
|
245
|
+
aoa = [
|
246
|
+
['First', 'Second', 'Third'],
|
247
|
+
['1', '2', '3.2'],
|
248
|
+
['4', '5', '6.4'],
|
249
|
+
['7', '8', '9.0'],
|
250
|
+
[10, 11, 12.1]
|
251
|
+
]
|
252
|
+
tab = Table.new(aoa)
|
253
|
+
expect(tab.class).to eq(Table)
|
254
|
+
expect(tab.rows.size).to eq(4)
|
255
|
+
expect(tab.headers.sort).to eq [:first, :second, :third]
|
256
|
+
tab.rows.each do |row|
|
257
|
+
row.each_pair do |k, _v|
|
258
|
+
expect(k.class).to eq Symbol
|
259
|
+
end
|
260
|
+
expect(row[:first].is_a?(Numeric)).to be true
|
261
|
+
expect(row[:second].is_a?(Numeric)).to be true
|
262
|
+
expect(row[:third].is_a?(BigDecimal)).to be true
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should be create-able from an Array of Arrays sans Header' do
|
267
|
+
aoa = [
|
268
|
+
['1', '2', '3.2'],
|
269
|
+
['4', '5', '6.4'],
|
270
|
+
['7', '8', '9.0'],
|
271
|
+
[7, 8, 9.3]
|
272
|
+
]
|
273
|
+
# rubocop:enable Style/WordArray
|
274
|
+
tab = Table.new(aoa)
|
275
|
+
expect(tab.class).to eq(Table)
|
276
|
+
expect(tab.rows.size).to eq(4)
|
277
|
+
expect(tab.headers.sort).to eq [:col1, :col2, :col3]
|
278
|
+
tab.rows.each do |row|
|
279
|
+
row.each_pair do |k, _v|
|
280
|
+
expect(k.class).to eq Symbol
|
281
|
+
end
|
282
|
+
expect(row[:col1].is_a?(Numeric)).to be true
|
283
|
+
expect(row[:col2].is_a?(Numeric)).to be true
|
284
|
+
expect(row[:col3].is_a?(BigDecimal)).to be true
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'should be create-able from an Array of Hashes' do
|
289
|
+
aoh = [
|
290
|
+
{ a: '1', 'Two words' => '2', c: '3.2' },
|
291
|
+
{ a: '4', 'Two words' => '5', c: '6.4' },
|
292
|
+
{ a: '7', 'Two words' => '8', c: '9.0' },
|
293
|
+
{ a: 10, 'Two words' => 11, c: 12.4 }
|
294
|
+
]
|
295
|
+
tab = Table.new(aoh)
|
296
|
+
expect(tab.class).to eq(Table)
|
297
|
+
expect(tab.rows.size).to eq(4)
|
298
|
+
expect(tab.rows[0].keys.sort).to eq [:a, :c, :two_words]
|
299
|
+
tab.rows.each do |row|
|
300
|
+
row.each_pair do |k, _v|
|
301
|
+
expect(k.class).to eq Symbol
|
302
|
+
end
|
303
|
+
expect(row[:a].is_a?(Numeric)).to be true
|
304
|
+
expect(row[:two_words].is_a?(Numeric)).to be true
|
305
|
+
expect(row[:c].is_a?(BigDecimal)).to be true
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe 'column operations' do
|
311
|
+
it 'should be able to sum a column' do
|
312
|
+
aoh = [
|
313
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
314
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
315
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
316
|
+
]
|
317
|
+
tab = Table.new(aoh)
|
318
|
+
expect(tab[:a].sum).to eq 12
|
319
|
+
expect(tab[:two_words].sum).to eq 15
|
320
|
+
expect(tab[:c].sum).to eq 19_423
|
321
|
+
expect(tab[:d].sum).to eq 'appleorangepear'
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should be able to sum a column ignoring nils' do
|
325
|
+
aoh = [
|
326
|
+
{ a: '', 'Two words' => '2', c: '', d: 'apple' },
|
327
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
328
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
329
|
+
]
|
330
|
+
tab = Table.new(aoh)
|
331
|
+
expect(tab[:a].sum).to eq 11
|
332
|
+
expect(tab[:two_words].sum).to eq 15
|
333
|
+
expect(tab[:c].sum).to eq 16_300
|
334
|
+
expect(tab[:d].sum).to eq 'appleorangepear'
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should be able to report its headings' do
|
338
|
+
tab = Table.new(StringIO.new(@csv_file_body), '.csv')
|
339
|
+
expect(tab.headers.sort)
|
340
|
+
.to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
|
341
|
+
end
|
342
|
+
|
343
|
+
it 'should be able to extract a column as an array' do
|
344
|
+
aoh = [
|
345
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
346
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
347
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
348
|
+
]
|
349
|
+
tab = Table.new(aoh)
|
350
|
+
expect(tab[:a].to_a).to eq [1, 4, 7]
|
351
|
+
expect(tab[:c].to_a).to eq [3123, 6412, 9888]
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'should be able to sum a column' do
|
355
|
+
aoh = [
|
356
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
357
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
358
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
359
|
+
]
|
360
|
+
tab = Table.new(aoh)
|
361
|
+
expect(tab[:a].sum).to eq 12
|
362
|
+
expect(tab[:c].sum).to eq 19_423
|
363
|
+
expect(tab[:c].sum.is_a?(Integer)).to be true
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'should be able to average a column' do
|
367
|
+
aoh = [
|
368
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
369
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
370
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
371
|
+
]
|
372
|
+
tab = Table.new(aoh)
|
373
|
+
expect(tab[:a].avg).to eq 4
|
374
|
+
expect(tab[:c].avg.round(4)).to eq 6474.3333
|
375
|
+
expect(tab[:c].avg.class).to eq BigDecimal
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'should be able to get column minimum' do
|
379
|
+
aoh = [
|
380
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
381
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
382
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
383
|
+
]
|
384
|
+
tab = Table.new(aoh)
|
385
|
+
expect(tab[:a].min).to eq 1
|
386
|
+
expect(tab[:c].min.round(4)).to eq 3123
|
387
|
+
expect(tab[:c].min.is_a?(Integer)).to be true
|
388
|
+
expect(tab[:d].min).to eq 'apple'
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'should be able to get column maximum' do
|
392
|
+
aoh = [
|
393
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
394
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
395
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
396
|
+
]
|
397
|
+
tab = Table.new(aoh)
|
398
|
+
expect(tab[:a].max).to eq 7
|
399
|
+
expect(tab[:c].max.round(4)).to eq 9888
|
400
|
+
expect(tab[:c].max.is_a?(Integer)).to be true
|
401
|
+
expect(tab[:d].max).to eq 'pear'
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
describe 'footers' do
|
406
|
+
it 'should be able to add a total footer to the table' do
|
407
|
+
aoh = [
|
408
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
409
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
410
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
411
|
+
]
|
412
|
+
tab = Table.new(aoh)
|
413
|
+
tab.add_sum_footer([:a, :c, :two_words])
|
414
|
+
expect(tab.footers[:total][:a]).to eq 12
|
415
|
+
expect(tab.footers[:total][:c]).to eq 19_423
|
416
|
+
expect(tab.footers[:total][:two_words]).to eq 15
|
417
|
+
expect(tab.footers[:total][:d]).to be_nil
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'should be able to add an average footer to the table' do
|
421
|
+
aoh = [
|
422
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
423
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
424
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
425
|
+
]
|
426
|
+
tab = Table.new(aoh)
|
427
|
+
tab.add_avg_footer([:a, :c, :two_words])
|
428
|
+
expect(tab.footers[:average][:a]).to eq 4
|
429
|
+
expect(tab.footers[:average][:c].round(4)).to eq 6474.3333
|
430
|
+
expect(tab.footers[:average][:two_words]).to eq 5
|
431
|
+
expect(tab.footers[:average][:d]).to be_nil
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'should be able to add a minimum footer to the table' do
|
435
|
+
aoh = [
|
436
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
437
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
438
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
439
|
+
]
|
440
|
+
tab = Table.new(aoh)
|
441
|
+
tab.add_min_footer([:a, :c, :two_words])
|
442
|
+
expect(tab.footers[:minimum][:a]).to eq 1
|
443
|
+
expect(tab.footers[:minimum][:c]).to eq 3123
|
444
|
+
expect(tab.footers[:minimum][:two_words]).to eq 2
|
445
|
+
expect(tab.footers[:minimum][:d]).to be_nil
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'should be able to add a maximum footer to the table' do
|
449
|
+
aoh = [
|
450
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
451
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
452
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
453
|
+
]
|
454
|
+
tab = Table.new(aoh)
|
455
|
+
tab.add_max_footer([:a, :c, :two_words])
|
456
|
+
expect(tab.footers[:maximum][:a]).to eq 7
|
457
|
+
expect(tab.footers[:maximum][:c]).to eq 9888
|
458
|
+
expect(tab.footers[:maximum][:two_words]).to eq 8
|
459
|
+
expect(tab.footers[:maximum][:d]).to be_nil
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe 'sorting' do
|
464
|
+
it 'should be able to sort its rows on one column' do
|
465
|
+
aoh = [
|
466
|
+
{ a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
|
467
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
468
|
+
{ a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
|
469
|
+
]
|
470
|
+
tab = Table.new(aoh).order_by(:a)
|
471
|
+
expect(tab.rows[0][:a]).to eq 4
|
472
|
+
end
|
473
|
+
|
474
|
+
it 'should be able to sort its rows on multiple columns' do
|
475
|
+
aoh = [
|
476
|
+
{ a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
|
477
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
478
|
+
{ a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
|
479
|
+
]
|
480
|
+
tab = Table.new(aoh).order_by(:d, :c)
|
481
|
+
expect(tab.rows[0][:a]).to eq 7
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'should be able to reverse sort its rows on one column' do
|
485
|
+
aoh = [
|
486
|
+
{ a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
|
487
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
488
|
+
{ a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
|
489
|
+
]
|
490
|
+
tab = Table.new(aoh).order_by(:d!)
|
491
|
+
expect(tab.rows[0][:d]).to eq 'orange'
|
492
|
+
expect(tab.rows[2][:d]).to eq 'apple'
|
493
|
+
end
|
494
|
+
|
495
|
+
it 'should sort its rows on mixed forward and reverse columns' do
|
496
|
+
aoh = [
|
497
|
+
{ a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
|
498
|
+
{ a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
|
499
|
+
{ a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
|
500
|
+
]
|
501
|
+
tab = Table.new(aoh).order_by(:d!, :c)
|
502
|
+
expect(tab.rows[0][:d]).to eq 'orange'
|
503
|
+
expect(tab.rows[1][:d]).to eq 'apple'
|
504
|
+
expect(tab.rows[1][:c]).to eq 1888
|
505
|
+
expect(tab.rows[2][:d]).to eq 'apple'
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
describe 'union' do
|
510
|
+
it 'should be able to union with a compatible table' do
|
511
|
+
aoh = [
|
512
|
+
{ a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
|
513
|
+
{ a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
|
514
|
+
{ a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
|
515
|
+
]
|
516
|
+
tab1 = Table.new(aoh)
|
517
|
+
aoh2 = [
|
518
|
+
{ t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
|
519
|
+
{ t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
|
520
|
+
{ t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
|
521
|
+
]
|
522
|
+
tab2 = Table.new(aoh2)
|
523
|
+
utab = tab1.union(tab2)
|
524
|
+
expect(utab.rows.size).to eq(6)
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'should throw an exception for union with different sized tables' do
|
528
|
+
aoh = [
|
529
|
+
{ a: '5', 'Two words' => '20', c: '3,123' },
|
530
|
+
{ a: '4', 'Two words' => '5', c: 6412 },
|
531
|
+
{ a: '7', 'Two words' => '8', c: '$1,888' }
|
532
|
+
]
|
533
|
+
tab1 = Table.new(aoh)
|
534
|
+
aoh2 = [
|
535
|
+
{ t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
|
536
|
+
{ t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
|
537
|
+
{ t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
|
538
|
+
]
|
539
|
+
tab2 = Table.new(aoh2)
|
540
|
+
expect {
|
541
|
+
tab1.union(tab2)
|
542
|
+
}.to raise_error(/different number of columns/)
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'should throw an exception for union with different types' do
|
546
|
+
aoh = [
|
547
|
+
{ a: '5', 'Two words' => '20', s: '5143', c: '3123' },
|
548
|
+
{ a: '4', 'Two words' => '5', s: 412, c: 6412 },
|
549
|
+
{ a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
|
550
|
+
]
|
551
|
+
tab1 = Table.new(aoh)
|
552
|
+
aoh2 = [
|
553
|
+
{ t: '8', 'Two worlds' => '65', s: '2016-01-17', u: 'kiwi' },
|
554
|
+
{ t: '87', 'Two worlds' => '12', s: Date.today, u: 'banana' },
|
555
|
+
{ t: '13', 'Two worlds' => '11', s: '[2015-05-21]', u: 'grape' }
|
556
|
+
]
|
557
|
+
tab2 = Table.new(aoh2)
|
558
|
+
expect {
|
559
|
+
tab1.union(tab2)
|
560
|
+
}.to raise_error(/different types/)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
describe 'select' do
|
565
|
+
it 'should be able to select by column names' do
|
566
|
+
aoh = [
|
567
|
+
{ a: '5', 'Two words' => '20', s: '5143', c: '3123' },
|
568
|
+
{ a: '4', 'Two words' => '5', s: 412, c: 6412 },
|
569
|
+
{ a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
|
570
|
+
]
|
571
|
+
tab1 = Table.new(aoh)
|
572
|
+
tab2 = tab1.select(:s, :a, :c)
|
573
|
+
expect(tab2.headers).to eq [:s, :a, :c]
|
574
|
+
end
|
575
|
+
|
576
|
+
it 'should be able to select by column names renaming columns' do
|
577
|
+
aoh = [
|
578
|
+
{ a: '5', 'Two words' => '20', s: '5143', c: '3123' },
|
579
|
+
{ a: '4', 'Two words' => '5', s: 412, c: 6412 },
|
580
|
+
{ a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
|
581
|
+
]
|
582
|
+
tab1 = Table.new(aoh)
|
583
|
+
tab2 = tab1.select(s: :former_s, a: :new_a, c: :renew_c)
|
584
|
+
expect(tab2.headers).to eq [:former_s, :new_a, :renew_c]
|
585
|
+
end
|
586
|
+
|
587
|
+
it 'should be able to select new columns computed from prior' do
|
588
|
+
aoh = [
|
589
|
+
{ a: '5', 'Two words' => '20', s: '5143', c: '3123' },
|
590
|
+
{ a: '4', 'Two words' => '5', s: 412, c: 6412 },
|
591
|
+
{ a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
|
592
|
+
]
|
593
|
+
tab1 = Table.new(aoh)
|
594
|
+
tab2 = tab1.select(:two_words, row: '@row', s_squared: 's * s',
|
595
|
+
arb: 's_squared / (a + c).to_d')
|
596
|
+
expect(tab2.headers).to eq [:two_words, :row, :s_squared, :arb]
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
describe 'where' do
|
601
|
+
it 'should be able to filter rows by expression' do
|
602
|
+
tab1 = Table.new(StringIO.new(@csv_file_body), '.csv')
|
603
|
+
tab2 = tab1.where("date < Date.parse('2006-06-01')")
|
604
|
+
expect(tab2[:date].max).to be < Date.parse('2006-06-01')
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
describe 'group_by' do
|
609
|
+
it 'should be able to group by equal columns' do
|
610
|
+
tab1 = Table.new(StringIO.new(@csv_file_body), '.csv')
|
611
|
+
tab2 = tab1.group_by(:date, :code, shares: :sum, ref: :first)
|
612
|
+
expect(tab2.headers).to eq([:date, :code, :sum_shares, :first_ref,
|
613
|
+
:first_rawshares, :first_price, :first_info])
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
describe 'output' do
|
618
|
+
it 'should be able to return itself as an array of arrays' do
|
619
|
+
aoh = [
|
620
|
+
{ a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
|
621
|
+
{ a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
|
622
|
+
{ a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
|
623
|
+
]
|
624
|
+
tab = Table.new(aoh)
|
625
|
+
aoa = tab.to_org
|
626
|
+
expect(aoa.class).to eq Array
|
627
|
+
expect(aoa[0].class).to eq Array
|
628
|
+
expect(aoa[0][0]).to eq 'A'
|
629
|
+
end
|
630
|
+
|
631
|
+
it 'should be able to output an org babel aoa' do
|
632
|
+
# This is what the data looks like when called from org babel code
|
633
|
+
# blocks.
|
634
|
+
tab =
|
635
|
+
[['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info'],
|
636
|
+
[1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1'],
|
637
|
+
[2, '2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ZMPEF1'],
|
638
|
+
[7, '2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ZMEAC'],
|
639
|
+
[8, '2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ZMEAC'],
|
640
|
+
[9, '2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ZMEAC'],
|
641
|
+
[10, '2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ZMEAC'],
|
642
|
+
[11, '2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ZMEAC'],
|
643
|
+
[12, '2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ZMEAC'],
|
644
|
+
[13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ZMEAC'],
|
645
|
+
[14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC'],
|
646
|
+
[15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC'],
|
647
|
+
[16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC']]
|
648
|
+
tg = Table.new(tab).add_sum_footer([:raw, :shares, :price])
|
649
|
+
aoa = tg.to_org(formats: { raw: '%,', shares: '%,', price: '%,4' })
|
650
|
+
expect(aoa[-1][0]).to eq 'Total'
|
651
|
+
expect(aoa[-1][1]).to eq ''
|
652
|
+
expect(aoa[-1][2]).to eq ''
|
653
|
+
expect(aoa[-1][3]).to eq '1,166,733'
|
654
|
+
expect(aoa[-1][4]).to eq '1,020,119'
|
655
|
+
expect(aoa[-1][5]).to eq '276.5135'
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|