fat_table 0.2.6 → 0.2.7

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.
@@ -25,7 +25,7 @@ module FatTable
25
25
  end
26
26
 
27
27
  # Taken from the Rainbow gem's list of valid colors.
28
- self.valid_colors = %w(
28
+ self.valid_colors = %w[
29
29
  none black blue brown cyan darkgray gray green lightgray lime magenta
30
30
  olive orange pink purple red teal violet white yellow AntiqueWhite1
31
31
  AntiqueWhite2 AntiqueWhite3 AntiqueWhite4 Aquamarine1 Aquamarine2
@@ -76,7 +76,7 @@ module FatTable
76
76
  Turquoise1 Turquoise2 Turquoise3 Turquoise4 VioletRed1 VioletRed2
77
77
  VioletRed3 VioletRed4 Wheat1 Wheat2 Wheat3 Wheat4 Yellow1 Yellow2 Yellow3
78
78
  Yellow4
79
- )
79
+ ]
80
80
 
81
81
  # LaTeX commands to load the needed packages based on the :environement
82
82
  # option. For now, just handles the default 'longtable' :environment. The
@@ -4,10 +4,9 @@ module FatTable
4
4
  # timestamps and the connector at the beginning of hlines is a '|' rather than
5
5
  # a '+' as for text tables.
6
6
  class OrgFormatter < Formatter
7
-
8
7
  self.default_format = default_format.dup
9
- self.default_format[:date_fmt] = '[%F]'
10
- self.default_format[:datetime_fmt] = '[%F %a %H:%M:%S]'
8
+ default_format[:date_fmt] = '[%F]'
9
+ default_format[:datetime_fmt] = '[%F %a %H:%M:%S]'
11
10
 
12
11
  private
13
12
 
@@ -20,7 +19,7 @@ module FatTable
20
19
 
21
20
  def pre_header(widths)
22
21
  result = '|'
23
- widths.values.each do |w|
22
+ widths.each_value do |w|
24
23
  result += '-' * (w + 2) + '+'
25
24
  end
26
25
  result[-1] = '|'
@@ -53,7 +52,7 @@ module FatTable
53
52
 
54
53
  def hline(widths)
55
54
  result = '|'
56
- widths.values.each do |w|
55
+ widths.each_value do |w|
57
56
  result += '-' * (w + 2) + '+'
58
57
  end
59
58
  result[-1] = '|'
@@ -62,7 +61,7 @@ module FatTable
62
61
 
63
62
  def post_footers(widths)
64
63
  result = '|'
65
- widths.values.each do |w|
64
+ widths.each_value do |w|
66
65
  result += '-' * (w + 2) + '+'
67
66
  end
68
67
  result[-1] = '|'
@@ -1,5 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
-
3
1
  require 'rainbow'
4
2
 
5
3
  module FatTable
@@ -219,7 +217,7 @@ module FatTable
219
217
 
220
218
  def pre_header(widths)
221
219
  result = upper_left
222
- widths.values.each do |w|
220
+ widths.each_value do |w|
223
221
  result += double_rule * (w + 2) + upper_tee
224
222
  end
225
223
  result[-1] = upper_right
@@ -253,7 +251,7 @@ module FatTable
253
251
 
254
252
  def hline(widths)
255
253
  result = left_tee
256
- widths.values.each do |w|
254
+ widths.each_value do |w|
257
255
  result += horizontal_rule * (w + 2) + single_cross
258
256
  end
259
257
  result[-1] = right_tee
@@ -287,7 +285,7 @@ module FatTable
287
285
 
288
286
  def post_footers(widths)
289
287
  result = lower_left
290
- widths.values.each do |w|
288
+ widths.each_value do |w|
291
289
  result += double_rule * (w + 2) + lower_tee
292
290
  end
293
291
  result[-1] = lower_right
@@ -4,7 +4,6 @@ module FatTable
4
4
  # connector at the beginning of hlines is a '+' rather than a '|' as for org
5
5
  # tables.
6
6
  class TextFormatter < Formatter
7
-
8
7
  private
9
8
 
10
9
  # Does this Formatter require a second pass over the cells to align the
@@ -16,7 +15,7 @@ module FatTable
16
15
 
17
16
  def pre_header(widths)
18
17
  result = '+'
19
- widths.values.each do |w|
18
+ widths.each_value do |w|
20
19
  result += '=' * (w + 2) + '+'
21
20
  end
22
21
  result[-1] = '+'
@@ -49,7 +48,7 @@ module FatTable
49
48
 
50
49
  def hline(widths)
51
50
  result = '+'
52
- widths.values.each do |w|
51
+ widths.each_value do |w|
53
52
  result += '-' * (w + 2) + '+'
54
53
  end
55
54
  result[-1] = '+'
@@ -82,7 +81,7 @@ module FatTable
82
81
 
83
82
  def post_footers(widths)
84
83
  result = '+'
85
- widths.values.each do |w|
84
+ widths.each_value do |w|
86
85
  result += '=' * (w + 2) + '+'
87
86
  end
88
87
  result[-1] = '+'
@@ -1,4 +1,5 @@
1
- unless {a: 1}.respond_to?(:fetch_values)
1
+ unless { a: 1 }.respond_to?(:fetch_values)
2
+ # Add fetch_values if this version of ruby does not define it.
2
3
  class Hash
3
4
  def fetch_values(*keys)
4
5
  result = []
@@ -14,3 +15,30 @@ unless {a: 1}.respond_to?(:fetch_values)
14
15
  end
15
16
  end
16
17
  end
18
+
19
+ unless ''.respond_to?(:match?)
20
+ # Add String#match? to pre-2.4 ruby
21
+ class String
22
+ def match?(re)
23
+ self =~ re
24
+ end
25
+ end
26
+ end
27
+
28
+ unless //.respond_to?(:match?)
29
+ # Add Regexp#match? to pre-2.4 ruby
30
+ class Regexp
31
+ def match?(str)
32
+ self =~ str
33
+ end
34
+ end
35
+ end
36
+
37
+ unless ''.respond_to?(:strip_heredoc)
38
+ class String
39
+ def strip_heredoc
40
+ indent = chomp.scan(/^\s*/).min.size
41
+ gsub(/^\s{#{indent}}/, '')
42
+ end
43
+ end
44
+ end
@@ -49,9 +49,9 @@ module FatTable
49
49
  # spaces converted to underscore and everything down-cased. So, the heading,
50
50
  # 'Two Words' becomes the header +:two_words+.
51
51
  class Table
52
-
53
52
  # An Array of FatTable::Columns that constitute the table.
54
53
  attr_reader :columns
54
+ attr_accessor :boundaries
55
55
 
56
56
  ###########################################################################
57
57
  # Constructors
@@ -140,8 +140,8 @@ module FatTable
140
140
 
141
141
  # :category: Constructors
142
142
 
143
- # Construct a new table from another FatTable::Table object +table+. Inherit any
144
- # group boundaries from the input table.
143
+ # Construct a new table from another FatTable::Table object +table+. Inherit
144
+ # any group boundaries from the input table.
145
145
  def self.from_table(table)
146
146
  table.deep_dup
147
147
  end
@@ -151,7 +151,8 @@ module FatTable
151
151
  # Construct a Table by running a SQL +query+ against the database set up
152
152
  # with FatTable.set_db, with the rows of the query result as rows.
153
153
  def self.from_sql(query)
154
- raise UserError, 'FatTable.db must be set with FatTable.set_db' if FatTable.db.nil?
154
+ msg = 'FatTable.db must be set with FatTable.set_db'
155
+ raise UserError, msg if FatTable.db.nil?
155
156
  result = Table.new
156
157
  FatTable.db[query].each do |h|
157
158
  result << h
@@ -166,15 +167,16 @@ module FatTable
166
167
  class << self
167
168
  private
168
169
 
169
- # Construct table from an array of hashes or an array of any object that can
170
- # respond to #to_h. If an array element is a nil, mark it as a group
170
+ # Construct table from an array of hashes or an array of any object that
171
+ # can respond to #to_h. If an array element is a nil, mark it as a group
171
172
  # boundary in the Table.
172
173
  def from_array_of_hashes(hashes, hlines: false)
173
174
  result = new
174
175
  hashes.each do |hsh|
175
176
  if hsh.nil?
176
177
  unless hlines
177
- raise UserError, 'found an hline in input with hlines false; try setting hlines true'
178
+ msg = 'found an hline in input: try setting hlines true'
179
+ raise UserError, msg
178
180
  end
179
181
  result.mark_boundary
180
182
  next
@@ -219,7 +221,8 @@ module FatTable
219
221
  rows[first_data_row..-1].each do |row|
220
222
  if row.nil?
221
223
  unless hlines
222
- raise UserError, 'found an hline in input with hlines false; try setting hlines true'
224
+ msg = 'found an hline in input: try setting hlines true'
225
+ raise UserError, msg
223
226
  end
224
227
  result.mark_boundary
225
228
  next
@@ -253,15 +256,15 @@ module FatTable
253
256
  io.each do |line|
254
257
  unless table_found
255
258
  # Skip through the file until a table is found
256
- next unless line =~ table_re
257
- unless line =~ hrule_re
259
+ next unless line.match?(table_re)
260
+ unless line.match?(hrule_re)
258
261
  line = line.sub(/\A\s*\|/, '').sub(/\|\s*\z/, '')
259
262
  rows << line.split('|').map(&:clean)
260
263
  end
261
264
  table_found = true
262
265
  next
263
266
  end
264
- break unless line =~ table_re
267
+ break unless line.match?(table_re)
265
268
  if !header_found && line =~ hrule_re
266
269
  rows << nil
267
270
  header_found = true
@@ -311,13 +314,16 @@ module FatTable
311
314
  def [](key)
312
315
  case key
313
316
  when Integer
314
- raise UserError, "index '#{key}' out of range" unless (0..size-1).cover?(key.abs)
317
+ msg = "index '#{key}' out of range"
318
+ raise UserError, msg unless (0..size - 1).cover?(key.abs)
315
319
  rows[key]
316
320
  when String
317
- raise UserError, "header '#{key}' not in table" unless headers.include?(key)
321
+ msg = "header '#{key}' not in table"
322
+ raise UserError, msg unless headers.include?(key)
318
323
  column(key).items
319
324
  when Symbol
320
- raise UserError, "header ':#{key}' not in table" unless headers.include?(key)
325
+ msg = "header ':#{key}' not in table"
326
+ raise UserError, msg unless headers.include?(key)
321
327
  column(key).items
322
328
  else
323
329
  raise UserError, "cannot index table with a #{key.class}"
@@ -413,9 +419,9 @@ module FatTable
413
419
  rows
414
420
  end
415
421
 
416
- #############################################################################
422
+ ############################################################################
417
423
  # Enumerable
418
- #############################################################################
424
+ ############################################################################
419
425
 
420
426
  public
421
427
 
@@ -430,9 +436,6 @@ module FatTable
430
436
  end
431
437
  end
432
438
 
433
-
434
- public
435
-
436
439
  # :category: Attributes
437
440
 
438
441
  # Boundaries mark the last row in each "group" within the table. The last
@@ -505,16 +508,6 @@ module FatTable
505
508
 
506
509
  # :stopdoc:
507
510
 
508
- # Reader for boundaries, but not public.
509
- def boundaries
510
- @boundaries
511
- end
512
-
513
- # Writer for boundaries, but not public.
514
- def boundaries=(bounds)
515
- @boundaries = bounds
516
- end
517
-
518
511
  # Make sure size - 1 is last boundary and that they are unique and sorted.
519
512
  def normalize_boundaries
520
513
  unless empty?
@@ -682,9 +675,8 @@ module FatTable
682
675
  ivars = ivars.merge(new_cols[:ivars])
683
676
  new_cols.delete(:ivars)
684
677
  end
685
- before_hook = '@row += 1'
686
678
  if new_cols.key?(:before_hook)
687
- before_hook += "; #{new_cols[:before_hook]}"
679
+ before_hook = new_cols[:before_hook].to_s
688
680
  new_cols.delete(:before_hook)
689
681
  end
690
682
  after_hook = nil
@@ -702,32 +694,35 @@ module FatTable
702
694
  # Set the group number in the before hook and run the hook with the
703
695
  # local variables set to the row before the new row is evaluated.
704
696
  grp = row_index_to_group_index(old_k)
705
- vars = old_row.merge(__group: grp)
706
- ev.eval_before_hook(vars)
697
+ ev.update_ivars(row: old_k + 1, group: grp)
698
+ ev.eval_before_hook(locals: old_row)
707
699
  # Compute the new row.
708
700
  new_row = {}
709
701
  cols.each do |k|
710
702
  h = k.as_sym
711
- raise UserError, "Column '#{h}' in select does not exist" unless column?(h)
703
+ msg = "Column '#{h}' in select does not exist"
704
+ raise UserError, msg unless column?(h)
712
705
  new_row[h] = old_row[h]
713
706
  end
714
- new_cols.each_pair do |key, val|
707
+ new_cols.each_pair do |key, expr|
715
708
  key = key.as_sym
716
709
  vars = old_row.merge(new_row)
717
- case val
710
+ case expr
718
711
  when Symbol
719
- raise UserError, "Column '#{val}' in select does not exist" unless vars.keys.include?(val)
720
- new_row[key] = vars[val]
712
+ msg = "Column '#{expr}' in select does not exist"
713
+ raise UserError, msg unless vars.keys.include?(expr)
714
+ new_row[key] = vars[expr]
721
715
  when String
722
- new_row[key] = ev.evaluate(val, vars: vars)
716
+ new_row[key] = ev.evaluate(expr, locals: vars)
723
717
  else
724
- raise UserError, "Hash parameter '#{key}' to select must be a symbol or string"
718
+ msg = "Hash parameter '#{key}' to select must be a symbol or string"
719
+ raise UserError, msg
725
720
  end
726
721
  end
727
722
  # Set the group number and run the hook with the local variables set to
728
723
  # the row after the new row is evaluated.
729
- vars = new_row.merge(__group: grp)
730
- ev.eval_after_hook(vars)
724
+ # vars = new_row.merge(__group: grp)
725
+ ev.eval_after_hook(locals: new_row)
731
726
  result << new_row
732
727
  end
733
728
  result.boundaries = boundaries
@@ -753,14 +748,13 @@ module FatTable
753
748
  col = Column.new(header: h)
754
749
  result.add_column(col)
755
750
  end
756
- ev = Evaluator.new(ivars: { row: 0, group: 0 },
757
- before: '@row += 1')
751
+ ev = Evaluator.new(ivars: { row: 0, group: 0 })
758
752
  rows.each_with_index do |row, k|
759
753
  grp = row_index_to_group_index(k)
760
- vars = row.merge(__group: grp)
761
- ev.eval_before_hook(vars)
762
- result << row if ev.evaluate(expr, vars: vars)
763
- ev.eval_after_hook(vars)
754
+ ev.update_ivars(row: k + 1, group: grp)
755
+ ev.eval_before_hook(locals: row)
756
+ result << row if ev.evaluate(expr, locals: row)
757
+ ev.eval_after_hook(locals: row)
764
758
  end
765
759
  result.normalize_boundaries
766
760
  result
@@ -878,10 +872,12 @@ module FatTable
878
872
  add_boundaries: true,
879
873
  inherit_boundaries: false)
880
874
  unless columns.size == other.columns.size
881
- raise UserError, 'Cannot apply a set operation to tables with a different number of columns.'
875
+ msg = "can't apply set ops to tables with a different number of columns"
876
+ raise UserError, msg
882
877
  end
883
878
  unless columns.map(&:type) == other.columns.map(&:type)
884
- raise UserError, 'Cannot apply a set operation to tables with different column types.'
879
+ msg = "can't apply a set ops to tables with different column types."
880
+ raise UserError, msg
885
881
  end
886
882
  other_rows = other.rows.map { |r| r.replace_keys(headers) }
887
883
  result = Table.new
@@ -902,7 +898,7 @@ module FatTable
902
898
  public
903
899
 
904
900
  # An Array of symbols for the valid join types.
905
- JOIN_TYPES = [:inner, :left, :right, :full, :cross].freeze
901
+ JOIN_TYPES = %i[inner left right full cross].freeze
906
902
 
907
903
  # :category: Operators
908
904
  #
@@ -982,7 +978,8 @@ module FatTable
982
978
  # These may be needed for outer joins.
983
979
  self_row_nils = headers.map { |h| [h, nil] }.to_h
984
980
  other_row_nils = other.headers.map { |h| [h, nil] }.to_h
985
- join_expression, other_common_heads = build_join_expression(exps, other, join_type)
981
+ join_exp, other_common_heads =
982
+ build_join_expression(exps, other, join_type)
986
983
  ev = Evaluator.new
987
984
  result = Table.new
988
985
  other_rows = other.rows
@@ -993,7 +990,7 @@ module FatTable
993
990
  # Same as other_row, but with keys that are common with self and equal
994
991
  # in value, removed, so the output table need not repeat them.
995
992
  locals = build_locals_hash(row_a: self_row, row_b: other_row)
996
- matches = ev.evaluate(join_expression, vars: locals)
993
+ matches = ev.evaluate(join_exp, locals: locals)
997
994
  next unless matches
998
995
  self_row_matched = other_row_matches[k] = true
999
996
  out_row = build_out_row(row_a: self_row, row_b: other_row,
@@ -1001,19 +998,18 @@ module FatTable
1001
998
  type: join_type)
1002
999
  result << out_row
1003
1000
  end
1004
- if join_type == :left || join_type == :full
1005
- unless self_row_matched
1006
- out_row = build_out_row(row_a: self_row, row_b: other_row_nils, type: join_type)
1007
- result << out_row
1008
- end
1009
- end
1001
+ next unless %i[left full].include?(join_type)
1002
+ next if self_row_matched
1003
+ result << build_out_row(row_a: self_row,
1004
+ row_b: other_row_nils,
1005
+ type: join_type)
1010
1006
  end
1011
- if join_type == :right || join_type == :full
1007
+ if %i[right full].include?(join_type)
1012
1008
  other_rows.each_with_index do |other_row, k|
1013
- unless other_row_matches[k]
1014
- out_row = build_out_row(row_a: self_row_nils, row_b: other_row, type: join_type)
1015
- result << out_row
1016
- end
1009
+ next if other_row_matches[k]
1010
+ result << build_out_row(row_a: self_row_nils,
1011
+ row_b: other_row,
1012
+ type: join_type)
1017
1013
  end
1018
1014
  end
1019
1015
  result.normalize_boundaries
@@ -1100,8 +1096,8 @@ module FatTable
1100
1096
  b_common_heads = []
1101
1097
  if exps.empty?
1102
1098
  if common_heads.empty?
1103
- raise UserError,
1104
- 'A non-cross join with no common column names requires join expressions'
1099
+ msg = "#{type}-join with no common column names needs join expression"
1100
+ raise UserError, msg
1105
1101
  else
1106
1102
  # A Natural join on all common heads
1107
1103
  common_heads.each do |h|
@@ -1126,7 +1122,9 @@ module FatTable
1126
1122
  end
1127
1123
  if partial_result
1128
1124
  # Second of a pair
1129
- ensure_common_types!(self_h: a_head, other_h: last_sym, other: other)
1125
+ ensure_common_types!(self_h: a_head,
1126
+ other_h: last_sym,
1127
+ other: other)
1130
1128
  partial_result << "#{a_head}_a)"
1131
1129
  and_conds << partial_result
1132
1130
  partial_result = nil
@@ -1142,7 +1140,9 @@ module FatTable
1142
1140
  end
1143
1141
  if partial_result
1144
1142
  # Second of a pair
1145
- ensure_common_types!(self_h: last_sym, other_h: b_head, other: other)
1143
+ ensure_common_types!(self_h: last_sym,
1144
+ other_h: b_head,
1145
+ other: other)
1146
1146
  partial_result << "#{b_head}_b)"
1147
1147
  and_conds << partial_result
1148
1148
  partial_result = nil
@@ -1158,12 +1158,13 @@ module FatTable
1158
1158
  # We were expecting the second of a modified pair, but got an
1159
1159
  # unmodified symbol instead.
1160
1160
  msg =
1161
- "must follow '#{last_sym}' by qualified exp from the other table"
1161
+ "follow '#{last_sym}' by qualified exp from the other table"
1162
1162
  raise UserError, msg
1163
1163
  end
1164
1164
  # We have an unqualified symbol that must appear in both tables
1165
1165
  unless common_heads.include?(exp)
1166
- raise UserError, "unqualified column '#{exp}' must occur in both tables"
1166
+ msg = "unqualified column '#{exp}' must occur in both tables"
1167
+ raise UserError, msg
1167
1168
  end
1168
1169
  ensure_common_types!(self_h: exp, other_h: exp, other: other)
1169
1170
  and_conds << "(#{exp}_a == #{exp}_b)"
@@ -1174,7 +1175,8 @@ module FatTable
1174
1175
  # qualified.
1175
1176
  and_conds << "(#{exp})"
1176
1177
  else
1177
- raise UserError, "invalid join expression '#{exp}' of class #{exp.class}"
1178
+ msg = "invalid join expression '#{exp}' of class #{exp.class}"
1179
+ raise UserError, msg
1178
1180
  end
1179
1181
  end
1180
1182
  [and_conds.join(' && '), b_common_heads]
@@ -1185,15 +1187,15 @@ module FatTable
1185
1187
  # have the same types.
1186
1188
  def ensure_common_types!(self_h:, other_h:, other:)
1187
1189
  unless column(self_h).type == other.column(other_h).type
1188
- raise UserError,
1189
- "type of column '#{self_h}' does not match type of column '#{other_h}"
1190
+ msg = "column '#{self_h}' type does not match column '#{other_h}"
1191
+ raise UserError, msg
1190
1192
  end
1191
1193
  self
1192
1194
  end
1193
1195
 
1194
- ###################################################################################
1196
+ ############################################################################
1195
1197
  # Group By
1196
- ###################################################################################
1198
+ ############################################################################
1197
1199
 
1198
1200
  public
1199
1201
 
@@ -1277,7 +1279,8 @@ module FatTable
1277
1279
 
1278
1280
  # Add a FatTable::Column object +col+ to the table.
1279
1281
  def add_column(col)
1280
- raise "Table already has a column with header '#{col.header}'" if column?(col.header)
1282
+ msg = "Table already has a column with header '#{col.header}'"
1283
+ raise msg if column?(col.header)
1281
1284
  columns << col
1282
1285
  self
1283
1286
  end
@@ -1324,7 +1327,8 @@ module FatTable
1324
1327
  #
1325
1328
  def to_any(fmt_type, options = {})
1326
1329
  fmt = fmt_type.as_sym
1327
- raise UserError, "unknown format '#{fmt}'" unless FatTable::FORMATS.include?(fmt)
1330
+ msg = "unknown format '#{fmt}'"
1331
+ raise UserError, msg unless FatTable::FORMATS.include?(fmt)
1328
1332
  method = "to_#{fmt}"
1329
1333
  if block_given?
1330
1334
  send method, options, &Proc.new