Skip to content

Commit

Permalink
Implement String#initialize_copy (and #replace)
Browse files Browse the repository at this point in the history
  • Loading branch information
richardboehme committed Mar 23, 2022
1 parent dd06562 commit d72af07
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/natalie/string_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class StringObject : public Object {
}

Value initialize(Env *, Value);
Value initialize_copy(Env *, Value);
Value ltlt(Env *, Value);

bool eql(Value arg) const {
Expand Down
1 change: 1 addition & 0 deletions lib/natalie/compiler/binding_gen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ def generate_name
gen.binding('String', 'include?', 'StringObject', 'include', argc: 1, pass_env: true, pass_block: false, return_type: :bool)
gen.binding('String', 'index', 'StringObject', 'index', argc: 1, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'initialize', 'StringObject', 'initialize', argc: 0..1, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'initialize_copy', 'StringObject', 'initialize_copy', argc: 1, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'inspect', 'StringObject', 'inspect', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'intern', 'StringObject', 'to_sym', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'length', 'StringObject', 'size', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
Expand Down
7 changes: 7 additions & 0 deletions spec/core/string/replace_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/replace'

describe "String#replace" do
it_behaves_like :string_replace, :replace
end
79 changes: 79 additions & 0 deletions spec/core/string/shared/replace.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
describe :string_replace, shared: true do
it "returns self" do
a = "a"
a.send(@method, "b").should equal(a)
end

it "replaces the content of self with other" do
a = "some string"
a.send(@method, "another string")
a.should == "another string"
end

ruby_version_is ''...'2.7' do
it "taints self if other is tainted" do
a = ""
b = "".taint
a.send(@method, b)
a.should.tainted?
end

it "does not untaint self if other is untainted" do
a = "".taint
b = ""
a.send(@method, b)
a.should.tainted?
end

it "untrusts self if other is untrusted" do
a = ""
b = "".untrust
a.send(@method, b)
a.should.untrusted?
end

it "does not trust self if other is trusted" do
a = "".untrust
b = ""
a.send(@method, b)
a.should.untrusted?
end
end

# NATFIXME: Implement Encoding::UTF-16LE
xit "replaces the encoding of self with that of other" do
a = "".encode("UTF-16LE")
b = "".encode("UTF-8")
a.send(@method, b)
a.encoding.should == Encoding::UTF_8
end

# NATFIXME: Implement Encoding::US_ASCII
xit "carries over the encoding invalidity" do
a = "\u{8765}".force_encoding('ascii')
"".send(@method, a).valid_encoding?.should be_false
end

it "tries to convert other to string using to_str" do
other = mock('x')
other.should_receive(:to_str).and_return("converted to a string")
"hello".send(@method, other).should == "converted to a string"
end

it "raises a TypeError if other can't be converted to string" do
-> { "hello".send(@method, 123) }.should raise_error(TypeError)
-> { "hello".send(@method, []) }.should raise_error(TypeError)
-> { "hello".send(@method, mock('x')) }.should raise_error(TypeError)
end

it "raises a FrozenError on a frozen instance that is modified" do
a = "hello".freeze
-> { a.send(@method, "world") }.should raise_error(FrozenError)
end

# see [ruby-core:23666]
it "raises a FrozenError on a frozen instance when self-replacing" do
a = "hello".freeze
-> { a.send(@method, a) }.should raise_error(FrozenError)
end
end
3 changes: 3 additions & 0 deletions src/string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class String
alias replace initialize_copy
end
9 changes: 9 additions & 0 deletions src/string_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ Value StringObject::initialize(Env *env, Value arg) {
return this;
}

Value StringObject::initialize_copy(Env *env, Value arg) {
assert_not_frozen(env);
if (!arg->is_string() && arg->respond_to(env, "to_str"_s))
arg = arg->send(env, "to_str"_s);
arg->assert_type(env, Type::String, "String");
m_string = arg->as_string()->string();
return this;
}

Value StringObject::ltlt(Env *env, Value arg) {
concat(env, 1, &arg);
return this;
Expand Down

0 comments on commit d72af07

Please sign in to comment.