Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

663 lines (520 sloc) 20.389 kb
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#gsub with pattern and replacement" do
before :each do
@kcode = $KCODE
end
after :each do
$KCODE = @kcode
end
it "inserts the replacement around every character when the pattern collapses" do
"hello".gsub(//, ".").should == ".h.e.l.l.o."
end
it "respects $KCODE when the pattern collapses" do
str = "こにちわ"
reg = %r!!
$KCODE = "utf-8"
str.gsub(reg, ".").should == ".こ.に.ち.わ."
end
it "doesn't freak out when replacing ^" do
"Text\n".gsub(/^/, ' ').should == " Text\n"
"Text\nFoo".gsub(/^/, ' ').should == " Text\n Foo"
end
it "returns a copy of self with all occurrences of pattern replaced with replacement" do
"hello".gsub(/[aeiou]/, '*').should == "h*ll*"
str = "hello homely world. hah!"
str.gsub(/\Ah\S+\s*/, "huh? ").should == "huh? homely world. hah!"
end
it "ignores a block if supplied" do
"food".gsub(/f/, "g") { "w" }.should == "good"
end
it "supports \\G which matches at the beginning of the remaining (non-matched) string" do
str = "hello homely world. hah!"
str.gsub(/\Gh\S+\s*/, "huh? ").should == "huh? huh? world. hah!"
end
it "supports /i for ignoring case" do
str = "Hello. How happy are you?"
str.gsub(/h/i, "j").should == "jello. jow jappy are you?"
str.gsub(/H/i, "j").should == "jello. jow jappy are you?"
end
it "doesn't interpret regexp metacharacters if pattern is a string" do
"12345".gsub('\d', 'a').should == "12345"
'\d'.gsub('\d', 'a').should == "a"
end
it "replaces \\1 sequences with the regexp's corresponding capture" do
str = "hello"
str.gsub(/([aeiou])/, '<\1>').should == "h<e>ll<o>"
str.gsub(/(.)/, '\1\1').should == "hheelllloo"
str.gsub(/.(.?)/, '<\0>(\1)').should == "<he>(e)<ll>(l)<o>()"
str.gsub(/.(.)+/, '\1').should == "o"
str = "ABCDEFGHIJKLabcdefghijkl"
re = /#{"(.)" * 12}/
str.gsub(re, '\1').should == "Aa"
str.gsub(re, '\9').should == "Ii"
# Only the first 9 captures can be accessed in MRI
str.gsub(re, '\10').should == "A0a0"
end
it "treats \\1 sequences without corresponding captures as empty strings" do
str = "hello!"
str.gsub("", '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("h", '<\1>').should == "<>ello!"
str.gsub(//, '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(/./, '\1\2\3').should == ""
str.gsub(/.(.{20})?/, '\1').should == ""
end
it "replaces \\& and \\0 with the complete match" do
str = "hello!"
str.gsub("", '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("", '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("he", '<\0>').should == "<he>llo!"
str.gsub("he", '<\&>').should == "<he>llo!"
str.gsub("l", '<\0>').should == "he<l><l>o!"
str.gsub("l", '<\&>').should == "he<l><l>o!"
str.gsub(//, '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(//, '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(/../, '<\0>').should == "<he><ll><o!>"
str.gsub(/../, '<\&>').should == "<he><ll><o!>"
str.gsub(/(.)./, '<\0>').should == "<he><ll><o!>"
end
it "replaces \\` with everything before the current match" do
str = "hello!"
str.gsub("", '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
str.gsub("h", '<\`>').should == "<>ello!"
str.gsub("l", '<\`>').should == "he<he><hel>o!"
str.gsub("!", '<\`>').should == "hello<hello>"
str.gsub(//, '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
str.gsub(/../, '<\`>').should == "<><he><hell>"
end
it "replaces \\' with everything after the current match" do
str = "hello!"
str.gsub("", '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
str.gsub("h", '<\\\'>').should == "<ello!>ello!"
str.gsub("ll", '<\\\'>').should == "he<o!>o!"
str.gsub("!", '<\\\'>').should == "hello<>"
str.gsub(//, '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
str.gsub(/../, '<\\\'>').should == "<llo!><o!><>"
end
it "replaces \\+ with the last paren that actually matched" do
str = "hello!"
str.gsub(/(.)(.)/, '\+').should == "el!"
str.gsub(/(.)(.)+/, '\+').should == "!"
str.gsub(/(.)()/, '\+').should == ""
str.gsub(/(.)(.{20})?/, '<\+>').should == "<h><e><l><l><o><!>"
str = "ABCDEFGHIJKLabcdefghijkl"
re = /#{"(.)" * 12}/
str.gsub(re, '\+').should == "Ll"
end
it "treats \\+ as an empty string if there was no captures" do
"hello!".gsub(/./, '\+').should == ""
end
it "maps \\\\ in replacement to \\" do
"hello".gsub(/./, '\\\\').should == '\\' * 5
end
it "leaves unknown \\x escapes in replacement untouched" do
"hello".gsub(/./, '\\x').should == '\\x' * 5
"hello".gsub(/./, '\\y').should == '\\y' * 5
end
it "leaves \\ at the end of replacement untouched" do
"hello".gsub(/./, 'hah\\').should == 'hah\\' * 5
end
it "taints the result if the original string or replacement is tainted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.taint; a_t.taint; empty_t.taint
hello_t.gsub(/./, a).tainted?.should == true
hello_t.gsub(/./, empty).tainted?.should == true
hello.gsub(/./, a_t).tainted?.should == true
hello.gsub(/./, empty_t).tainted?.should == true
hello.gsub(//, empty_t).tainted?.should == true
hello.gsub(//.taint, "foo").tainted?.should == false
end
ruby_version_is "1.9" do
it "untrusts the result if the original string or replacement is untrusted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.untrust; a_t.untrust; empty_t.untrust
hello_t.gsub(/./, a).untrusted?.should == true
hello_t.gsub(/./, empty).untrusted?.should == true
hello.gsub(/./, a_t).untrusted?.should == true
hello.gsub(/./, empty_t).untrusted?.should == true
hello.gsub(//, empty_t).untrusted?.should == true
hello.gsub(//.untrust, "foo").untrusted?.should == false
end
end
it "tries to convert pattern to a string using to_str" do
pattern = mock('.')
def pattern.to_str() "." end
"hello.".gsub(pattern, "!").should == "hello!"
end
it "raises a TypeError when pattern can't be converted to a string" do
lambda { "hello".gsub([], "x") }.should raise_error(TypeError)
lambda { "hello".gsub(Object.new, "x") }.should raise_error(TypeError)
lambda { "hello".gsub(nil, "x") }.should raise_error(TypeError)
end
it "tries to convert replacement to a string using to_str" do
replacement = mock('hello_replacement')
def replacement.to_str() "hello_replacement" end
"hello".gsub(/hello/, replacement).should == "hello_replacement"
end
it "raises a TypeError when replacement can't be converted to a string" do
lambda { "hello".gsub(/[aeiou]/, []) }.should raise_error(TypeError)
lambda { "hello".gsub(/[aeiou]/, Object.new) }.should raise_error(TypeError)
lambda { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError)
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("").gsub(//, "").should be_kind_of(StringSpecs::MyString)
StringSpecs::MyString.new("").gsub(/foo/, "").should be_kind_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_kind_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").gsub("foo", "").should be_kind_of(StringSpecs::MyString)
end
# Note: $~ cannot be tested because mspec messes with it
it "sets $~ to MatchData of last match and nil when there's none" do
'hello.'.gsub('hello', 'x')
$~[0].should == 'hello'
'hello.'.gsub('not', 'x')
$~.should == nil
'hello.'.gsub(/.(.)/, 'x')
$~[0].should == 'o.'
'hello.'.gsub(/not/, 'x')
$~.should == nil
end
end
ruby_version_is "1.9" do
describe "String#gsub with pattern and Hash" do
it "returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key" do
"hello".gsub(/./, 'l' => 'L').should == "LL"
"hello!".gsub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
"hello".gsub('l', 'l' => 'el').should == 'heelelo'
end
it "ignores keys that don't correspond to matches" do
"hello".gsub(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
end
it "returns an empty string if the pattern matches but the hash specifies no replacements" do
"hello".gsub(/./, 'z' => 'L').should == ""
end
it "ignores non-String keys" do
"hello".gsub(/(ll)/, 'll' => 'r', :ll => 'z').should == "hero"
end
it "uses a key's value as many times as needed" do
"food".gsub(/o/, 'o' => '0').should == "f00d"
end
it "uses the hash's default value for missing keys" do
hsh = new_hash
hsh.default='?'
hsh['o'] = '0'
"food".gsub(/./, hsh).should == "?00?"
end
it "coerces the hash values with #to_s" do
hsh = new_hash
hsh.default=[]
hsh['o'] = 0
obj = mock('!')
obj.should_receive(:to_s).and_return('!')
hsh['!'] = obj
"food!".gsub(/./, hsh).should == "[]00[]!"
end
it "uses the hash's value set from default_proc for missing keys" do
hsh = new_hash
hsh.default_proc = lambda { |k,v| 'lamb' }
"food!".gsub(/./, hsh).should == "lamblamblamblamblamb"
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub('l', 'l' => 'L')
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub('not', 'ot' => 'to')
$~.should == nil
'hello.'.gsub(/.(.)/, 'o' => ' hole')
$~[0].should == 'o.'
'hello.'.gsub(/not/, 'z' => 'glark')
$~.should == nil
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub(/(.+)/, 'hello' => repl ).should == repl
end
it "untrusts the result if the original string is untrusted" do
str = "Ghana".untrust
str.gsub(/[Aa]na/, 'ana' => '').untrusted?.should be_true
end
it "untrusts the result if a hash value is untrusted" do
str = "Ghana"
str.gsub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
end
it "taints the result if the original string is tainted" do
str = "Ghana".taint
str.gsub(/[Aa]na/, 'ana' => '').tainted?.should be_true
end
it "taints the result if a hash value is tainted" do
str = "Ghana"
str.gsub(/a$/, 'a' => 'di'.taint).tainted?.should be_true
end
end
describe "String#gsub! with pattern and Hash" do
it "returns self with all occurrences of pattern replaced with the value of the corresponding hash key" do
"hello".gsub!(/./, 'l' => 'L').should == "LL"
"hello!".gsub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
"hello".gsub!('l', 'l' => 'el').should == 'heelelo'
end
it "ignores keys that don't correspond to matches" do
"hello".gsub!(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
end
it "replaces self with an empty string if the pattern matches but the hash specifies no replacements" do
"hello".gsub!(/./, 'z' => 'L').should == ""
end
it "ignores non-String keys" do
"hello".gsub!(/(ll)/, 'll' => 'r', :ll => 'z').should == "hero"
end
it "uses a key's value as many times as needed" do
"food".gsub!(/o/, 'o' => '0').should == "f00d"
end
it "uses the hash's default value for missing keys" do
hsh = new_hash
hsh.default='?'
hsh['o'] = '0'
"food".gsub!(/./, hsh).should == "?00?"
end
it "coerces the hash values with #to_s" do
hsh = new_hash
hsh.default=[]
hsh['o'] = 0
obj = mock('!')
obj.should_receive(:to_s).and_return('!')
hsh['!'] = obj
"food!".gsub!(/./, hsh).should == "[]00[]!"
end
it "uses the hash's value set from default_proc for missing keys" do
hsh = new_hash
hsh.default_proc = lambda { |k,v| 'lamb' }
"food!".gsub!(/./, hsh).should == "lamblamblamblamblamb"
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub!('l', 'l' => 'L')
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub!('not', 'ot' => 'to')
$~.should == nil
'hello.'.gsub!(/.(.)/, 'o' => ' hole')
$~[0].should == 'o.'
'hello.'.gsub!(/not/, 'z' => 'glark')
$~.should == nil
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub!(/(.+)/, 'hello' => repl ).should == repl
end
it "keeps untrusted state" do
str = "Ghana".untrust
str.gsub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true
end
it "untrusts self if a hash value is untrusted" do
str = "Ghana"
str.gsub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
end
it "keeps tainted state" do
str = "Ghana".taint
str.gsub!(/[Aa]na/, 'ana' => '').tainted?.should be_true
end
it "taints self if a hash value is tainted" do
str = "Ghana"
str.gsub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true
end
end
end
describe "String#gsub with pattern and block" do
it "returns a copy of self with all occurrences of pattern replaced with the block's return value" do
"hello".gsub(/./) { |s| s.succ + ' ' }.should == "i f m m p "
"hello!".gsub(/(.)(.)/) { |*a| a.inspect }.should == '["he"]["ll"]["o!"]'
"hello".gsub('l') { 'x'}.should == 'hexxo'
end
it "sets $~ for access from the block" do
str = "hello"
str.gsub(/([aeiou])/) { "<#{$~[1]}>" }.should == "h<e>ll<o>"
str.gsub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>ll<o>"
str.gsub("l") { "<#{$~[0]}>" }.should == "he<l><l>o"
offsets = []
str.gsub(/([aeiou])/) do
md = $~
md.string.should == str
offsets << md.offset(0)
str
end.should == "hhellollhello"
offsets.should == [[1, 2], [4, 5]]
end
it "restores $~ after leaving the block" do
[/./, "l"].each do |pattern|
old_md = nil
"hello".gsub(pattern) do
old_md = $~
"ok".match(/./)
"x"
end
$~[0].should == old_md[0]
$~.string.should == "hello"
end
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub('l') { 'x' }
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub('not') { 'x' }
$~.should == nil
'hello.'.gsub(/.(.)/) { 'x' }
$~[0].should == 'o.'
'hello.'.gsub(/not/) { 'x' }
$~.should == nil
end
ruby_version_is ""..."1.9" do
it "raises a RuntimeError if the string is modified while substituting" do
str = "hello"
lambda { str.gsub(//) { str[0] = 'x' } }.should raise_error(RuntimeError)
end
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub(/(.+)/) { repl }.should == repl
end
it "converts the block's return value to a string using to_s" do
replacement = mock('hello_replacement')
def replacement.to_s() "hello_replacement" end
"hello".gsub(/hello/) { replacement }.should == "hello_replacement"
obj = mock('ok')
def obj.to_s() "ok" end
"hello".gsub(/.+/) { obj }.should == "ok"
end
ruby_version_is "1.9" do
it "untrusts the result if the original string or replacement is untrusted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.untrust; a_t.untrust; empty_t.untrust
hello_t.gsub(/./) { a }.untrusted?.should == true
hello_t.gsub(/./) { empty }.untrusted?.should == true
hello.gsub(/./) { a_t }.untrusted?.should == true
hello.gsub(/./) { empty_t }.untrusted?.should == true
hello.gsub(//) { empty_t }.untrusted?.should == true
hello.gsub(//.untrust) { "foo" }.untrusted?.should == false
end
end
end
describe "String#gsub! with pattern and replacement" do
it "modifies self in place and returns self" do
a = "hello"
a.gsub!(/[aeiou]/, '*').should equal(a)
a.should == "h*ll*"
end
it "taints self if replacement is tainted" do
a = "hello"
a.gsub!(/./.taint, "foo").tainted?.should == false
a.gsub!(/./, "foo".taint).tainted?.should == true
end
ruby_version_is "1.9" do
it "untrusts self if replacement is untrusted" do
a = "hello"
a.gsub!(/./.untrust, "foo").untrusted?.should == false
a.gsub!(/./, "foo".untrust).untrusted?.should == true
end
end
it "returns nil if no modifications were made" do
a = "hello"
a.gsub!(/z/, '*').should == nil
a.gsub!(/z/, 'z').should == nil
a.should == "hello"
end
ruby_version_is ""..."1.9" do
it "does not raise an error if the frozen string would not be modified" do
s = "hello"
s.freeze
s.gsub!(/ROAR/, "x").should be_nil
end
it "raises a TypeError if the frozen string would be modified" do
s = "hello"
s.freeze
lambda { s.gsub!(/e/, "e") }.should raise_error(TypeError)
lambda { s.gsub!(/[aeiou]/, '*') }.should raise_error(TypeError)
end
end
# See [ruby-core:23666]
ruby_version_is "1.9" do
it "raises a RuntimeError when self is frozen" do
s = "hello"
s.freeze
lambda { s.gsub!(/ROAR/, "x") }.should raise_error(RuntimeError)
lambda { s.gsub!(/e/, "e") }.should raise_error(RuntimeError)
lambda { s.gsub!(/[aeiou]/, '*') }.should raise_error(RuntimeError)
end
end
end
describe "String#gsub! with pattern and block" do
it "modifies self in place and returns self" do
a = "hello"
a.gsub!(/[aeiou]/) { '*' }.should equal(a)
a.should == "h*ll*"
end
it "taints self if block's result is tainted" do
a = "hello"
a.gsub!(/./.taint) { "foo" }.tainted?.should == false
a.gsub!(/./) { "foo".taint }.tainted?.should == true
end
ruby_version_is "1.9" do
it "untrusts self if block's result is untrusted" do
a = "hello"
a.gsub!(/./.untrust) { "foo" }.untrusted?.should == false
a.gsub!(/./) { "foo".untrust }.untrusted?.should == true
end
end
it "returns nil if no modifications were made" do
a = "hello"
a.gsub!(/z/) { '*' }.should == nil
a.gsub!(/z/) { 'z' }.should == nil
a.should == "hello"
end
ruby_version_is ""..."1.9" do
it "does not raise an error if the frozen string would not be modified" do
s = "hello"
s.freeze
s.gsub!(/ROAR/) { "x" }.should be_nil
end
deviates_on :rubinius do
# MRI 1.8.x is inconsistent here, raising a TypeError when not passed
# a block and a RuntimeError when passed a block. This is arguably a
# bug in MRI. In 1.9, both situations raise a RuntimeError.
it "raises a TypeError if the frozen string would be modified" do
s = "hello"
s.freeze
lambda { s.gsub!(/e/) { "e" } }.should raise_error(TypeError)
lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(TypeError)
end
end
not_compliant_on :rubinius do
it "raises a RuntimeError if the frozen string would be modified" do
s = "hello"
s.freeze
lambda { s.gsub!(/e/) { "e" } }.should raise_error(RuntimeError)
lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(RuntimeError)
end
end
end
# See [ruby-core:23663]
ruby_version_is "1.9" do
it "raises a RuntimeError when self is frozen" do
s = "hello"
s.freeze
lambda { s.gsub!(/ROAR/) { "x" } }.should raise_error(RuntimeError)
lambda { s.gsub!(/e/) { "e" } }.should raise_error(RuntimeError)
lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(RuntimeError)
end
end
end
Jump to Line
Something went wrong with that request. Please try again.