tb 0.9 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README +13 -11
  3. data/lib/tb.rb +14 -6
  4. data/lib/tb/catreader.rb +2 -2
  5. data/lib/tb/cmd_consecutive.rb +6 -2
  6. data/lib/tb/cmd_crop.rb +22 -3
  7. data/lib/tb/cmd_cross.rb +24 -0
  8. data/lib/tb/cmd_cut.rb +20 -10
  9. data/lib/tb/cmd_git.rb +20 -7
  10. data/lib/tb/cmd_group.rb +32 -0
  11. data/lib/tb/cmd_gsub.rb +21 -0
  12. data/lib/tb/cmd_join.rb +28 -0
  13. data/lib/tb/cmd_ls.rb +9 -0
  14. data/lib/tb/cmd_melt.rb +15 -0
  15. data/lib/tb/cmd_mheader.rb +15 -0
  16. data/lib/tb/cmd_nest.rb +27 -6
  17. data/lib/tb/cmd_newfield.rb +19 -2
  18. data/lib/tb/cmd_rename.rb +20 -0
  19. data/lib/tb/{cmd_grep.rb → cmd_search.rb} +37 -23
  20. data/lib/tb/cmd_shape.rb +69 -25
  21. data/lib/tb/cmd_sort.rb +20 -0
  22. data/lib/tb/cmd_tar.rb +38 -0
  23. data/lib/tb/cmd_to_json.rb +2 -2
  24. data/lib/tb/cmd_to_ltsv.rb +3 -3
  25. data/lib/tb/cmd_to_pnm.rb +3 -3
  26. data/lib/tb/cmd_to_tsv.rb +3 -3
  27. data/lib/tb/cmd_to_yaml.rb +3 -3
  28. data/lib/tb/cmd_unmelt.rb +15 -0
  29. data/lib/tb/cmd_unnest.rb +31 -7
  30. data/lib/tb/cmdmain.rb +2 -0
  31. data/lib/tb/cmdtop.rb +1 -1
  32. data/lib/tb/cmdutil.rb +9 -62
  33. data/lib/tb/csv.rb +21 -79
  34. data/lib/tb/enumerable.rb +42 -68
  35. data/lib/tb/enumerator.rb +15 -7
  36. data/lib/tb/{fieldset.rb → hashreader.rb} +37 -56
  37. data/lib/tb/hashwriter.rb +54 -0
  38. data/lib/tb/headerreader.rb +108 -0
  39. data/lib/tb/headerwriter.rb +116 -0
  40. data/lib/tb/json.rb +17 -15
  41. data/lib/tb/ltsv.rb +35 -96
  42. data/lib/tb/ndjson.rb +63 -0
  43. data/lib/tb/numericreader.rb +66 -0
  44. data/lib/tb/numericwriter.rb +61 -0
  45. data/lib/tb/pnm.rb +206 -200
  46. data/lib/tb/ropen.rb +54 -59
  47. data/lib/tb/tsv.rb +39 -71
  48. data/sample/excel2csv +24 -25
  49. data/sample/poi-xls2csv.rb +13 -14
  50. data/tb.gemspec +154 -0
  51. data/test/test_cmd_cat.rb +28 -6
  52. data/test/test_cmd_consecutive.rb +8 -3
  53. data/test/test_cmd_cut.rb +14 -4
  54. data/test/test_cmd_git_log.rb +50 -50
  55. data/test/test_cmd_grep.rb +6 -6
  56. data/test/test_cmd_gsub.rb +7 -2
  57. data/test/test_cmd_ls.rb +70 -62
  58. data/test/test_cmd_shape.rb +43 -6
  59. data/test/test_cmd_svn_log.rb +26 -27
  60. data/test/test_cmd_to_csv.rb +10 -5
  61. data/test/test_cmd_to_json.rb +16 -0
  62. data/test/test_cmd_to_ltsv.rb +2 -2
  63. data/test/test_cmd_to_pp.rb +7 -2
  64. data/test/test_csv.rb +74 -62
  65. data/test/test_ex_enumerable.rb +0 -1
  66. data/test/test_fileenumerator.rb +3 -3
  67. data/test/test_headercsv.rb +43 -0
  68. data/test/test_json.rb +2 -2
  69. data/test/test_ltsv.rb +22 -17
  70. data/test/test_ndjson.rb +62 -0
  71. data/test/test_numericcsv.rb +36 -0
  72. data/test/test_pnm.rb +69 -70
  73. data/test/test_reader.rb +27 -124
  74. data/test/test_tbenum.rb +18 -18
  75. data/test/test_tsv.rb +21 -32
  76. data/test/util_tbtest.rb +12 -0
  77. metadata +41 -19
  78. data/lib/tb/basic.rb +0 -1070
  79. data/lib/tb/reader.rb +0 -106
  80. data/lib/tb/record.rb +0 -158
  81. data/test/test_basic.rb +0 -403
  82. data/test/test_fieldset.rb +0 -42
  83. data/test/test_record.rb +0 -61
@@ -1,6 +1,6 @@
1
- # lib/tb/ropen.rb - Tb::Reader.open
1
+ # lib/tb/ropen.rb - Tb.open_reader
2
2
  #
3
- # Copyright (C) 2011-2013 Tanaka Akira <akr@fsij.org>
3
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without
6
6
  # modification, are permitted provided that the following conditions
@@ -28,70 +28,65 @@
28
28
  # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29
29
  # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
- def Tb.open_reader(filename, opts={})
32
- opts = opts.dup
33
- case filename
34
- when /\Acsv:/
35
- filename = $'
36
- table_reader_maker = lambda {|io| Tb::CSVReader.new(io) }
37
- when /\Atsv:/
38
- filename = $'
39
- table_reader_maker = lambda {|io| Tb::TSVReader.new(io) }
40
- when /\Altsv:/
41
- filename = $'
42
- pairs_reader_maker = lambda {|io| Tb::LTSVReader.new(io) }
43
- when /\Ap[pgbn]m:/
44
- filename = $'
45
- table_reader_maker = lambda {|io| Tb.pnm_stream_input(io) }
46
- when /\Ajson:/
47
- filename = $'
48
- whole_reader_maker = lambda {|io| Tb::JSONReader.new(io.read) }
49
- when /\.csv\z/
50
- table_reader_maker = lambda {|io| Tb::CSVReader.new(io) }
51
- when /\.tsv\z/
52
- table_reader_maker = lambda {|io| Tb::TSVReader.new(io) }
53
- when /\.ltsv\z/
54
- pairs_reader_maker = lambda {|io| Tb::LTSVReader.new(io) }
55
- when /\.p[pgbn]m\z/
56
- table_reader_maker = lambda {|io| Tb.pnm_stream_input(io) }
57
- when /\.json\z/
58
- whole_reader_maker = lambda {|io| Tb::JSONReader.new(io.read) }
31
+ Tb::FormatHash = {
32
+ 'csv' => { :reader => Tb::HeaderCSVReader, :writer => Tb::HeaderCSVWriter},
33
+ 'ncsv' => { :reader => Tb::NumericCSVReader, :writer => Tb::NumericCSVWriter},
34
+ 'tsv' => { :reader => Tb::HeaderTSVReader, :writer => Tb::HeaderTSVWriter},
35
+ 'ntsv' => { :reader => Tb::NumericTSVReader, :writer => Tb::NumericTSVWriter},
36
+ 'ltsv' => { :reader => Tb::LTSVReader, :writer => Tb::LTSVWriter},
37
+ 'pnm' => { :reader => Tb::PNMReader, :writer => Tb::PNMWriter},
38
+ 'ppm' => { :reader => Tb::PNMReader, :writer => Tb::PNMWriter},
39
+ 'pgm' => { :reader => Tb::PNMReader, :writer => Tb::PNMWriter},
40
+ 'pbm' => { :reader => Tb::PNMReader, :writer => Tb::PNMWriter},
41
+ 'json' => { :reader => Tb::JSONReader, :writer => Tb::JSONWriter},
42
+ 'ndjson' => { :reader => Tb::NDJSONReader, :writer => Tb::NDJSONWriter},
43
+ }
44
+
45
+ def Tb.undecorate_filename(filename, numeric)
46
+ if filename.respond_to?(:to_str)
47
+ filename = filename.to_str
48
+ elsif filename.respond_to?(:to_path)
49
+ filename = filename.to_path
59
50
  else
60
- table_reader_maker = lambda {|io| Tb::CSVReader.new(io) }
61
- end
62
- if !filename.respond_to?(:to_str) && !filename.respond_to?(:to_path)
63
51
  raise ArgumentError, "unexpected filename: #{filename.inspect}"
64
52
  end
65
- if whole_reader_maker
66
- if filename == '-'
67
- reader = whole_reader_maker.call($stdin)
68
- else
69
- reader = File.open(filename) {|io|
70
- whole_reader_maker.call(io)
71
- }
72
- end
73
- elsif pairs_reader_maker
74
- if filename == '-'
75
- reader = pairs_reader_maker.call($stdin)
76
- else
77
- reader = pairs_reader_maker.call(File.open(filename))
53
+ if /\A([a-z0-9]{2,}):/ =~ filename
54
+ fmt = $1
55
+ filename = $'
56
+ err("unexpected format: #{fmt.inspect}") if !Tb::FormatHash.has_key?(fmt)
57
+ elsif /\.([a-z0-9]+{2,})\z/ =~ filename
58
+ fmt = $1
59
+ fmt = 'csv' if !Tb::FormatHash.has_key?(fmt)
60
+ else
61
+ fmt = 'csv'
62
+ end
63
+ if numeric
64
+ case fmt
65
+ when 'csv' then fmt = 'ncsv'
66
+ when 'tsv' then fmt = 'ntsv'
78
67
  end
68
+ end
69
+ return filename, fmt
70
+ end
71
+
72
+ def Tb.open_reader(filename, numeric=false)
73
+ filename, fmt = Tb.undecorate_filename(filename, numeric)
74
+ factory = Tb::FormatHash.fetch(fmt)[:reader]
75
+ io_opened = nil
76
+ if filename == '-'
77
+ reader = factory.new($stdin)
79
78
  else
80
- # table_reader_maker should be available.
81
- reader = Tb::Reader.new(opts) {|body|
82
- if filename == '-'
83
- rawreader = table_reader_maker.call($stdin)
84
- body.call(rawreader)
85
- else
86
- File.open(filename) {|io|
87
- rawreader = table_reader_maker.call(io)
88
- body.call(rawreader)
89
- }
90
- end
91
- }
79
+ io_opened = File.open(filename)
80
+ reader = factory.new(io_opened)
92
81
  end
93
82
  if block_given?
94
- yield reader
83
+ begin
84
+ yield reader
85
+ ensure
86
+ if io_opened && !io_opened.closed?
87
+ io_opened.close
88
+ end
89
+ end
95
90
  else
96
91
  reader
97
92
  end
@@ -1,6 +1,6 @@
1
1
  # lib/tb/tsv.rb - TSV related fetures for table library
2
2
  #
3
- # Copyright (C) 2010-2012 Tanaka Akira <akr@fsij.org>
3
+ # Copyright (C) 2010-2014 Tanaka Akira <akr@fsij.org>
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without
6
6
  # modification, are permitted provided that the following conditions
@@ -30,89 +30,57 @@
30
30
 
31
31
  require 'stringio'
32
32
 
33
- class Tb
34
- def Tb.load_tsv(filename, *header_fields, &block)
35
- Tb.parse_tsv(File.read(filename), *header_fields, &block)
33
+ module Tb
34
+ def Tb.tsv_fields_join(values)
35
+ values.map {|v| v.to_s.gsub(/[\t\r\n]/, ' ') }.join("\t") + "\n"
36
36
  end
37
37
 
38
- def Tb.parse_tsv(tsv, *header_fields)
39
- aa = []
40
- tsv_stream_input(tsv) {|ary|
41
- aa << ary
42
- }
43
- aa = yield aa if block_given?
44
- if header_fields.empty?
45
- reader = Tb::Reader.new {|body| body.call(aa) }
46
- reader.to_tb
47
- else
48
- header = header_fields
49
- arys = aa
50
- t = Tb.new(header)
51
- arys.each {|ary|
52
- ary << nil while ary.length < header.length
53
- t.insert_values header, ary
54
- }
55
- t
56
- end
38
+ def Tb.tsv_fields_split(line)
39
+ line = line.chomp("\n")
40
+ line = line.chomp("\r")
41
+ line.split(/\t/, -1)
57
42
  end
58
43
 
59
- def Tb.tsv_stream_input(tsv)
60
- tsvreader = TSVReader.new(tsv)
61
- while ary = tsvreader.shift
62
- yield ary
44
+ class HeaderTSVReader < HeaderReader
45
+ def initialize(io)
46
+ super lambda {
47
+ line = io.gets
48
+ if line
49
+ Tb.tsv_fields_split(line)
50
+ else
51
+ nil
52
+ end
53
+ }
63
54
  end
64
- nil
65
55
  end
66
56
 
67
- class TSVReader
68
- def initialize(input)
69
- if input.respond_to? :to_str
70
- @input = StringIO.new(input)
71
- else
72
- @input = input
73
- end
74
- end
75
-
76
- def shift
77
- line = @input.gets
78
- return nil if !line
79
- line = line.chomp("\n")
80
- line = line.chomp("\r")
81
- line.split(/\t/, -1)
57
+ class HeaderTSVWriter < HeaderWriter
58
+ # io is an object which has "<<" method.
59
+ def initialize(io)
60
+ super lambda {|ary|
61
+ io << Tb.tsv_fields_join(ary)
62
+ }
82
63
  end
83
64
  end
84
65
 
85
- def Tb.tsv_stream_output(out)
86
- gen = Object.new
87
- gen.instance_variable_set(:@out, out)
88
- def gen.<<(ary)
89
- @out << Tb.tsv_fields_join(ary) << "\n"
66
+ class NumericTSVReader < NumericReader
67
+ def initialize(io)
68
+ super lambda {
69
+ line = io.gets
70
+ if line
71
+ Tb.tsv_fields_split(line)
72
+ else
73
+ nil
74
+ end
75
+ }
90
76
  end
91
- yield gen
92
77
  end
93
78
 
94
- # :call-seq:
95
- # generate_tsv(out='', fields=nil) {|recordids| modified_recordids }
96
- # generate_tsv(out='', fields=nil)
97
- #
98
- def generate_tsv(out='', fields=nil, &block)
99
- if fields.nil?
100
- fields = list_fields
101
- end
102
- recordids = list_recordids
103
- if block_given?
104
- recordids = yield(recordids)
105
- end
106
- Tb.tsv_stream_output(out) {|gen|
107
- gen << fields
108
- recordids.each {|recordid|
109
- gen << get_values(recordid, *fields)
79
+ class NumericTSVWriter < NumericWriter
80
+ def initialize(io)
81
+ super lambda {|ary|
82
+ io << Tb.tsv_fields_join(ary)
110
83
  }
111
- }
112
- out
113
- end
114
-
115
- def Tb.tsv_fields_join(values)
116
- values.map {|v| v.to_s.gsub(/[\t\r\n]/, ' ') }.join("\t")
84
+ end
117
85
  end
118
86
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # sample/excel2csv - XLS to CSV converter using Microsoft Excel via WIN32OLE.
4
4
  #
5
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
5
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
6
6
  #
7
7
  # Redistribution and use in source and binary forms, with or without
8
8
  # modification, are permitted provided that the following conditions
@@ -205,7 +205,7 @@ end
205
205
  def generate_csv_row(csvgen, row)
206
206
  row = row.dup
207
207
  row.pop while !row.empty? && row.last.nil?
208
- csvgen << row
208
+ csvgen.call(row)
209
209
  end
210
210
 
211
211
  def convert_sheet(filename, sheet, csvgen)
@@ -243,30 +243,29 @@ excel = WIN32OLE.new('Excel.Application')
243
243
  WIN32OLE.const_load(excel,Excel)
244
244
  begin
245
245
  excel.displayAlerts = false
246
- Tb.csv_stream_output($stdout) {|csvgen|
247
- ARGV.each {|excel_filename|
248
- if File.directory? excel_filename
249
- filenames = []
250
- Find.find(excel_filename) {|path| filenames << path if /\.xls\z/ =~ path }
251
- else
252
- filenames = [excel_filename]
253
- end
254
- filenames.each {|ifn0|
255
- ifn = getAbsolutePath(ifn0)
256
- wb = excel.Workbooks.Open(ifn)
257
- begin
258
- if $opt_all_sheets
259
- wb.Worksheets.each {|sheet|
260
- sheetname = sheet.Name
261
- convert_sheet(ifn0, sheet, csvgen)
262
- }
263
- else
264
- convert_sheet(ifn0, wb.Worksheets(1), csvgen)
265
- end
266
- ensure
267
- wb.Close
246
+ csvgen = lambda {|row| $stdout << row.to_csv }
247
+ ARGV.each {|excel_filename|
248
+ if File.directory? excel_filename
249
+ filenames = []
250
+ Find.find(excel_filename) {|path| filenames << path if /\.xls\z/ =~ path }
251
+ else
252
+ filenames = [excel_filename]
253
+ end
254
+ filenames.each {|ifn0|
255
+ ifn = getAbsolutePath(ifn0)
256
+ wb = excel.Workbooks.Open(ifn)
257
+ begin
258
+ if $opt_all_sheets
259
+ wb.Worksheets.each {|sheet|
260
+ sheetname = sheet.Name
261
+ convert_sheet(ifn0, sheet, csvgen)
262
+ }
263
+ else
264
+ convert_sheet(ifn0, wb.Worksheets(1), csvgen)
268
265
  end
269
- }
266
+ ensure
267
+ wb.Close
268
+ end
270
269
  }
271
270
  }
272
271
  ensure
@@ -2,7 +2,7 @@
2
2
 
3
3
  # sample/poi-xls2csv.rb - XLS to CSV convert using Apache POI with JRuby.
4
4
  #
5
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
5
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
6
6
  #
7
7
  # Redistribution and use in source and binary forms, with or without
8
8
  # modification, are permitted provided that the following conditions
@@ -354,7 +354,7 @@ def convert_sheet(filename, book, i, csvgen)
354
354
  sheetname += ":sheetname" if $opt_type
355
355
  sheet_header << sheetname
356
356
  end
357
- csvgen << (sheet_header + convert_horizontal_borders(sheet, merged, rownums.first-1, min_firstcol)) if $opt_border
357
+ csvgen.call (sheet_header + convert_horizontal_borders(sheet, merged, rownums.first-1, min_firstcol)) if $opt_border
358
358
  rownums.each {|y|
359
359
  record = []
360
360
  row = sheet.getRow(y)
@@ -370,8 +370,8 @@ def convert_sheet(filename, book, i, csvgen)
370
370
  }
371
371
  end
372
372
  end
373
- csvgen << (sheet_header + record)
374
- csvgen << (sheet_header + convert_horizontal_borders(sheet, merged, y, min_firstcol)) if $opt_border
373
+ csvgen.call (sheet_header + record)
374
+ csvgen.call (sheet_header + convert_horizontal_borders(sheet, merged, y, min_firstcol)) if $opt_border
375
375
  }
376
376
  end
377
377
 
@@ -386,16 +386,15 @@ def convert_book(filename, input, csvgen)
386
386
  end
387
387
  end
388
388
 
389
- Tb.csv_stream_output($stdout) {|csvgen|
390
- argv = ARGV.empty? ? ['-'] : ARGV
391
- argv.each {|filename|
392
- if filename == '-'
393
- input = java.lang.System.in
394
- else
395
- input = java.io.FileInputStream.new(filename)
396
- end
397
- convert_book(filename, input, csvgen)
398
- }
389
+ csvgen = lambdas {|row| $stdout << row.to_csv }
390
+ argv = ARGV.empty? ? ['-'] : ARGV
391
+ argv.each {|filename|
392
+ if filename == '-'
393
+ input = java.lang.System.in
394
+ else
395
+ input = java.io.FileInputStream.new(filename)
396
+ end
397
+ convert_book(filename, input, csvgen)
399
398
  }
400
399
 
401
400
  exit true
@@ -0,0 +1,154 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'tb'
3
+ s.version = '1.0'
4
+ s.date = '2014-10-29'
5
+ s.author = 'Tanaka Akira'
6
+ s.email = 'akr@fsij.org'
7
+ s.required_ruby_version = '>= 1.9.2'
8
+ s.add_development_dependency 'test-unit', '~> 2.5.5'
9
+ s.files = %w[
10
+ README
11
+ bin/tb
12
+ lib/tb.rb
13
+ lib/tb/catreader.rb
14
+ lib/tb/cmd_cat.rb
15
+ lib/tb/cmd_consecutive.rb
16
+ lib/tb/cmd_crop.rb
17
+ lib/tb/cmd_cross.rb
18
+ lib/tb/cmd_cut.rb
19
+ lib/tb/cmd_git.rb
20
+ lib/tb/cmd_group.rb
21
+ lib/tb/cmd_gsub.rb
22
+ lib/tb/cmd_help.rb
23
+ lib/tb/cmd_join.rb
24
+ lib/tb/cmd_ls.rb
25
+ lib/tb/cmd_melt.rb
26
+ lib/tb/cmd_mheader.rb
27
+ lib/tb/cmd_nest.rb
28
+ lib/tb/cmd_newfield.rb
29
+ lib/tb/cmd_rename.rb
30
+ lib/tb/cmd_search.rb
31
+ lib/tb/cmd_shape.rb
32
+ lib/tb/cmd_sort.rb
33
+ lib/tb/cmd_svn.rb
34
+ lib/tb/cmd_tar.rb
35
+ lib/tb/cmd_to_csv.rb
36
+ lib/tb/cmd_to_json.rb
37
+ lib/tb/cmd_to_ltsv.rb
38
+ lib/tb/cmd_to_pnm.rb
39
+ lib/tb/cmd_to_pp.rb
40
+ lib/tb/cmd_to_tsv.rb
41
+ lib/tb/cmd_to_yaml.rb
42
+ lib/tb/cmd_unmelt.rb
43
+ lib/tb/cmd_unnest.rb
44
+ lib/tb/cmdmain.rb
45
+ lib/tb/cmdtop.rb
46
+ lib/tb/cmdutil.rb
47
+ lib/tb/csv.rb
48
+ lib/tb/customcmp.rb
49
+ lib/tb/customeq.rb
50
+ lib/tb/enumerable.rb
51
+ lib/tb/enumerator.rb
52
+ lib/tb/ex_enumerable.rb
53
+ lib/tb/ex_enumerator.rb
54
+ lib/tb/fileenumerator.rb
55
+ lib/tb/func.rb
56
+ lib/tb/hashreader.rb
57
+ lib/tb/hashwriter.rb
58
+ lib/tb/headerreader.rb
59
+ lib/tb/headerwriter.rb
60
+ lib/tb/json.rb
61
+ lib/tb/ltsv.rb
62
+ lib/tb/ndjson.rb
63
+ lib/tb/numericreader.rb
64
+ lib/tb/numericwriter.rb
65
+ lib/tb/pager.rb
66
+ lib/tb/pnm.rb
67
+ lib/tb/revcmp.rb
68
+ lib/tb/ropen.rb
69
+ lib/tb/search.rb
70
+ lib/tb/tsv.rb
71
+ lib/tb/zipper.rb
72
+ sample/colors.ppm
73
+ sample/excel2csv
74
+ sample/gradation.pgm
75
+ sample/langs.csv
76
+ sample/poi-xls2csv.rb
77
+ sample/poi-xls2csv.sh
78
+ sample/tbplot
79
+ tb.gemspec
80
+ test-all-cov.rb
81
+ test-all.rb
82
+ ]
83
+ s.test_files = %w[
84
+ test/test_catreader.rb
85
+ test/test_cmd_cat.rb
86
+ test/test_cmd_consecutive.rb
87
+ test/test_cmd_crop.rb
88
+ test/test_cmd_cross.rb
89
+ test/test_cmd_cut.rb
90
+ test/test_cmd_git_log.rb
91
+ test/test_cmd_grep.rb
92
+ test/test_cmd_group.rb
93
+ test/test_cmd_gsub.rb
94
+ test/test_cmd_help.rb
95
+ test/test_cmd_join.rb
96
+ test/test_cmd_ls.rb
97
+ test/test_cmd_melt.rb
98
+ test/test_cmd_mheader.rb
99
+ test/test_cmd_nest.rb
100
+ test/test_cmd_newfield.rb
101
+ test/test_cmd_rename.rb
102
+ test/test_cmd_shape.rb
103
+ test/test_cmd_sort.rb
104
+ test/test_cmd_svn_log.rb
105
+ test/test_cmd_tar_tvf.rb
106
+ test/test_cmd_to_csv.rb
107
+ test/test_cmd_to_json.rb
108
+ test/test_cmd_to_ltsv.rb
109
+ test/test_cmd_to_pnm.rb
110
+ test/test_cmd_to_pp.rb
111
+ test/test_cmd_to_tsv.rb
112
+ test/test_cmd_to_yaml.rb
113
+ test/test_cmd_unmelt.rb
114
+ test/test_cmd_unnest.rb
115
+ test/test_cmdtty.rb
116
+ test/test_cmdutil.rb
117
+ test/test_csv.rb
118
+ test/test_customcmp.rb
119
+ test/test_customeq.rb
120
+ test/test_ex_enumerable.rb
121
+ test/test_fileenumerator.rb
122
+ test/test_headercsv.rb
123
+ test/test_json.rb
124
+ test/test_ltsv.rb
125
+ test/test_ndjson.rb
126
+ test/test_numericcsv.rb
127
+ test/test_pager.rb
128
+ test/test_pnm.rb
129
+ test/test_reader.rb
130
+ test/test_revcmp.rb
131
+ test/test_search.rb
132
+ test/test_tbenum.rb
133
+ test/test_tsv.rb
134
+ test/test_zipper.rb
135
+ test/util_tbtest.rb
136
+ ]
137
+ s.has_rdoc = true
138
+ s.homepage = 'https://github.com/akr/tb'
139
+ s.require_path = 'lib'
140
+ s.executables << 'tb'
141
+ s.summary = 'manipulation tool for tables: CSV, TSV, LTSV, JSON, NDJSON, etc.'
142
+ s.description = <<'End'
143
+ tb is a manipulation tool for tables.
144
+
145
+ tb provides a command and a library for manipulating tables:
146
+ Unix filter like operations (sort, cat, cut, ls, etc.),
147
+ SQL like operations (join, group, etc.),
148
+ other table operations (search, gsub, rename, cross, melt, unmelt, etc.),
149
+ information extractions (git, svn, tar),
150
+ and more.
151
+
152
+ tb supports various table formats: CSV, TSV, JSON, NDJSON, LTSV, etc.
153
+ End
154
+ end