Skip to content

Commit

Permalink
Implement String#include?
Browse files Browse the repository at this point in the history
  • Loading branch information
FnControlOption committed Jan 7, 2022
1 parent a4ec813 commit ec4139e
Show file tree
Hide file tree
Showing 4 changed files with 46 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 @@ -152,6 +152,7 @@ class StringObject : public Object {
bool eq(Env *, Value arg);
Value eqtilde(Env *, Value);
Value force_encoding(Env *, Value);
bool include(Env *, Value);
Value ljust(Env *, Value, Value);
Value lstrip(Env *) const;
Value lstrip_in_place(Env *);
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 @@ -796,6 +796,7 @@ def generate_name
gen.binding('String', 'eql?', 'StringObject', 'eql', argc: 1, pass_env: false, pass_block: false, return_type: :bool)
gen.binding('String', 'force_encoding', 'StringObject', 'force_encoding', argc: 1, pass_env: true, pass_block: false, return_type: :Object)
gen.binding('String', 'gsub', 'StringObject', 'gsub', argc: 1..2, pass_env: true, pass_block: true, return_type: :Object)
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', 'inspect', 'StringObject', 'inspect', argc: 0, pass_env: true, pass_block: false, return_type: :Object)
Expand Down
36 changes: 36 additions & 0 deletions spec/core/string/include_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

describe "String#include? with String" do
it "returns true if self contains other_str" do
"hello".include?("lo").should == true
"hello".include?("ol").should == false
end

it "ignores subclass differences" do
"hello".include?(StringSpecs::MyString.new("lo")).should == true
StringSpecs::MyString.new("hello").include?("lo").should == true
StringSpecs::MyString.new("hello").include?(StringSpecs::MyString.new("lo")).should == true
end

it "tries to convert other to string using to_str" do
other = mock('lo')
other.should_receive(:to_str).and_return("lo")

"hello".include?(other).should == true
end

it "raises a TypeError if other can't be converted to string" do
-> { "hello".include?([]) }.should raise_error(TypeError)
-> { "hello".include?('h'.ord) }.should raise_error(TypeError)
-> { "hello".include?(mock('x')) }.should raise_error(TypeError)
end

# NATFIXME: Implement EUC-JP encoding
xit "raises an Encoding::CompatibilityError if the encodings are incompatible" do
pat = "ア".encode Encoding::EUC_JP
-> do
"あれ".include?(pat)
end.should raise_error(Encoding::CompatibilityError)
end
end
8 changes: 8 additions & 0 deletions src/string_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,14 @@ Value StringObject::split(Env *env, Value splitter, Value max_count_value) {
}
}

bool StringObject::include(Env *env, Value arg) {
auto to_str = "to_str"_s;
if (!arg->is_string() && arg->respond_to(env, to_str))
arg = arg->send(env, to_str);
arg->assert_type(env, Object::Type::String, "String");
return ::strstr(c_str(), arg->as_string()->c_str()) != nullptr;
}

Value StringObject::ljust(Env *env, Value length_obj, Value pad_obj) {
length_obj->assert_type(env, Object::Type::Integer, "Integer");
size_t length = length_obj->as_integer()->to_nat_int_t() < 0 ? 0 : length_obj->as_integer()->to_nat_int_t();
Expand Down

0 comments on commit ec4139e

Please sign in to comment.