object_table 0.3.4 → 0.4.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/README.md +206 -108
  4. data/lib/object_table/basic_grid.rb +1 -1
  5. data/lib/object_table/column.rb +6 -7
  6. data/lib/object_table/factory.rb +46 -0
  7. data/lib/object_table/grouping/grid.rb +47 -0
  8. data/lib/object_table/grouping.rb +109 -0
  9. data/lib/object_table/joining.rb +71 -0
  10. data/lib/object_table/masked_column.rb +2 -2
  11. data/lib/object_table/printing.rb +69 -0
  12. data/lib/object_table/stacking.rb +66 -0
  13. data/lib/object_table/static_view.rb +2 -5
  14. data/lib/object_table/table_methods.rb +35 -22
  15. data/lib/object_table/util.rb +19 -0
  16. data/lib/object_table/version.rb +1 -1
  17. data/lib/object_table/view.rb +7 -5
  18. data/lib/object_table/view_methods.rb +3 -2
  19. data/lib/object_table.rb +8 -19
  20. data/object_table.gemspec +2 -0
  21. data/spec/object_table/column_spec.rb +2 -2
  22. data/spec/object_table/grouping_spec.rb +475 -0
  23. data/spec/object_table/static_view_spec.rb +2 -2
  24. data/spec/object_table/util_spec.rb +43 -0
  25. data/spec/object_table/view_spec.rb +6 -16
  26. data/spec/object_table_spec.rb +45 -3
  27. data/spec/subclassing_spec.rb +44 -5
  28. data/spec/support/joining_example.rb +171 -0
  29. data/spec/support/object_table_example.rb +124 -29
  30. data/spec/support/stacking_example.rb +111 -0
  31. data/spec/support/utils.rb +8 -0
  32. data/spec/support/view_example.rb +10 -13
  33. metadata +20 -12
  34. data/lib/object_table/group.rb +0 -10
  35. data/lib/object_table/grouped.rb +0 -93
  36. data/lib/object_table/printable.rb +0 -72
  37. data/lib/object_table/stacker.rb +0 -59
  38. data/lib/object_table/table_child.rb +0 -19
  39. data/spec/object_table/grouped_spec.rb +0 -351
  40. data/spec/support/stacker_example.rb +0 -158
@@ -0,0 +1,475 @@
1
+ require 'object_table'
2
+ require 'object_table/grouping'
3
+
4
+ describe ObjectTable::Grouping do
5
+ let(:col1) { ((1..100).to_a + (-100..-1).to_a).shuffle }
6
+ let(:col2) { NArray.float(10, 200).random }
7
+
8
+ let(:table){ ObjectTable.new(col1: col1, col2: col2 ) }
9
+ let(:grouped){ described_class.new(table){ {pos: col1 > 0} } }
10
+
11
+ let(:positive) { (table.col1 > 0).where }
12
+ let(:negative) { (table.col1 < 0).where }
13
+
14
+ let(:pos_group) { table.where{|t| positive} }
15
+ let(:neg_group) { table.where{|t| negative} }
16
+
17
+ describe '.generate_name' do
18
+ let(:prefix){ 'key_' }
19
+ subject{ described_class.generate_name(prefix, existing_keys) }
20
+
21
+ context 'with no matching keys' do
22
+ let(:existing_keys){ ['a', 'b', 'c'] }
23
+ it 'should suffix the key with 0' do
24
+ expect(subject).to eql "key_0"
25
+ end
26
+ end
27
+
28
+ context 'with matching keys' do
29
+ let(:existing_keys){ ['key_1', 'key_67', 'key_8', 'abcd'] }
30
+ it 'should suffix the key with the next available number' do
31
+ expect(subject).to eql "key_68"
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ describe '#initialize' do
38
+
39
+ context 'when the block takes an argument' do
40
+ it 'should not evaluate in the context of the table' do
41
+ rspec_context = self
42
+
43
+ grouped = described_class.new(table) do |tbl|
44
+ receiver = eval('self', binding)
45
+ expect(receiver).to_not be table
46
+ expect(receiver).to be rspec_context
47
+ {}
48
+ end
49
+ grouped._keys # call _keys to make it call the block
50
+ end
51
+
52
+ it 'should pass the table into the block' do
53
+ grouped = described_class.new(table) do |tbl|
54
+ expect(tbl).to be table
55
+ {}
56
+ end
57
+ grouped._keys # call _keys to make it call the block
58
+ end
59
+ end
60
+
61
+ context 'when the block takes no arguments' do
62
+ it 'should call the block in the context of the table' do
63
+ _ = self
64
+ grouped = described_class.new(table) do
65
+ receiver = eval('self', binding)
66
+ _.expect(receiver).to _.be _.table
67
+ {}
68
+ end
69
+ grouped._keys # call _keys to make it call the block
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ context 'with changes to the parent' do
76
+ subject{ grouped }
77
+
78
+ it 'should mirror changes to the parent' do
79
+ expect(subject._keys).to eq (table.col1 > 0).to_a.zip
80
+ table[:col1] = NArray.int(200).fill(2)
81
+ table[:col1][0] = -100
82
+ expect(subject._keys).to eql ([0] + [1] * 199).zip
83
+ end
84
+ end
85
+
86
+ describe '#_keys' do
87
+ let(:grouped){ described_class.new(table){ {key1: col1 > 0, key2: col2 > 0.5} } }
88
+
89
+ it 'should return the keys' do
90
+ keys = grouped._keys.transpose
91
+ expect(keys).to eql [(table.col1 > 0).to_a, (table.col2 > 0.5).to_a]
92
+ end
93
+
94
+ it 'should set the names' do
95
+ grouped._keys
96
+ expect(grouped.instance_variable_get('@names')).to eql [:key1, :key2]
97
+ end
98
+
99
+ context 'when grouping by columns' do
100
+ let(:table){ ObjectTable.new(key1: [0]*4 + [1]*4, key2: [0, 0, 1, 1]*2, data: 1..8 ) }
101
+ let(:grouped){ described_class.new(table, :key1, :key2) }
102
+
103
+ it 'should use the columns as keys' do
104
+ keys = grouped._keys.transpose
105
+ expect(keys).to eql [table.key1.to_a, table.key2.to_a]
106
+ end
107
+
108
+ it 'should set the names' do
109
+ grouped._keys
110
+ expect(grouped.instance_variable_get('@names')).to eql [:key1, :key2]
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#each' do
116
+
117
+ context 'when the block takes an argument' do
118
+ it 'should not evaluate in the context of the group' do
119
+ rspec_context = self
120
+
121
+ grouped.each do |group|
122
+ receiver = eval('self', binding)
123
+ expect(receiver).to_not be_a ObjectTable::Group
124
+ expect(receiver).to be rspec_context
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'when the block takes no arguments' do
130
+ it 'should call the block in the context of the group' do
131
+ _ = self
132
+ grouped.each do
133
+ receiver = eval('self', binding)
134
+ _.expect(receiver).to _.be_a ObjectTable::Group
135
+ end
136
+ end
137
+ end
138
+
139
+ it 'should yield the groups' do
140
+ groups = [pos_group, neg_group]
141
+ grouped.each do |group|
142
+ expect(groups).to include group
143
+ groups -= [group]
144
+ end
145
+ end
146
+
147
+ it 'should give access to the keys' do
148
+ keys = []
149
+ grouped.each{ keys << Hash[@K.each_pair.to_a] }
150
+ expect(keys).to match_array [{pos: 0}, {pos: 1}]
151
+ end
152
+
153
+ it 'should give access to the correct key' do
154
+ keys = []
155
+ correct_keys = []
156
+ grouped.each do
157
+ keys << [@K.pos]
158
+ correct_keys << (col1 > 0).to_a.uniq
159
+ end
160
+
161
+ expect(keys).to match_array(correct_keys)
162
+ end
163
+
164
+ context 'with no block' do
165
+ it 'should return an enumerator' do
166
+ expect(grouped.each).to be_a Enumerator
167
+ end
168
+
169
+ it 'should enumerate the groups' do
170
+ groups = [pos_group, neg_group]
171
+ grouped.each.each do |group|
172
+ expect(groups).to include group
173
+ groups -= [group]
174
+ end
175
+ end
176
+
177
+ end
178
+ end
179
+
180
+ describe '#apply' do
181
+ subject{ grouped.apply{|group| group.col2.sum} }
182
+
183
+ it 'should return a table with the group keys' do
184
+ expect(subject).to be_a ObjectTable
185
+ expect(subject.colnames).to include :pos
186
+ end
187
+
188
+ it 'should concatenate the results of the block' do
189
+ value = [neg_group.col2.sum, pos_group.col2.sum]
190
+ expect(subject.sort_by(subject.pos)).to eql ObjectTable.new(pos: [0, 1], v_0: value)
191
+ end
192
+
193
+ describe 'value column auto naming' do
194
+ it 'should auto name the value column' do
195
+ grouped = described_class.new(table){{parity: 1}}
196
+ result = grouped.apply{|group| group.col1.sum}
197
+ expect(result).to have_column :v_0
198
+ expect(result.v_0.to_a).to eql [table.col1.sum]
199
+ end
200
+
201
+ it 'should auto name the value column' do
202
+ grouped = described_class.new(table){{v_0: 1}}
203
+ result = grouped.apply{|group| group.col1.sum}
204
+ expect(result).to have_column :v_1
205
+ expect(result.v_1.to_a).to eql [table.col1.sum]
206
+ end
207
+ end
208
+
209
+ context 'with results that are grids' do
210
+ subject{ grouped.apply{ @R[sum: col1.sum, mean: col2.mean] } }
211
+
212
+ it 'should return a table with the group keys' do
213
+ expect(subject).to be_a ObjectTable
214
+ expect(subject.colnames).to include :pos
215
+ end
216
+
217
+ it 'should stack the grids' do
218
+ expect(subject.sort_by(subject.pos)).to eql ObjectTable.new(
219
+ pos: [0, 1],
220
+ sum: [neg_group.col1.sum, pos_group.col1.sum],
221
+ mean: [neg_group.col2.mean, pos_group.col2.mean],
222
+ )
223
+ end
224
+ end
225
+
226
+ context 'with results that are tables' do
227
+ subject{ grouped.apply{ ObjectTable.new(sum: col1.sum, mean: col2.mean) } }
228
+
229
+ it 'should return a table with the group keys' do
230
+ expect(subject).to be_a ObjectTable
231
+ expect(subject.colnames).to include :pos
232
+ end
233
+
234
+ it 'should stack the grids' do
235
+ expect(subject.sort_by(subject.pos)).to eql ObjectTable.new(
236
+ pos: [0, 1],
237
+ sum: [neg_group.col1.sum, pos_group.col1.sum],
238
+ mean: [neg_group.col2.mean, pos_group.col2.mean],
239
+ )
240
+ end
241
+ end
242
+
243
+ context 'with results that are arrays' do
244
+ subject{ grouped.apply{ [col1[0], col1[-1]] } }
245
+
246
+ it 'should return a table with the group keys' do
247
+ expect(subject).to be_a ObjectTable
248
+ expect(subject.colnames).to include :pos
249
+ end
250
+
251
+ it 'should stack the grids' do
252
+ expect(subject.where{pos.eq 0}.v_0).to eq neg_group.col1[[0, -1]]
253
+ expect(subject.where{pos.eq 1}.v_0).to eq pos_group.col1[[0, -1]]
254
+ end
255
+ end
256
+
257
+ context 'with results that are narrays' do
258
+ subject{ grouped.apply{ col2 < 0.5 } }
259
+
260
+ it 'should return a table with the group keys' do
261
+ expect(subject).to be_a ObjectTable
262
+ expect(subject.colnames).to include :pos
263
+ end
264
+
265
+ it 'should stack the grids' do
266
+ expect(subject.where{pos.eq 0}.v_0).to eq (neg_group.col2 < 0.5)
267
+ expect(subject.where{pos.eq 1}.v_0).to eq (pos_group.col2 < 0.5)
268
+ end
269
+ end
270
+
271
+ context 'when the block takes an argument' do
272
+ it 'should not evaluate in the context of the group' do
273
+ rspec_context = self
274
+
275
+ grouped.apply do |group|
276
+ receiver = eval('self', binding)
277
+ expect(receiver).to_not be_a ObjectTable::Group
278
+ expect(receiver).to be rspec_context
279
+ nil
280
+ end
281
+ end
282
+ end
283
+
284
+ context 'when the block takes no arguments' do
285
+ it 'should call the block in the context of the group' do
286
+ _ = self
287
+ grouped.apply do
288
+ receiver = eval('self', binding)
289
+ _.expect(receiver).to _.be_a ObjectTable::Group
290
+ nil
291
+ end
292
+ end
293
+ end
294
+
295
+ context 'with a matrix key' do
296
+ let(:ngroups) { 10 }
297
+ let(:table) do
298
+ ObjectTable.new(
299
+ key1: 10.times.map{[rand, 'abc']} * ngroups,
300
+ key2: 10.times.map{[rand, 'def', 'ghi']} * ngroups,
301
+ value: (ngroups*10).times.map{rand},
302
+ )
303
+ end
304
+
305
+ let(:grouped) { described_class.new(table, :key1, :key2) }
306
+ subject{ grouped.apply{|group| group.value.sum} }
307
+
308
+ it 'should return a table with the group keys' do
309
+ expect(subject).to be_a ObjectTable
310
+ expect(subject.colnames).to include :key1
311
+ expect(subject.colnames).to include :key2
312
+ end
313
+
314
+ it 'should preserve the dimensions of the keys' do
315
+ expect(subject.key1.shape[0...-1]).to eql table.key1.shape[0...-1]
316
+ expect(subject.key2.shape[0...-1]).to eql table.key2.shape[0...-1]
317
+ end
318
+
319
+ context 'with vector values' do
320
+ subject{ grouped.apply{|group| group.value[0...10]} }
321
+
322
+ it 'should work' do
323
+ expect{subject}.to_not raise_error
324
+ end
325
+ end
326
+ end
327
+
328
+ context 'on an empty table' do
329
+ let(:table) { ObjectTable.new(col1: [], col2: []) }
330
+
331
+ it 'should return a table with no rows and only key columns' do
332
+ expect(subject.nrows).to eql 0
333
+ expect(subject.columns.keys).to eql [:pos]
334
+ end
335
+ end
336
+
337
+ end
338
+
339
+
340
+ describe '#reduce' do
341
+ let(:col2) { (NArray.float(10, 200).random * 100).to_i }
342
+ subject{ grouped.reduce{|row| row.R[:col2] += row.col2.sum } }
343
+
344
+ it 'should return a table with the group keys' do
345
+ expect(subject).to be_a ObjectTable
346
+ expect(subject.colnames).to include :pos
347
+ end
348
+
349
+ it 'should concatenate the results of the block' do
350
+ value = [neg_group.col2.sum, pos_group.col2.sum]
351
+ expect(subject.sort_by(subject.pos)).to eql ObjectTable.new(pos: [0, 1], col2: value)
352
+ end
353
+
354
+ context 'with results that are narrays' do
355
+ subject{ grouped.reduce{|row| row.R[:col2] += row.col2 } }
356
+
357
+ it 'should return a table with the group keys' do
358
+ expect(subject).to be_a ObjectTable
359
+ expect(subject.colnames).to include :pos
360
+ end
361
+
362
+ it 'should stack the grids' do
363
+ expect(subject.where{pos.eq 0}.col2).to eq neg_group.col2.sum(1).reshape(10, 1)
364
+ expect(subject.where{pos.eq 1}.col2).to eq pos_group.col2.sum(1).reshape(10, 1)
365
+ end
366
+ end
367
+
368
+ context 'with results that are arrays' do
369
+ subject{ grouped.reduce(col2: []){ @R[:col2] += col2.to_a } }
370
+
371
+ it 'should return a table with the group keys' do
372
+ expect(subject).to be_a ObjectTable
373
+ expect(subject.colnames).to include :pos
374
+ end
375
+
376
+ it 'should stack the grids' do
377
+ expect(subject.where{pos.eq 0}.col2).to eq neg_group.col2.reshape(1000, 1)
378
+ expect(subject.where{pos.eq 1}.col2).to eq pos_group.col2.reshape(1000, 1)
379
+ end
380
+ end
381
+
382
+ context 'when the block takes an argument' do
383
+ it 'should not evaluate in the context of the group' do
384
+ rspec_context = self
385
+
386
+ grouped.reduce do |group|
387
+ receiver = eval('self', binding)
388
+ expect(receiver).to_not be_a described_class
389
+ expect(receiver).to be rspec_context
390
+ nil
391
+ end
392
+ end
393
+ end
394
+
395
+ context 'when the block takes no arguments' do
396
+ it 'should call the block in the context of the row' do
397
+ _ = self
398
+ grouped.reduce do
399
+ receiver = eval('self', binding)
400
+ _.expect(receiver).to _.be_a Struct
401
+ nil
402
+ end
403
+ end
404
+ end
405
+
406
+ describe 'defaults' do
407
+ it 'should default to 0' do
408
+ grouped.reduce do |row|
409
+ expect(row.R[:key]).to eql 0
410
+ break
411
+ end
412
+ end
413
+
414
+ it 'should enforce defaults' do
415
+ grouped.reduce(key: 1) do |row|
416
+ expect(row.R[:key]).to eql 1
417
+ break
418
+ end
419
+ end
420
+
421
+ it 'should fail if defaults are badly specified' do
422
+ expect{ grouped.reduce(123){} }.to raise_error
423
+ end
424
+ end
425
+
426
+ context 'with a matrix key' do
427
+ let(:group_count) { 10 }
428
+ let(:group_size) { 15 }
429
+
430
+ let(:table) do
431
+ ObjectTable.new(
432
+ key1: group_count.times.map{[rand, 'abc']} * group_size,
433
+ key2: group_count.times.map{[rand, 'def', 'ghi']} * group_size,
434
+ value: (group_size * group_count).times.map{rand},
435
+ )
436
+ end
437
+
438
+ let(:grouped) { described_class.new(table, :key1, :key2) }
439
+ subject{ grouped.reduce{@R[:value] += value} }
440
+
441
+ it 'should return a table with the group keys' do
442
+ expect(subject).to be_a ObjectTable
443
+ expect(subject.colnames).to include :key1
444
+ expect(subject.colnames).to include :key2
445
+ end
446
+
447
+ it 'should preserve the dimensions of the keys' do
448
+ expect(subject.key1.shape[0...-1]).to eql table.key1.shape[0...-1]
449
+ expect(subject.key2.shape[0...-1]).to eql table.key2.shape[0...-1]
450
+ end
451
+
452
+ context 'with vector values' do
453
+ subject{ grouped.reduce(value: []){ @R[:value] += [value] } }
454
+
455
+ it 'should work' do
456
+ expect{subject}.to_not raise_error
457
+ expect(subject.nrows).to eql group_count
458
+ expect(subject.value.shape).to eq [group_size, group_count]
459
+ end
460
+ end
461
+ end
462
+
463
+ context 'on an empty table' do
464
+ let(:table) { ObjectTable.new(col1: [], col2: []) }
465
+
466
+ it 'should return a table with no rows and only key columns' do
467
+ expect(subject.nrows).to eql 0
468
+ expect(subject.columns.keys).to eql [:pos]
469
+ end
470
+ end
471
+
472
+ end
473
+
474
+
475
+ end
@@ -5,6 +5,6 @@ require 'support/object_table_example'
5
5
  require 'support/view_example'
6
6
 
7
7
  describe ObjectTable::StaticView do
8
- it_behaves_like 'an object table', ObjectTable::StaticView
9
- it_behaves_like 'a table view', ObjectTable::StaticView
8
+ it_behaves_like 'an object table'
9
+ it_behaves_like 'a table view'
10
10
  end
@@ -0,0 +1,43 @@
1
+ require 'object_table'
2
+ require 'object_table/util'
3
+
4
+ describe ObjectTable::Util do
5
+ let(:nrows) { 100 }
6
+ let(:col1) { NArray.float(10, nrows).random }
7
+ let(:col2) { NArray.object(nrows).map!{rand} }
8
+ let(:col3) { NArray.float(nrows).random }
9
+
10
+ let(:table) { ObjectTable.new(col1: col2, col2: col2, col3: col3) }
11
+
12
+ describe '.get_rows' do
13
+ subject{ described_class.get_rows(table, [:col1, :col2]) }
14
+
15
+ it 'should return an array of rows' do
16
+ table.each_row(:col1, :col2).zip(subject) do |(col1, col2), row|
17
+ expect(row).to eql [col1, col2]
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '.group_indices' do
23
+ let(:_key) { %w{ a a b b a b c a } }
24
+ let(:key) { NArray.to_na(_key) }
25
+
26
+ subject{ described_class.group_indices(key) }
27
+
28
+ it 'should return a hash' do
29
+ expect(subject).to be_a Hash
30
+ end
31
+
32
+ it 'should have all the keys' do
33
+ expect(subject.keys).to match_array(_key.uniq)
34
+ end
35
+
36
+ it 'should group indices by key' do
37
+ subject.each do |k, indices|
38
+ expect(indices).to eq key.eq(k).where.to_a
39
+ end
40
+ end
41
+ end
42
+
43
+ end
@@ -5,8 +5,8 @@ require 'support/object_table_example'
5
5
  require 'support/view_example'
6
6
 
7
7
  describe ObjectTable::View do
8
- it_behaves_like 'an object table', ObjectTable::View
9
- it_behaves_like 'a table view', ObjectTable::View
8
+ it_behaves_like 'an object table'
9
+ it_behaves_like 'a table view'
10
10
 
11
11
  describe '#initialize' do
12
12
  let(:table) { ObjectTable.new(col1: [1, 2, 3], col2: 5) }
@@ -96,13 +96,8 @@ describe ObjectTable::View do
96
96
  let(:table) { ObjectTable.new(col1: [1, 2, 3], col2: 5) }
97
97
  let(:view) { ObjectTable::View.new(table){ col1 > 2 } }
98
98
 
99
- let(:block) { Proc.new{'success!'} }
100
-
101
- subject{ view.cache_indices(&block) }
102
-
103
- it 'should call the block' do
104
- expect(block).to receive(:call)
105
- subject
99
+ it 'should yield to the block' do
100
+ expect{|b| view.cache_indices(&b)}.to yield_control
106
101
  end
107
102
  end
108
103
 
@@ -110,13 +105,8 @@ describe ObjectTable::View do
110
105
  let(:table) { ObjectTable.new(col1: [1, 2, 3], col2: 5) }
111
106
  let(:view) { ObjectTable::View.new(table){ col1 > 2 } }
112
107
 
113
- let(:block) { Proc.new{'success!'} }
114
-
115
- subject{ view.cache_columns(&block) }
116
-
117
- it 'should call the block' do
118
- expect(block).to receive(:call)
119
- subject
108
+ it 'should yield to the block' do
109
+ expect{|b| view.cache_columns(&b)}.to yield_control
120
110
  end
121
111
  end
122
112
 
@@ -1,11 +1,11 @@
1
1
  require 'object_table'
2
2
 
3
3
  require 'support/object_table_example'
4
- require 'support/stacker_example'
4
+ require 'support/stacking_example'
5
+ require 'support/joining_example'
5
6
 
6
7
  describe ObjectTable do
7
- it_behaves_like 'an object table', ObjectTable
8
- it_behaves_like 'a table stacker'
8
+ it_behaves_like 'an object table'
9
9
 
10
10
  describe '#initialize' do
11
11
  let(:columns){ {col1: [1, 2, 3], col2: NArray[4, 5, 6], col3: 7..9, col4: 10} }
@@ -253,4 +253,46 @@ describe ObjectTable do
253
253
  end
254
254
  end
255
255
 
256
+ describe '.stack' do
257
+ it_behaves_like 'a stacking operation' do
258
+ subject{ ObjectTable.stack(*grids) }
259
+
260
+ it 'should duplicate the contents' do
261
+ grids.each do |chunk|
262
+ expect(subject).to_not be chunk
263
+ end
264
+ end
265
+
266
+ context 'with no arguments' do
267
+ let(:grids){ [] }
268
+ it 'should return an empty table' do
269
+ expect(subject).to eql described_class.new
270
+ end
271
+ end
272
+
273
+ context 'with only a non-grid/table' do
274
+ let(:grids) { ['not a table'] }
275
+ it 'should fail' do
276
+ expect{subject}.to raise_error
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ describe '#stack!' do
283
+ it_behaves_like 'a stacking operation' do
284
+ subject{ grids[0].stack! *grids[1..-1] }
285
+
286
+ it 'should modify the table' do
287
+ expect(subject).to be grids[0]
288
+ end
289
+ end
290
+ end
291
+
292
+ describe '.join' do
293
+ it_behaves_like 'a table joiner' do
294
+ subject{ ObjectTable.join(left, right, :key1, :key2, type: join_type) }
295
+ end
296
+ end
297
+
256
298
  end
@@ -17,11 +17,12 @@ describe 'Subclassing ObjectTable and friends' do
17
17
  end
18
18
  end
19
19
 
20
- include Mixin
20
+ # include Mixin
21
21
 
22
- class StaticView < StaticView; include Mixin; end
23
- class View < View; include Mixin; end
24
- class Group < Group; include Mixin; end
22
+ # class StaticView < StaticView; include Mixin; end
23
+ # class View < View; include Mixin; end
24
+ # class Group < Group; include Mixin; end
25
+ fully_include Mixin
25
26
  end
26
27
 
27
28
  let(:table){ MyTable.new(a: 0...100, b: 100.times.map{rand}) }
@@ -32,6 +33,22 @@ describe 'Subclassing ObjectTable and friends' do
32
33
  expect(subject.a_plus_b).to eq (subject.a + subject.b)
33
34
  end
34
35
 
36
+ it 'should include the mixin' do
37
+ expect(MyTable).to be < MyTable::Mixin
38
+ end
39
+
40
+ it 'should create new subclasses of views' do
41
+ expect(MyTable::View).to be < ObjectTable::View
42
+ expect(MyTable::StaticView).to be < ObjectTable::StaticView
43
+ expect(MyTable::Group).to be < ObjectTable::Group
44
+ end
45
+
46
+ it 'should include modules into view subclasses' do
47
+ expect(MyTable::View).to be < MyTable::Mixin
48
+ expect(MyTable::StaticView).to be < MyTable::Mixin
49
+ expect(MyTable::Group).to be < MyTable::Mixin
50
+ end
51
+
35
52
  describe '#clone' do
36
53
  it 'should be an instance of the table subclass' do
37
54
  expect(table.clone).to be_a MyTable
@@ -149,6 +166,12 @@ describe 'Subclassing ObjectTable and friends' do
149
166
 
150
167
  end
151
168
 
169
+ describe '#reduce' do
170
+ it 'should aggregate into a subclassed table' do
171
+ expect(subject.reduce{}).to be_a MyTable
172
+ end
173
+ end
174
+
152
175
  end
153
176
 
154
177
  describe '#sort_by' do
@@ -159,4 +182,20 @@ describe 'Subclassing ObjectTable and friends' do
159
182
  end
160
183
  end
161
184
 
162
- end
185
+ describe '#stack' do
186
+ let(:stacked){ table.stack(table) }
187
+
188
+ it 'should return an instance of the table subclass' do
189
+ expect(stacked).to be_a MyTable
190
+ end
191
+ end
192
+
193
+ describe '#join' do
194
+ let(:joined){ table.join(table, :a) }
195
+
196
+ it 'should return an instance of the table subclass' do
197
+ expect(joined).to be_a MyTable
198
+ end
199
+ end
200
+
201
+ end