From fd07f94f711cefef1d58169ec0e727de1a73d0c8 Mon Sep 17 00:00:00 2001
From: "D.E. Akers" <0x0dea@gmail.com>
Date: Fri, 19 Jun 2015 03:10:57 -0400
Subject: [PATCH] Add case equality arity to Enumerable's sequence predicates.

---
 ChangeLog              |  6 +++++
 enum.c                 | 68 +++++++++++++++++++++++++++++++++++++-------------
 test/ruby/test_enum.rb | 14 +++++++++++
 3 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 116b9ec..c30f78e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Fri Jun 19 16:10:22 2015  D.E. Akers  <0x0dea@gmail.com>
+
+	* enum.c: add case equality arity to sequence predicates.
+
+	* test/ruby/test_enum.rb: add tests for above.
+
 Fri Jun 19 14:53:35 2015  Nobuyoshi Nakada  <nobu@ruby-lang.org>

 	* proc.c (rb_mod_define_method): now requires a block direct to
diff --git a/enum.c b/enum.c
index 4b1e119..9bb338a 100644
--- a/enum.c
+++ b/enum.c
@@ -1043,7 +1043,7 @@ enum_sort_by(VALUE obj)
     return ary;
 }

-#define ENUMFUNC(name) rb_block_given_p() ? name##_iter_i : name##_i
+#define ENUMFUNC(name, argc) argc ? name##_eqq : rb_block_given_p() ? name##_iter_i : name##_i

 #define DEFINE_ENUMFUNCS(name) \
 static VALUE enum_##name##_func(VALUE result, struct MEMO *memo); \
@@ -1061,6 +1061,12 @@ name##_iter_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
 } \
 \
 static VALUE \
+name##_eqq(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo)) \
+{ \
+    return enum_##name##_func(rb_funcall(MEMO_CAST(memo)->v2, id_eqq, 1, i), MEMO_CAST(memo)); \
+} \
+\
+static VALUE \
 enum_##name##_func(VALUE result, struct MEMO *memo)

 DEFINE_ENUMFUNCS(all)
@@ -1075,6 +1081,7 @@ DEFINE_ENUMFUNCS(all)
 /*
  *  call-seq:
  *     enum.all? [{ |obj| block } ]   -> true or false
+ *     enum.all?(pattern)             -> true or false
  *
  *  Passes each element of the collection to the given block. The method
  *  returns <code>true</code> if the block never returns
@@ -1083,17 +1090,23 @@ DEFINE_ENUMFUNCS(all)
  *  cause #all? to return +true+ when none of the collection members are
  *  +false+ or +nil+.
  *
+ *  If instead a pattern is supplied, the method returns whether
+ *  <code>pattern === element</code> for every element of <i>enum</i>.
+ *
  *     %w[ant bear cat].all? { |word| word.length >= 3 } #=> true
  *     %w[ant bear cat].all? { |word| word.length >= 4 } #=> false
+ *     %w[ant bear cat].all?(/t/)                        #=> false
+ *     [1, 2i, 3.14].all?(Numeric)                       #=> true
  *     [nil, true, 99].all?                              #=> false
  *
  */

 static VALUE
-enum_all(VALUE obj)
+enum_all(int argc, VALUE *argv, VALUE obj)
 {
-    struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
-    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
+    struct MEMO *memo = MEMO_NEW(Qtrue, *argv, 0);
+    rb_check_arity(argc, 0, 1);
+    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all, argc), (VALUE)memo);
     return memo->v1;
 }

@@ -1109,6 +1122,7 @@ DEFINE_ENUMFUNCS(any)
 /*
  *  call-seq:
  *     enum.any? [{ |obj| block }]   -> true or false
+ *     enum.any?(pattern)            -> true or false
  *
  *  Passes each element of the collection to the given block. The method
  *  returns <code>true</code> if the block ever returns a value other
@@ -1117,17 +1131,23 @@ DEFINE_ENUMFUNCS(any)
  *  will cause #any? to return +true+ if at least one of the collection
  *  members is not +false+ or +nil+.
  *
+ *  If instead a pattern is supplied, the method returns whether
+ *  <code>pattern === element</code> for any element of <i>enum</i>.
+ *
  *     %w[ant bear cat].any? { |word| word.length >= 3 } #=> true
  *     %w[ant bear cat].any? { |word| word.length >= 4 } #=> true
+ *     %w[ant bear cat].any?(/d/)                        #=> false
  *     [nil, true, 99].any?                              #=> true
+ *     [nil, true, 99].any?(Float)                       #=> false
  *
  */

 static VALUE
-enum_any(VALUE obj)
+enum_any(int argc, VALUE *argv, VALUE obj)
 {
-    struct MEMO *memo = MEMO_NEW(Qfalse, 0, 0);
-    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
+    struct MEMO *memo = MEMO_NEW(Qfalse, *argv, 0);
+    rb_check_arity(argc, 0, 1);
+    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any, argc), (VALUE)memo);
     return memo->v1;
 }

@@ -1352,6 +1372,7 @@ nmin_run(VALUE obj, VALUE num, int by, int rev)
 /*
  *  call-seq:
  *     enum.one? [{ |obj| block }]   -> true or false
+ *     enum.one?(pattern)            -> true or false
  *
  *  Passes each element of the collection to the given block. The method
  *  returns <code>true</code> if the block returns <code>true</code>
@@ -1359,20 +1380,26 @@ nmin_run(VALUE obj, VALUE num, int by, int rev)
  *  <code>true</code> only if exactly one of the collection members is
  *  true.
  *
+ *  If instead a pattern is supplied, the method returns whether
+ *  <code>pattern === element</code> for exactly one element of <i>enum</i>.
+ *
  *     %w{ant bear cat}.one? { |word| word.length == 4 }  #=> true
  *     %w{ant bear cat}.one? { |word| word.length > 4 }   #=> false
  *     %w{ant bear cat}.one? { |word| word.length < 4 }   #=> false
+ *     %w{ant bear cat}.one?(/t/)                         #=> false
  *     [ nil, true, 99 ].one?                             #=> false
  *     [ nil, true, false ].one?                          #=> true
+ *     [ nil, true, 99 ].one?(Fixnum)                     #=> true
  *
  */
 static VALUE
-enum_one(VALUE obj)
+enum_one(int argc, VALUE *argv, VALUE obj)
 {
-    struct MEMO *memo = MEMO_NEW(Qundef, 0, 0);
+    struct MEMO *memo = MEMO_NEW(Qundef, *argv, 0);
     VALUE result;

-    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
+    rb_check_arity(argc, 0, 1);
+    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one, argc), (VALUE)memo);
     result = memo->v1;
     if (result == Qundef) return Qfalse;
     return result;
@@ -1390,23 +1417,30 @@ DEFINE_ENUMFUNCS(none)
 /*
  *  call-seq:
  *     enum.none? [{ |obj| block }]   -> true or false
+ *     enum.none?(pattern)            -> true or false
  *
  *  Passes each element of the collection to the given block. The method
  *  returns <code>true</code> if the block never returns <code>true</code>
  *  for all elements. If the block is not given, <code>none?</code> will return
  *  <code>true</code> only if none of the collection members is true.
  *
+ *  If instead a pattern is supplied, the method returns whether
+ *  <code>pattern === element</code> for none of the elements of <i>enum</i>.
+ *
  *     %w{ant bear cat}.none? { |word| word.length == 5 } #=> true
  *     %w{ant bear cat}.none? { |word| word.length >= 4 } #=> false
+ *     %w{ant bear cat}.none?(/d/)                        #=> true
  *     [].none?                                           #=> true
  *     [nil].none?                                        #=> true
  *     [nil, false].none?                                 #=> true
+ *     [nil, false].none?(NilClass)                       #=> false
  */
 static VALUE
-enum_none(VALUE obj)
+enum_none(int argc, VALUE *argv, VALUE obj)
 {
-    struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
-    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
+    struct MEMO *memo = MEMO_NEW(Qtrue, *argv, 0);
+    rb_check_arity(argc, 0, 1);
+    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none, argc), (VALUE)memo);
     return memo->v1;
 }

@@ -3501,10 +3535,10 @@ Init_Enumerable(void)
     rb_define_method(rb_mEnumerable, "partition", enum_partition, 0);
     rb_define_method(rb_mEnumerable, "group_by", enum_group_by, 0);
     rb_define_method(rb_mEnumerable, "first", enum_first, -1);
-    rb_define_method(rb_mEnumerable, "all?", enum_all, 0);
-    rb_define_method(rb_mEnumerable, "any?", enum_any, 0);
-    rb_define_method(rb_mEnumerable, "one?", enum_one, 0);
-    rb_define_method(rb_mEnumerable, "none?", enum_none, 0);
+    rb_define_method(rb_mEnumerable, "all?", enum_all, -1);
+    rb_define_method(rb_mEnumerable, "any?", enum_any, -1);
+    rb_define_method(rb_mEnumerable, "one?", enum_one, -1);
+    rb_define_method(rb_mEnumerable, "none?", enum_none, -1);
     rb_define_method(rb_mEnumerable, "min", enum_min, -1);
     rb_define_method(rb_mEnumerable, "max", enum_max, -1);
     rb_define_method(rb_mEnumerable, "minmax", enum_minmax, 0);
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index db03b7b..8f7b3e9 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -236,6 +236,8 @@ class TestEnumerable < Test::Unit::TestCase
     assert_equal(false, @obj.all? {|x| x < 3 })
     assert_equal(true, @obj.all?)
     assert_equal(false, [true, true, false].all?)
+    assert_equal(true, @obj.all?(Fixnum))
+    assert_equal(false, @obj.all?(1..2))
   end

   def test_any
@@ -243,25 +245,37 @@ class TestEnumerable < Test::Unit::TestCase
     assert_equal(false, @obj.any? {|x| x > 3 })
     assert_equal(true, @obj.any?)
     assert_equal(false, [false, false, false].any?)
+    assert_equal(true, @obj.any?(1..2))
+    assert_equal(false, @obj.any?(Float))
   end

   def test_one
     assert(@obj.one? {|x| x == 3 })
     assert(!(@obj.one? {|x| x == 1 }))
     assert(!(@obj.one? {|x| x == 4 }))
+    assert(@obj.one?(3..4))
+    assert(!(@obj.one?(1..2)))
+    assert(!(@obj.one?(4..5)))
     assert(%w{ant bear cat}.one? {|word| word.length == 4})
     assert(!(%w{ant bear cat}.one? {|word| word.length > 4}))
     assert(!(%w{ant bear cat}.one? {|word| word.length < 4}))
+    assert(%w{ant bear cat}.one?(/b/))
+    assert(!(%w{ant bear cat}.one?(/t/)))
     assert(!([ nil, true, 99 ].one?))
     assert([ nil, true, false ].one?)
+    assert([nil, true, 99].one?(NilClass))
   end

   def test_none
     assert(@obj.none? {|x| x == 4 })
     assert(!(@obj.none? {|x| x == 1 }))
     assert(!(@obj.none? {|x| x == 3 }))
+    assert(@obj.none?(4..5))
+    assert(!(@obj.none?(1..3)))
     assert(%w{ant bear cat}.none? {|word| word.length == 5})
     assert(!(%w{ant bear cat}.none? {|word| word.length >= 4}))
+    assert(%w{ant bear cat}.none?(/d/))
+    assert(!(%w{ant bear cat}.none?(/b/)))
     assert([].none?)
     assert([nil].none?)
     assert([nil,false].none?)
--
2.4.4

