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,4 +1,4 @@
1
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -40,9 +40,9 @@ def (Tb::Cmd).main_to_pnm(argv)
40
40
  op_to_pnm.parse!(argv)
41
41
  exit_if_help('to-pnm')
42
42
  argv = ['-'] if argv.empty?
43
- tbl = Tb::CatReader.open(argv, Tb::Cmd.opt_N).to_tb
43
+ reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
44
44
  with_output {|out|
45
- tbl.generate_pnm(out)
45
+ reader.write_to_pnm(out)
46
46
  }
47
47
  end
48
48
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -40,9 +40,9 @@ def (Tb::Cmd).main_to_tsv(argv)
40
40
  op_to_tsv.parse!(argv)
41
41
  exit_if_help('to-tsv')
42
42
  argv = ['-'] if argv.empty?
43
- tbl = Tb::CatReader.open(argv, Tb::Cmd.opt_N).to_tb
43
+ reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
44
44
  with_output {|out|
45
- tbl_generate_tsv(tbl, out)
45
+ reader.write_to_tsv(out, !Tb::Cmd.opt_N)
46
46
  }
47
47
  end
48
48
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -41,8 +41,8 @@ def (Tb::Cmd).main_to_yaml(argv)
41
41
  op_to_yaml.parse!(argv)
42
42
  exit_if_help('to-yaml')
43
43
  argv = ['-'] if argv.empty?
44
- tbl = Tb::CatReader.open(argv, Tb::Cmd.opt_N).to_tb
45
- ary = tbl.map {|rec| rec.to_h }
44
+ reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
45
+ ary = reader.to_a
46
46
  with_output {|out|
47
47
  YAML.dump(ary, out)
48
48
  out.puts
@@ -58,6 +58,21 @@ def (Tb::Cmd).op_unmelt
58
58
  op
59
59
  end
60
60
 
61
+ Tb::Cmd.def_vhelp('unmelt', <<'End')
62
+ Example:
63
+
64
+ % cat tst.csv
65
+ foo,variable,value
66
+ A,bar,1
67
+ A,baz,x
68
+ B,bar,2
69
+ B,baz,y
70
+ % tb unmelt tst.csv
71
+ foo,bar,baz
72
+ A,1,x
73
+ B,2,y
74
+ End
75
+
61
76
  def (Tb::Cmd).main_unmelt(argv)
62
77
  op_unmelt.parse!(argv)
63
78
  exit_if_help('unmelt')
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2011-2012 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -41,6 +41,27 @@ def (Tb::Cmd).op_unnest
41
41
  op
42
42
  end
43
43
 
44
+ Tb::Cmd.def_vhelp('unnest', <<'End')
45
+ Example:
46
+
47
+ % cat tst.csv
48
+ author,item
49
+ A,"name,length
50
+ foo,3
51
+ bar,5
52
+ "
53
+ B,"name,length
54
+ baz,2
55
+ qux,8
56
+ "
57
+ % tb unnest item tst.csv
58
+ author,name,length
59
+ A,foo,3
60
+ A,bar,5
61
+ B,baz,2
62
+ B,qux,8
63
+ End
64
+
44
65
  def (Tb::Cmd).main_unnest(argv)
45
66
  op_unnest.parse!(argv)
46
67
  exit_if_help('unnest')
@@ -64,13 +85,16 @@ def (Tb::Cmd).main_unnest(argv)
64
85
  elsif v.nil?
65
86
  pairs2[f] = v
66
87
  else
67
- nested_tbl = Tb.parse_csv(v)
68
- nested_tbl.list_fields.each {|f2|
88
+ aa = CSV.parse(v)
89
+ reader = Tb::HeaderReader.new(lambda { aa.shift })
90
+ nested_tbl_rows = reader.to_a
91
+ nested_tbl_header = reader.get_named_header
92
+ nested_tbl_header.each {|f2|
69
93
  unless nested_fields.has_key? f2
70
94
  nested_fields[f2] = nested_fields.size
71
95
  end
72
96
  }
73
- pairs2[f] = nested_tbl
97
+ pairs2[f] = [nested_tbl_header, nested_tbl_rows]
74
98
  end
75
99
  }
76
100
  y2.yield pairs2
@@ -93,13 +117,13 @@ def (Tb::Cmd).main_unnest(argv)
93
117
  }.each {|pairs|
94
118
  pairs2 = {}
95
119
  pairs.each {|f, v| pairs2[f] = v if f != target_field }
96
- ntbl = pairs[target_field]
97
- if ntbl.nil? || ntbl.empty?
120
+ ntbl_header, ntbl_rows = pairs[target_field]
121
+ if ntbl_header.nil? || ntbl_rows.empty?
98
122
  if Tb::Cmd.opt_unnest_outer
99
123
  y.yield pairs2
100
124
  end
101
125
  else
102
- ntbl.each {|npairs|
126
+ ntbl_rows.each {|npairs|
103
127
  pairs3 = pairs2.dup
104
128
  npairs.each {|nf, nv|
105
129
  if Tb::Cmd.opt_unnest_prefix
@@ -45,5 +45,7 @@ def (Tb::Cmd).main(argv)
45
45
  rescue SystemExit
46
46
  $stderr.puts $!.message if $!.message != 'exit'
47
47
  raise
48
+ rescue Errno::EPIPE
49
+ exit false
48
50
  end
49
51
 
@@ -43,7 +43,7 @@ require 'tb/cmd_to_pnm'
43
43
  require 'tb/cmd_to_json'
44
44
  require 'tb/cmd_to_yaml'
45
45
  require 'tb/cmd_to_pp'
46
- require 'tb/cmd_grep'
46
+ require 'tb/cmd_search'
47
47
  require 'tb/cmd_gsub'
48
48
  require 'tb/cmd_sort'
49
49
  require 'tb/cmd_cut'
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2011-2013 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2011-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -131,39 +131,11 @@ def split_field_list_argument(arg)
131
131
  end
132
132
 
133
133
  def split_csv_argument(arg)
134
- Tb.csv_stream_input(arg) {|ary| return ary }
135
- return []
134
+ return CSV.new(arg).shift || []
136
135
  end
137
136
 
138
137
  def tablereader_open(filename, &b)
139
- Tb.open_reader(filename, {:numeric=>Tb::Cmd.opt_N}, &b)
140
- end
141
-
142
- def tbl_generate_tsv(tbl, out)
143
- if Tb::Cmd.opt_N
144
- header = tbl.list_fields
145
- Tb.tsv_stream_output(out) {|gen|
146
- tbl.each {|rec|
147
- gen << rec.values_at(*header)
148
- }
149
- }
150
- else
151
- tbl.generate_tsv(out)
152
- end
153
- end
154
-
155
- def tbl_generate_ltsv(tbl, out)
156
- if Tb::Cmd.opt_N
157
- header = tbl.list_fields
158
- Tb.ltsv_stream_output(out) {|gen|
159
- tbl.each {|rec|
160
- assoc = header.map {|f| [f, rec[f]] }
161
- gen << assoc
162
- }
163
- }
164
- else
165
- tbl.generate_ltsv(out)
166
- end
138
+ Tb.open_reader(filename, Tb::Cmd.opt_N, &b)
167
139
  end
168
140
 
169
141
  def with_output(filename=Tb::Cmd.opt_output)
@@ -191,37 +163,12 @@ def with_output(filename=Tb::Cmd.opt_output)
191
163
  end
192
164
 
193
165
  def output_tbenum(te)
194
- filename = Tb::Cmd.opt_output
195
- if /\A([a-z0-9]{2,}):/ =~ filename
196
- fmt = $1
197
- filename = $'
198
- else
199
- fmt = nil
200
- end
201
- if !fmt
202
- case filename
203
- when /\.csv\z/
204
- fmt = 'csv'
205
- when /\.ltsv\z/
206
- fmt = 'ltsv'
207
- when /\.json\z/
208
- fmt = 'json'
209
- end
210
- end
211
- if fmt
212
- case fmt
213
- when 'csv'
214
- write_proc = lambda {|out| te.write_to_csv(out, !Tb::Cmd.opt_N) }
215
- when 'ltsv'
216
- write_proc = lambda {|out| te.write_to_ltsv(out) }
217
- when 'json'
218
- write_proc = lambda {|out| te.write_to_json(out) }
219
- else
220
- err("unexpected format: #{fmt.inspect}")
221
- end
222
- end
223
- write_proc ||= lambda {|out| te.write_to_csv(out, !Tb::Cmd.opt_N) }
166
+ filename = Tb::Cmd.opt_output || '-'
167
+ numeric = Tb::Cmd.opt_N
168
+ filename, fmt = Tb.undecorate_filename(filename, numeric)
169
+ factory = Tb::FormatHash.fetch(fmt)[:writer]
224
170
  with_output(filename) {|out|
225
- write_proc.call(out)
171
+ writer = factory.new(out)
172
+ te.write_with(writer)
226
173
  }
227
174
  end
@@ -1,6 +1,6 @@
1
1
  # lib/tb/csv.rb - CSV related fetures for table library
2
2
  #
3
- # Copyright (C) 2010-2013 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,95 +30,37 @@
30
30
 
31
31
  require 'csv'
32
32
 
33
- class Tb
34
- def Tb.load_csv(filename, *header_fields, &block)
35
- Tb.parse_csv(File.read(filename), *header_fields, &block)
33
+ module Tb
34
+ def Tb.csv_encode_row(ary)
35
+ ary.to_csv
36
36
  end
37
37
 
38
- def Tb.parse_csv(csv, *header_fields)
39
- aa = []
40
- csv_stream_input(csv) {|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
38
+ class HeaderCSVReader < HeaderReader
39
+ def initialize(io)
40
+ aryreader = CSV.new(io)
41
+ super lambda { aryreader.shift }
56
42
  end
57
43
  end
58
44
 
59
- def Tb.csv_stream_input(csv, &b)
60
- csvreader = CSVReader.new(csv)
61
- csvreader.each(&b)
62
- nil
63
- end
64
-
65
- def Tb.csv_read_aa(csv)
66
- aa = []
67
- Tb.csv_stream_input(csv) {|ary| aa << ary }
68
- aa
69
- end
70
-
71
- class CSVReader
72
- def initialize(input)
73
- @csv = CSV.new(input)
74
- end
75
-
76
- def shift
77
- @csv.shift
78
- end
79
-
80
- def each
81
- while ary = self.shift
82
- yield ary
83
- end
84
- nil
45
+ class HeaderCSVWriter < HeaderWriter
46
+ # io is an object which has "<<" method.
47
+ def initialize(io)
48
+ super lambda {|ary| io << ary.to_csv}
85
49
  end
86
50
  end
87
51
 
88
- def Tb.csv_stream_output(out)
89
- require 'csv'
90
- gen = Object.new
91
- gen.instance_variable_set(:@out, out)
92
- def gen.<<(ary)
93
- @out << ary.to_csv
52
+ class NumericCSVReader < NumericReader
53
+ def initialize(io)
54
+ aryreader = CSV.new(io)
55
+ super lambda { aryreader.shift }
94
56
  end
95
- yield gen
96
- end
97
-
98
- def Tb.csv_encode_row(ary)
99
- require 'csv'
100
- ary.to_csv
101
57
  end
102
58
 
103
- # :call-seq:
104
- # generate_csv(out='', fields=nil) {|recordids| modified_recordids }
105
- # generate_csv(out='', fields=nil)
106
- #
107
- def generate_csv(out='', fields=nil, &block)
108
- if fields.nil?
109
- fields = list_fields
59
+ class NumericCSVWriter < NumericWriter
60
+ # io is an object which has "<<" method.
61
+ def initialize(io)
62
+ super lambda {|ary| io << ary.to_csv }
110
63
  end
111
- require 'csv'
112
- recordids = list_recordids
113
- if block_given?
114
- recordids = yield(recordids)
115
- end
116
- Tb.csv_stream_output(out) {|gen|
117
- gen << fields
118
- recordids.each {|recordid|
119
- gen << get_values(recordid, *fields)
120
- }
121
- }
122
- out
123
64
  end
65
+
124
66
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2012-2013 Tanaka Akira <akr@fsij.org>
1
+ # Copyright (C) 2012-2014 Tanaka Akira <akr@fsij.org>
2
2
  #
3
3
  # Redistribution and use in source and binary forms, with or without
4
4
  # modification, are permitted provided that the following conditions
@@ -93,7 +93,7 @@ module Tb::Enumerable
93
93
  # creates a new Tb::Enumerator object which have
94
94
  # new field named by _field_ with the value returned by the block.
95
95
  #
96
- # t1 = Tb.new %w[a b], [1, 2], [3, 4]
96
+ # t1 = Tb::Enumerator.from_header_and_values %w[a b], [1, 2], [3, 4]
97
97
  # p t1.newfield("x") {|row| row["a"] + row["b"] + 100 }.to_a
98
98
  # #=> [{"x"=>103, "a"=>1, "b"=>2},
99
99
  # # {"x"=>107, "a"=>3, "b"=>4}]
@@ -102,11 +102,11 @@ module Tb::Enumerable
102
102
  Tb::Enumerator.new {|y|
103
103
  self.with_header {|header|
104
104
  if header
105
- y.set_header(Tb::FieldSet.normalize([field, *header]))
105
+ y.set_header([field, *header])
106
106
  end
107
107
  }.each {|row|
108
108
  keys = row.keys
109
- keys = Tb::FieldSet.normalize([field, *keys])
109
+ keys = [field, *keys]
110
110
  vals = row.values
111
111
  vals = [yield(row), *vals]
112
112
  y << Hash[keys.zip(vals)]
@@ -176,80 +176,46 @@ module Tb::Enumerable
176
176
  natjoin2(tbl2, missing_value, retain_left, retain_right)
177
177
  end
178
178
 
179
- def to_tb
180
- tb = Tb.new
181
- self.each {|pairs|
182
- pairs.each {|k, v|
183
- unless tb.has_field? k
184
- tb.define_field(k)
185
- end
179
+ def write_with(writer)
180
+ header_proc = nil
181
+ if writer.header_required?
182
+ header_proc = lambda {|header|
183
+ writer.header_generator = lambda { header }
186
184
  }
187
- tb.insert pairs
185
+ end
186
+ body_proc = lambda {|pairs|
187
+ writer.put_hash pairs
188
188
  }
189
- tb
189
+ header_and_each(header_proc, &body_proc)
190
+ writer.finish
190
191
  end
191
192
 
192
193
  def write_to_csv(io, with_header=true)
193
- stream = nil
194
- header = []
195
- fgen = fnew = nil
196
- self.with_cumulative_header {|header0|
197
- if !with_header
198
- stream = true
199
- elsif header0
200
- stream = true
201
- io.puts Tb.csv_encode_row(header0)
202
- else
203
- stream = false
204
- fgen, fnew = Tb::FileEnumerator.gen_new
205
- end
206
- }.each {|pairs, header1|
207
- pairs = Hash[pairs] unless pairs.respond_to? :has_key?
208
- header = header1
209
- if stream
210
- fs = header.dup
211
- while !fs.empty? && !pairs.has_key?(fs.last)
212
- fs.pop
213
- end
214
- ary = fs.map {|f| pairs[f] }
215
- io.puts Tb.csv_encode_row(ary)
216
- else
217
- fgen.call Hash[pairs]
218
- end
219
- }
220
- if !stream
221
- if with_header
222
- io.puts Tb.csv_encode_row(header)
223
- end
224
- fnew.call.each {|pairs|
225
- fs = header.dup
226
- while !fs.empty? && !pairs.has_key?(fs.last)
227
- fs.pop
228
- end
229
- ary = fs.map {|f| pairs[f] }
230
- io.puts Tb.csv_encode_row(ary)
231
- }
194
+ if with_header
195
+ write_with(Tb::HeaderCSVWriter.new(io))
196
+ else
197
+ write_with(Tb::NumericCSVWriter.new(io))
232
198
  end
233
199
  end
234
200
 
235
- def write_to_ltsv(out)
236
- self.each {|pairs|
237
- out.print Tb.ltsv_assoc_join(pairs)
238
- out.print "\n"
239
- }
201
+ def write_to_tsv(io, with_header=true)
202
+ if with_header
203
+ write_with(Tb::HeaderTSVWriter.new(io))
204
+ else
205
+ write_with(Tb::NumericTSVWriter.new(io))
206
+ end
240
207
  end
241
208
 
242
- def write_to_json(out)
243
- require 'json'
244
- out.print "["
245
- sep = nil
246
- self.each {|pairs|
247
- out.print sep if sep
248
- out.print JSON.pretty_generate(Hash[pairs.to_a])
249
- sep = ",\n"
250
- }
251
- out.puts "]"
252
- nil
209
+ def write_to_ltsv(io)
210
+ write_with(Tb::LTSVWriter.new(io))
211
+ end
212
+
213
+ def write_to_json(io)
214
+ write_with(Tb::JSONWriter.new(io))
215
+ end
216
+
217
+ def write_to_pnm(io)
218
+ write_with(Tb::PNMWriter.new(io))
253
219
  end
254
220
 
255
221
  def extsort_by(opts={}, &cmpvalue_from)
@@ -270,3 +236,11 @@ module Tb::Enumerable
270
236
  }
271
237
  end
272
238
  end
239
+
240
+ module Tb::EnumerableWithEach
241
+ include Tb::Enumerable
242
+
243
+ def each(&b)
244
+ header_and_each(nil, &b)
245
+ end
246
+ end