tb 0.9 → 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -15,7 +15,44 @@ class TestTbCmdShape < Test::Unit::TestCase
15
15
  FileUtils.rmtree @tmpdir
16
16
  end
17
17
 
18
- def test_basic
18
+ def test_ndjson
19
+ File.open(i="i.ndjson", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
20
+ {"a":0, "b":1}
21
+ {"a":4, "b":5, "c":6}
22
+ End
23
+ Tb::Cmd.main_shape(['-o', o="o.csv", i])
24
+ assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
25
+ filename,records,min_pairs,max_pairs
26
+ i.ndjson,2,2,3
27
+ End
28
+ end
29
+
30
+ def test_csv
31
+ File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
32
+ a,b,c
33
+ 0,1
34
+ 4,5,6
35
+ End
36
+ Tb::Cmd.main_shape(['-o', o="o.csv", i])
37
+ assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
38
+ filename,records,min_pairs,max_pairs,header_fields,min_fields,max_fields
39
+ i.csv,2,2,3,3,2,3
40
+ End
41
+ end
42
+
43
+ def test_output_ndjson
44
+ File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
45
+ a,b,c
46
+ 0,1
47
+ 4,5,6
48
+ End
49
+ Tb::Cmd.main_shape(['-o', o="o.ndjson", i])
50
+ assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
51
+ {"filename":"i.csv","records":2,"min_pairs":2,"max_pairs":3,"header_fields":3,"min_fields":2,"max_fields":3}
52
+ End
53
+ end
54
+
55
+ def test_extra_field
19
56
  File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
20
57
  a,b,c
21
58
  0,1
@@ -23,8 +60,8 @@ class TestTbCmdShape < Test::Unit::TestCase
23
60
  End
24
61
  Tb::Cmd.main_shape(['-o', o="o.csv", i])
25
62
  assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
26
- header_fields,min_fields,max_fields,records,filename
27
- 3,2,4,2,i.csv
63
+ filename,records,min_pairs,max_pairs,header_fields,min_fields,max_fields
64
+ i.csv,2,2,3,3,2,4
28
65
  End
29
66
  end
30
67
 
@@ -41,9 +78,9 @@ class TestTbCmdShape < Test::Unit::TestCase
41
78
  End
42
79
  Tb::Cmd.main_shape(['-o', o="o.csv", i1, i2])
43
80
  assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
44
- header_fields,min_fields,max_fields,records,filename
45
- 1,1,1,2,i1.csv
46
- 2,2,2,2,i2.csv
81
+ filename,records,min_pairs,max_pairs,header_fields,min_fields,max_fields
82
+ i1.csv,2,1,1,1,1,1
83
+ i2.csv,2,2,2,2,2,2
47
84
  End
48
85
  end
49
86
 
@@ -34,10 +34,10 @@ class TestTbCmdSvnLog < Test::Unit::TestCase
34
34
  system("svn commit -q -m baz foo hoge")
35
35
  system("svn update -q") # update the revision of the directory.
36
36
  Tb::Cmd.main_svn(['-o', o="o.csv"])
37
- result = File.read(o)
38
- tb = Tb.parse_csv(result)
39
- assert_equal(1, tb.size)
40
- assert_match(/baz/, tb.get_record(0)["msg"])
37
+ aa = CSV.read(o)
38
+ assert_equal(2, aa.length)
39
+ header, row = aa
40
+ assert_match(/baz/, row[header.index "msg"])
41
41
  end
42
42
 
43
43
  def test_verbose
@@ -49,16 +49,15 @@ class TestTbCmdSvnLog < Test::Unit::TestCase
49
49
  system("svn commit -q -m baz foo hoge")
50
50
  system("svn update -q") # update the revision of the directory.
51
51
  Tb::Cmd.main_svn(['-o', o="o.csv", '--', '-v'])
52
- result = File.read(o)
53
- tb = Tb.parse_csv(result)
54
- assert_equal(2, tb.size)
55
- recs = [tb.get_record(0), tb.get_record(1)]
56
- recs = recs.sort_by {|rec| rec['path'] }
57
- recs.each {|rec|
58
- assert_match(/baz/, rec["msg"])
52
+ aa = CSV.read(o)
53
+ assert_equal(3, aa.length)
54
+ header, *rows = aa
55
+ rows = rows.sort_by {|rec| rows[header.index 'path'] }
56
+ rows.each {|row|
57
+ assert_match(/baz/, row[header.index "msg"])
59
58
  }
60
- assert_match(/foo/, recs[0]["path"])
61
- assert_match(/hoge/, recs[1]["path"])
59
+ assert_match(/foo/, rows[0][header.index "path"])
60
+ assert_match(/hoge/, rows[1][header.index "path"])
62
61
  end
63
62
 
64
63
  def test_log_xml
@@ -71,18 +70,18 @@ class TestTbCmdSvnLog < Test::Unit::TestCase
71
70
  system("svn update -q") # update the revision of the directory.
72
71
  ###
73
72
  Tb::Cmd.main_svn(['-o', o="o.csv"])
74
- result = File.read(o)
75
- tb = Tb.parse_csv(result)
76
- assert_equal(1, tb.size)
77
- assert_match(/baz/, tb.get_record(0)["msg"])
73
+ aa = CSV.read(o)
74
+ assert_equal(2, aa.length)
75
+ header, row = aa
76
+ assert_match(/baz/, row[header.index "msg"])
78
77
  ###
79
78
  system("svn log --xml > log.xml")
80
79
  FileUtils.rmtree('.svn')
81
80
  Tb::Cmd.main_svn(['-o', o="o.csv", '--svn-log-xml=log.xml'])
82
- result = File.read(o)
83
- tb = Tb.parse_csv(result)
84
- assert_equal(1, tb.size)
85
- assert_match(/baz/, tb.get_record(0)["msg"])
81
+ aa = CSV.read(o)
82
+ assert_equal(2, aa.length)
83
+ header, row = aa
84
+ assert_match(/baz/, row[header.index "msg"])
86
85
  end
87
86
 
88
87
  def test_no_props
@@ -98,11 +97,11 @@ class TestTbCmdSvnLog < Test::Unit::TestCase
98
97
  system("svn propdel -q svn:log --revprop -r 1 .")
99
98
  ###
100
99
  Tb::Cmd.main_svn(['-o', o="o.csv"])
101
- result = File.read(o)
102
- tb = Tb.parse_csv(result)
103
- assert_equal(1, tb.size)
104
- assert_equal('(no author)', tb.get_record(0)["author"])
105
- assert_equal('(no date)', tb.get_record(0)["date"])
106
- assert_equal('', tb.get_record(0)["msg"])
100
+ aa = CSV.read(o)
101
+ assert_equal(2, aa.length)
102
+ header, row = aa
103
+ assert_equal('(no author)', row[header.index "author"])
104
+ assert_equal('(no date)', row[header.index "date"])
105
+ assert_equal('', row[header.index "msg"])
107
106
  end
108
107
  end
@@ -1,6 +1,7 @@
1
1
  require 'test/unit'
2
2
  require 'tb/cmdtop'
3
3
  require 'tmpdir'
4
+ require_relative 'util_tbtest'
4
5
 
5
6
  class TestTbCmdToCSV < Test::Unit::TestCase
6
7
  def setup
@@ -49,18 +50,22 @@ class TestTbCmdToCSV < Test::Unit::TestCase
49
50
  End
50
51
  end
51
52
 
52
- def test_complement_header
53
+ def test_short_header
53
54
  File.open(i="i.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
54
55
  a,b
55
56
  0,1,2
56
57
  4,5,6
57
58
  End
58
- Tb::Cmd.main_to_csv(['-o', o="o.csv", i])
59
+ o = "o.csv"
60
+ stderr = capture_stderr {
61
+ Tb::Cmd.main_to_csv(['-o', o, i])
62
+ }
59
63
  assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
60
- a,b,1
61
- 0,1,2
62
- 4,5,6
64
+ a,b
65
+ 0,1
66
+ 4,5
63
67
  End
68
+ assert_match(/Header too short/, stderr)
64
69
  end
65
70
 
66
71
  def test_numeric
@@ -68,6 +68,22 @@ class TestTbCmdToJSON < Test::Unit::TestCase
68
68
  End
69
69
  end
70
70
 
71
+ def test_ltsv_to_json1
72
+ File.open(i="i.ltsv", "w") {|f| f << "foo:bar\n" }
73
+ Tb::Cmd.main_to_json(['-o', o="o.json", i])
74
+ assert_equal(<<-"End".gsub(/\s/, ''), File.read(o).gsub(/\s/, ''))
75
+ [{"foo":"bar"}]
76
+ End
77
+ end
78
+
79
+ def test_ltsv_to_json2
80
+ File.open(i="i.ltsv", "w") {|f| f << "foo:bar\nbaz:qux\n" }
81
+ Tb::Cmd.main_to_json(['-o', o="o.json", i])
82
+ assert_equal(<<-"End".gsub(/\s/, ''), File.read(o).gsub(/\s/, ''))
83
+ [{"foo":"bar"}, {"baz":"qux"}]
84
+ End
85
+ end
86
+
71
87
  def test_twofile
72
88
  File.open(i1="i1.csv", "w") {|f| f << <<-"End".gsub(/^[ \t]+/, '') }
73
89
  a,b
@@ -57,8 +57,8 @@ class TestTbCmdToLTSV < Test::Unit::TestCase
57
57
  assert_equal(<<-"End".gsub(/^[ \t]+/, ''), File.read(o))
58
58
  a:1\tb:2
59
59
  a:3\tb:4
60
- a:6\tb:5
61
- a:8\tb:7
60
+ b:5\ta:6
61
+ b:7\ta:8
62
62
  End
63
63
  end
64
64
 
@@ -1,6 +1,7 @@
1
1
  require 'test/unit'
2
2
  require 'tb/cmdtop'
3
3
  require 'tmpdir'
4
+ require_relative 'util_tbtest'
4
5
 
5
6
  class TestTbCmdToPP < Test::Unit::TestCase
6
7
  def setup
@@ -33,10 +34,14 @@ class TestTbCmdToPP < Test::Unit::TestCase
33
34
  a,b
34
35
  0,1,2,3
35
36
  End
36
- Tb::Cmd.main_to_pp(['-o', o="o.pp", i])
37
+ o = "o.pp"
38
+ stderr = capture_stderr {
39
+ Tb::Cmd.main_to_pp(['-o', o, i])
40
+ }
37
41
  assert_equal(<<-"End".gsub(/\s/, ''), File.read(o).gsub(/\s/, ''))
38
- { "a" => "0", "b" => "1", "1" => "2", "2" => "3" }
42
+ { "a" => "0", "b" => "1" }
39
43
  End
44
+ assert_match(/Header too short/, stderr)
40
45
  end
41
46
 
42
47
  def test_twofile
@@ -1,98 +1,110 @@
1
1
  require 'tb'
2
2
  require 'test/unit'
3
+ require_relative 'util_tbtest'
3
4
 
4
5
  class TestTbCSV < Test::Unit::TestCase
6
+ def parse_csv(csv)
7
+ Tb::HeaderCSVReader.new(StringIO.new(csv)).to_a
8
+ end
9
+
10
+ def generate_csv(ary)
11
+ writer = Tb::HeaderCSVWriter.new(out = '')
12
+ ary.each {|h| writer.put_hash h }
13
+ writer.finish
14
+ out
15
+ end
16
+
5
17
  def test_parse
6
- csv = <<-'End'.gsub(/^\s+/, '')
18
+ t = parse_csv(<<-'End'.gsub(/^\s+/, ''))
19
+ a,b
20
+ 1,2
21
+ 3,4
22
+ End
23
+ assert_equal(
24
+ [{"a"=>"1", "b"=>"2"},
25
+ {"a"=>"3", "b"=>"4"}],
26
+ t)
27
+ end
28
+
29
+ def test_parse_empty_line_before_header
30
+ empty_line = "\n"
31
+ t = parse_csv(empty_line + <<-'End'.gsub(/^\s+/, ''))
7
32
  a,b
8
33
  1,2
9
34
  3,4
10
35
  End
11
- t = Tb.parse_csv(csv)
12
- records = []
13
- t.each_record {|record|
14
- records << record.to_h_with_reserved
15
- }
16
36
  assert_equal(
17
- [{"_recordid"=>0, "a"=>"1", "b"=>"2"},
18
- {"_recordid"=>1, "a"=>"3", "b"=>"4"}],
19
- records)
37
+ [{"a"=>"1", "b"=>"2"},
38
+ {"a"=>"3", "b"=>"4"}],
39
+ t)
20
40
  end
21
41
 
22
- def test_parse_empty
23
- csv = <<-'End'.gsub(/^\s+/, '')
42
+ def test_parse_empty_value
43
+ t = parse_csv(<<-'End'.gsub(/^\s+/, ''))
24
44
  a,b,c
25
45
  1,,2
26
46
  3,"",4
27
47
  End
28
- t = Tb.parse_csv(csv)
29
- records = []
30
- t.each_record {|record|
31
- records << record.to_h_with_reserved
32
- }
33
48
  assert_equal(
34
- [{"_recordid"=>0, "a"=>"1", "c"=>"2"},
35
- {"_recordid"=>1, "a"=>"3", "b"=>"", "c"=>"4"}],
36
- records)
49
+ [{"a"=>"1", "b"=>nil, "c"=>"2"},
50
+ {"a"=>"3", "b"=>"", "c"=>"4"}],
51
+ t)
37
52
  end
38
53
 
39
- def test_parse_conv
40
- csv = "foo\na,b\n1,2\n"
41
- t = Tb.parse_csv(csv) {|aa|
42
- assert_equal([%w[foo],
43
- %w[a b],
44
- %w[1 2]],
45
- aa)
46
- aa.shift
47
- aa
48
- }
49
- records = []
50
- t.each_record {|record|
51
- records << record.to_h_with_reserved
52
- }
53
- assert_equal(
54
- [{"_recordid"=>0, "a"=>"1", "b"=>"2"}],
55
- records)
54
+ def test_parse_newline
55
+ t = parse_csv("\n")
56
+ assert_equal([], t)
56
57
  end
57
58
 
58
59
  def test_generate
59
- t = Tb.new %w[a b], [1, 2], [3, 4]
60
- out = t.generate_csv('', ['a', 'b'])
61
- assert_equal(<<-'End'.gsub(/^\s+/, ''), out)
60
+ t = [{'a' => 1, 'b' => 2},
61
+ {'a' => 3, 'b' => 4}]
62
+ assert_equal(<<-'End'.gsub(/^\s+/, ''), generate_csv(t))
62
63
  a,b
63
64
  1,2
64
65
  3,4
65
66
  End
66
67
  end
67
68
 
68
- def test_generate_without_explicit_fields
69
- t = Tb.new %w[a b], [1, 2], [3, 4]
70
- out = t.generate_csv('')
71
- assert_equal(<<-'End'.gsub(/^\s+/, ''), out)
72
- a,b
73
- 1,2
74
- 3,4
75
- End
76
- end
77
-
78
- def test_generate_with_block
79
- t = Tb.new %w[a b], [1, 2], [3, 4]
80
- out = t.generate_csv('', ['a', 'b']) {|recids| recids.reverse }
81
- assert_equal(<<-'End'.gsub(/^\s+/, ''), out)
82
- a,b
83
- 3,4
84
- 1,2
85
- End
86
- end
87
-
88
69
  def test_generate_empty
89
- t = Tb.new %w[a b c], [1, nil, 2], [3, '', 4]
90
- out = t.generate_csv('', ['a', 'b', 'c'])
91
- assert_equal(<<-'End'.gsub(/^\s+/, ''), out)
70
+ t = [{'a' => 1, 'b' => nil, 'c' => 2},
71
+ {'a' => 3, 'b' => '', 'c' => 4}]
72
+ assert_equal(<<-'End'.gsub(/^\s+/, ''), generate_csv(t))
92
73
  a,b,c
93
74
  1,,2
94
75
  3,"",4
95
76
  End
96
77
  end
97
78
 
79
+ def test_parse_ambiguous_header
80
+ t = nil
81
+ stderr = capture_stderr {
82
+ t = parse_csv(<<-'End'.gsub(/^\s+/, ''))
83
+ a,b,a,b,c
84
+ 0,1,2,3,4
85
+ 5,6,7,8,9
86
+ End
87
+ }
88
+ assert_equal(
89
+ [{"a"=>"0", "b"=>"1", "c"=>"4"},
90
+ {"a"=>"5", "b"=>"6", "c"=>"9"}],
91
+ t)
92
+ assert_match(/Ambiguous header field/, stderr)
93
+ end
94
+
95
+ def test_parse_empty_header_field
96
+ t = nil
97
+ stderr = capture_stderr {
98
+ t = parse_csv(<<-'End'.gsub(/^\s+/, ''))
99
+ a,,c
100
+ 0,1,2
101
+ 5,6,7
102
+ End
103
+ }
104
+ assert_equal(
105
+ [{"a"=>"0", "c"=>"2"},
106
+ {"a"=>"5", "c"=>"7"}],
107
+ t)
108
+ assert_match(/Empty header field/, stderr)
109
+ end
98
110
  end
@@ -46,7 +46,6 @@ class TestTbEnumerable < Test::Unit::TestCase
46
46
  a.tb_categorize(:color, lambda {|e| true }, :seed=>0, :op=>lambda {|s, v| s+1 }))
47
47
 
48
48
  assert_raise(ArgumentError) { a.tb_categorize(:color, lambda {|e| true }, :seed=>0,
49
- :seed=>nil,
50
49
  :op=>lambda {|s, v| s+1 },
51
50
  :update=>lambda {|ks, s, v| s+1 }) }
52
51
 
@@ -204,7 +204,7 @@ class TestTbFileEnumerator < Test::Unit::TestCase
204
204
  end
205
205
 
206
206
  def test_to_fileheaderenumerator_reader
207
- tb = Tb.new %w[a b], [1, 2], [3, 4]
207
+ tb = Tb::Enumerator.from_header_and_values %w[a b], [1, 2], [3, 4]
208
208
  fe = tb.to_fileenumerator
209
209
  iter = fe.each
210
210
  assert_respond_to(iter, :next)
@@ -222,7 +222,7 @@ class TestTbFileEnumerator < Test::Unit::TestCase
222
222
  end
223
223
 
224
224
  def test_to_fileheaderenumerator_with_header_reader
225
- tb = Tb.new %w[a b], [1, 2], [3, 4]
225
+ tb = Tb::Enumerator.from_header_and_values %w[a b], [1, 2], [3, 4]
226
226
  header = nil
227
227
  fe = tb.with_header {|h0|
228
228
  header = h0
@@ -244,7 +244,7 @@ class TestTbFileEnumerator < Test::Unit::TestCase
244
244
  end
245
245
 
246
246
  def test_fileheaderenumerator_open_reader
247
- tb = Tb.new %w[a b], [1, 2], [3, 4]
247
+ tb = Tb::Enumerator.from_header_and_values %w[a b], [1, 2], [3, 4]
248
248
  fe = tb.to_fileenumerator
249
249
  iter0 = nil
250
250
  fe.open_reader {|iter|