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/