fat_table 0.2.6 → 0.2.7

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