fat_core 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,990 +0,0 @@
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
-
129
- @org_file_body_with_groups = <<EOS
130
-
131
- #+TBLNAME: morgan_tab
132
- |-----+------------+------+---------+--------+----------+--------|
133
- | Ref | Date | Code | Raw | Shares | Price | Info |
134
- |-----+------------+------+---------+--------+----------+--------|
135
- | 29 | 2013-05-02 | P | 795,546 | 2,609 | 1.18500 | ZMPEF1 |
136
- |-----+------------+------+---------+--------+----------+--------|
137
- | 30 | 2013-05-02 | P | 118,186 | 388 | 11.85000 | ZMPEF1 |
138
- | 31 | 2013-05-02 | P | 340,948 | 1,926 | 1.18500 | ZMPEF2 |
139
- | 32 | 2013-05-02 | P | 50,651 | 286 | 11.85000 | ZMPEF2 |
140
- |-----+------------+------+---------+--------+----------+--------|
141
- | 33 | 2013-05-20 | S | 12,000 | 32 | 28.28040 | ZMEAC |
142
- | 34 | 2013-05-20 | S | 85,000 | 226 | 28.32240 | ZMEAC |
143
- | 35 | 2013-05-20 | S | 33,302 | 88 | 28.63830 | ZMEAC |
144
- | 36 | 2013-05-23 | S | 8,000 | 21 | 27.10830 | ZMEAC |
145
- | 37 | 2013-05-23 | S | 23,054 | 61 | 26.80150 | ZMEAC |
146
- | 38 | 2013-05-23 | S | 39,906 | 106 | 25.17490 | ZMEAC |
147
- | 39 | 2013-05-29 | S | 13,459 | 36 | 24.74640 | ZMEAC |
148
- |-----+------------+------+---------+--------+----------+--------|
149
- | 40 | 2013-05-29 | S | 15,700 | 42 | 24.77900 | ZMEAC |
150
- | 41 | 2013-05-29 | S | 15,900 | 42 | 24.58020 | ZMEAC |
151
- | 42 | 2013-05-30 | S | 6,679 | 18 | 25.04710 | ZMEAC |
152
- |-----+------------+------+---------+--------+----------+--------|
153
-
154
- EOS
155
- end
156
-
157
- describe 'construction' do
158
- it 'should be create-able from a CSV string' do
159
- tab = Table.from_csv_string(@csv_file_body)
160
- expect(tab.class).to eq(Table)
161
- expect(tab.rows.size).to be > 20
162
- expect(tab.headers.sort)
163
- .to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
164
- tab.rows.each do |row|
165
- row.each_pair do |k, _v|
166
- expect(k.class).to eq Symbol
167
- end
168
- expect(row[:code].class).to eq String
169
- expect(row[:date].class).to eq Date
170
- expect(row[:shares].is_a?(Numeric)).to be true
171
- unless row[:rawshares].nil?
172
- expect(row[:rawshares].is_a?(Numeric)).to be true
173
- end
174
- expect(row[:price].is_a?(Numeric)).to be true
175
- expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
176
- end
177
- end
178
-
179
- it 'should be create-able from an Org string' do
180
- tab = Table.from_org_string(@org_file_body)
181
- expect(tab.class).to eq(Table)
182
- expect(tab.rows.size).to be > 10
183
- expect(tab.headers.sort)
184
- .to eq [:code, :date, :info, :price, :raw, :ref, :shares]
185
- tab.rows.each do |row|
186
- row.each_pair do |k, _v|
187
- expect(k.class).to eq Symbol
188
- end
189
- expect(row[:code].class).to eq String
190
- expect(row[:date].class).to eq Date
191
- expect(row[:shares].is_a?(Numeric)).to be true
192
- unless row[:rawshares].nil?
193
- expect(row[:rawshares].is_a?(Numeric)).to be true
194
- end
195
- expect(row[:price].is_a?(BigDecimal)).to be true
196
- expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
197
- expect(row[:info].class).to eq String
198
- end
199
- end
200
-
201
- it 'should be create-able from an Org string with groups' do
202
- tab = Table.from_org_string(@org_file_body)
203
- expect(tab.class).to eq(Table)
204
- expect(tab.rows.size).to be > 10
205
- expect(tab.headers.sort)
206
- .to eq [:code, :date, :info, :price, :raw, :ref, :shares]
207
- tab.rows.each do |row|
208
- row.each_pair do |k, _v|
209
- expect(k.class).to eq Symbol
210
- end
211
- expect(row[:code].class).to eq String
212
- expect(row[:date].class).to eq Date
213
- expect(row[:shares].is_a?(Numeric)).to be true
214
- unless row[:rawshares].nil?
215
- expect(row[:rawshares].is_a?(Numeric)).to be true
216
- end
217
- expect(row[:price].is_a?(BigDecimal)).to be true
218
- expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
219
- expect(row[:info].class).to eq String
220
- end
221
- end
222
-
223
- it 'should be create-able from a CSV file' do
224
- File.open('/tmp/junk.csv', 'w') { |f| f.write(@csv_file_body) }
225
- tab = Table.from_csv_file('/tmp/junk.csv')
226
- expect(tab.class).to eq(Table)
227
- expect(tab.rows.size).to be > 20
228
- expect(tab.headers.sort)
229
- .to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
230
- tab.rows.each do |row|
231
- row.each_pair do |k, _v|
232
- expect(k.class).to eq Symbol
233
- end
234
- expect(row[:code].class).to eq String
235
- expect(row[:date].class).to eq Date
236
- expect(row[:shares].is_a?(Numeric)).to be true
237
- unless row[:rawshares].nil?
238
- expect(row[:rawshares].is_a?(Numeric)).to be true
239
- end
240
- expect(row[:price].is_a?(BigDecimal)).to be true
241
- expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
242
- expect(row[:info].class).to eq Date
243
- end
244
- end
245
-
246
- it 'should be create-able from an Org file' do
247
- File.open('/tmp/junk.org', 'w') { |f| f.write(@org_file_body) }
248
- tab = Table.from_org_file('/tmp/junk.org')
249
- expect(tab.class).to eq(Table)
250
- expect(tab.rows.size).to be > 10
251
- expect(tab.rows[0].keys.sort)
252
- .to eq [:code, :date, :info, :price, :raw, :ref, :shares]
253
- tab.rows.each do |row|
254
- row.each_pair do |k, _v|
255
- expect(k.class).to eq Symbol
256
- end
257
- expect(row[:code].class).to eq String
258
- expect(row[:date].class).to eq Date
259
- expect(row[:shares].is_a?(Numeric)).to be true
260
- unless row[:rawshares].nil?
261
- expect(row[:rawshares].is_a?(Numeric)).to be true
262
- end
263
- expect(row[:price].is_a?(BigDecimal)).to be true
264
- expect([Numeric, String].any? { |t| row[:ref].is_a?(t) }).to be true
265
- expect(row[:info].class).to eq String
266
- end
267
- end
268
-
269
- it 'should be create-able from an Array of Arrays with header and hrule' do
270
- # rubocop:disable Style/WordArray
271
- aoa = [
272
- ['First', 'Second', 'Third'],
273
- ['|---------+----------+---------|', nil, nil],
274
- ['1', '2', '3.2'],
275
- ['4', '5', '6.4'],
276
- ['7', '8', '9.0'],
277
- [10, 11, 12.1]
278
- ]
279
- tab = Table.from_aoa(aoa)
280
- expect(tab.class).to eq(Table)
281
- expect(tab.rows.size).to eq(4)
282
- expect(tab.rows[0].keys.sort).to eq [:first, :second, :third]
283
- tab.rows.each do |row|
284
- row.each_pair do |k, _v|
285
- expect(k.class).to eq Symbol
286
- end
287
- expect(row[:first].is_a?(Numeric)).to be true
288
- expect(row[:second].is_a?(Numeric)).to be true
289
- expect(row[:third].is_a?(BigDecimal)).to be true
290
- end
291
- end
292
-
293
- it 'should be create-able from an Array of Arrays with nil-marked header' do
294
- aoa = [
295
- ['First', 'Second', 'Third'],
296
- nil,
297
- ['1', '2', '3.2'],
298
- ['4', '5', '6.4'],
299
- ['7', '8', '9.0'],
300
- [10, 11, 12.1]
301
- ]
302
- tab = Table.from_aoa(aoa)
303
- expect(tab.class).to eq(Table)
304
- expect(tab.rows.size).to eq(4)
305
- expect(tab.headers.sort).to eq [:first, :second, :third]
306
- tab.rows.each do |row|
307
- row.each_pair do |k, _v|
308
- expect(k.class).to eq Symbol
309
- end
310
- expect(row[:first].is_a?(Numeric)).to be true
311
- expect(row[:second].is_a?(Numeric)).to be true
312
- expect(row[:third].is_a?(BigDecimal)).to be true
313
- end
314
- end
315
-
316
- it 'should be create-able from an Array of Arrays sans Header' do
317
- aoa = [
318
- ['1', '2', '3.2'],
319
- ['4', '5', '6.4'],
320
- ['7', '8', '9.0'],
321
- [7, 8, 9.3]
322
- ]
323
- # rubocop:enable Style/WordArray
324
- tab = Table.from_aoa(aoa)
325
- expect(tab.class).to eq(Table)
326
- expect(tab.rows.size).to eq(4)
327
- expect(tab.headers.sort).to eq [:col1, :col2, :col3]
328
- tab.rows.each do |row|
329
- row.each_pair do |k, _v|
330
- expect(k.class).to eq Symbol
331
- end
332
- expect(row[:col1].is_a?(Numeric)).to be true
333
- expect(row[:col2].is_a?(Numeric)).to be true
334
- expect(row[:col3].is_a?(BigDecimal)).to be true
335
- end
336
- end
337
-
338
- it 'should be create-able from an Array of Hashes' do
339
- aoh = [
340
- { a: '1', 'Two words' => '2', c: '3.2' },
341
- { a: '4', 'Two words' => '5', c: '6.4' },
342
- { a: '7', 'Two words' => '8', c: '9.0' },
343
- { a: 10, 'Two words' => 11, c: 12.4 }
344
- ]
345
- tab = Table.from_aoh(aoh)
346
- expect(tab.class).to eq(Table)
347
- expect(tab.rows.size).to eq(4)
348
- expect(tab.rows[0].keys.sort).to eq [:a, :c, :two_words]
349
- tab.rows.each do |row|
350
- row.each_pair do |k, _v|
351
- expect(k.class).to eq Symbol
352
- end
353
- expect(row[:a].is_a?(Numeric)).to be true
354
- expect(row[:two_words].is_a?(Numeric)).to be true
355
- expect(row[:c].is_a?(BigDecimal)).to be true
356
- end
357
- end
358
-
359
- it 'should set T F columns to Boolean' do
360
- cwd = File.dirname(__FILE__)
361
- dwtab = Table.from_org_file(cwd + '/../example_files/datawatch.org')
362
- expect(dwtab.column(:g10).type).to eq('Boolean')
363
- expect(dwtab.column(:qp10).type).to eq('Boolean')
364
- dwo = dwtab.where('qp10 || g10')
365
- dwo.rows.each do |row|
366
- expect(row[:qp10].class.to_s).to match(/TrueClass|FalseClass/)
367
- end
368
- end
369
- end
370
-
371
- describe 'indexing' do
372
- before :all do
373
- @tab = Table.from_aoh([
374
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
375
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
376
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }])
377
- end
378
-
379
- it 'should be able to index by column head' do
380
- expect(@tab[:a]).to eq([1, 4, 7])
381
- expect(@tab[:d]).to eq(%w(apple orange pear))
382
- expect { @tab[:r] }.to raise_error /not in table/
383
- end
384
-
385
- it 'should be able to index by row number' do
386
- expect(@tab[1]).to eq({a: 1, two_words: 2, c: 3123, d: 'apple'})
387
- expect(@tab[3]).to eq({a: 7, two_words: 8, c: 9888, d: 'pear'})
388
- expect { @tab[0] }.to raise_error(/out of range/)
389
- expect { @tab[4] }.to raise_error(/out of range/)
390
- end
391
- end
392
-
393
- describe 'column operations' do
394
- it 'should be able to sum a column' do
395
- aoh = [
396
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
397
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
398
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
399
- ]
400
- tab = Table.from_aoh(aoh)
401
- expect(tab.column(:a).sum).to eq 12
402
- expect(tab[:two_words].sum).to eq 15
403
- expect(tab.column(:c).sum).to eq 19_423
404
- expect(tab.column(:d).sum).to eq 'appleorangepear'
405
- end
406
-
407
- it 'should be able to sum a column ignoring nils' do
408
- aoh = [
409
- { a: '', 'Two words' => '2', c: '', d: 'apple' },
410
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
411
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
412
- ]
413
- tab = Table.from_aoh(aoh)
414
- expect(tab.column(:a).sum).to eq 11
415
- expect(tab.column(:two_words).sum).to eq 15
416
- expect(tab.column(:c).sum).to eq 16_300
417
- expect(tab.column(:d).sum).to eq 'appleorangepear'
418
- end
419
-
420
- it 'should be able to report its headings' do
421
- tab = Table.from_csv_string(@csv_file_body)
422
- expect(tab.headers.sort)
423
- .to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
424
- end
425
-
426
- it 'should be able to extract a column as an array' do
427
- aoh = [
428
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
429
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
430
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
431
- ]
432
- tab = Table.from_aoh(aoh)
433
- expect(tab[:a]).to eq [1, 4, 7]
434
- expect(tab[:c]).to eq [3123, 6412, 9888]
435
- end
436
-
437
- it 'should be able to sum a column' do
438
- aoh = [
439
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
440
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
441
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
442
- ]
443
- tab = Table.from_aoh(aoh)
444
- expect(tab.column(:a).sum).to eq 12
445
- expect(tab.column(:c).sum).to eq 19_423
446
- expect(tab.column(:c).sum.is_a?(Integer)).to be true
447
- end
448
-
449
- it 'should be able to average a column' do
450
- aoh = [
451
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
452
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
453
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
454
- ]
455
- tab = Table.from_aoh(aoh)
456
- expect(tab.column(:a).avg).to eq 4
457
- expect(tab.column(:c).avg.round(4)).to eq 6474.3333
458
- expect(tab.column(:c).avg.class).to eq BigDecimal
459
- end
460
-
461
- it 'should be able to get column minimum' do
462
- aoh = [
463
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
464
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
465
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
466
- ]
467
- tab = Table.from_aoh(aoh)
468
- expect(tab.column(:a).min).to eq 1
469
- expect(tab.column(:c).min.round(4)).to eq 3123
470
- expect(tab.column(:c).min.is_a?(Integer)).to be true
471
- expect(tab.column(:d).min).to eq 'apple'
472
- end
473
-
474
- it 'should be able to get column maximum' do
475
- aoh = [
476
- { a: '1', 'Two words' => '2', c: '3,123', d: 'apple' },
477
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
478
- { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
479
- ]
480
- tab = Table.from_aoh(aoh)
481
- expect(tab.column(:a).max).to eq 7
482
- expect(tab.column(:c).max.round(4)).to eq 9888
483
- expect(tab.column(:c).max.is_a?(Integer)).to be true
484
- expect(tab.column(:d).max).to eq 'pear'
485
- end
486
- end
487
-
488
- describe 'sorting' do
489
- it 'should be able to sort its rows on one column' do
490
- aoh = [
491
- { a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
492
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
493
- { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
494
- ]
495
- tab = Table.from_aoh(aoh).order_by(:a)
496
- expect(tab.rows[0][:a]).to eq 4
497
- end
498
-
499
- it 'should be able to sort its rows on multiple columns' do
500
- aoh = [
501
- { a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
502
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
503
- { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
504
- ]
505
- tab = Table.from_aoh(aoh).order_by(:d, :c)
506
- expect(tab.rows[0][:a]).to eq 7
507
- end
508
-
509
- it 'should be able to reverse sort its rows on one column' do
510
- aoh = [
511
- { a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
512
- { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
513
- { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
514
- ]
515
- tab = Table.from_aoh(aoh).order_by(:d!)
516
- expect(tab.rows[0][:d]).to eq 'orange'
517
- expect(tab.rows[2][:d]).to eq 'apple'
518
- end
519
-
520
- it 'should sort its rows on mixed forward and reverse columns' do
521
- aoh = [
522
- { a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
523
- { a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
524
- { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
525
- ]
526
- tab = Table.from_aoh(aoh).order_by(:d!, :c)
527
- expect(tab.rows[0][:d]).to eq 'orange'
528
- expect(tab.rows[1][:d]).to eq 'apple'
529
- expect(tab.rows[1][:c]).to eq 1888
530
- expect(tab.rows[2][:d]).to eq 'apple'
531
- end
532
- end
533
-
534
- describe 'union' do
535
- it 'should be able to union with a compatible table' do
536
- aoh = [
537
- { a: '5', 'Two words' => '20', c: '3,123', d: 'apple' },
538
- { a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
539
- { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
540
- ]
541
- tab1 = Table.from_aoh(aoh)
542
- aoh2 = [
543
- { t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
544
- { t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
545
- { t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
546
- ]
547
- tab2 = Table.from_aoh(aoh2)
548
- utab = tab1.union(tab2)
549
- expect(utab.rows.size).to eq(6)
550
- end
551
-
552
- it 'should throw an exception for union with different sized tables' do
553
- aoh = [
554
- { a: '5', 'Two words' => '20', c: '3,123' },
555
- { a: '4', 'Two words' => '5', c: 6412 },
556
- { a: '7', 'Two words' => '8', c: '$1,888' }
557
- ]
558
- tab1 = Table.from_aoh(aoh)
559
- aoh2 = [
560
- { t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
561
- { t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
562
- { t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
563
- ]
564
- tab2 = Table.from_aoh(aoh2)
565
- expect {
566
- tab1.union(tab2)
567
- }.to raise_error(/different number of columns/)
568
- end
569
-
570
- it 'should throw an exception for union with different types' do
571
- aoh = [
572
- { a: '5', 'Two words' => '20', s: '5143', c: '3123' },
573
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
574
- { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
575
- ]
576
- tab1 = Table.from_aoh(aoh)
577
- aoh2 = [
578
- { t: '8', 'Two worlds' => '65', s: '2016-01-17', u: 'kiwi' },
579
- { t: '87', 'Two worlds' => '12', s: Date.today, u: 'banana' },
580
- { t: '13', 'Two worlds' => '11', s: '[2015-05-21]', u: 'grape' }
581
- ]
582
- tab2 = Table.from_aoh(aoh2)
583
- expect {
584
- tab1.union(tab2)
585
- }.to raise_error(/different column types/)
586
- end
587
- end
588
-
589
- describe 'select' do
590
- it 'should be able to select by column names' do
591
- aoh = [
592
- { a: '5', 'Two words' => '20', s: '5143', c: '3123' },
593
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
594
- { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
595
- ]
596
- tab1 = Table.from_aoh(aoh)
597
- tab2 = tab1.select(:s, :a, :c)
598
- expect(tab2.headers).to eq [:s, :a, :c]
599
- end
600
-
601
- it 'should be able to select by column names renaming columns' do
602
- aoh = [
603
- { a: '5', 'Two words' => '20', s: '5143', c: '3123' },
604
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
605
- { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
606
- ]
607
- tab1 = Table.from_aoh(aoh)
608
- tab2 = tab1.select(former_s: :s, new_a: :a, renew_c: :c)
609
- expect(tab2.headers).to eq [:former_s, :new_a, :renew_c]
610
- end
611
-
612
- it 'should be able to select new columns computed from prior' do
613
- aoh = [
614
- { a: '5', 'Two words' => '20', s: '5143', c: '3123' },
615
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
616
- { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
617
- ]
618
- tab1 = Table.from_aoh(aoh)
619
- tab2 = tab1.select(:two_words, row: '@row', s_squared: 's * s',
620
- arb: 's_squared / (a + c).to_d')
621
- expect(tab2.headers).to eq [:two_words, :row, :s_squared, :arb]
622
- end
623
-
624
- it 'should be able to use old value of current column to compute new value' do
625
- aoh = [
626
- { a: '5', 'Two words' => '20', s: '5_143', c: '3123' },
627
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
628
- { a: '7', 'Two words' => '8', s: '$1821', c: '$1_888' }
629
- ]
630
- tab1 = Table.from_aoh(aoh)
631
- tab2 = tab1.select(:two_words, s: 's * s', nc: 'c + c', c: 'nc+nc')
632
- expect(tab2.headers).to eq [:two_words, :s, :nc, :c]
633
- expect(tab2[:s]).to eq([26450449, 169744, 3316041])
634
- expect(tab2[:c]).to eq([12492, 25648, 7552])
635
- end
636
-
637
- it 'should have access to @row and @group vars in evaluating' do
638
- aoh = [
639
- { a: '5', 'Two words' => '20', s: '5_143', c: '3123' },
640
- { a: '4', 'Two words' => '5', s: 412, c: 6412 },
641
- { a: '7', 'Two words' => '8', s: '$1721', c: '$1_888' },
642
- { a: '5', 'Two words' => '20', s: '4_143', c: '4123' },
643
- { a: '4', 'Two words' => '5', s: 512, c: 5412 },
644
- { a: '7', 'Two words' => '8', s: '$1621', c: '$2_888' },
645
- { a: '5', 'Two words' => '20', s: '3_143', c: '5123' },
646
- { a: '4', 'Two words' => '5', s: 412, c: 4412 },
647
- { a: '7', 'Two words' => '8', s: '$1521', c: '$3_888' }
648
- ]
649
- tab = Table.from_aoh(aoh).order_by(:a, :two_words)
650
- tab2 = tab.select(:a, :two_words, number: '@row', group: '@group')
651
- expect(tab2.headers).to eq [:a, :two_words, :number, :group]
652
- expect(tab2[:number]).to eq([1, 2, 3, 4, 5, 6, 7, 8, 9])
653
- expect(tab2[:group]).to eq([1, 1, 1, 2, 2, 2, 3, 3, 3])
654
- end
655
- end
656
-
657
- describe 'where' do
658
- it 'should be able to filter rows by expression' do
659
- tab1 = Table.from_csv_string(@csv_file_body)
660
- tab2 = tab1.where("date < Date.parse('2006-06-01')")
661
- expect(tab2[:date].max).to be < Date.parse('2006-06-01')
662
- end
663
-
664
- it 'should where by boolean columns' do
665
- aoa =
666
- [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
667
- nil,
668
- [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
669
- [2, '2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ZMPEF1', 'T'],
670
- [7, '2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ZMEAC', 'F'],
671
- [8, '2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ZMEAC', 'T'],
672
- [9, '2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ZMEAC', 'T'],
673
- [10, '2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ZMEAC', 'T'],
674
- [11, '2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ZMEAC', 'F'],
675
- [12, '2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ZMEAC', 'T'],
676
- [13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ZMEAC', 'T'],
677
- [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
678
- [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
679
- [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
680
- tab = Table.from_aoa(aoa)
681
- tab2 = tab.where('!bool || code == "P"')
682
- expect(tab2.rows.size).to eq(5)
683
- tab2 = tab.where('code == "S" && raw < 10_000')
684
- expect(tab2.rows.size).to eq(2)
685
- tab2 = tab.where('@row > 10')
686
- expect(tab2.rows.size).to eq(2)
687
- tab2 = tab.where('info =~ /zmeac/i')
688
- expect(tab2.rows.size).to eq(10)
689
- tab2 = tab.where('info =~ /xxxx/')
690
- expect(tab2.rows.size).to eq(0)
691
- end
692
-
693
- it 'where clause with row and group' do
694
- aoa =
695
- [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
696
- nil,
697
- [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
698
- [2, '2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ZMPEF1', 'T'],
699
- [7, '2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ZMEAC', 'F'],
700
- [8, '2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ZMEAC', 'T'],
701
- [9, '2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ZMEAC', 'T'],
702
- [10, '2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ZMEAC', 'T'],
703
- [11, '2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ZMEAC', 'F'],
704
- [12, '2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ZMEAC', 'T'],
705
- [13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ZMEAC', 'T'],
706
- [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
707
- [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
708
- [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
709
- tab = Table.from_aoa(aoa).order_by(:date, :code)
710
- tab2 = tab.where('@row > 10')
711
- expect(tab2.rows.size).to eq(2)
712
- tab2 = tab.where('@group == 3')
713
- expect(tab2.rows.size).to eq(3)
714
- end
715
- end
716
-
717
- describe 'group_by' do
718
- it 'should be able to group by equal columns' do
719
- tab1 = Table.from_csv_string(@csv_file_body)
720
- tab2 = tab1.group_by(:date, :code, shares: :sum, ref: :first)
721
- expect(tab2.headers).to eq([:date, :code, :sum_shares, :first_ref,
722
- :first_rawshares, :first_price, :first_info])
723
- end
724
- end
725
-
726
- describe 'join' do
727
- # These tests are taken from https://www.tutorialspoint.com/postgresql/postgresql_using_joins.htm
728
- before :all do
729
- @tab_a = Table.from_aoh([
730
- { id: 1, name: 'Paul', age: 32, address: 'California', salary: 20000, join_date: '2001-07-13' },
731
- { id: 3, name: 'Teddy', age: 23, address: 'Norway', salary: 20000},
732
- { id: 4, name: 'Mark', age: 25, address: 'Rich-Mond', salary: 65000, join_date: '2007-12-13' },
733
- { id: 5, name: 'David', age: 27, address: 'Texas', salary: 85000, join_date: '2007-12-13' },
734
- { id: 2, name: 'Allen', age: 25, address: 'Texas', salary: nil, join_date: '2007-12-13' },
735
- { id: 8, name: 'Paul', age: 24, address: 'Houston', salary: 20000, join_date: '2005-07-13' },
736
- { id: 9, name: 'James', age: 44, address: 'Norway', salary: 5000, join_date: '2005-07-13' },
737
- { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' }
738
- ])
739
- @tab_b = Table.from_aoh([
740
- { id: 1, dept: 'IT Billing', emp_id: 1 },
741
- { id: 2, dept: 'Engineering', emp_id: 2 },
742
- { id: 3, dept: 'Finance', emp_id: 7 }
743
- ])
744
- end
745
-
746
- it 'should be able to do an inner join' do
747
- join_tab = @tab_a.join(@tab_b, :id_a, :emp_id_b)
748
- expect(join_tab.class).to eq Table
749
- expect(join_tab.size).to eq(2)
750
- expect(join_tab[:name]).to include('Paul')
751
- expect(join_tab[:name]).to include('Allen')
752
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
753
- :join_date, :id_b, :dept])
754
- end
755
-
756
- it 'should be able to do an inner join on a string exp' do
757
- join_tab = @tab_a.join(@tab_b, 'id_a == emp_id_b')
758
- expect(join_tab.class).to eq Table
759
- expect(join_tab.size).to eq(2)
760
- expect(join_tab[:name]).to include('Paul')
761
- expect(join_tab[:name]).to include('Allen')
762
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
763
- :join_date, :id_b, :dept, :emp_id])
764
- end
765
-
766
- it 'should be able to do a left join' do
767
- join_tab = @tab_a.left_join(@tab_b, :id_a, :emp_id_b)
768
- expect(join_tab.class).to eq Table
769
- expect(join_tab.size).to eq(8)
770
- expect(join_tab[:name]).to include('Paul')
771
- expect(join_tab[:name]).to include('Allen')
772
- expect(join_tab[:name]).to include('Teddy')
773
- expect(join_tab[:name]).to include('Mark')
774
- expect(join_tab[:name]).to include('David')
775
- expect(join_tab[:name]).to include('James')
776
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
777
- :join_date, :id_b, :dept, :emp_id])
778
- end
779
-
780
- it 'should be able to do a right join' do
781
- join_tab = @tab_a.right_join(@tab_b, :id_a, :emp_id_b)
782
- expect(join_tab.class).to eq Table
783
- expect(join_tab.size).to eq(3)
784
- expect(join_tab[:name]).to include('Paul')
785
- expect(join_tab[:name]).to include('Allen')
786
- expect(join_tab[:dept]).to include('IT Billing')
787
- expect(join_tab[:dept]).to include('Engineering')
788
- expect(join_tab[:dept]).to include('Finance')
789
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
790
- :join_date, :id_b, :dept, :emp_id])
791
- end
792
-
793
- it 'should be able to do a full join' do
794
- join_tab = @tab_a.full_join(@tab_b, :id_a, :emp_id_b)
795
- expect(join_tab.class).to eq Table
796
- expect(join_tab.size).to eq(9)
797
- expect(join_tab[:name]).to include('Paul')
798
- expect(join_tab[:name]).to include('Allen')
799
- expect(join_tab[:name]).to include('Teddy')
800
- expect(join_tab[:name]).to include('Mark')
801
- expect(join_tab[:name]).to include('David')
802
- expect(join_tab[:name]).to include('James')
803
- expect(join_tab[:dept]).to include('IT Billing')
804
- expect(join_tab[:dept]).to include('Engineering')
805
- expect(join_tab[:dept]).to include('Finance')
806
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
807
- :join_date, :id_b, :dept, :emp_id])
808
- end
809
-
810
- it 'should be able to do a cross join' do
811
- join_tab = @tab_a.cross_join(@tab_b)
812
- expect(join_tab.class).to eq Table
813
- expect(join_tab.size).to eq(24)
814
- expect(join_tab[:name]).to include('Paul')
815
- expect(join_tab[:name]).to include('Allen')
816
- expect(join_tab[:name]).to include('Teddy')
817
- expect(join_tab[:name]).to include('Mark')
818
- expect(join_tab[:name]).to include('David')
819
- expect(join_tab[:name]).to include('James')
820
- expect(join_tab[:dept]).to include('IT Billing')
821
- expect(join_tab[:dept]).to include('Engineering')
822
- expect(join_tab[:dept]).to include('Finance')
823
- expect(join_tab.headers).to eq([:id, :name, :age, :address, :salary,
824
- :join_date, :id_b, :dept, :emp_id])
825
- end
826
- end
827
-
828
- describe 'group boundaries' do
829
- before :all do
830
- @tab_a = Table.from_aoh([
831
- { id: 1, name: 'Paul', age: 32, address: 'California', salary: 20000, join_date: '2001-07-13' },
832
- { id: 3, name: 'Teddy', age: 23, address: 'Norway', salary: 20000},
833
- { id: 4, name: 'Mark', age: 25, address: 'Rich-Mond', salary: 65000, join_date: '2007-12-13' },
834
- { id: 5, name: 'David', age: 27, address: 'Texas', salary: 85000, join_date: '2007-12-13' },
835
- { id: 2, name: 'Allen', age: 25, address: 'Texas', salary: nil, join_date: '2007-12-13' },
836
- { id: 8, name: 'Paul', age: 24, address: 'Houston', salary: 20000, join_date: '2005-07-13' },
837
- { id: 9, name: 'James', age: 44, address: 'Norway', salary: 5000, join_date: '2005-07-13' },
838
- { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' }
839
- ])
840
- # Union compatible with tab_a
841
- @tab_a1 = Table.from_aoh([
842
- { id: 21, name: 'Paula', age: 23, address: 'Kansas', salary: 20000, join_date: '2001-07-13' },
843
- { id: 23, name: 'Jenny', age: 32, address: 'Missouri', salary: 20000},
844
- { id: 24, name: 'Forrest', age: 52, address: 'Richmond', salary: 65000, join_date: '2007-12-13' },
845
- { id: 25, name: 'Syrano', age: 72, address: 'Nebraska', salary: 85000, join_date: '2007-12-13' },
846
- # Next four are the same as row as in @tab_a
847
- { id: 2, name: 'Allen', age: 25, address: 'Texas', salary: nil, join_date: '2007-12-13' },
848
- { id: 8, name: 'Paul', age: 24, address: 'Houston', salary: 20000, join_date: '2005-07-13' },
849
- { id: 9, name: 'James', age: 44, address: 'Norway', salary: 5000, join_date: '2005-07-13' },
850
- { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' },
851
- { id: 22, name: 'Paula', age: 52, address: 'Iowa', salary: nil, join_date: '2007-12-13' },
852
- { id: 28, name: 'Paula', age: 42, address: 'Oklahoma', salary: 20000, join_date: '2005-07-13' },
853
- { id: 29, name: 'Patrick', age: 44, address: 'Lindsbourg', salary: 5000, join_date: '2005-07-13' },
854
- { id: 30, name: 'James', age: 54, address: 'Ottawa', salary: 5000, join_date: '2005-07-13' }
855
- ])
856
- @tab_b = Table.from_aoh([
857
- { id: 1, dept: 'IT Billing', emp_id: 1 },
858
- { id: 2, dept: 'Engineering', emp_id: 2 },
859
- { id: 3, dept: 'Finance', emp_id: 7 }
860
- ])
861
- @aoa =
862
- [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
863
- nil,
864
- [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
865
- nil,
866
- [2, '2013-05-02', 'P', 118_186.40, 118_186.4, 11.8500, 'ZMPEF1', 'T'],
867
- [7, '2013-05-20', 'S', 12_000.00, 5046.00, 28.2804, 'ZMEAC', 'F'],
868
- [8, '2013-05-20', 'S', 85_000.00, 35_742.50, 28.3224, 'ZMEAC', 'T'],
869
- nil,
870
- [9, '2013-05-20', 'S', 33_302.00, 14_003.49, 28.6383, 'ZMEAC', 'T'],
871
- [10, '2013-05-23', 'S', 8000.00, 3364.00, 27.1083, 'ZMEAC', 'T'],
872
- [11, '2013-05-23', 'S', 23_054.00, 9694.21, 26.8015, 'ZMEAC', 'F'],
873
- [12, '2013-05-23', 'S', 39_906.00, 16_780.47, 25.1749, 'ZMEAC', 'T'],
874
- [13, '2013-05-29', 'S', 13_459.00, 5659.51, 24.7464, 'ZMEAC', 'T'],
875
- [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
876
- [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
877
- nil,
878
- [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
879
- @aoh = [
880
- { id: 1, name: 'Paul', age: 32, address: 'California', salary: 20000, join_date: '2001-07-13' },
881
- nil,
882
- { id: 3, name: 'Teddy', age: 23, address: 'Norway', salary: 20000},
883
- { id: 4, name: 'Mark', age: 25, address: 'Rich-Mond', salary: 65000, join_date: '2007-12-13' },
884
- { id: 5, name: 'David', age: 27, address: 'Texas', salary: 85000, join_date: '2007-12-13' },
885
- nil,
886
- { id: 2, name: 'Allen', age: 25, address: 'Texas', salary: nil, join_date: '2007-12-13' },
887
- { id: 8, name: 'Paul', age: 24, address: 'Houston', salary: 20000, join_date: '2005-07-13' },
888
- { id: 9, name: 'James', age: 44, address: 'Norway', salary: 5000, join_date: '2005-07-13' },
889
- nil,
890
- { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' }
891
- ]
892
- end
893
-
894
- it 'an empty table should have no groups' do
895
- expect(Table.new.groups.size).to eq(0)
896
- end
897
-
898
- it 'default group boundaries of whole table' do
899
- expect(@tab_a.groups.size).to eq(1)
900
- end
901
-
902
- it 'add group boundaries on reading from org text' do
903
- tab = Table.from_org_string(@org_file_body_with_groups)
904
- expect(tab.groups.size).to eq(4)
905
- expect(tab.groups[0].size).to eq(1)
906
- expect(tab.groups[1].size).to eq(3)
907
- expect(tab.groups[2].size).to eq(7)
908
- expect(tab.groups[3].size).to eq(3)
909
- end
910
-
911
- it 'add group boundaries on reading from aoa' do
912
- tab = Table.from_aoa(@aoa)
913
- expect(tab.groups.size).to eq(4)
914
- expect(tab.groups[0].size).to eq(1)
915
- expect(tab.groups[1].size).to eq(3)
916
- expect(tab.groups[2].size).to eq(7)
917
- expect(tab.groups[3].size).to eq(1)
918
- end
919
-
920
- it 'add group boundaries on reading from aoh' do
921
- tab = Table.from_aoh(@aoh)
922
- expect(tab.groups.size).to eq(4)
923
- expect(tab.groups[0].size).to eq(1)
924
- expect(tab.groups[1].size).to eq(3)
925
- expect(tab.groups[2].size).to eq(3)
926
- expect(tab.groups[3].size).to eq(1)
927
- end
928
-
929
- it 'add group boundaries on order_by' do
930
- tab = @tab_a.order_by(:name)
931
- # Now the table is ordered by name, and the names are: Allen, David,
932
- # James, James, Mark, Paul, Paul, Teddy. So there are groups of size 1,
933
- # 1, 2, 1, 2, and 1. Six groups in all.
934
- expect(tab.groups.size).to eq(6)
935
- expect(tab.groups[0].size).to eq(1)
936
- expect(tab.groups[1].size).to eq(1)
937
- expect(tab.groups[2].size).to eq(2)
938
- tab.groups[2].each do |row|
939
- expect(row[:name]).to eq('James')
940
- end
941
- expect(tab.groups[3].size).to eq(1)
942
- expect(tab.groups[4].size).to eq(2)
943
- tab.groups[4].each do |row|
944
- expect(row[:name]).to eq('Paul')
945
- end
946
- expect(tab.groups[5].size).to eq(1)
947
- end
948
-
949
- it 'add group boundaries on union_all' do
950
- tab = @tab_a.union_all(@tab_a1)
951
- expect(tab.size).to eq(20)
952
- expect(tab.groups.size).to eq(2)
953
- expect(tab.groups[0].size).to eq(8)
954
- expect(tab.groups[1].size).to eq(12)
955
- end
956
-
957
- it 'inherit group boundaries on union_all' do
958
- tab1 = @tab_a.order_by(:name)
959
- tab2 = @tab_a1.order_by(:name)
960
- tab = tab1.union_all(tab2)
961
- expect(tab.size).to eq(20)
962
- expect(tab.groups.size).to eq(tab1.groups.size + tab2.groups.size)
963
- tab.groups.each do |grp|
964
- names = grp.map {|r| r[:name]}
965
- expect(names.uniq.size).to eq(1)
966
- end
967
- end
968
-
969
- it 'inherit group boundaries on select' do
970
- tab = @tab_a.order_by(:name).select(:name, :age, :join_date)
971
- # Now the table is ordered by name, and the names are: Allen, David,
972
- # James, James, Mark, Paul, Paul, Teddy. So there are groups of size 1,
973
- # 1, 2, 1, 2, and 1. Six groups in all.
974
- expect(tab.groups.size).to eq(6)
975
- expect(tab.groups[0].size).to eq(1)
976
- expect(tab.groups[1].size).to eq(1)
977
- expect(tab.groups[2].size).to eq(2)
978
- tab.groups[2].each do |row|
979
- expect(row[:name]).to eq('James')
980
- end
981
- expect(tab.groups[3].size).to eq(1)
982
- expect(tab.groups[4].size).to eq(2)
983
- tab.groups[4].each do |row|
984
- expect(row[:name]).to eq('Paul')
985
- end
986
- expect(tab.groups[5].size).to eq(1)
987
- end
988
- end
989
- end
990
- end