Skip to content

Commit

Permalink
Enumerator.new: raise unless block given
Browse files Browse the repository at this point in the history
Has been deprecated since c73b6bd.
[Feature #17116] [ruby-dev:50945]
  • Loading branch information
shyouhei authored and nobu committed Dec 22, 2020
1 parent 7204b81 commit fa356a7
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 83 deletions.
66 changes: 23 additions & 43 deletions enumerator.c
Expand Up @@ -420,15 +420,31 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *ar
return enum_obj;
}

static VALUE
convert_to_feasible_size_value(VALUE obj)
{
if (NIL_P(obj)) {
return obj;
}
else if (rb_respond_to(obj, id_call)) {
return obj;
}
else if (RB_FLOAT_TYPE_P(obj) && RFLOAT_VALUE(obj) == HUGE_VAL) {
return obj;
}
else {
return rb_to_int(obj);
}
}

/*
* call-seq:
* Enumerator.new(size = nil) { |yielder| ... }
* Enumerator.new(obj, method = :each, *args)
*
* Creates a new Enumerator object, which can be used as an
* Enumerable.
*
* In the first form, iteration is defined by the given block, in
* Iteration is defined by the given block, in
* which a "yielder" object, given as block parameter, can be used to
* yield a value by calling the +yield+ method (aliased as <code><<</code>):
*
Expand All @@ -445,52 +461,16 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, const VALUE *ar
* The optional parameter can be used to specify how to calculate the size
* in a lazy fashion (see Enumerator#size). It can either be a value or
* a callable object.
*
* In the deprecated second form, a generated Enumerator iterates over the
* given object using the given method with the given arguments passed.
*
* Use of this form is discouraged. Use Object#enum_for or Object#to_enum
* instead.
*
* e = Enumerator.new(ObjectSpace, :each_object)
* #-> ObjectSpace.enum_for(:each_object)
*
* e.select { |obj| obj.is_a?(Class) } # => array of all classes
*
*/
static VALUE
enumerator_initialize(int argc, VALUE *argv, VALUE obj)
{
VALUE recv, meth = sym_each;
VALUE size = Qnil;
int kw_splat = 0;

if (rb_block_given_p()) {
rb_check_arity(argc, 0, 1);
recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc());
if (argc) {
if (NIL_P(argv[0]) || rb_respond_to(argv[0], id_call) ||
(RB_TYPE_P(argv[0], T_FLOAT) && RFLOAT_VALUE(argv[0]) == HUGE_VAL)) {
size = argv[0];
}
else {
size = rb_to_int(argv[0]);
}
argc = 0;
}
}
else {
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
rb_warn_deprecated("Enumerator.new without a block", "Object#to_enum");
recv = *argv++;
if (--argc) {
meth = *argv++;
--argc;
}
kw_splat = rb_keyword_given_p();
}
VALUE iter = rb_block_proc();
VALUE recv = generator_init(generator_allocate(rb_cGenerator), iter);
VALUE arg0 = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
VALUE size = convert_to_feasible_size_value(arg0);

return enumerator_init(obj, recv, meth, argc, argv, 0, size, kw_splat);
return enumerator_init(obj, recv, sym_each, 0, 0, 0, size, false);
}

/* :nodoc: */
Expand Down
6 changes: 4 additions & 2 deletions spec/ruby/core/enumerator/initialize_spec.rb
Expand Up @@ -11,8 +11,10 @@
Enumerator.should have_private_instance_method(:initialize, false)
end

it "returns self when given an object" do
@uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
ruby_version_is ''...'3.0' do
it "returns self when given an object" do
@uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
end
end

it "returns self when given a block" do
Expand Down
66 changes: 38 additions & 28 deletions spec/ruby/core/enumerator/new_spec.rb
@@ -1,42 +1,52 @@
require_relative '../../spec_helper'

describe "Enumerator.new" do
it "creates a new custom enumerator with the given object, iterator and arguments" do
enum = Enumerator.new(1, :upto, 3)
enum.should be_an_instance_of(Enumerator)
end
context "no block given" do
ruby_version_is '3.0' do
it "raises" do
-> { Enumerator.new(1, :upto, 3) }.should raise_error(ArgumentError)
end
end

it "creates a new custom enumerator that responds to #each" do
enum = Enumerator.new(1, :upto, 3)
enum.respond_to?(:each).should == true
end
ruby_version_is ''...'3.0' do
it "creates a new custom enumerator with the given object, iterator and arguments" do
enum = Enumerator.new(1, :upto, 3)
enum.should be_an_instance_of(Enumerator)
end

it "creates a new custom enumerator that runs correctly" do
Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3]
end
it "creates a new custom enumerator that responds to #each" do
enum = Enumerator.new(1, :upto, 3)
enum.respond_to?(:each).should == true
end

it "aliases the second argument to :each" do
Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a
end
it "creates a new custom enumerator that runs correctly" do
Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3]
end

it "doesn't check for the presence of the iterator method" do
Enumerator.new(nil).should be_an_instance_of(Enumerator)
end
it "aliases the second argument to :each" do
Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a
end

it "uses the latest define iterator method" do
class StrangeEach
def each
yield :foo
it "doesn't check for the presence of the iterator method" do
Enumerator.new(nil).should be_an_instance_of(Enumerator)
end
end
enum = Enumerator.new(StrangeEach.new)
enum.to_a.should == [:foo]
class StrangeEach
def each
yield :bar

it "uses the latest define iterator method" do
class StrangeEach
def each
yield :foo
end
end
enum = Enumerator.new(StrangeEach.new)
enum.to_a.should == [:foo]
class StrangeEach
def each
yield :bar
end
end
enum.to_a.should == [:bar]
end
end
enum.to_a.should == [:bar]
end

context "when passed a block" do
Expand Down
14 changes: 4 additions & 10 deletions test/ruby/test_enumerator.rb
Expand Up @@ -69,22 +69,16 @@ def (o = Object.new).each

def test_initialize
assert_equal([1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).to_a)
begin
deprecated_bak, Warning[:deprecated] = Warning[:deprecated], true
_, err = capture_io do
assert_equal([1, 2, 3], Enumerator.new(@obj, :foo, 1, 2, 3).to_a)
end
assert_match 'Enumerator.new without a block is deprecated', err
ensure
Warning[:deprecated] = deprecated_bak
end
assert_raise(ArgumentError) {
Enumerator.new(@obj, :foo, 1, 2, 3)
}
assert_equal([1, 2, 3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3))
assert_raise(ArgumentError) { Enumerator.new }

enum = @obj.to_enum
assert_raise(NoMethodError) { enum.each {} }
enum.freeze
assert_raise(FrozenError) {
assert_raise(ArgumentError) {
capture_io do
# warning: Enumerator.new without a block is deprecated; use Object#to_enum
enum.__send__(:initialize, @obj, :foo)
Expand Down

0 comments on commit fa356a7

Please sign in to comment.