From 9137dc2618469e1bb410bc381ca8b75db6bb17e8 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 12 Jul 2019 17:23:48 -0700 Subject: [PATCH] Support returning an enumerator for String#sub and #sub! If String#sub and #sub! are called with a single argument and no block, this change makes them return an enumerator for consistency with #gsub and #gsub!, instead of raising an ArgumentError. Implements [Feature #8853] --- string.c | 18 ++++++++++++++---- test/ruby/test_string.rb | 13 +++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/string.c b/string.c index 8d7d2ad7b9..ac834d8996 100644 --- a/string.c +++ b/string.c @@ -4987,11 +4987,13 @@ rb_pat_search(VALUE pat, VALUE str, long pos, int set_backref_str) * call-seq: * str.sub!(pattern, replacement) -> str or nil * str.sub!(pattern) {|match| block } -> str or nil + * str.sub!(pattern) -> enumerator * * Performs the same substitution as String#sub in-place. * * Returns +str+ if a substitution was performed or +nil+ if no substitution - * was performed. + * was performed. If no block and no replacement is given, an + * enumerator is returned instead. */ static VALUE @@ -5001,11 +5003,11 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) int iter = 0; int tainted = 0; long plen; - int min_arity = rb_block_given_p() ? 1 : 2; long beg; - rb_check_arity(argc, min_arity, 2); + rb_check_arity(argc, 1, 2); if (argc == 1) { + RETURN_ENUMERATOR(str, argc, argv); iter = 1; } else { @@ -5111,6 +5113,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) * str.sub(pattern, replacement) -> new_str * str.sub(pattern, hash) -> new_str * str.sub(pattern) {|match| block } -> new_str + * str.sub(pattern) -> enumerator * * Returns a copy of +str+ with the _first_ occurrence of +pattern+ * replaced by the second argument. The +pattern+ is typically a Regexp; if @@ -5137,6 +5140,9 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) * $&, and $' will be set appropriately. The value * returned by the block will be substituted for the match on each call. * + * When neither a block nor a second argument is supplied, an + * Enumerator is returned. + * * The result inherits any tainting in the original string or any supplied * replacement string. * @@ -5151,8 +5157,12 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_sub(int argc, VALUE *argv, VALUE str) { + VALUE ret; str = rb_str_dup(str); - rb_str_sub_bang(argc, argv, str); + ret = rb_str_sub_bang(argc, argv, str); + if (argc == 1 && !rb_block_given_p()) { + return ret; + } return str; } diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 26de52dedb..ce073d52a6 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -1270,6 +1270,19 @@ def test_gsub! assert_nil(a.sub!(S('X'), S('Y'))) end + def test_sub_enumerator + enum = 'abc'.sub("b") + assert_equal("b", enum.next) + assert_raise(StopIteration) { enum.next } + + str = 'abc' + enum = str.sub!("b") + enum.feed 'z' + assert_equal("b", enum.next) + assert_raise(StopIteration) { enum.next } + assert_equal("azc", str) + end + def test_sub_hash assert_equal('azc', 'abc'.sub(/b/, "b" => "z")) assert_equal('ac', 'abc'.sub(/b/, {})) -- 2.21.0