Add Integer.try_convert [Feature #15211]

This commit is contained in:
Nobuyoshi Nakada 2018-10-07 13:02:46 +09:00
parent eee709595c
commit 301d194ee3
Notes: git 2021-07-16 17:50:23 +09:00
6 changed files with 83 additions and 3 deletions

View File

@ -92,6 +92,10 @@ Outstanding ones only.
* 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.
[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

View File

@ -77,6 +77,7 @@ VALUE rb_int_lshift(VALUE x, VALUE y);
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);

View File

@ -5247,6 +5247,12 @@ rb_int_s_isqrt(VALUE self, VALUE num)
}
}
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)
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");

View File

@ -3232,19 +3232,23 @@ rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method)
#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)
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)
{

View File

@ -0,0 +1,40 @@
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

View File

@ -660,4 +660,21 @@ class TestInteger < Test::Unit::TestCase
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