From 9773b36e7468e7649ab3f01cf88c154dad265e55 Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Fri, 29 May 2026 16:05:11 +0900 Subject: [PATCH] Add specs for #each passing source yields by block arity (Enumerator and Lazy) Unlike the Enumerable collection methods, which pack yielded source values via rb_enum_values_pack() (0 args -> nil, 1 arg -> the value, N args -> Array), #each passes them to the block by ordinary block arity (rb_yield_values2() in CRuby): a single-argument block receives the first value, a multi-argument block destructures, and a splat block gathers them. Pin this explicitly via a shared describe used by both Enumerator#each and Enumerator::Lazy#each, complementing the value_packing specs. --- core/enumerator/each_spec.rb | 8 ++++++ core/enumerator/lazy/each_spec.rb | 11 ++++++++ core/enumerator/shared/each.rb | 46 +++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 core/enumerator/lazy/each_spec.rb create mode 100644 core/enumerator/shared/each.rb diff --git a/core/enumerator/each_spec.rb b/core/enumerator/each_spec.rb index 03be53fe0..baab987dd 100644 --- a/core/enumerator/each_spec.rb +++ b/core/enumerator/each_spec.rb @@ -1,6 +1,14 @@ require_relative '../../spec_helper' +require_relative 'shared/each' describe "Enumerator#each" do + describe "passing source-yielded arguments to the block" do + before :each do + @object = -> e { e } + end + it_behaves_like :enum_each, nil + end + before :each do object_each_with_arguments = Object.new def object_each_with_arguments.each_with_arguments(arg, *args) diff --git a/core/enumerator/lazy/each_spec.rb b/core/enumerator/lazy/each_spec.rb new file mode 100644 index 000000000..3d44d230d --- /dev/null +++ b/core/enumerator/lazy/each_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' +require_relative '../shared/each' + +describe "Enumerator::Lazy#each" do + describe "passing source-yielded arguments to the block (matches Enumerator#each)" do + before :each do + @object = -> e { e.lazy } + end + it_behaves_like :enum_each, nil + end +end diff --git a/core/enumerator/shared/each.rb b/core/enumerator/shared/each.rb new file mode 100644 index 000000000..18ca77320 --- /dev/null +++ b/core/enumerator/shared/each.rb @@ -0,0 +1,46 @@ +# #each passes source-yielded values to the block by ordinary block arity +# (rb_yield_values2 semantics in CRuby), unlike the Enumerable collection methods +# which pack them via rb_enum_values_pack() (see enumerable/shared/value_packing.rb). +describe :enum_each, shared: true do + # @object must be set to a Proc that wraps an Enumerator into the receiver + # under test (e.g. -> e { e } for Enumerator#each, -> e { e.lazy } for Lazy#each). + describe "with a source that yields multiple values" do + before :each do + @enum = @object.call(Enumerator.new { |y| y.yield 1, 2; y.yield 3, 4 }) + end + + it "yields the first value to a single-argument block" do + collected = [] + @enum.each { |x| collected << x } + collected.should == [1, 3] + end + + it "yields each value to a multi-argument block" do + collected = [] + @enum.each { |x, y| collected << [x, y] } + collected.should == [[1, 2], [3, 4]] + end + + it "gathers the values for a splat block" do + collected = [] + @enum.each { |*args| collected << args } + collected.should == [[1, 2], [3, 4]] + end + end + + describe "with a source that yields a single value" do + it "yields the value to a single-argument block" do + collected = [] + @object.call(Enumerator.new { |y| y.yield 7; y.yield 8 }).each { |x| collected << x } + collected.should == [7, 8] + end + end + + describe "with a source that yields no value" do + it "yields nil to a single-argument block" do + collected = [] + @object.call(Enumerator.new { |y| y.yield; y.yield }).each { |x| collected << x } + collected.should == [nil, nil] + end + end +end