ruby-changes:58259
From: Nobuyoshi <ko1@a...>
Date: Wed, 16 Oct 2019 01:42:50 +0900 (JST)
Subject: [ruby-changes:58259] 929d5fd3b9 (master): Comparable#clamp with a range [Feature #14784]
https://git.ruby-lang.org/ruby.git/commit/?id=929d5fd3b9 From 929d5fd3b99c1413f737ff16cf0680698036e60f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Tue, 15 Oct 2019 22:32:10 +0900 Subject: Comparable#clamp with a range [Feature #14784] diff --git a/NEWS b/NEWS index a3283a6..36660de 100644 --- a/NEWS +++ b/NEWS @@ -142,6 +142,12 @@ Array:: https://github.com/ruby/ruby/blob/trunk/NEWS#L142 * Added Array#intersection. [Feature #16155] +Comparable:: + + Modified method:: + + * Comparable#clamp now accepts a Range argument. [Feature #14784] + Complex:: New method:: diff --git a/common.mk b/common.mk index ff56a0c..19aa723 100644 --- a/common.mk +++ b/common.mk @@ -1636,6 +1636,7 @@ class.$(OBJEXT): {$(VPATH)}thread_native.h https://github.com/ruby/ruby/blob/trunk/common.mk#L1636 class.$(OBJEXT): {$(VPATH)}vm_core.h class.$(OBJEXT): {$(VPATH)}vm_debug.h class.$(OBJEXT): {$(VPATH)}vm_opts.h +compar.$(OBJEXT): $(hdrdir)/ruby.h compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h compar.$(OBJEXT): {$(VPATH)}assert.h compar.$(OBJEXT): {$(VPATH)}compar.c @@ -1643,6 +1644,7 @@ compar.$(OBJEXT): {$(VPATH)}config.h https://github.com/ruby/ruby/blob/trunk/common.mk#L1644 compar.$(OBJEXT): {$(VPATH)}defines.h compar.$(OBJEXT): {$(VPATH)}id.h compar.$(OBJEXT): {$(VPATH)}intern.h +compar.$(OBJEXT): {$(VPATH)}internal.h compar.$(OBJEXT): {$(VPATH)}missing.h compar.$(OBJEXT): {$(VPATH)}st.h compar.$(OBJEXT): {$(VPATH)}subst.h diff --git a/compar.c b/compar.c index 027328d..fe4cc42 100644 --- a/compar.c +++ b/compar.c @@ -11,6 +11,7 @@ https://github.com/ruby/ruby/blob/trunk/compar.c#L11 #include "ruby/ruby.h" #include "id.h" +#include "internal.h" VALUE rb_mComparable; @@ -176,24 +177,46 @@ cmp_between(VALUE x, VALUE min, VALUE max) https://github.com/ruby/ruby/blob/trunk/compar.c#L177 /* * call-seq: * obj.clamp(min, max) -> obj + * obj.clamp(range) -> obj * - * Returns _min_ if _obj_ <code><=></code> _min_ is less than zero, - * _max_ if _obj_ <code><=></code> _max_ is greater than zero and - * _obj_ otherwise. + * In the first form, returns _min_ if _obj_ <code><=></code> _min_ is + * less than zero, _max_ if _obj_ <code><=></code> _max_ is greater + * than zero and _obj_ otherwise. In the second form, clamps by + * _range.min_ and _range.max_. If _range_ is an exclusive range, + * raises an ArgumentError. * * 12.clamp(0, 100) #=> 12 * 523.clamp(0, 100) #=> 100 * -3.123.clamp(0, 100) #=> 0 + * 12.clamp(0..100) #=> 12 + * 523.clamp(0..100) #=> 100 + * -3.123.clamp(0..100) #=> 0 * * 'd'.clamp('a', 'f') #=> 'd' * 'z'.clamp('a', 'f') #=> 'f' + * 'd'.clamp('a'..'f') #=> 'd' + * 'z'.clamp('a'..'f') #=> 'f' + * + * 12.clamp(0...100) #=> ArgumentError */ static VALUE -cmp_clamp(VALUE x, VALUE min, VALUE max) +cmp_clamp(int argc, VALUE *argv, VALUE x) { + VALUE min, max; int c; + if (rb_scan_args(argc, argv, "11", &min, &max) == 1) { + VALUE range = min; + int excl; + if (!rb_range_values(range, &min, &max, &excl)) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)", + rb_builtin_class_name(range)); + } + if (excl || NIL_P(min) || NIL_P(max)) { + rb_raise(rb_eArgError, "cannot clamp with an exclusive range"); + } + } if (cmpint(min, max) > 0) { rb_raise(rb_eArgError, "min argument must be smaller than max argument"); } @@ -259,5 +282,5 @@ Init_Comparable(void) https://github.com/ruby/ruby/blob/trunk/compar.c#L282 rb_define_method(rb_mComparable, "<", cmp_lt, 1); rb_define_method(rb_mComparable, "<=", cmp_le, 1); rb_define_method(rb_mComparable, "between?", cmp_between, 2); - rb_define_method(rb_mComparable, "clamp", cmp_clamp, 2); + rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1); } diff --git a/spec/ruby/core/comparable/clamp_spec.rb b/spec/ruby/core/comparable/clamp_spec.rb index 6d21622..393496f 100644 --- a/spec/ruby/core/comparable/clamp_spec.rb +++ b/spec/ruby/core/comparable/clamp_spec.rb @@ -2,10 +2,12 @@ require_relative '../../spec_helper' https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/comparable/clamp_spec.rb#L2 require_relative 'fixtures/classes' describe 'Comparable#clamp' do - it 'raises an Argument error unless given 2 parameters' do - c = ComparableSpecs::Weird.new(0) - -> { c.clamp(c) }.should raise_error(ArgumentError) - -> { c.clamp(c, c, c) }.should raise_error(ArgumentError) + ruby_version_is ""..."2.7" do + it 'raises an Argument error unless given 2 parameters' do + c = ComparableSpecs::Weird.new(0) + -> { c.clamp(c) }.should raise_error(ArgumentError) + -> { c.clamp(c, c, c) }.should raise_error(ArgumentError) + end end it 'raises an Argument error unless the 2 parameters are correctly ordered' do @@ -45,4 +47,42 @@ describe 'Comparable#clamp' do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/comparable/clamp_spec.rb#L47 c.clamp(one, two).should equal(two) end + + ruby_version_is "2.7" do + it 'returns self if within the given range parameters' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + three = ComparableSpecs::WithOnlyCompareDefined.new(3) + c = ComparableSpecs::Weird.new(2) + + c.clamp(one..two).should equal(c) + c.clamp(two..two).should equal(c) + c.clamp(one..three).should equal(c) + c.clamp(two..three).should equal(c) + end + + it 'returns the minimum value of the range parameters if smaller than it' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(0) + + c.clamp(one..two).should equal(one) + end + + it 'returns the maximum value of the range parameters if greater than it' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(3) + + c.clamp(one..two).should equal(two) + end + + it 'raises an Argument error if the range parameter is exclusive' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(3) + + -> { c.clamp(one...two) }.should raise_error(ArgumentError) + end + end end diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb index 4f1d38a..c363a03 100644 --- a/test/ruby/test_comparable.rb +++ b/test/ruby/test_comparable.rb @@ -90,6 +90,29 @@ class TestComparable < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_comparable.rb#L90 } end + def test_clamp_with_range + cmp->(x) do 0 <=> x end + assert_equal(1, @o.clamp(1..2)) + assert_equal(-1, @o.clamp(-2..-1)) + assert_equal(@o, @o.clamp(-1..3)) + + assert_equal(1, @o.clamp(1..1)) + assert_equal(@o, @o.clamp(0..0)) + + assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') { + @o.clamp(1...2) + } + assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') { + @o.clamp(1...) + } + assert_raise_with_message(ArgumentError, 'cannot clamp with an exclusive range') { + @o.clamp(...2) + } + assert_raise_with_message(ArgumentError, 'min argument must be smaller than max argument') { + @o.clamp(2..1) + } + end + def test_err assert_raise(ArgumentError) { 1.0 < nil } assert_raise(ArgumentError) { 1.0 < Object.new } -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/