Skip to content

Commit e3c91e6

Browse files
committed
Add missing Enumerator::Product methods
1 parent 7bb3285 commit e3c91e6

File tree

8 files changed

+366
-19
lines changed

8 files changed

+366
-19
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require_relative '../../../spec_helper'
2+
require_relative '../../enumerable/shared/enumeratorized'
3+
4+
ruby_version_is "3.2" do
5+
describe "Enumerator::Product#each" do
6+
it_behaves_like :enumeratorized_with_origin_size, :each, Enumerator::Product.new([1, 2], [:a, :b])
7+
8+
it "yields each element of Cartesian product of enumerators" do
9+
enum = Enumerator::Product.new([1, 2], [:a, :b])
10+
acc = []
11+
enum.each { |e| acc << e }
12+
acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
13+
end
14+
15+
it "calls #each_entry method on enumerators" do
16+
object1 = Object.new
17+
def object1.each_entry
18+
yield 1
19+
yield 2
20+
end
21+
22+
object2 = Object.new
23+
def object2.each_entry
24+
yield :a
25+
yield :b
26+
end
27+
28+
enum = Enumerator::Product.new(object1, object2)
29+
acc = []
30+
enum.each { |e| acc << e }
31+
acc.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
32+
end
33+
34+
it "raises a NoMethodError if the object doesn't respond to #each_entry" do
35+
-> {
36+
Enumerator::Product.new(Object.new).each {}
37+
}.should raise_error(NoMethodError, /undefined method `each_entry' for/)
38+
end
39+
40+
it "returns enumerator if not given a block" do
41+
enum = Enumerator::Product.new([1, 2], [:a, :b])
42+
enum.each.should.kind_of?(Enumerator)
43+
44+
enum = Enumerator::Product.new([1, 2], [:a, :b])
45+
enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
46+
end
47+
48+
it "doesn't accept arguments" do
49+
Enumerator::Product.instance_method(:each).arity.should == 0
50+
end
51+
52+
it "yields each element to a block that takes multiple arguments" do
53+
enum = Enumerator::Product.new([1, 2], [:a, :b])
54+
55+
acc = []
56+
enum.each { |x, y| acc << x }
57+
acc.should == [1, 1, 2, 2]
58+
59+
acc = []
60+
enum.each { |x, y| acc << y }
61+
acc.should == [:a, :b, :a, :b]
62+
63+
acc = []
64+
enum.each { |x, y, z| acc << z }
65+
acc.should == [nil, nil, nil, nil]
66+
end
67+
end
68+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require_relative '../../../spec_helper'
2+
3+
ruby_version_is "3.2" do
4+
describe "Enumerator::Product#initialize_copy" do
5+
it "replaces content of the receiver with content of the other object" do
6+
enum = Enumerator::Product.new([true, false])
7+
enum2 = Enumerator::Product.new([1, 2], [:a, :b])
8+
9+
enum.send(:initialize_copy, enum2)
10+
enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
11+
end
12+
13+
it "returns self" do
14+
enum = Enumerator::Product.new([true, false])
15+
enum2 = Enumerator::Product.new([1, 2], [:a, :b])
16+
17+
enum.send(:initialize_copy, enum2).should.equal?(enum)
18+
end
19+
20+
it "is a private method" do
21+
Enumerator::Product.should have_private_instance_method(:initialize_copy, false)
22+
end
23+
24+
it "does nothing if the argument is the same as the receiver" do
25+
enum = Enumerator::Product.new(1..2)
26+
enum.send(:initialize_copy, enum).should.equal?(enum)
27+
28+
enum.freeze
29+
enum.send(:initialize_copy, enum).should.equal?(enum)
30+
end
31+
32+
it "raises FrozenError if the receiver is frozen" do
33+
enum = Enumerator::Product.new(1..2)
34+
enum2 = Enumerator::Product.new(3..4)
35+
36+
-> { enum.freeze.send(:initialize_copy, enum2) }.should raise_error(FrozenError)
37+
end
38+
39+
it "raises TypeError if the objects are of different class" do
40+
enum = Enumerator::Product.new(1..2)
41+
enum2 = Class.new(Enumerator::Product).new(3..4)
42+
43+
-> { enum.send(:initialize_copy, enum2) }.should raise_error(TypeError, 'initialize_copy should take same class object')
44+
-> { enum2.send(:initialize_copy, enum) }.should raise_error(TypeError, 'initialize_copy should take same class object')
45+
end
46+
47+
it "raises ArgumentError if the argument is not initialized yet" do
48+
enum = Enumerator::Product.new(1..2)
49+
enum2 = Enumerator::Product.allocate
50+
51+
-> { enum.send(:initialize_copy, enum2) }.should raise_error(ArgumentError, 'uninitialized product')
52+
end
53+
end
54+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require_relative '../../../spec_helper'
2+
3+
ruby_version_is "3.2" do
4+
describe "Enumerator::Product#initialize" do
5+
before :each do
6+
@uninitialized = Enumerator::Product.allocate
7+
end
8+
9+
it "is a private method" do
10+
Enumerator::Product.should have_private_instance_method(:initialize, false)
11+
end
12+
13+
it "returns self" do
14+
@uninitialized.send(:initialize).should equal(@uninitialized)
15+
end
16+
17+
it "accepts many arguments" do
18+
@uninitialized.send(:initialize, 0..1, 2..3, 4..5).should equal(@uninitialized)
19+
end
20+
21+
it "accepts arguments that are not Enumerable nor responding to :each_entry" do
22+
@uninitialized.send(:initialize, Object.new).should equal(@uninitialized)
23+
end
24+
25+
describe "on frozen instance" do
26+
it "raises a FrozenError" do
27+
-> {
28+
@uninitialized.freeze.send(:initialize, 0..1)
29+
}.should raise_error(FrozenError)
30+
end
31+
end
32+
end
33+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require_relative '../../../spec_helper'
2+
3+
ruby_version_is "3.2" do
4+
describe "Enumerator::Product#inspect" do
5+
it "returns a String including enumerators" do
6+
enum = Enumerator::Product.new([1, 2], [:a, :b])
7+
enum.inspect.should == "#<Enumerator::Product: [[1, 2], [:a, :b]]>"
8+
end
9+
10+
it "represents a recursive element with '[...]'" do
11+
enum = [1, 2]
12+
enum_recursive = Enumerator::Product.new(enum)
13+
14+
enum << enum_recursive
15+
enum_recursive.inspect.should == "#<Enumerator::Product: [[1, 2, #<Enumerator::Product: ...>]]>"
16+
end
17+
18+
it "returns a not initialized representation if #initialized is not called yet" do
19+
Enumerator::Product.allocate.inspect.should == "#<Enumerator::Product: uninitialized>"
20+
end
21+
end
22+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
require_relative '../../../spec_helper'
2+
3+
ruby_version_is "3.2" do
4+
describe "Enumerator::Product#rewind" do
5+
before :each do
6+
@enum = Enumerator::Product.new([1, 2].each.to_enum, [:a, :b].each.to_enum)
7+
end
8+
9+
it "resets the enumerator to its initial state" do
10+
@enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
11+
@enum.rewind
12+
@enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
13+
end
14+
15+
it "returns self" do
16+
@enum.rewind.should.equal? @enum
17+
end
18+
19+
it "has no effect on a new enumerator" do
20+
@enum.rewind
21+
@enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
22+
end
23+
24+
it "has no effect if called multiple, consecutive times" do
25+
@enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
26+
@enum.rewind
27+
@enum.rewind
28+
@enum.each.to_a.should == [[1, :a], [1, :b], [2, :a], [2, :b]]
29+
end
30+
31+
it "calls the enclosed object's rewind method if one exists" do
32+
obj = mock('rewinder')
33+
enum = Enumerator::Product.new(obj.to_enum)
34+
35+
obj.should_receive(:rewind)
36+
enum.rewind
37+
end
38+
39+
it "does nothing if the object doesn't have a #rewind method" do
40+
obj = mock('rewinder')
41+
enum = Enumerator::Product.new(obj.to_enum)
42+
43+
enum.rewind.should == enum
44+
end
45+
46+
it "calls a rewind method on each enumerable in direct order" do
47+
ScratchPad.record []
48+
49+
object1 = Object.new
50+
def object1.rewind; ScratchPad << :object1; end
51+
52+
object2 = Object.new
53+
def object2.rewind; ScratchPad << :object2; end
54+
55+
object3 = Object.new
56+
def object3.rewind; ScratchPad << :object3; end
57+
58+
enum = Enumerator::Product.new(object1, object2, object3)
59+
enum.rewind
60+
61+
ScratchPad.recorded.should == [:object1, :object2, :object3]
62+
end
63+
end
64+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
require_relative '../../../spec_helper'
2+
3+
ruby_version_is "3.2" do
4+
describe "Enumerator::Product#size" do
5+
it "returns the total size of the enumerator product calculated by multiplying the sizes of enumerables in the product" do
6+
product = Enumerator::Product.new(1..2, 1..3, 1..4)
7+
product.size.should == 24 # 2 * 3 * 4
8+
end
9+
10+
it "returns nil if any enumerable reports its size as nil" do
11+
enum = Object.new
12+
def enum.size; nil; end
13+
14+
product = Enumerator::Product.new(1..2, enum)
15+
product.size.should == nil
16+
end
17+
18+
it "returns Float::INFINITY if any enumerable reports its size as Float::INFINITY" do
19+
enum = Object.new
20+
def enum.size; Float::INFINITY; end
21+
22+
product = Enumerator::Product.new(1..2, enum)
23+
product.size.should == Float::INFINITY
24+
end
25+
26+
it "returns -Float::INFINITY if any enumerable reports its size as -Float::INFINITY" do
27+
enum = Object.new
28+
def enum.size; -Float::INFINITY; end
29+
30+
product = Enumerator::Product.new(1..2, enum)
31+
product.size.should == -Float::INFINITY
32+
end
33+
34+
it "returns nil if any enumerable reports its size as Float::NAN" do
35+
enum = Object.new
36+
def enum.size; Float::NAN; end
37+
38+
product = Enumerator::Product.new(1..2, enum)
39+
product.size.should == nil
40+
end
41+
42+
it "returns nil if any enumerable doesn't respond to #size" do
43+
enum = Object.new
44+
product = Enumerator::Product.new(1..2, enum)
45+
product.size.should == nil
46+
end
47+
48+
it "returns nil if any enumerable reports a not-convertible to Integer" do
49+
enum = Object.new
50+
def enum.size; :symbol; end
51+
52+
product = Enumerator::Product.new(1..2, enum)
53+
product.size.should == nil
54+
end
55+
56+
it "returns nil if any enumerable reports a non-Integer but convertible to Integer size" do
57+
enum = Object.new
58+
def enum.size; 1.0; end
59+
60+
product = Enumerator::Product.new(1..2, enum)
61+
product.size.should == nil
62+
end
63+
end
64+
end

0 commit comments

Comments
 (0)