[前][次][番号順一覧][スレッド一覧]

ruby-changes:66817

From: Nobuyoshi <ko1@a...>
Date: Fri, 16 Jul 2021 17:50:23 +0900 (JST)
Subject: [ruby-changes:66817] 301d194ee3 (master): Add Integer.try_convert [Feature #15211]

https://git.ruby-lang.org/ruby.git/commit/?id=301d194ee3

From 301d194ee3b49e6b078eccb999dd538e9bfa8c7c Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Sun, 7 Oct 2018 13:02:46 +0900
Subject: Add Integer.try_convert [Feature #15211]

---
 NEWS.md                                    |  5 ++++
 internal/numeric.h                         |  1 +
 numeric.c                                  |  7 ++++++
 object.c                                   | 16 +++++++++---
 spec/ruby/core/integer/try_convert_spec.rb | 40 ++++++++++++++++++++++++++++++
 test/ruby/test_integer.rb                  | 17 +++++++++++++
 6 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 spec/ruby/core/integer/try_convert_spec.rb

diff --git a/NEWS.md b/NEWS.md
index b30d707..793da5b 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -92,6 +92,10 @@ Outstanding ones only. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L92
     * File.dirname now accepts an optional argument for the level to
       strip path components. [[Feature #12194]]
 
+* Integer
+
+    * Integer.try_convert is added. [[Feature #15211]]
+
 * Module
 
     * Module#prepend now modifies the ancestor chain if the receiver
@@ -191,6 +195,7 @@ Excluding feature bug fixes. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L195
 [Feature #12194]: https://bugs.ruby-lang.org/issues/12194
 [Feature #14256]: https://bugs.ruby-lang.org/issues/14256
 [Feature #15198]: https://bugs.ruby-lang.org/issues/15198
+[Feature #15211]: https://bugs.ruby-lang.org/issues/15211
 [Feature #16043]: https://bugs.ruby-lang.org/issues/16043
 [Feature #16806]: https://bugs.ruby-lang.org/issues/16806
 [Feature #17312]: https://bugs.ruby-lang.org/issues/17312
diff --git a/internal/numeric.h b/internal/numeric.h
index 32d5bd2..82ba4c1 100644
--- a/internal/numeric.h
+++ b/internal/numeric.h
@@ -77,6 +77,7 @@ VALUE rb_int_lshift(VALUE x, VALUE y); https://github.com/ruby/ruby/blob/trunk/internal/numeric.h#L77
 VALUE rb_int_div(VALUE x, VALUE y);
 int rb_int_positive_p(VALUE num);
 int rb_int_negative_p(VALUE num);
+VALUE rb_check_integer_type(VALUE);
 VALUE rb_num_pow(VALUE x, VALUE y);
 VALUE rb_float_ceil(VALUE num, int ndigits);
 VALUE rb_float_floor(VALUE x, int ndigits);
diff --git a/numeric.c b/numeric.c
index c9dc24b..345067c 100644
--- a/numeric.c
+++ b/numeric.c
@@ -5247,6 +5247,12 @@ rb_int_s_isqrt(VALUE self, VALUE num) https://github.com/ruby/ruby/blob/trunk/numeric.c#L5247
     }
 }
 
+static VALUE
+int_s_try_convert(VALUE self, VALUE num)
+{
+    return rb_check_integer_type(num);
+}
+
 /*
  *  Document-class: ZeroDivisionError
  *
@@ -5473,6 +5479,7 @@ Init_Numeric(void) https://github.com/ruby/ruby/blob/trunk/numeric.c#L5479
     rb_undef_alloc_func(rb_cInteger);
     rb_undef_method(CLASS_OF(rb_cInteger), "new");
     rb_define_singleton_method(rb_cInteger, "sqrt", rb_int_s_isqrt, 1);
+    rb_define_singleton_method(rb_cInteger, "try_convert", int_s_try_convert, 1);
 
     rb_define_method(rb_cInteger, "to_s", int_to_s, -1);
     rb_define_alias(rb_cInteger, "inspect", "to_s");
diff --git a/object.c b/object.c
index 695a11e..43c9727 100644
--- a/object.c
+++ b/object.c
@@ -3232,19 +3232,23 @@ rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method) https://github.com/ruby/ruby/blob/trunk/object.c#L3232
 #define try_to_int(val, mid, raise) \
     convert_type_with_id(val, "Integer", mid, raise, -1)
 
-ALWAYS_INLINE(static VALUE rb_to_integer(VALUE val, const char *method, ID mid));
+ALWAYS_INLINE(static VALUE rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise));
+/* Integer specific rb_check_convert_type_with_id */
 static inline VALUE
-rb_to_integer(VALUE val, const char *method, ID mid)
+rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise)
 {
     VALUE v;
 
     if (RB_INTEGER_TYPE_P(val)) return val;
-    v = try_to_int(val, mid, TRUE);
+    v = try_to_int(val, mid, raise);
+    if (!raise && NIL_P(v)) return Qnil;
     if (!RB_INTEGER_TYPE_P(v)) {
         conversion_mismatch(val, "Integer", method, v);
     }
     return v;
 }
+#define rb_to_integer(val, method, mid) \
+    rb_to_integer_with_id_exception(val, method, mid, TRUE)
 
 /**
  * Tries to convert \a val into \c Integer.
@@ -3371,6 +3375,12 @@ rb_Integer(VALUE val) https://github.com/ruby/ruby/blob/trunk/object.c#L3375
     return rb_convert_to_integer(val, 0, TRUE);
 }
 
+VALUE
+rb_check_integer_type(VALUE val)
+{
+    return rb_to_integer_with_id_exception(val, "to_int", idTo_int, FALSE);
+}
+
 int
 rb_bool_expected(VALUE obj, const char *flagname)
 {
diff --git a/spec/ruby/core/integer/try_convert_spec.rb b/spec/ruby/core/integer/try_convert_spec.rb
new file mode 100644
index 0000000..45c66ee
--- /dev/null
+++ b/spec/ruby/core/integer/try_convert_spec.rb
@@ -0,0 +1,40 @@ https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/integer/try_convert_spec.rb#L1
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "3.1" do
+  describe "Integer.try_convert" do
+    it "returns the argument if it's an Integer" do
+      x = 42
+      Integer.try_convert(x).should equal(x)
+    end
+
+    it "returns nil when the argument does not respond to #to_int" do
+      Integer.try_convert(Object.new).should be_nil
+    end
+
+    it "sends #to_int to the argument and returns the result if it's nil" do
+      obj = mock("to_int")
+      obj.should_receive(:to_int).and_return(nil)
+      Integer.try_convert(obj).should be_nil
+    end
+
+    it "sends #to_int to the argument and returns the result if it's an Integer" do
+      x = 234
+      obj = mock("to_int")
+      obj.should_receive(:to_int).and_return(x)
+      Integer.try_convert(obj).should equal(x)
+    end
+
+    it "sends #to_int to the argument and raises TypeError if it's not a kind of Integer" do
+      obj = mock("to_int")
+      obj.should_receive(:to_int).and_return(Object.new)
+      -> { Integer.try_convert obj }.should raise_error(TypeError)
+    end
+
+    it "does not rescue exceptions raised by #to_int" do
+      obj = mock("to_int")
+      obj.should_receive(:to_int).and_raise(RuntimeError)
+      -> { Integer.try_convert obj }.should raise_error(RuntimeError)
+    end
+  end
+end
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 2755987..1cd256a 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -660,4 +660,21 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L660
     def o.fdiv(x); 1; end
     assert_equal(1.0, 1.fdiv(o))
   end
+
+  def test_try_convert
+    assert_equal(1, Integer.try_convert(1))
+    assert_equal(1, Integer.try_convert(1.0))
+    assert_nil Integer.try_convert("1")
+    o = Object.new
+    assert_nil Integer.try_convert(o)
+    def o.to_i; 1; end
+    assert_nil Integer.try_convert(o)
+    o = Object.new
+    def o.to_int; 1; end
+    assert_equal(1, Integer.try_convert(o))
+
+    o = Object.new
+    def o.to_int; Object.new; end
+    assert_raise_with_message(TypeError, /can't convert Object to Integer/) {Integer.try_convert(o)}
+  end
 end
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]