Project

General

Profile

Actions

Feature #16348

closed

Proposal: Symbol#start_with?, Symbol#end_with?, and Symbol#include?

Added by kamipo (Ryuta Kamizono) over 4 years ago. Updated over 4 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-dev:50862]

Description

When replacing #match? to #start_with?, #end_with?, and #include? for some reason (address to https://bugs.ruby-lang.org/issues/13083 etc), we frequently hit missing Symbol#start_with?, Symbol#end_with?, and Symbol#include? in spite of Symbol#match? exists.

https://github.com/rails/rails/commit/63256bc5d7dd77b2cce82df46c53249dab2dc2a8
https://github.com/rails/rails/commit/a8e812964d711fa03843e76ae50f5ff81cdc9e00

Is this inconsistency intentional?
If not so, Symbol#start_with?, Symbol#end_with?, and Symbol#include? prevents such like an issue.

Updated by shevegen (Robert A. Heiler) over 4 years ago

I have no particular pro/con opinion. When it comes to class Symbol, though, I believe
that this is is a design decision for matz. I understand that some of the comments
in the other threads are related to rails, but the suggestion here would mean to also
add three methods to class Symbol, so the impact of such a change should be considered,
if there is one (including the semantics and design decisions; remember strange
things such as HashWithIndirectAccess).

Updated by naruse (Yui NARUSE) over 4 years ago

diff --git a/string.c b/string.c
index 554aabad4b..d53fdc89fd 100644
--- a/string.c
+++ b/string.c
@@ -11166,6 +11166,64 @@ sym_swapcase(int argc, VALUE *argv, VALUE sym)
     return rb_str_intern(rb_str_swapcase(argc, argv, rb_sym2str(sym)));
 }
 
+/*
+ *  call-seq:
+ *     sym.include? other_str   -> true or false
+ *
+ *  Returns <code>true</code> if <i>str</i> contains the given string or
+ *  character.
+ *
+ *     :hello.include? "lo"   #=> true
+ *     :hello.include? "ol"   #=> false
+ *     :hello.include? ?h     #=> true
+ */
+
+static VALUE
+sym_include(VALUE sym, VALUE arg)
+{
+    return rb_str_include(rb_sym2str(sym), arg);
+}
+
+/*
+ *  call-seq:
+ *     sym.start_with?([prefixes]+)   -> true or false
+ *
+ *  Returns true if +sym+ starts with one of the +prefixes+ given.
+ *  Each of the +prefixes+ should be a String or a Regexp.
+ *
+ *    :hello.start_with?("hell")               #=> true
+ *    :hello.start_with?(/H/i)                 #=> true
+ *
+ *    # returns true if one of the prefixes matches.
+ *    :hello.start_with?("heaven", "hell")     #=> true
+ *    :hello.start_with?("heaven", "paradise") #=> false
+ */
+
+static VALUE
+sym_start_with(int argc, VALUE *argv, VALUE sym)
+{
+    return rb_str_start_with(argc, argv, rb_sym2str(sym));
+}
+
+/*
+ *  call-seq:
+ *     sym.end_with?([suffixes]+)   -> true or false
+ *
+ *  Returns true if +sym+ ends with one of the +suffixes+ given.
+ *
+ *    :hello.end_with?("ello")               #=> true
+ *
+ *    # returns true if one of the +suffixes+ matches.
+ *    :hello.end_with?("heaven", "ello")     #=> true
+ *    :hello.end_with?("heaven", "paradise") #=> false
+ */
+
+static VALUE
+sym_end_with(int argc, VALUE *argv, VALUE sym)
+{
+    return rb_str_end_with(argc, argv, rb_sym2str(sym));
+}
+
 /*
  * call-seq:
  *   sym.encoding   -> encoding
@@ -11452,5 +11510,9 @@ Init_String(void)
     rb_define_method(rb_cSymbol, "capitalize", sym_capitalize, -1);
     rb_define_method(rb_cSymbol, "swapcase", sym_swapcase, -1);
 
+    rb_define_method(rb_cSymbol, "include?", sym_include, 1);
+    rb_define_method(rb_cSymbol, "start_with?", sym_start_with, -1);
+    rb_define_method(rb_cSymbol, "end_with?", sym_end_with, -1);
+
     rb_define_method(rb_cSymbol, "encoding", sym_encoding, 0);
 }
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index d657f1aae6..d3a3af508f 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -568,4 +568,34 @@ def ==(obj)
       puts :a == :a
     RUBY
   end
+
+  def test_start_with?
+    assert_equal(true, :hello.start_with?("hel"))
+    assert_equal(false, :hello.start_with?("el"))
+    assert_equal(true, :hello.start_with?("el", "he"))
+
+    bug5536 = '[ruby-core:40623]'
+    assert_raise(TypeError, bug5536) {:str.start_with? :not_convertible_to_string}
+
+    assert_equal(true, :hello.start_with?(/hel/))
+    assert_equal("hel", $&)
+    assert_equal(false, :hello.start_with?(/el/))
+    assert_nil($&)
+  end
+
+  def test_end_with?
+    assert_equal(true, :hello.end_with?("llo"))
+    assert_equal(false, :hello.end_with?("ll"))
+    assert_equal(true, :hello.end_with?("el", "lo"))
+
+    bug5536 = '[ruby-core:40623]'
+    assert_raise(TypeError, bug5536) {:str.end_with? :not_convertible_to_string}
+  end
+
+  def test_include?
+    assert_include(:foobar, ?f)
+    assert_include(:foobar, "foo")
+    assert_not_include(:foobar, "baz")
+    assert_not_include(:foobar, ?z)
+  end
 end

Updated by nobu (Nobuyoshi Nakada) over 4 years ago

naruse (Yui NARUSE) wrote:

+ *     sym.include? other_str   -> true or false
+ *
+ *  Returns <code>true</code> if <i>str</i> contains the given string or

str should be sym.

Updated by duerst (Martin Dürst) over 4 years ago

I personally think that we should be restrictive in adding new methods to Symbol. Otherwise, this very quickly becomes a slippery slope, and the distinction between Symbol and String becomes less and less clear.

I was personally surprised when I implemented feature #10085 that Symbol had #upcase and friends, I wouldn't have guessed that.

Updated by matz (Yukihiro Matsumoto) over 4 years ago

I am OK with adding start_with? and end_with?.
I am a bit reluctant with adding include? since Symbol does not include any character. Persuade me further if you really need Symbol#include?.

Matz.

Updated by Hanmac (Hans Mackowiak) over 4 years ago

there is also symbol[string] which can be used as replace for Symbol#include?

Actions #7

Updated by naruse (Yui NARUSE) over 4 years ago

  • Status changed from Open to Closed

Applied in changeset git|b5fbefbf2c14742f6d46ecdf3ce712062dfb1d0a.


Added Symbol#start_with? and Symbol#end_with? method. [Feature #16348]

Actions #8

Updated by naruse (Yui NARUSE) over 4 years ago

duerst (Martin Dürst) wrote:

I personally think that we should be restrictive in adding new methods to Symbol. Otherwise, this very quickly becomes a slippery slope, and the distinction between Symbol and String becomes less and less clear.

I was personally surprised when I implemented feature #10085 that Symbol had #upcase and friends, I wouldn't have guessed that.

As far as I remember, once Matz tried and gave up to unify String and Symbol, he said Symbol should have similar set of methods while people needed.
After a decade some but small number of methods are added even if including this.
I think there's no worry.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0