@@ -36,36 +36,48 @@ def each_field(delimiter)
36
36
def each ( delimiter , field_count , start_match )
37
37
return enum_for ( __method__ , delimiter , field_count , start_match ) unless block_given?
38
38
39
- # The number of fields processed since passing the last start-of-fields
40
- # match
41
- seen_fields = 0
39
+ chunked = each_field ( delimiter ) . chunk ( &chunk_matches ( start_match ) )
42
40
43
- fields = each_field ( delimiter )
41
+ yield_fields = lambda do |( _ , chunk ) |
42
+ chunk . each { |e | yield e }
43
+ ( field_count - chunk . size ) . times { yield "" }
44
+ end
45
+
46
+ # Skip junk that might appear before the first start-of-fields match
47
+ begin
48
+ n , chunk = chunked . next
49
+ yield_fields . call ( [ n , chunk ] ) unless n . zero?
50
+ rescue StopIteration
51
+ return
52
+ end
44
53
45
- # Close over +field_count+ and +seen_fields+ to yield empty strings to
46
- # the caller when we've already hit the next start-of-fields match
47
- yield_remaining = -> { ( field_count - seen_fields ) . times { yield "" } }
54
+ chunked . each ( &yield_fields )
55
+ end
48
56
49
- # Advance until the first start-of-fields match
50
- loop { break if fields . next =~ start_match }
57
+ private
51
58
52
- fields . each do |field |
53
- # If the current field is the start-of-fields match...
54
- if field . match? ( start_match )
55
- # Fill out any remaining (unparseable) fields with empty strings
56
- yield_remaining . call
59
+ # @param [Regexp] start_match a +Regexp+ that, when matched against the
60
+ # input stream, signifies the beginning of the next series of fields to
61
+ # yield
62
+ # @return [Proc] a unary +Proc+ that returns +nil+ if the argument mathes
63
+ # the +start_match+ +Regexp+, and otherwise returns the number of
64
+ # start-of-fields signifiers so far encountered.
65
+ # @example
66
+ # chunker = chunk_matches /<=>/
67
+ # chunked = %w[foo fighters <=> bar none <=> baz luhrmann].chunk(&chunker)
68
+ # chunked.to_a
69
+ # #=> [[0, ["foo", "fighters"]], [1, ["bar", "none"]], [2, ["baz", "luhrmann"]]]
70
+ def chunk_matches ( start_match )
71
+ i = 0
57
72
58
- seen_fields = 0
59
- elsif seen_fields < field_count
60
- yield field
61
- seen_fields += 1
73
+ lambda do |e |
74
+ if e . match? ( start_match )
75
+ i += 1
76
+ nil
77
+ else
78
+ i
62
79
end
63
80
end
64
-
65
- # One last filling-out of empty fields if we're at the end of the stream
66
- yield_remaining . call
67
-
68
- read . close unless read . closed?
69
81
end
70
82
end
71
83
end
0 commit comments