object_table 0.1.0 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14a01cfa80b77105e5a243dbaacc894fec8fa9ad
4
- data.tar.gz: 6bf1d3259550812b745eee202f0937bf7c278732
3
+ metadata.gz: 4b1327f62f465f8305a265b6a7f865c5ca43a66b
4
+ data.tar.gz: 8dc9d380fecb0ac14556f3cbe38acae87ec943b3
5
5
  SHA512:
6
- metadata.gz: 1106df0b67029df89d4e8f52e171946f38f4a19c6c16fc0c316823731c8d073fa565b35ce141c2dc399c8d2f2d9002ab11e25178b38cb686db656fc5e4e5fb09
7
- data.tar.gz: 8a6423b764125e33c092c7d80dbc5fab723995f82873528f8d72e441d54bbe2340b2f9a2022e521fe2d762db0dcbe0fbe90cdfce78643e3479932bb46ad242fe
6
+ metadata.gz: 666c0551cef6acff77df23fee41986c49b05e2959bb770d0c1ed06cee357774c9e57a31f359eb50603be8d9f87e03b216eec4272a1cb6e2b8ab420a934513c40
7
+ data.tar.gz: 18841d8933d04755b955fd6882fb7d8480d199d442306c0a02d44b041984f7f131f0f81d7bd4e5c4da277b4fca91847bae7d35e2a8fc63771119f8a14989e222
data/README.md CHANGED
@@ -57,7 +57,18 @@ Otherwise the scalars are extended to match the length of the vector columns
57
57
  - `#stack(table1, table2, ...)` appends then supplied tables
58
58
  - `#apply(&block)` evaluates `block` in the context of the table
59
59
  - `#where(&block)` filters the table
60
- - `#group(&block)` splits the table into groups
60
+ - `#group_by(&block)` splits the table into groups
61
+
62
+ For any methods taking a block, when passing a block which takes an argument, the block will be called with the table as the argument, otherwise (block with no arguments), the block is `#instance_eval`ed in the context of the block.
63
+
64
+ ```ruby
65
+ >>> data = ObjectTable.new
66
+ # block with argument, binding is preserved
67
+ >>> data.apply{|tbl| self.class }
68
+ Object
69
+ >>> data.apply{ self.class }
70
+ ObjectTable
71
+ ```
61
72
 
62
73
  ### Getting columns
63
74
 
@@ -79,7 +90,7 @@ You can get a column by using `#[]` or using the column name as a method.
79
90
 
80
91
  ### Setting columns
81
92
 
82
- You can set/add columns by using `#[]=`.
93
+ You can set/add columns by using `#[]=`. This works for both vectors and scalars. Scalars are given a default type of object.
83
94
 
84
95
  ```ruby
85
96
  >>> data = ObjectTable.new(a: [1, 2, 3], b: 100, c: ['a', 'b', 'c'])
@@ -121,6 +132,37 @@ IndexError: dst.shape[0]=3 != src.shape[0]=4
121
132
  IndexError: dst.shape[0]=3 != src.shape[0]=4
122
133
  ```
123
134
 
135
+ #### `#set_column(name, value, typecode='object', shape...)`
136
+
137
+ `#[]=` really just calls `#set_column`, but you can have more control over the columns by calling `#set_column` yourself and adding additional arguments. Additional arguments control the shape and type of the column. They are the same as for `NArray.new`
138
+
139
+ ```ruby
140
+ >>> data = ObjectTable.new(col0: [0]*3)
141
+ >>> data[:col1] = [1, 2, 3]
142
+ >>> data.col1
143
+ => ObjectTable::Column.int(3):
144
+ [ 1, 2, 3 ]
145
+
146
+ # this time, let's make it a float instead
147
+ >>> data.set_column(:col2, [1, 2, 3], 'float')
148
+ >>> data.col2
149
+ => ObjectTable::Column.float(3):
150
+ [ 1.0, 2.0, 3.0 ]
151
+
152
+ >>> data[:col3] = 4
153
+ >>> data.col3
154
+ => ObjectTable::Column.object(3):
155
+ [ 4, 4, 4 ]
156
+
157
+ # this time, let's make it multi dimensional
158
+ >>> data.set_column(:col4, 4, 'int', 5)
159
+ >>> data.col4
160
+ => ObjectTable::Column.int(5,3):
161
+ [ [ 4, 4, 4, 4, 4 ],
162
+ [ 4, 4, 4, 4, 4 ],
163
+ [ 4, 4, 4, 4, 4 ] ]
164
+ ```
165
+
124
166
  ### Operating on columns
125
167
 
126
168
  All standard NArray operations apply (addition, subtraction etc.)
@@ -136,7 +178,6 @@ Missing methods are vectorised over the column
136
178
  ### `#apply`
137
179
 
138
180
  This is just a convenience method.
139
- It basically `#instance_eval`s the block passed to it.
140
181
 
141
182
  ```ruby
142
183
  >>> data = ObjectTable.new(a: [1, 2, 3], b: [4, 5, 6])
@@ -154,20 +195,47 @@ It basically `#instance_eval`s the block passed to it.
154
195
  0: 1 4 4
155
196
  1: 2 5 10
156
197
  2: 3 6 18
157
- a b c
198
+ a b c
199
+
200
+ # if you don't want it to steal the binding (self), make the block take an argument
201
+ >>> data.apply{|tbl| tbl.a + tbl.c }
202
+ => ObjectTable::Column.int(3):
203
+ [ 5, 12, 21 ]
204
+ ```
205
+
206
+ If you return a grid (e.g. through the `@R` shortcut) it will be coerced to a table.
207
+
208
+ ```ruby
209
+ >>> data = ObjectTable.new(a: [1, 2, 3], b: [4, 5, 6])
210
+ # let's make a new table but with a=a*3
211
+ >>> data.apply{ @R[a: a*3, b: b] }
212
+ => ObjectTable(3, 2)
213
+ a b
214
+ 0: 3 4
215
+ 1: 6 5
216
+ 2: 9 6
217
+ a b
218
+
219
+ # or if you called apply expecting an argument
220
+ >>> data.apply{|tbl| tbl.R[a: tbl.a*3, b: tbl.b] }
221
+ => ObjectTable(3, 2)
222
+ a b
223
+ 0: 3 4
224
+ 1: 6 5
225
+ 2: 9 6
226
+ a b
158
227
  ```
159
228
 
160
229
  ## Filtering
161
230
 
162
231
  Use the `#where` method and pass a filtering block.
163
- The block is evaluated in the context of the table (like for `#apply`).
164
- This creates a `TempView` which syncs with the parent table.
232
+ This creates a `View`, which syncs with the parent table.
165
233
  This means any changes made to the parent also affect the view.
166
234
 
167
235
  ```ruby
168
236
  >>> data = ObjectTable.new(a: 0...5, b: 5...10)
169
237
  >>> a_lt_3 = data.where{ a < 3 }
170
- => ObjectTable::TempView(3, 2)
238
+ => ObjectTable::View(3, 2)
171
239
  a b
172
240
  0: 0 5
173
241
  1: 1 6
@@ -178,7 +246,7 @@ This means any changes made to the parent also affect the view.
178
246
  >>> data[:b] = data.b.reverse
179
247
  # and the view gets updated too
180
248
  >>> a_lt_3
181
- => ObjectTable::TempView(3, 2)
249
+ => ObjectTable::View(3, 2)
182
250
  a b
183
251
  0: 0 9
184
252
  1: 1 8
@@ -187,7 +255,7 @@ This means any changes made to the parent also affect the view.
187
255
 
188
256
  # you can also chain #where calls
189
257
  >>> data.where{ a < 3 }.where{ b > 7 }
190
- => ObjectTable::TempView(3, 2)
258
+ => ObjectTable::View(3, 2)
191
259
  a b
192
260
  0: 0 9
193
261
  1: 1 8
@@ -196,8 +264,7 @@ This means any changes made to the parent also affect the view.
196
264
  >>> data.where{ a < 3 && b > 7 }
197
265
  ```
198
266
 
199
- Changes are propagated to the parent.
200
- This means any changes made to the view also affect the parent.
267
+ Any changes made to the view also affect the parent.
201
268
 
202
269
  ```ruby
203
270
  >>> data.where{ a < 3 }[:b] = 100
@@ -243,66 +310,131 @@ Added columns have a default value of `nil` outside the view.
243
310
  a b c
244
311
  ```
245
312
 
246
- ### Other notes
313
+ ### `#apply`
314
+
315
+ Using `#apply` creates a `StaticView`. Any modifications made to the parent will not refresh the static view. Changes to the static view still affect the parent however.
316
+
317
+ ```ruby
318
+ >>> data = ObjectTable.new(a: 0...5, b: 5...10)
319
+
320
+ >>> a_lt_3 = data.where{ a < 3 }
321
+ => ObjectTable::View(3, 2)
322
+ a b
323
+ 0: 0 5
324
+ 1: 1 6
325
+ 2: 2 7
326
+ a b
327
+ >>> a_lt_3[:a] = 5
328
+ # our view will refresh, so we can't see the changes!
329
+ >>> a_lt_3
330
+ => ObjectTable::View(0, 2)
331
+ a b
332
+ a b
247
333
 
248
- You can also use `#apply` on a view (as for a table).
334
+ # use apply instead
335
+ >>> data = ObjectTable.new(a: 0...5, b: 5...10)
336
+ >>> data.where{a < 3}.apply{ self[:a] = 5; p self; nil }
337
+ ObjectTable::StaticView(3, 2)
338
+ a b
339
+ 0: 5 5
340
+ 1: 5 6
341
+ 2: 5 7
342
+ a b
343
+ => nil
344
+ ```
345
+
346
+ You should never try to use a static view outside of its `#apply` block.
347
+
348
+
349
+ ### Other notes
249
350
 
250
351
  If you want to filter a table and keep that data (i.e. without it syncing with the parent, propagating changes etc.) just `#clone` it.
251
352
 
353
+
252
354
  ## Grouping (and aggregating)
253
355
 
254
- Use the `#group` method and pass a block that returns grouping keys.
356
+ Use the `#group_by` method and pass column names or a block that returns grouping keys.
255
357
  Then call `#each` to iterate through the groups or `#apply` to aggregate the results.
256
- The blocks are evaluated in the context of the table (in the case of `#apply`, the context of the group).
257
358
 
258
- The argument to `#group` should be a hash mapping key name => key. See the below example.
359
+ The argument to `#group_by` should be a hash mapping key name => key. See the below example.
259
360
 
260
361
  ```ruby
261
- >>> data = ObjectTable.new(name: ['John', 'Tom', 'Jim', 'Tim', 'Jack'], value: 1..5)
362
+ >>> data = ObjectTable.new(name: ['John', 'Tom', 'John', 'Tom', 'Jim'], value: 1..5)
363
+ => ObjectTable(5, 2)
262
364
  name value
263
365
  0: "John" 1
264
366
  1: "Tom" 2
265
- 2: "Jim" 3
266
- 3: "Tim" 4
267
- 4: "Jack" 5
268
- name value
269
-
270
- # group by the first letter of the name and print out each group
271
- >>> data.group{ {initial: name.map{|n| n[0]}} }.each{ p self; puts }
272
- ObjectTable::View(3, 2)
273
- name value
274
- 0: "John" 1
275
- 1: "Jim" 3
276
- 2: "John" 5
367
+ 2: "John" 3
368
+ 3: "Tom" 4
369
+ 4: "Jim" 5
277
370
  name value
278
371
 
279
- ObjectTable::View(2, 2)
280
- name value
281
- 0: "Tom" 2
282
- 1: "Tim" 4
283
- name value
284
-
285
- # calculate the average 'value' for each group and get the result in a table
286
- >>> data.group{ {initial: name.map{|n| n[0]}} }.apply{ value.mean }
287
- => ObjectTable(2, 2)
288
- initial v_0
289
- 0: "J" 3.0
290
- 1: "T" 3.0
291
- initial v_0
372
+ # group by the name and get the no. of rows in each group
373
+ >>> num_rows = []
374
+ >>> data.group_by(:name).each{ num_rows.push(nrows) }
375
+ >>> num_rows
376
+ => [2, 2, 1]
377
+
378
+ # or group with a block
379
+ >>> num_rows = []
380
+ # let's group by initial letter of the name
381
+ >>> data.group_by{ {initial: name.map{|n| n[0]}} }.each{ num_rows.push(nrows) }
382
+ >>> num_rows
383
+ => [3, 2]
384
+ ```
385
+
386
+ The group keys are accessible through the `@K` shortcut
387
+
388
+ ```ruby
389
+ >>> data = ObjectTable.new(name: ['John', 'Tom', 'John', 'Tom', 'Jim'], value: 1..5)
390
+ >>> data.group_by(:name).each{ p @K }
391
+ {:name=>"John"}
392
+ {:name=>"Tom"}
393
+ {:name=>"Jim"}
394
+
395
+ # or if you are using a block with args
396
+ >>> data.group_by(:name).each{|grp| p grp.K }
397
+ {:name=>"John"}
398
+ {:name=>"Tom"}
399
+ {:name=>"Jim"}
292
400
  ```
293
401
 
402
+
294
403
  ### Aggregation
295
404
 
405
+ Call `#apply` and the results are stored into a table.
406
+
407
+ ```ruby
408
+ >>> data = ObjectTable.new(name: ['John', 'Tom', 'John', 'Tom', 'Jim'], value: 1..5)
409
+ >>> data.group_by(:name).apply{ value.mean }
410
+ => ObjectTable(3, 2)
411
+ name v_0
412
+ 0: "John" 2.0
413
+ 1: "Tom" 3.0
414
+ 2: "Jim" 5.0
415
+ name v_0
416
+ ```
417
+
296
418
  Normally you can only have one aggregated column with a default name of v_0.
297
419
  You can have more columns and set column names by making a `ObjectTable` or using the @R shortcut.
298
420
 
299
421
  ```ruby
300
- >>> data.group{ {initial: name.map{|n| n[0]}} }.apply{ @R[ mean: value.mean, sum: value.sum ] }
301
- => ObjectTable(2, 4)
302
- initial mean sum std
303
- 0: "J" 3.0 9 2.0
304
- 1: "T" 3.0 6 1.4142135623730951
305
- initial mean sum std
422
+ >>> data.group_by(:name).apply{ @R[ mean: value.mean, sum: value.sum] }
423
+ => ObjectTable(3, 3)
424
+ name mean sum
425
+ 0: "John" 2.0 4
426
+ 1: "Tom" 3.0 6
427
+ 2: "Jim" 5.0 5
428
+ name mean sum
429
+
430
+ # or if you are using a block with args
431
+ >>> data.group_by(:name).apply{|grp| grp.R[ mean: grp.value.mean, sum: grp.value.sum] }
432
+ => ObjectTable(3, 3)
433
+ name mean sum
434
+ 0: "John" 2.0 4
435
+ 1: "Tom" 3.0 6
436
+ 2: "Jim" 5.0 5
437
+ name mean sum
306
438
  ```
307
439
 
308
440
  ### Assigning to columns
@@ -310,13 +442,74 @@ You can have more columns and set column names by making a `ObjectTable` or usin
310
442
  Assigning to columns will assign by group.
311
443
 
312
444
  ```ruby
313
- >>> data.group{ {initial: name.map{|n| n[0]}} }.each{ self[:num_same_initial] = nrows }
445
+ # every row with the same name will get the same group_values
446
+ >>> data.group_by(:name).each{|grp| grp[:group_values] = grp.value.to_a.join(',') }
314
447
  => ObjectTable(5, 3)
315
- name value num_same_initial
316
- 0: "John" 1 3
317
- 1: "Tom" 2 2
318
- 2: "Jim" 3 3
319
- 3: "Tim" 4 2
320
- 4: "John" 5 3
321
- name value num_same_initial
448
+ name value group_values
449
+ 0: "John" 1 "1,3"
450
+ 1: "Tom" 2 "2,4"
451
+ 2: "John" 3 "1,3"
452
+ 3: "Tom" 4 "2,4"
453
+ 4: "Jim" 5 "5"
454
+ name value group_values
455
+ ```
456
+
457
+ ## Subclassing ObjectTable
458
+
459
+ The act of subclassing itself is easy, but any methods you add won't be available to child views and groups.
460
+
461
+ ```ruby
462
+ >>> class BrokenTable < ObjectTable
463
+ def a_plus_b
464
+ a + b
465
+ end
466
+ end
467
+
468
+ >>> data = BrokenTable.new(a: 1..3, b: 4..6)
469
+ >>> data.a_plus_b
470
+ => ObjectTable::Column.int(3):
471
+ [ 5, 7, 9 ]
472
+
473
+ # this won't work!
474
+ >>> data.where{ a > 1 }.a_plus_b
475
+ NoMethodError: undefined method `a_plus_b' for #<ObjectTable::View:0x000000011d4dd0>
476
+ ```
477
+
478
+ To make it work, you'll need to subclass `View`, `StaticView` and `Group` too. Then set the `Table` constant on each. The easiest way is just to include a module.
479
+
480
+ ```ruby
481
+ >>> class WorkingTable < ObjectTable
482
+ module Mixin
483
+ # this mixin will set the Table constant on each of the subclasses
484
+ Table = WorkingTable
485
+
486
+ def a_plus_b
487
+ a + b
488
+ end
489
+ end
490
+
491
+ include Mixin
492
+
493
+ # subclass each of these and include the Mixin too
494
+ class StaticView < StaticView; include Mixin; end
495
+ class View < View; include Mixin; end
496
+ class Group < Group; include Mixin; end
497
+ end
498
+
499
+ >>> data = WorkingTable.new(a: 1..3, b: 4..6)
500
+ >>> data.a_plus_b
501
+ => ObjectTable::Column.int(3):
502
+ [ 5, 7, 9 ]
503
+
504
+ # hurrah!
505
+ >>> data.where{ a > 1 }.a_plus_b
506
+ => ObjectTable::Column.int(2):
507
+ [ 7, 9 ]
508
+
509
+ # also works in groups!
510
+ >>> data.group_by{{odd: a % 2}}.each do
511
+ p "when a % 2 == #{@K[:odd]}, a + b == #{a_plus_b.to_a}"
512
+ end
513
+ "when a % 2 == 1, a + b == [5, 9]"
514
+ "when a % 2 == 0, a + b == [7]"
322
515
  ```
@@ -13,7 +13,7 @@ class ObjectTable::BasicGrid < Hash
13
13
  narrays, scalars = scalars.partition{|k, v| v.is_a?(NArray) }
14
14
 
15
15
  unique_rows = arrays.map{|k, v| v.count}
16
- unique_rows += narrays.map{|k, v| v.shape.last}
16
+ unique_rows += narrays.map{|k, v| v.shape.last or 0}
17
17
  unique_rows = unique_rows.uniq
18
18
 
19
19
  if rows
@@ -0,0 +1,10 @@
1
+ require_relative 'static_view'
2
+
3
+ class ObjectTable::Group < ObjectTable::StaticView
4
+ attr_reader :K
5
+
6
+ def initialize(parent, keys, value)
7
+ super(parent, value)
8
+ @K = keys
9
+ end
10
+ end
@@ -1,17 +1,8 @@
1
- require_relative 'view'
1
+ require_relative 'group'
2
2
 
3
3
  class ObjectTable::Grouped
4
4
  DEFAULT_VALUE_PREFIX = 'v_'
5
5
 
6
- class Group < ObjectTable::View
7
- attr_reader :K
8
-
9
- def initialize(parent, keys, value)
10
- super(parent, value)
11
- @K = keys
12
- end
13
- end
14
-
15
6
  def initialize(parent, names, groups)
16
7
  @parent = parent
17
8
  @names = names
@@ -19,19 +10,23 @@ class ObjectTable::Grouped
19
10
  end
20
11
 
21
12
  def each(&block)
13
+ group_cls = @parent.class::Table::Group
14
+
22
15
  @groups.each do |k, v|
23
16
  names = @names.zip(k)
24
- Group.new(@parent, Hash[names], v).apply &block
17
+ group_cls.new(@parent, Hash[names], v).apply &block
25
18
  end
26
19
  @parent
27
20
  end
28
21
 
29
22
  def apply(&block)
23
+ table_cls = @parent.class::Table
24
+ group_cls = table_cls::Group
30
25
  value_key = self.class._generate_name(DEFAULT_VALUE_PREFIX, @names).to_sym
31
26
 
32
27
  data = @groups.map do |k, v|
33
28
  names = @names.zip(k)
34
- value = Group.new(@parent, Hash[names], v).apply &block
29
+ value = group_cls.new(@parent, Hash[names], v).apply &block
35
30
 
36
31
  if value.is_a?(ObjectTable::TableMethods)
37
32
  value = value.columns
@@ -46,7 +41,7 @@ class ObjectTable::Grouped
46
41
  grid._ensure_uniform_columns!
47
42
  end
48
43
 
49
- ObjectTable.stack(*data)
44
+ table_cls.stack(*data)
50
45
  end
51
46
 
52
47
  def self._generate_name(prefix, existing_names)
@@ -28,8 +28,10 @@ class ObjectTable::MaskedColumn < ObjectTable::Column
28
28
  alias_method :super_slice_assign, :[]=
29
29
 
30
30
  def []=(*keys, value)
31
- parent[*padded_dims, indices[*keys]] = value
32
- super
31
+ unless (value.is_a?(Array) or value.is_a?(NArray)) and value.empty?
32
+ parent[*padded_dims, indices[*keys]] = value
33
+ super
34
+ end
33
35
  end
34
36
 
35
37
  # make destructive methods affect parent
@@ -0,0 +1,24 @@
1
+ require_relative 'view_methods'
2
+ require_relative 'basic_grid'
3
+ require_relative 'masked_column'
4
+
5
+ class ObjectTable::StaticView
6
+ include ObjectTable::ViewMethods
7
+ attr_reader :indices
8
+
9
+ def initialize(parent, indices)
10
+ super()
11
+ @parent = parent
12
+ @indices = indices
13
+ columns
14
+ end
15
+
16
+ def columns
17
+ @columns ||= super
18
+ end
19
+
20
+ def add_column(name, *args)
21
+ @columns[name] = super
22
+ end
23
+
24
+ end
@@ -1,6 +1,11 @@
1
1
  require 'forwardable'
2
2
 
3
3
  module ObjectTable::TableMethods
4
+ # this line is important!! which classes to use to make Views/StaticViews/Groups
5
+ # are taken from this constant, e.g. Table::View
6
+ Table = ObjectTable
7
+
8
+
4
9
  extend Forwardable
5
10
 
6
11
  attr_reader :R
@@ -19,7 +24,7 @@ module ObjectTable::TableMethods
19
24
  end
20
25
 
21
26
  def nrows
22
- columns.empty? ? 0 : columns.values.first.shape[-1]
27
+ columns.empty? ? 0 : (columns.values.first.shape[-1] or 0)
23
28
  end
24
29
 
25
30
  def ncols
@@ -41,14 +46,16 @@ module ObjectTable::TableMethods
41
46
 
42
47
  if (value.is_a?(Array) or value.is_a?(NArray)) and args.empty?
43
48
  value = NArray.to_na(value)
44
- unless value.shape[-1] == nrows
45
- raise ArgumentError.new("Expected size of last dimension to be #{nrows}, was #{value.shape[-1]}")
49
+ unless (value.shape[-1] or 0) == nrows
50
+ raise ArgumentError.new("Expected size of last dimension to be #{nrows}, was #{value.shape[-1].inspect}")
46
51
  end
47
52
 
48
53
  args = [value.typecode] + value.shape[0...-1]
49
54
  end
50
55
 
51
56
  column = add_column(name, *args)
57
+ return column if value.is_a?(NArray) and value.empty?
58
+
52
59
  begin
53
60
  column[] = value
54
61
  rescue Exception => e
@@ -63,25 +70,30 @@ module ObjectTable::TableMethods
63
70
  end
64
71
 
65
72
  def apply(&block)
66
- result = instance_eval &block
73
+ if block.arity == 0
74
+ result = instance_eval &block
75
+ else
76
+ result = block.call(self)
77
+ end
78
+
67
79
  if result.is_a? ObjectTable::BasicGrid
68
- result = ObjectTable.new(result)
80
+ result = self.class::Table.new(result)
69
81
  end
70
82
  result
71
83
  end
72
84
 
73
85
  def where(&block)
74
- ObjectTable::TempView.new(self, &block)
86
+ self.class::Table::View.new(self, &block)
75
87
  end
76
88
 
77
- def group(*args, &block)
89
+ def group_by(*args, &block)
78
90
  ObjectTable::TempGrouped.new(self, *args, &block)
79
91
  end
80
92
 
81
93
  def sort_by(*keys)
82
94
  sort_index = _get_sort_index(keys)
83
95
  cols = ObjectTable::BasicGrid[columns.map{|k, v| [k, v[sort_index]]}]
84
- ObjectTable.new(cols)
96
+ self.class::Table.new(cols)
85
97
  end
86
98
 
87
99
  def method_missing(meth, *args, &block)
@@ -136,7 +148,7 @@ module ObjectTable::TableMethods
136
148
 
137
149
  def clone
138
150
  cols = ObjectTable::BasicGrid[columns.map{|k, v| [k, v.clone]}]
139
- ObjectTable.new(cols)
151
+ self.class::Table.new(cols)
140
152
  end
141
153
 
142
154
  def _get_sort_index(columns)
@@ -22,7 +22,7 @@ class ObjectTable::TempGrouped
22
22
 
23
23
  def _keys
24
24
  if @names.empty?
25
- keys = @parent.instance_eval(&@grouper)
25
+ keys = @parent.apply(&@grouper)
26
26
  raise 'Group keys must be hashes' unless keys.is_a?(Hash)
27
27
  keys = ObjectTable::BasicGrid.new.replace keys
28
28
  else
@@ -1,3 +1,3 @@
1
1
  class ObjectTable
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end