From 5c4352c37e0adffbb4943d36c21f357b8a60e881 Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Wed, 27 May 2026 22:21:05 +0900 Subject: [PATCH 1/2] Add specs for Enumerable value packing rule (0->nil, 1->value, N->Array) The rb_enum_values_pack rule in enum.c packs yielded source values when they flow through any Enumerable collection method or Lazy stage: 0 args -> nil, 1 arg -> the value, N args -> Array. Pin this explicitly via a shared describe used by both Enumerable#take and Enumerator::Lazy#take. --- core/enumerable/shared/value_packing.rb | 25 +++++++++++++++++++++++++ core/enumerable/take_spec.rb | 8 ++++++++ core/enumerator/lazy/take_spec.rb | 8 ++++++++ 3 files changed, 41 insertions(+) create mode 100644 core/enumerable/shared/value_packing.rb diff --git a/core/enumerable/shared/value_packing.rb b/core/enumerable/shared/value_packing.rb new file mode 100644 index 000000000..9c4b4edb1 --- /dev/null +++ b/core/enumerable/shared/value_packing.rb @@ -0,0 +1,25 @@ +describe :enumerable_value_packing, shared: true do + # @take must be set to a Proc that returns the take-result whose #each + # yields packed values (e.g. -> e { e.take(1) } or -> e { e.lazy.take(1) }). + + it "yields a single nil for a zero-argument source yield" do + e = Enumerator.new { |y| y.yield } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [nil] + end + + it "yields the value for a single-argument source yield" do + e = Enumerator.new { |y| y.yield :v } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [:v] + end + + it "yields a packed Array for a multi-argument source yield" do + e = Enumerator.new { |y| y.yield 1, 2 } + args = nil + @take.call(e).each { |*a| args = a } + args.should == [[1, 2]] + end +end diff --git a/core/enumerable/take_spec.rb b/core/enumerable/take_spec.rb index 8cc746f88..ca439b750 100644 --- a/core/enumerable/take_spec.rb +++ b/core/enumerable/take_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/take' +require_relative 'shared/value_packing' describe "Enumerable#take" do it "requires an argument" do @@ -10,4 +11,11 @@ describe "when passed an argument" do it_behaves_like :enumerable_take, :take end + + describe "value packing of source yields" do + before :each do + @take = -> e { e.take(1) } + end + it_behaves_like :enumerable_value_packing, nil + end end diff --git a/core/enumerator/lazy/take_spec.rb b/core/enumerator/lazy/take_spec.rb index f92360f54..2dd5b939e 100644 --- a/core/enumerator/lazy/take_spec.rb +++ b/core/enumerator/lazy/take_spec.rb @@ -2,8 +2,16 @@ require_relative '../../../spec_helper' require_relative 'fixtures/classes' +require_relative '../../enumerable/shared/value_packing' describe "Enumerator::Lazy#take" do + describe "value packing of source yields (matches Enumerable#take)" do + before :each do + @take = -> e { e.lazy.take(1) } + end + it_behaves_like :enumerable_value_packing, nil + end + before :each do @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy From 8bb8782e6d855afe12e986a463ff77a056b63a35 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 27 May 2026 22:53:35 +0200 Subject: [PATCH 2/2] Add comment for where the behavior comes from --- core/enumerable/shared/value_packing.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/core/enumerable/shared/value_packing.rb b/core/enumerable/shared/value_packing.rb index 9c4b4edb1..ff77f45cd 100644 --- a/core/enumerable/shared/value_packing.rb +++ b/core/enumerable/shared/value_packing.rb @@ -1,3 +1,4 @@ +# This is the behavior of rb_enum_values_pack() in CRuby describe :enumerable_value_packing, shared: true do # @take must be set to a Proc that returns the take-result whose #each # yields packed values (e.g. -> e { e.take(1) } or -> e { e.lazy.take(1) }).