fat_core 1.5.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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