From: "ksss (Yuki Kurihara) via ruby-core" Date: 2026-10-09T03:59:55+00:00 Subject: [ruby-core:127022] [Ruby Bug#22420] `require "strscan"` no longer activates the default gem, so a newer installed strscan is ignored and later loaded twice Issue #22420 has been updated by ksss (Yuki Kurihara). I opened a pull request: https://github.com/ruby/ruby/pull/19275 ---------------------------------------- Bug #22420: `require "strscan"` no longer activates the default gem, so a newer installed strscan is ignored and later loaded twice https://bugs.ruby-lang.org/issues/22420#change-119418 * Author: ksss (Yuki Kurihara) * Status: Open * ruby -v: ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- 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" # $ 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). -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/