Project

General

Profile

Actions

Bug #22420

open

`require "strscan"` no longer activates the default gem, so a newer installed strscan is ignored and later loaded twice

Bug #22420: `require "strscan"` no longer activates the default gem, so a newer installed strscan is ignored and later loaded twice

Added by ksss (Yuki Kurihara) about 15 hours ago. Updated about 14 hours ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [arm64-darwin25]
[ruby-core:127021]

Description

Since Ruby 4.0.0, require "strscan" loads the default version from rubylibdir even when a newer strscan gem is installed, and does not activate the default gem. If the gem is activated later, its extension is loaded on top of the one already loaded.

Reproduction

$ ruby -v
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [arm64-darwin25]

$ gem install strscan    # 3.1.8, newer than the default 3.1.6

$ ruby -e 'require "strscan"; p StringScanner::Version; p Gem.loaded_specs["strscan"]&.version'
"3.1.6"
nil

$ ruby -e 'require "strscan"; gem "strscan"; require "strscan"; p StringScanner::Version'
.../gems/strscan-3.1.8/lib/strscan.bundle: warning: already initialized constant StringScanner::Version
.../gems/strscan-3.1.8/lib/strscan.bundle: warning: already initialized constant StringScanner::Id
"3.1.8"

The second case happens in practice when a gem depending on strscan (e.g. rouge) is required after strscan.

Expected

Same as Ruby 3.3:

$ ruby -e 'require "strscan"; p StringScanner::Version; p Gem.loaded_specs["strscan"]&.version'
"3.1.8"
#<Gem::Version "3.1.8">

$ ruby -e 'require "strscan"; gem "strscan"; require "strscan"; p StringScanner::Version'
"3.1.8"

Cause

The installed default gemspec has wrong files. It has lib/lib/strscan/strscan.rb, and lacks strscan.bundle:

$ ruby -e 'p Gem::Specification.load(Dir[Gem.default_specifications_dir + "/strscan-*.gemspec"][0]).files.grep_v(/\Adoc\//)'
[".rdoc_options", "COPYING", "LICENSE.txt", "ext/strscan/extconf.rb", "ext/strscan/strscan.c", "lib/lib/strscan/strscan.rb"]

So Gem.register_default_spec maps no strscan feature to the default gem.

Both come from load_gemspec in tool/rbinstall.rb:

  • It prepends lib/ to every .rb entry that does not exist relative to the current directory. This is meant for the files collected from the build (relative to the gem's lib directory), but is also applied to the files listed by the gemspec itself (relative to the gem root), so they become lib/lib/....
  • The gemspec is evaluated with binding of load_gemspec, and strscan.gemspec assigns a local variable files, which overwrites the argument holding the collected files. So strscan.bundle is dropped.

require "io/console" is also no longer mapped to the default gem for the same reason. Other default gems (json, openssl, psych, date, ...) get lib/lib/... entries as well, though their main features are still mapped.

Ruby 3.4 and earlier replaced spec.files with the collected files, so this is a regression from the changes to load_gemspec for 4.0 (7ce339244953a6c3add543854292c61e9f5bc14b, d6b40320b2cfecf9802e664a4869353d195fcc6d).

Actions

Also available in: PDF Atom