Skip to content
Browse files

Merge pull request #1330 from frodriguezsanc/master

Fixed spec 1.9: String#lines
  • Loading branch information...
2 parents 5a9984b + 4e325a6 commit ae8f034f6688be3af553e851e29ac83dcf3934f1 @davorb davorb committed Oct 22, 2011
Showing with 211 additions and 1 deletion.
  1. +108 −0 kernel/common/string18.rb
  2. +103 −0 kernel/common/string19.rb
  3. +0 −1 spec/tags/19/ruby/core/string/lines_tags.txt
View
108 kernel/common/string18.rb
@@ -567,4 +567,112 @@ def <<(other)
append(other)
end
alias_method :concat, :<<
+
+ # Splits <i>self</i> using the supplied parameter as the record separator
+ # (<code>$/</code> by default), passing each substring in turn to the supplied
+ # block. If a zero-length record separator is supplied, the string is split on
+ # <code>\n</code> characters, except that multiple successive newlines are
+ # appended together.
+ #
+ # print "Example one\n"
+ # "hello\nworld".each { |s| p s }
+ # print "Example two\n"
+ # "hello\nworld".each('l') { |s| p s }
+ # print "Example three\n"
+ # "hello\n\n\nworld".each('') { |s| p s }
+ #
+ # <em>produces:</em>
+ #
+ # Example one
+ # "hello\n"
+ # "world"
+ # Example two
+ # "hel"
+ # "l"
+ # "o\nworl"
+ # "d"
+ # Example three
+ # "hello\n\n\n"
+ # "world"
+ def each_line(sep=$/)
+ return to_enum(:each_line, sep) unless block_given?
+
+ # weird edge case.
+ if sep.nil?
+ yield self
+ return self
+ end
+
+ sep = StringValue(sep)
+
+ pos = 0
+
+ size = @num_bytes
+ orig_data = @data
+
+ # If the separator is empty, we're actually in paragraph mode. This
+ # is used so infrequently, we'll handle it completely separately from
+ # normal line breaking.
+ if sep.empty?
+ sep = "\n\n"
+ pat_size = 2
+
+ while pos < size
+ nxt = find_string(sep, pos)
+ break unless nxt
+
+ while @data[nxt] == 10 and nxt < @num_bytes
+ nxt += 1
+ end
+
+ match_size = nxt - pos
+
+ # string ends with \n's
+ break if pos == @num_bytes
+
+ str = substring(pos, match_size)
+ yield str unless str.empty?
+
+ # detect mutation within the block
+ if !@data.equal?(orig_data) or @num_bytes != size
+ raise RuntimeError, "string modified while iterating"
+ end
+
+ pos = nxt
+ end
+
+ # No more separates, but we need to grab the last part still.
+ fin = substring(pos, @num_bytes - pos)
+ yield fin if fin and !fin.empty?
+
+ else
+
+ # This is the normal case.
+ pat_size = sep.size
+
+ while pos < size
+ nxt = find_string(sep, pos)
+ break unless nxt
+
+ match_size = nxt - pos
+ str = substring(pos, match_size + pat_size)
+ yield str unless str.empty?
+
+ # detect mutation within the block
+ if !@data.equal?(orig_data) or @num_bytes != size
+ raise RuntimeError, "string modified while iterating"
+ end
+
+ pos = nxt + pat_size
+ end
+
+ # No more separates, but we need to grab the last part still.
+ fin = substring(pos, @num_bytes - pos)
+ yield fin unless fin.empty?
+ end
+
+ self
+ end
+
+ alias_method :lines, :each_line
end
View
103 kernel/common/string19.rb
@@ -645,4 +645,107 @@ def chr
end
end
+ # Splits <i>self</i> using the supplied parameter as the record separator
+ # (<code>$/</code> by default), passing each substring in turn to the supplied
+ # block. If a zero-length record separator is supplied, the string is split on
+ # <code>\n</code> characters, except that multiple successive newlines are
+ # appended together.
+ #
+ # print "Example one\n"
+ # "hello\nworld".each { |s| p s }
+ # print "Example two\n"
+ # "hello\nworld".each('l') { |s| p s }
+ # print "Example three\n"
+ # "hello\n\n\nworld".each('') { |s| p s }
+ #
+ # <em>produces:</em>
+ #
+ # Example one
+ # "hello\n"
+ # "world"
+ # Example two
+ # "hel"
+ # "l"
+ # "o\nworl"
+ # "d"
+ # Example three
+ # "hello\n\n\n"
+ # "world"
+ def each_line(sep=$/)
+ return to_enum(:each_line, sep) unless block_given?
+
+ # weird edge case.
+ if sep.nil?
+ yield self
+ return self
+ end
+
+ sep = StringValue(sep)
+
+ pos = 0
+
+ size = @num_bytes
+ orig_data = @data
+
+ # If the separator is empty, we're actually in paragraph mode. This
+ # is used so infrequently, we'll handle it completely separately from
+ # normal line breaking.
+ if sep.empty?
+ sep = "\n\n"
+ pat_size = 2
+
+ while pos < size
+ nxt = find_string(sep, pos)
+ break unless nxt
+
+ while @data[nxt] == 10 and nxt < @num_bytes
+ nxt += 1
+ end
+
+ match_size = nxt - pos
+
+ # string ends with \n's
+ break if pos == @num_bytes
+
+ str = substring(pos, match_size)
+ yield str unless str.empty?
+
+ # detect mutation within the block
+ if !@data.equal?(orig_data) or @num_bytes != size
+ raise RuntimeError, "string modified while iterating"
+ end
+
+ pos = nxt
+ end
+
+ # No more separates, but we need to grab the last part still.
+ fin = substring(pos, @num_bytes - pos)
+ yield fin if fin and !fin.empty?
+
+ else
+
+ # This is the normal case.
+ pat_size = sep.size
+ unmodified_self = clone
+
+ while pos < size
+ nxt = unmodified_self.find_string(sep, pos)
+ break unless nxt
+
+ match_size = nxt - pos
+ str = unmodified_self.substring(pos, match_size + pat_size)
+ yield str unless str.empty?
+
+ pos = nxt + pat_size
+ end
+
+ # No more separates, but we need to grab the last part still.
+ fin = unmodified_self.substring(pos, @num_bytes - pos)
+ yield fin unless fin.empty?
+ end
+
+ self
+ end
+
+ alias_method :lines, :each_line
end
View
1 spec/tags/19/ruby/core/string/lines_tags.txt
@@ -1 +0,0 @@
-fails:String#lines does not care if the string is modified while substituting

0 comments on commit ae8f034

Please sign in to comment.
Something went wrong with that request. Please try again.