fat_core 1.5.3 → 1.6.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: 2d398b7ceb368a1c99d20a78b6b223b7d6d0fee1
4
- data.tar.gz: 3107bfa35da91b30a2001b56f85daef907ce9789
3
+ metadata.gz: f2a1154179dc04fcdfca93f7e883ead5572b5ea4
4
+ data.tar.gz: 0b93b77f79888904d940124f51c00afb50beae61
5
5
  SHA512:
6
- metadata.gz: 6c982073829b3209c0a03dfb9f16e30b0d6aaa321fc7720de5dac5a9c6bb36db7c9560087a53cee6a4fefa06273f317d8df25e7ac0a90d60efca87ee876e5ab1
7
- data.tar.gz: 090803a0545f56bb24d7fdda0d27c5c21ff27b9eaa73ef69e3190ac5b74d60a02ffac6a2bf16b649e64547f19edabfa9ce44d4ab6172167e1814de3b272855b1
6
+ metadata.gz: caf9a264b140596a98fcbeaaea71f01890c1677b941c1b0e9118c446b9e81190ca83366a0e967df76e5f2d9b3ceb0f024bf36f75370ce8c6770be9c7e569c865
7
+ data.tar.gz: 1f032a4c6fade3021ae2c9beb6a16ec15b06eaec9d16c75cd070d5526cd1cae71b305562e353d6f3747665d7ffcff641146b81ee1d6232c9c582190bccc60d77
@@ -32,66 +32,62 @@ module FatCore
32
32
  # 'Two Words' becomes the hash header :two_words.
33
33
  #
34
34
  # An entire column can be retrieved by header from a Table, thus,
35
- # #+BEGIN_EXAMPLE
36
- # tab = Table.new("example.org")
35
+ #
36
+ # tab = Table.from_org_file("example.org")
37
37
  # tab[:age].avg
38
- # #+END_EXAMPLE
38
+ #
39
39
  # will extract the entire ~:age~ column and compute its average, since Column
40
40
  # objects respond to aggregate methods, such as ~sum~, ~min~, ~max~, and ~avg~.
41
41
  class Table
42
42
  attr_reader :columns, :footers
43
43
 
44
- TYPES = %w(NilClass TrueClass FalseClass Date DateTime Numeric String)
45
-
46
44
  def initialize(input = nil, ext = '.csv')
47
45
  @columns = []
48
46
  @footers = {}
49
47
  @boundaries = []
50
- return self if input.nil?
51
- case input
52
- when IO, StringIO
53
- case ext
54
- when /csv/i
55
- from_csv(input)
56
- when /org/i
57
- from_org(input)
58
- else
59
- raise "Don't know how to read a '#{ext}' file."
60
- end
61
- when String
62
- ext = File.extname(input).downcase
63
- File.open(input, 'r') do |io|
64
- case ext
65
- when '.csv'
66
- from_csv(io)
67
- when '.org'
68
- from_org(io)
69
- else
70
- raise "Don't know how to read a '#{ext}' file."
71
- end
72
- end
73
- when Array
74
- case input[0]
75
- when Array
76
- from_array_of_arrays(input)
77
- when Hash
78
- from_array_of_hashes(input)
79
- when Table
80
- from_table(input)
81
- else
82
- if input[0].respond_to?(:to_hash)
83
- from_array_of_hashes(input)
84
- else
85
- raise ArgumentError,
86
- "Cannot initialize Table with an array of #{input[0].class}"
87
- end
88
- end
48
+ end
49
+
50
+ ###########################################################################
51
+ # Constructors
52
+ ###########################################################################
53
+
54
+ def self.from_csv_string(str)
55
+ from_csv_io(StringIO.new(str))
56
+ end
57
+
58
+ def self.from_csv_file(fname)
59
+ File.open(fname, 'r') do |io|
60
+ from_csv_io(io)
61
+ end
62
+ end
63
+
64
+ def self.from_org_string(str)
65
+ from_org_io(StringIO.new(str))
66
+ end
67
+
68
+ def self.from_org_file(fname)
69
+ File.open(fname, 'r') do |io|
70
+ from_org_io(io)
71
+ end
72
+ end
73
+
74
+ def self.from_aoa(aoa)
75
+ from_array_of_arrays(aoa)
76
+ end
77
+
78
+ def self.from_aoh(aoh)
79
+ if aoh[0].respond_to?(:to_h)
80
+ from_array_of_hashes(aoh)
89
81
  else
90
82
  raise ArgumentError,
91
- "Cannot initialize Table with #{input.class}"
83
+ "Cannot initialize Table with an array of #{input[0].class}"
92
84
  end
93
85
  end
94
86
 
87
+ def self.from_table(table)
88
+ from_aoh(table.rows)
89
+ end
90
+
95
91
  # Return the column with the given header.
96
92
  def column(key)
97
93
  columns.detect { |c| c.header == key.as_sym }
@@ -219,6 +215,16 @@ module FatCore
219
215
  groups
220
216
  end
221
217
 
218
+ # Mark a boundary at k, and if k is nil, the last row in the table
219
+ # as a group boundary.
220
+ def mark_boundary(k = nil)
221
+ if k
222
+ boundaries.push(k)
223
+ else
224
+ boundaries.push(size - 1)
225
+ end
226
+ end
227
+
222
228
  protected
223
229
 
224
230
  # Reader for boundaries, but not public.
@@ -240,16 +246,6 @@ module FatCore
240
246
  boundaries
241
247
  end
242
248
 
243
- # Mark a boundary at k, and if k is nil, the last row in the table
244
- # as a group boundary.
245
- def mark_boundary(k = nil)
246
- if k
247
- boundaries.push(k)
248
- else
249
- boundaries.push(size - 1)
250
- end
251
- end
252
-
253
249
  # Concatenate the array of argument bounds to this table's boundaries, but
254
250
  # increase each of the indexes in bounds by shift. This is used in the
255
251
  # #union_all method.
@@ -885,6 +881,7 @@ module FatCore
885
881
  # using this method as a primitive.
886
882
  def add_row(row, mark: false)
887
883
  row.each_pair do |k, v|
884
+ binding.pry if k.nil?
888
885
  key = k.as_sym
889
886
  columns << Column.new(header: k) unless column?(k)
890
887
  column(key) << v
@@ -904,97 +901,103 @@ module FatCore
904
901
  self
905
902
  end
906
903
 
907
- private
904
+ class << self
908
905
 
909
- # Construct table from an array of hashes or an array of any object that can
910
- # respond to #to_hash.
911
- def from_array_of_hashes(rows)
912
- rows.each do |row|
913
- if row.nil?
914
- mark_boundary
915
- next
916
- end
917
- add_row(row.to_hash)
918
- end
919
- self
920
- end
906
+ private
921
907
 
922
- # Construct a new table from an array of arrays. If the second element of
923
- # the array is a nil, a string that looks like an hrule, or an array whose
924
- # first element is a string that looks like an hrule, interpret the first
925
- # element of the array as a row of headers. Otherwise, synthesize headers of
926
- # the form "col1", "col2", ... and so forth. The remaining elements are
927
- # taken as the body of the table, except that if an element of the outer
928
- # array is a nil or a string that looks like an hrule, mark the preceding
929
- # row as a boundary.
930
- def from_array_of_arrays(rows)
931
- hrule_re = /\A\s*\|[-+]+/
932
- headers = []
933
- if rows[1].nil? || rows[1] =~ hrule_re || rows[1].first =~ hrule_re
934
- # Take the first row as headers
935
- # Use first row 0 as headers
936
- headers = rows[0].map(&:as_sym)
937
- first_data_row = 2
938
- else
939
- # Synthesize headers
940
- headers = (1..rows[0].size).to_a.map { |k| "col#{k}".as_sym }
941
- first_data_row = 0
942
- end
943
- rows[first_data_row..-1].each do |row|
944
- if row.nil? || row[0] =~ hrule_re
945
- mark_boundary
946
- next
908
+ # Construct table from an array of hashes or an array of any object that can
909
+ # respond to #to_hash.
910
+ def from_array_of_hashes(hashes)
911
+ result = Table.new
912
+ hashes.each do |hsh|
913
+ if hsh.nil?
914
+ result.mark_boundary
915
+ next
916
+ end
917
+ result << hsh.to_h
947
918
  end
948
- row = row.map { |s| s.to_s.strip }
949
- hash_row = Hash[headers.zip(row)]
950
- add_row(hash_row)
951
- end
952
- self
953
- end
954
-
955
- def from_csv(io)
956
- ::CSV.new(io, headers: true, header_converters: :symbol,
957
- skip_blanks: true).each do |row|
958
- add_row(row.to_hash)
919
+ result
920
+ end
921
+
922
+ # Construct a new table from an array of arrays. If the second element of
923
+ # the array is a nil, a string that looks like an hrule, or an array whose
924
+ # first element is a string that looks like an hrule, interpret the first
925
+ # element of the array as a row of headers. Otherwise, synthesize headers of
926
+ # the form "col1", "col2", ... and so forth. The remaining elements are
927
+ # taken as the body of the table, except that if an element of the outer
928
+ # array is a nil or a string that looks like an hrule, mark the preceding
929
+ # row as a boundary.
930
+ def from_array_of_arrays(rows)
931
+ result = Table.new
932
+ hrule_re = /\A\s*\|[-+]+/
933
+ headers = []
934
+ if rows[1].nil? || rows[1] =~ hrule_re || rows[1].first =~ hrule_re
935
+ # Take the first row as headers
936
+ # Use first row 0 as headers
937
+ headers = rows[0].map(&:as_sym)
938
+ first_data_row = 2
939
+ else
940
+ # Synthesize headers
941
+ headers = (1..rows[0].size).to_a.map { |k| "col#{k}".as_sym }
942
+ first_data_row = 0
943
+ end
944
+ rows[first_data_row..-1].each do |row|
945
+ if row.nil? || row[0] =~ hrule_re
946
+ result.mark_boundary
947
+ next
948
+ end
949
+ row = row.map { |s| s.to_s.strip }
950
+ hash_row = Hash[headers.zip(row)]
951
+ result << hash_row
952
+ end
953
+ result
959
954
  end
960
- self
961
- end
962
955
 
963
- # Form rows of table by reading the first table found in the org file.
964
- def from_org(io)
965
- table_re = /\A\s*\|/
966
- hrule_re = /\A\s*\|[-+]+/
967
- rows = []
968
- table_found = false
969
- header_found = false
970
- io.each do |line|
971
- unless table_found
972
- # Skip through the file until a table is found
973
- next unless line =~ table_re
974
- unless line =~ hrule_re
956
+ def from_csv_io(io)
957
+ result = new
958
+ ::CSV.new(io, headers: true, header_converters: :symbol,
959
+ skip_blanks: true).each do |row|
960
+ result << row.to_h
961
+ end
962
+ result
963
+ end
964
+
965
+ # Form rows of table by reading the first table found in the org file.
966
+ def from_org_io(io)
967
+ table_re = /\A\s*\|/
968
+ hrule_re = /\A\s*\|[-+]+/
969
+ rows = []
970
+ table_found = false
971
+ header_found = false
972
+ io.each do |line|
973
+ unless table_found
974
+ # Skip through the file until a table is found
975
+ next unless line =~ table_re
976
+ unless line =~ hrule_re
977
+ line = line.sub(/\A\s*\|/, '').sub(/\|\s*\z/, '')
978
+ rows << line.split('|').map(&:clean)
979
+ end
980
+ table_found = true
981
+ next
982
+ end
983
+ break unless line =~ table_re
984
+ if !header_found && line =~ hrule_re
985
+ rows << nil
986
+ header_found = true
987
+ next
988
+ elsif header_found && line =~ hrule_re
989
+ # Mark the boundary with a nil
990
+ rows << nil
991
+ elsif line !~ table_re
992
+ # Stop reading at the second hline
993
+ break
994
+ else
975
995
  line = line.sub(/\A\s*\|/, '').sub(/\|\s*\z/, '')
976
996
  rows << line.split('|').map(&:clean)
977
997
  end
978
- table_found = true
979
- next
980
- end
981
- break unless line =~ table_re
982
- if !header_found && line =~ hrule_re
983
- rows << nil
984
- header_found = true
985
- next
986
- elsif header_found && line =~ hrule_re
987
- # Mark the boundary with a nil
988
- rows << nil
989
- elsif line !~ table_re
990
- # Stop reading at the second hline
991
- break
992
- else
993
- line = line.sub(/\A\s*\|/, '').sub(/\|\s*\z/, '')
994
- rows << line.split('|').map(&:clean)
995
998
  end
999
+ from_array_of_arrays(rows)
996
1000
  end
997
- from_array_of_arrays(rows)
998
1001
  end
999
1002
  end
1000
1003
  end
@@ -1,7 +1,7 @@
1
1
  module FatCore
2
2
  MAJOR = 1
3
- MINOR = 5
4
- PATCH = 3
3
+ MINOR = 6
4
+ PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].compact.join('.')
7
7
  end
@@ -155,8 +155,8 @@ EOS
155
155
  end
156
156
 
157
157
  describe 'construction' do
158
- it 'should be create-able from a CSV IO object' do
159
- tab = Table.new(StringIO.new(@csv_file_body), '.csv')
158
+ it 'should be create-able from a CSV string' do
159
+ tab = Table.from_csv_string(@csv_file_body)
160
160
  expect(tab.class).to eq(Table)
161
161
  expect(tab.rows.size).to be > 20
162
162
  expect(tab.headers.sort)
@@ -176,8 +176,8 @@ EOS
176
176
  end
177
177
  end
178
178
 
179
- it 'should be create-able from an Org IO object' do
180
- tab = Table.new(StringIO.new(@org_file_body), '.org')
179
+ it 'should be create-able from an Org string' do
180
+ tab = Table.from_org_string(@org_file_body)
181
181
  expect(tab.class).to eq(Table)
182
182
  expect(tab.rows.size).to be > 10
183
183
  expect(tab.headers.sort)
@@ -198,8 +198,8 @@ EOS
198
198
  end
199
199
  end
200
200
 
201
- it 'should be create-able from an Org IO object with groups' do
202
- tab = Table.new(StringIO.new(@org_file_body), '.org')
201
+ it 'should be create-able from an Org string with groups' do
202
+ tab = Table.from_org_string(@org_file_body)
203
203
  expect(tab.class).to eq(Table)
204
204
  expect(tab.rows.size).to be > 10
205
205
  expect(tab.headers.sort)
@@ -222,7 +222,7 @@ EOS
222
222
 
223
223
  it 'should be create-able from a CSV file' do
224
224
  File.open('/tmp/junk.csv', 'w') { |f| f.write(@csv_file_body) }
225
- tab = Table.new('/tmp/junk.csv')
225
+ tab = Table.from_csv_file('/tmp/junk.csv')
226
226
  expect(tab.class).to eq(Table)
227
227
  expect(tab.rows.size).to be > 20
228
228
  expect(tab.headers.sort)
@@ -243,9 +243,9 @@ EOS
243
243
  end
244
244
  end
245
245
 
246
- it 'should be create-able from an Org IO object' do
246
+ it 'should be create-able from an Org file' do
247
247
  File.open('/tmp/junk.org', 'w') { |f| f.write(@org_file_body) }
248
- tab = Table.new('/tmp/junk.org')
248
+ tab = Table.from_org_file('/tmp/junk.org')
249
249
  expect(tab.class).to eq(Table)
250
250
  expect(tab.rows.size).to be > 10
251
251
  expect(tab.rows[0].keys.sort)
@@ -276,7 +276,7 @@ EOS
276
276
  ['7', '8', '9.0'],
277
277
  [10, 11, 12.1]
278
278
  ]
279
- tab = Table.new(aoa)
279
+ tab = Table.from_aoa(aoa)
280
280
  expect(tab.class).to eq(Table)
281
281
  expect(tab.rows.size).to eq(4)
282
282
  expect(tab.rows[0].keys.sort).to eq [:first, :second, :third]
@@ -299,7 +299,7 @@ EOS
299
299
  ['7', '8', '9.0'],
300
300
  [10, 11, 12.1]
301
301
  ]
302
- tab = Table.new(aoa)
302
+ tab = Table.from_aoa(aoa)
303
303
  expect(tab.class).to eq(Table)
304
304
  expect(tab.rows.size).to eq(4)
305
305
  expect(tab.headers.sort).to eq [:first, :second, :third]
@@ -321,7 +321,7 @@ EOS
321
321
  [7, 8, 9.3]
322
322
  ]
323
323
  # rubocop:enable Style/WordArray
324
- tab = Table.new(aoa)
324
+ tab = Table.from_aoa(aoa)
325
325
  expect(tab.class).to eq(Table)
326
326
  expect(tab.rows.size).to eq(4)
327
327
  expect(tab.headers.sort).to eq [:col1, :col2, :col3]
@@ -342,7 +342,7 @@ EOS
342
342
  { a: '7', 'Two words' => '8', c: '9.0' },
343
343
  { a: 10, 'Two words' => 11, c: 12.4 }
344
344
  ]
345
- tab = Table.new(aoh)
345
+ tab = Table.from_aoh(aoh)
346
346
  expect(tab.class).to eq(Table)
347
347
  expect(tab.rows.size).to eq(4)
348
348
  expect(tab.rows[0].keys.sort).to eq [:a, :c, :two_words]
@@ -358,7 +358,7 @@ EOS
358
358
 
359
359
  it 'should set T F columns to Boolean' do
360
360
  cwd = File.dirname(__FILE__)
361
- dwtab = Table.new(cwd + '/../example_files/datawatch.org')
361
+ dwtab = Table.from_org_file(cwd + '/../example_files/datawatch.org')
362
362
  expect(dwtab.column(:g10).type).to eq('Boolean')
363
363
  expect(dwtab.column(:qp10).type).to eq('Boolean')
364
364
  dwo = dwtab.where('qp10 || g10')
@@ -377,7 +377,7 @@ EOS
377
377
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
378
378
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
379
379
  ]
380
- tab = Table.new(aoh)
380
+ tab = Table.from_aoh(aoh)
381
381
  expect(tab[:a].sum).to eq 12
382
382
  expect(tab[:two_words].sum).to eq 15
383
383
  expect(tab[:c].sum).to eq 19_423
@@ -390,7 +390,7 @@ EOS
390
390
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
391
391
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
392
392
  ]
393
- tab = Table.new(aoh)
393
+ tab = Table.from_aoh(aoh)
394
394
  expect(tab[:a].sum).to eq 11
395
395
  expect(tab[:two_words].sum).to eq 15
396
396
  expect(tab[:c].sum).to eq 16_300
@@ -398,7 +398,7 @@ EOS
398
398
  end
399
399
 
400
400
  it 'should be able to report its headings' do
401
- tab = Table.new(StringIO.new(@csv_file_body), '.csv')
401
+ tab = Table.from_csv_string(@csv_file_body)
402
402
  expect(tab.headers.sort)
403
403
  .to eq [:code, :date, :info, :price, :rawshares, :ref, :shares]
404
404
  end
@@ -409,7 +409,7 @@ EOS
409
409
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
410
410
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
411
411
  ]
412
- tab = Table.new(aoh)
412
+ tab = Table.from_aoh(aoh)
413
413
  expect(tab[:a].to_a).to eq [1, 4, 7]
414
414
  expect(tab[:c].to_a).to eq [3123, 6412, 9888]
415
415
  end
@@ -420,7 +420,7 @@ EOS
420
420
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
421
421
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
422
422
  ]
423
- tab = Table.new(aoh)
423
+ tab = Table.from_aoh(aoh)
424
424
  expect(tab[:a].sum).to eq 12
425
425
  expect(tab[:c].sum).to eq 19_423
426
426
  expect(tab[:c].sum.is_a?(Integer)).to be true
@@ -432,7 +432,7 @@ EOS
432
432
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
433
433
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
434
434
  ]
435
- tab = Table.new(aoh)
435
+ tab = Table.from_aoh(aoh)
436
436
  expect(tab[:a].avg).to eq 4
437
437
  expect(tab[:c].avg.round(4)).to eq 6474.3333
438
438
  expect(tab[:c].avg.class).to eq BigDecimal
@@ -444,7 +444,7 @@ EOS
444
444
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
445
445
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
446
446
  ]
447
- tab = Table.new(aoh)
447
+ tab = Table.from_aoh(aoh)
448
448
  expect(tab[:a].min).to eq 1
449
449
  expect(tab[:c].min.round(4)).to eq 3123
450
450
  expect(tab[:c].min.is_a?(Integer)).to be true
@@ -457,7 +457,7 @@ EOS
457
457
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
458
458
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
459
459
  ]
460
- tab = Table.new(aoh)
460
+ tab = Table.from_aoh(aoh)
461
461
  expect(tab[:a].max).to eq 7
462
462
  expect(tab[:c].max.round(4)).to eq 9888
463
463
  expect(tab[:c].max.is_a?(Integer)).to be true
@@ -472,7 +472,7 @@ EOS
472
472
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
473
473
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
474
474
  ]
475
- tab = Table.new(aoh)
475
+ tab = Table.from_aoh(aoh)
476
476
  tab.add_sum_footer([:a, :c, :two_words])
477
477
  expect(tab.footers[:total][:a]).to eq 12
478
478
  expect(tab.footers[:total][:c]).to eq 19_423
@@ -486,7 +486,7 @@ EOS
486
486
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
487
487
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
488
488
  ]
489
- tab = Table.new(aoh)
489
+ tab = Table.from_aoh(aoh)
490
490
  tab.add_avg_footer([:a, :c, :two_words])
491
491
  expect(tab.footers[:average][:a]).to eq 4
492
492
  expect(tab.footers[:average][:c].round(4)).to eq 6474.3333
@@ -500,7 +500,7 @@ EOS
500
500
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
501
501
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
502
502
  ]
503
- tab = Table.new(aoh)
503
+ tab = Table.from_aoh(aoh)
504
504
  tab.add_min_footer([:a, :c, :two_words])
505
505
  expect(tab.footers[:minimum][:a]).to eq 1
506
506
  expect(tab.footers[:minimum][:c]).to eq 3123
@@ -514,7 +514,7 @@ EOS
514
514
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
515
515
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
516
516
  ]
517
- tab = Table.new(aoh)
517
+ tab = Table.from_aoh(aoh)
518
518
  tab.add_max_footer([:a, :c, :two_words])
519
519
  expect(tab.footers[:maximum][:a]).to eq 7
520
520
  expect(tab.footers[:maximum][:c]).to eq 9888
@@ -530,7 +530,7 @@ EOS
530
530
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
531
531
  { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
532
532
  ]
533
- tab = Table.new(aoh).order_by(:a)
533
+ tab = Table.from_aoh(aoh).order_by(:a)
534
534
  expect(tab.rows[0][:a]).to eq 4
535
535
  end
536
536
 
@@ -540,7 +540,7 @@ EOS
540
540
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
541
541
  { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
542
542
  ]
543
- tab = Table.new(aoh).order_by(:d, :c)
543
+ tab = Table.from_aoh(aoh).order_by(:d, :c)
544
544
  expect(tab.rows[0][:a]).to eq 7
545
545
  end
546
546
 
@@ -550,7 +550,7 @@ EOS
550
550
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
551
551
  { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
552
552
  ]
553
- tab = Table.new(aoh).order_by(:d!)
553
+ tab = Table.from_aoh(aoh).order_by(:d!)
554
554
  expect(tab.rows[0][:d]).to eq 'orange'
555
555
  expect(tab.rows[2][:d]).to eq 'apple'
556
556
  end
@@ -561,7 +561,7 @@ EOS
561
561
  { a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
562
562
  { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
563
563
  ]
564
- tab = Table.new(aoh).order_by(:d!, :c)
564
+ tab = Table.from_aoh(aoh).order_by(:d!, :c)
565
565
  expect(tab.rows[0][:d]).to eq 'orange'
566
566
  expect(tab.rows[1][:d]).to eq 'apple'
567
567
  expect(tab.rows[1][:c]).to eq 1888
@@ -576,13 +576,13 @@ EOS
576
576
  { a: '4', 'Two words' => '5', c: 6412, d: 'orange' },
577
577
  { a: '7', 'Two words' => '8', c: '$1,888', d: 'apple' }
578
578
  ]
579
- tab1 = Table.new(aoh)
579
+ tab1 = Table.from_aoh(aoh)
580
580
  aoh2 = [
581
581
  { t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
582
582
  { t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
583
583
  { t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
584
584
  ]
585
- tab2 = Table.new(aoh2)
585
+ tab2 = Table.from_aoh(aoh2)
586
586
  utab = tab1.union(tab2)
587
587
  expect(utab.rows.size).to eq(6)
588
588
  end
@@ -593,13 +593,13 @@ EOS
593
593
  { a: '4', 'Two words' => '5', c: 6412 },
594
594
  { a: '7', 'Two words' => '8', c: '$1,888' }
595
595
  ]
596
- tab1 = Table.new(aoh)
596
+ tab1 = Table.from_aoh(aoh)
597
597
  aoh2 = [
598
598
  { t: '8', 'Two worlds' => '65', s: '5,143', u: 'kiwi' },
599
599
  { t: '87', 'Two worlds' => '12', s: 412, u: 'banana' },
600
600
  { t: '13', 'Two worlds' => '11', s: '$1,821', u: 'grape' }
601
601
  ]
602
- tab2 = Table.new(aoh2)
602
+ tab2 = Table.from_aoh(aoh2)
603
603
  expect {
604
604
  tab1.union(tab2)
605
605
  }.to raise_error(/different number of columns/)
@@ -611,13 +611,13 @@ EOS
611
611
  { a: '4', 'Two words' => '5', s: 412, c: 6412 },
612
612
  { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
613
613
  ]
614
- tab1 = Table.new(aoh)
614
+ tab1 = Table.from_aoh(aoh)
615
615
  aoh2 = [
616
616
  { t: '8', 'Two worlds' => '65', s: '2016-01-17', u: 'kiwi' },
617
617
  { t: '87', 'Two worlds' => '12', s: Date.today, u: 'banana' },
618
618
  { t: '13', 'Two worlds' => '11', s: '[2015-05-21]', u: 'grape' }
619
619
  ]
620
- tab2 = Table.new(aoh2)
620
+ tab2 = Table.from_aoh(aoh2)
621
621
  expect {
622
622
  tab1.union(tab2)
623
623
  }.to raise_error(/different column types/)
@@ -631,7 +631,7 @@ EOS
631
631
  { a: '4', 'Two words' => '5', s: 412, c: 6412 },
632
632
  { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
633
633
  ]
634
- tab1 = Table.new(aoh)
634
+ tab1 = Table.from_aoh(aoh)
635
635
  tab2 = tab1.select(:s, :a, :c)
636
636
  expect(tab2.headers).to eq [:s, :a, :c]
637
637
  end
@@ -642,7 +642,7 @@ EOS
642
642
  { a: '4', 'Two words' => '5', s: 412, c: 6412 },
643
643
  { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
644
644
  ]
645
- tab1 = Table.new(aoh)
645
+ tab1 = Table.from_aoh(aoh)
646
646
  tab2 = tab1.select(former_s: :s, new_a: :a, renew_c: :c)
647
647
  expect(tab2.headers).to eq [:former_s, :new_a, :renew_c]
648
648
  end
@@ -653,7 +653,7 @@ EOS
653
653
  { a: '4', 'Two words' => '5', s: 412, c: 6412 },
654
654
  { a: '7', 'Two words' => '8', s: '$1821', c: '$1888' }
655
655
  ]
656
- tab1 = Table.new(aoh)
656
+ tab1 = Table.from_aoh(aoh)
657
657
  tab2 = tab1.select(:two_words, row: '@row', s_squared: 's * s',
658
658
  arb: 's_squared / (a + c).to_d')
659
659
  expect(tab2.headers).to eq [:two_words, :row, :s_squared, :arb]
@@ -665,7 +665,7 @@ EOS
665
665
  { a: '4', 'Two words' => '5', s: 412, c: 6412 },
666
666
  { a: '7', 'Two words' => '8', s: '$1821', c: '$1_888' }
667
667
  ]
668
- tab1 = Table.new(aoh)
668
+ tab1 = Table.from_aoh(aoh)
669
669
  tab2 = tab1.select(:two_words, s: 's * s', nc: 'c + c', c: 'nc+nc')
670
670
  expect(tab2.headers).to eq [:two_words, :s, :nc, :c]
671
671
  expect(tab2[:s].items).to eq([26450449, 169744, 3316041])
@@ -684,7 +684,7 @@ EOS
684
684
  { a: '4', 'Two words' => '5', s: 412, c: 4412 },
685
685
  { a: '7', 'Two words' => '8', s: '$1521', c: '$3_888' }
686
686
  ]
687
- tab = Table.new(aoh).order_by(:a, :two_words)
687
+ tab = Table.from_aoh(aoh).order_by(:a, :two_words)
688
688
  tab2 = tab.select(:a, :two_words, number: '@row', group: '@group')
689
689
  expect(tab2.headers).to eq [:a, :two_words, :number, :group]
690
690
  expect(tab2[:number].items).to eq([1, 2, 3, 4, 5, 6, 7, 8, 9])
@@ -694,13 +694,13 @@ EOS
694
694
 
695
695
  describe 'where' do
696
696
  it 'should be able to filter rows by expression' do
697
- tab1 = Table.new(StringIO.new(@csv_file_body), '.csv')
697
+ tab1 = Table.from_csv_string(@csv_file_body)
698
698
  tab2 = tab1.where("date < Date.parse('2006-06-01')")
699
699
  expect(tab2[:date].max).to be < Date.parse('2006-06-01')
700
700
  end
701
701
 
702
702
  it 'should where by boolean columns' do
703
- tab =
703
+ aoa =
704
704
  [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
705
705
  nil,
706
706
  [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
@@ -715,7 +715,7 @@ EOS
715
715
  [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
716
716
  [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
717
717
  [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
718
- tab = Table.new(tab).add_sum_footer([:raw, :shares, :price])
718
+ tab = Table.from_aoa(aoa).add_sum_footer([:raw, :shares, :price])
719
719
  tab2 = tab.where('!bool || code == "P"')
720
720
  expect(tab2.rows.size).to eq(5)
721
721
  tab2 = tab.where('code == "S" && raw < 10_000')
@@ -729,7 +729,7 @@ EOS
729
729
  end
730
730
 
731
731
  it 'where select by row and group' do
732
- tab =
732
+ aoa =
733
733
  [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
734
734
  nil,
735
735
  [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
@@ -744,7 +744,7 @@ EOS
744
744
  [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
745
745
  [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
746
746
  [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
747
- tab = Table.new(tab).order_by(:date, :code)
747
+ tab = Table.from_aoa(aoa).order_by(:date, :code)
748
748
  tab2 = tab.where('@row > 10')
749
749
  expect(tab2.rows.size).to eq(2)
750
750
  tab2 = tab.where('@group == 3')
@@ -754,7 +754,7 @@ EOS
754
754
 
755
755
  describe 'group_by' do
756
756
  it 'should be able to group by equal columns' do
757
- tab1 = Table.new(StringIO.new(@csv_file_body), '.csv')
757
+ tab1 = Table.from_csv_string(@csv_file_body)
758
758
  tab2 = tab1.group_by(:date, :code, shares: :sum, ref: :first)
759
759
  expect(tab2.headers).to eq([:date, :code, :sum_shares, :first_ref,
760
760
  :first_rawshares, :first_price, :first_info])
@@ -764,7 +764,7 @@ EOS
764
764
  describe 'join' do
765
765
  # These tests are taken from https://www.tutorialspoint.com/postgresql/postgresql_using_joins.htm
766
766
  before :all do
767
- @tab_a = Table.new([
767
+ @tab_a = Table.from_aoh([
768
768
  { id: 1, name: 'Paul', age: 32, address: 'California', salary: 20000, join_date: '2001-07-13' },
769
769
  { id: 3, name: 'Teddy', age: 23, address: 'Norway', salary: 20000},
770
770
  { id: 4, name: 'Mark', age: 25, address: 'Rich-Mond', salary: 65000, join_date: '2007-12-13' },
@@ -774,7 +774,7 @@ EOS
774
774
  { id: 9, name: 'James', age: 44, address: 'Norway', salary: 5000, join_date: '2005-07-13' },
775
775
  { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' }
776
776
  ])
777
- @tab_b = Table.new([
777
+ @tab_b = Table.from_aoh([
778
778
  { id: 1, dept: 'IT Billing', emp_id: 1 },
779
779
  { id: 2, dept: 'Engineering', emp_id: 2 },
780
780
  { id: 3, dept: 'Finance', emp_id: 7 }
@@ -865,7 +865,7 @@ EOS
865
865
 
866
866
  describe 'group boundaries' do
867
867
  before :all do
868
- @tab_a = Table.new([
868
+ @tab_a = Table.from_aoh([
869
869
  { id: 1, name: 'Paul', age: 32, address: 'California', salary: 20000, join_date: '2001-07-13' },
870
870
  { id: 3, name: 'Teddy', age: 23, address: 'Norway', salary: 20000},
871
871
  { id: 4, name: 'Mark', age: 25, address: 'Rich-Mond', salary: 65000, join_date: '2007-12-13' },
@@ -876,7 +876,7 @@ EOS
876
876
  { id: 10, name: 'James', age: 45, address: 'Texas', salary: 5000, join_date: '2005-07-13' }
877
877
  ])
878
878
  # Union compatible with tab_a
879
- @tab_a1 = Table.new([
879
+ @tab_a1 = Table.from_aoh([
880
880
  { id: 21, name: 'Paula', age: 23, address: 'Kansas', salary: 20000, join_date: '2001-07-13' },
881
881
  { id: 23, name: 'Jenny', age: 32, address: 'Missouri', salary: 20000},
882
882
  { id: 24, name: 'Forrest', age: 52, address: 'Richmond', salary: 65000, join_date: '2007-12-13' },
@@ -891,7 +891,7 @@ EOS
891
891
  { id: 29, name: 'Patrick', age: 44, address: 'Lindsbourg', salary: 5000, join_date: '2005-07-13' },
892
892
  { id: 30, name: 'James', age: 54, address: 'Ottawa', salary: 5000, join_date: '2005-07-13' }
893
893
  ])
894
- @tab_b = Table.new([
894
+ @tab_b = Table.from_aoh([
895
895
  { id: 1, dept: 'IT Billing', emp_id: 1 },
896
896
  { id: 2, dept: 'Engineering', emp_id: 2 },
897
897
  { id: 3, dept: 'Finance', emp_id: 7 }
@@ -938,7 +938,7 @@ EOS
938
938
  end
939
939
 
940
940
  it 'add group boundaries on reading from org text' do
941
- tab = Table.new(StringIO.new(@org_file_body_with_groups), '.org')
941
+ tab = Table.from_org_string(@org_file_body_with_groups)
942
942
  expect(tab.groups.size).to eq(4)
943
943
  expect(tab.groups[0].size).to eq(1)
944
944
  expect(tab.groups[1].size).to eq(3)
@@ -947,7 +947,7 @@ EOS
947
947
  end
948
948
 
949
949
  it 'add group boundaries on reading from aoa' do
950
- tab = Table.new(@aoa)
950
+ tab = Table.from_aoa(@aoa)
951
951
  expect(tab.groups.size).to eq(4)
952
952
  expect(tab.groups[0].size).to eq(1)
953
953
  expect(tab.groups[1].size).to eq(3)
@@ -956,7 +956,7 @@ EOS
956
956
  end
957
957
 
958
958
  it 'add group boundaries on reading from aoh' do
959
- tab = Table.new(@aoh)
959
+ tab = Table.from_aoh(@aoh)
960
960
  expect(tab.groups.size).to eq(4)
961
961
  expect(tab.groups[0].size).to eq(1)
962
962
  expect(tab.groups[1].size).to eq(3)
@@ -1032,7 +1032,7 @@ EOS
1032
1032
  { a: '4', 'Two words' => '5', c: '6,412', d: 'orange' },
1033
1033
  { a: '7', 'Two words' => '8', c: '$9,888', d: 'pear' }
1034
1034
  ]
1035
- tab = Table.new(aoh)
1035
+ tab = Table.from_aoh(aoh)
1036
1036
  aoa = tab.to_org
1037
1037
  expect(aoa.class).to eq Array
1038
1038
  expect(aoa[0].class).to eq Array
@@ -1042,7 +1042,7 @@ EOS
1042
1042
  it 'should be able to output an org babel aoa' do
1043
1043
  # This is what the data looks like when called from org babel code
1044
1044
  # blocks.
1045
- tab =
1045
+ aoa =
1046
1046
  [['Ref', 'Date', 'Code', 'Raw', 'Shares', 'Price', 'Info', 'Bool'],
1047
1047
  nil,
1048
1048
  [1, '2013-05-02', 'P', 795_546.20, 795_546.2, 1.1850, 'ZMPEF1', 'T'],
@@ -1057,7 +1057,7 @@ EOS
1057
1057
  [14, '2013-05-29', 'S', 15_700.00, 6601.85, 24.7790, 'ZMEAC', 'F'],
1058
1058
  [15, '2013-05-29', 'S', 15_900.00, 6685.95, 24.5802, 'ZMEAC', 'T'],
1059
1059
  [16, '2013-05-30', 'S', 6_679.00, 2808.52, 25.0471, 'ZMEAC', 'T']]
1060
- tg = Table.new(tab).add_sum_footer([:raw, :shares, :price])
1060
+ tg = Table.from_aoa(aoa).add_sum_footer([:raw, :shares, :price])
1061
1061
  aoa = tg.to_org(formats: { raw: '%,', shares: '%,', price: '%,4' })
1062
1062
  expect(aoa[-1][0]).to eq 'Total'
1063
1063
  expect(aoa[-1][1]).to eq ''
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fat_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.3
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel E. Doherty