From 2856aa67df48409a1743b9126ff3bdc875ab2a98 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 26 Jan 2026 10:09:35 +0900 Subject: [PATCH 1/3] [DOC] State that an interpolated string in `%W` is not split It is split statically at the parse time, not the whole string is built then split. --- doc/syntax/literals.rdoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc index 87a891bf2d3c05..c876558d4e07b9 100644 --- a/doc/syntax/literals.rdoc +++ b/doc/syntax/literals.rdoc @@ -547,6 +547,13 @@ with %w (non-interpolable) or %W (interpolable): # (not nested array). %w[foo[bar baz]qux] # => ["foo[bar", "baz]qux"] +The interpolated string is treated as a single word even if it contains +whitespace. + + s = "bar baz" + %W[foo #{s} zot] #=> ["foo", "bar baz", "zot"] + %W[foo #{"bar baz zot"} qux] # => ["foo", "bar baz zot", "qux"] + The following characters are considered as white spaces to separate words: * space, ASCII 20h (SPC) From ee32a2dc9397dfd1574551fb48349f4a6d424385 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 02:03:37 +0000 Subject: [PATCH 2/3] Bump lewagon/wait-on-check-action from 1.4.1 to 1.5.0 Bumps [lewagon/wait-on-check-action](https://github.com/lewagon/wait-on-check-action) from 1.4.1 to 1.5.0. - [Release notes](https://github.com/lewagon/wait-on-check-action/releases) - [Changelog](https://github.com/lewagon/wait-on-check-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/lewagon/wait-on-check-action/compare/3603e826ee561ea102b58accb5ea55a1a7482343...74049309dfeff245fe8009a0137eacf28136cb3c) --- updated-dependencies: - dependency-name: lewagon/wait-on-check-action dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/dependabot_automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml index a95c7005c41054..7f52dc08b62cc6 100644 --- a/.github/workflows/dependabot_automerge.yml +++ b/.github/workflows/dependabot_automerge.yml @@ -17,7 +17,7 @@ jobs: id: metadata - name: Wait for status checks - uses: lewagon/wait-on-check-action@3603e826ee561ea102b58accb5ea55a1a7482343 # v1.4.1 + uses: lewagon/wait-on-check-action@74049309dfeff245fe8009a0137eacf28136cb3c # v1.5.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} ref: ${{ github.event.pull_request.head.sha || github.sha }} From 78d9b454ce553463b8ca2b928ab0e3cf92853e57 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Tue, 13 Jan 2026 19:04:11 +0100 Subject: [PATCH 3/3] [ruby/rubygems] Fix RubyGems not able to require the right gem: - Fix https://github.com/ruby/rubygems/issues/9238 - ### Problem This is an issue that bites gem maintainers from time to time, with the most recent one in https://github.com/minitest/minitest/issues/1040#issuecomment-3679370619 The issue is summarized as follow: 1) A gem "X" has a feature in "lib/feature.rb" 2) Maintainer wants to extract this feature into its own gem "Y" 3) Maintainer cut a release of X without that new feature. 4) Users install the new version of X and also install the new gem "Y" since the feature is now extracted. 5) When a call to "require 'feature'" is encountered, RG will fail to load the right gem, resulting in a `LoadError`. ### Details Now that we have two gems (old version of X and new gem Y) with the same path, RubyGems will detect that `feature.rb` can be loaded from the old version of X, but if the new version of X had already been loaded, then RubyGems will raise due to versions conflicting. ```ruby require 'x' # Loads the new version of X without the feature which was extracted. require 'feature' # Rubygems see that the old version of X include that file and tries to activate the spec. ``` ### Solution I propose that RubyGems fallback to a spec that's not yet loaded. We try to find a spec by its path and filter it out in case a spec with the same name has already been loaded. Its worth to note that RubyGems already has a `find_inactive_by_path` but we can't use it. This method only checks if the spec object is active and doesn't look if other spec with the same name have been loaded. The new method we are introducing verifies this. https://github.com/ruby/rubygems/commit/f298e2c68e --- lib/rubygems.rb | 7 ++++--- lib/rubygems/specification.rb | 9 +++++++++ lib/rubygems/specification_record.rb | 13 +++++++++++++ test/rubygems/test_require.rb | 16 ++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/rubygems.rb b/lib/rubygems.rb index b52dd1b9d3e99a..31abb7e56987ea 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -193,11 +193,12 @@ def self.try_activate(path) begin spec.activate rescue Gem::LoadError => e # this could fail due to gem dep collisions, go lax - spec_by_name = Gem::Specification.find_by_name(spec.name) - if spec_by_name.nil? + spec = Gem::Specification.find_unloaded_by_path(path) + spec ||= Gem::Specification.find_by_name(spec.name) + if spec.nil? raise e else - spec_by_name.activate + spec.activate end end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 3d1f2dad910485..aa495696adce3c 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -958,6 +958,15 @@ def self.find_by_path(path) specification_record.find_by_path(path) end + ## + # Return the best specification that contains the file matching +path+ + # amongst the specs that are not loaded. This method is different than + # +find_inactive_by_path+ as it will filter out loaded specs by their name. + + def self.find_unloaded_by_path(path) + specification_record.find_unloaded_by_path(path) + end + ## # Return the best specification that contains the file matching +path+ # amongst the specs that are not activated. diff --git a/lib/rubygems/specification_record.rb b/lib/rubygems/specification_record.rb index d08410096facdd..c7e5cbedb58ce6 100644 --- a/lib/rubygems/specification_record.rb +++ b/lib/rubygems/specification_record.rb @@ -154,6 +154,19 @@ def find_by_path(path) spec.to_spec end + ## + # Return the best specification that contains the file matching +path+ + # amongst the specs that are not loaded. This method is different than + # +find_inactive_by_path+ as it will filter out loaded specs by their name. + + def find_unloaded_by_path(path) + stub = stubs.find do |s| + next if Gem.loaded_specs[s.name] + s.contains_requirable_file? path + end + stub&.to_spec + end + ## # Return the best specification in the record that contains the file # matching +path+ amongst the specs that are not activated. diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb index f63c23c3159db5..e5f9d7bed2c901 100644 --- a/test/rubygems/test_require.rb +++ b/test/rubygems/test_require.rb @@ -431,6 +431,22 @@ def test_default_gem_only assert_equal %w[default-2.0.0.0], loaded_spec_names end + def test_multiple_gems_with_the_same_path_the_non_activated_spec_is_chosen + a1 = util_spec "a", "1", nil, "lib/ib.rb" + a2 = util_spec "a", "2", nil, "lib/foo.rb" + b1 = util_spec "b", "1", nil, "lib/ib.rb" + + install_specs a1, a2, b1 + + a2.activate + + assert_equal %w[a-2], loaded_spec_names + assert_empty unresolved_names + + assert_require "ib" + assert_equal %w[a-2 b-1], loaded_spec_names + end + def test_default_gem_require_activates_just_once default_gem_spec = new_default_spec("default", "2.0.0.0", nil, "default/gem.rb")