From: "mame (Yusuke Endoh) via ruby-core" Date: 2025-12-04T06:54:37+00:00 Subject: [ruby-core:124022] [Ruby Misc#21154] Document or change Module#autoload? Issue #21154 has been updated by mame (Yusuke Endoh). I'd like to share a simplified example that demonstrates the autoload/require issue. ```ruby # my_gem.rb module MyGem autoload :M, "my_gem/m" # (2) end # my_gem/m.rb module MyGem module M autoload :X, "my_gem/m/x" # (5) SingletonX = X.new # (6) end end # my_gem/m/x.rb module MyGem module M # (4) class X end end end ``` Running this with the following code results in `uninitialized constant MyGem::M::X (NameError)`. ```ruby # main.rb require "my_gem" # (1) require "my_gem/m/x" # (3) ``` ``` $ ruby -I. main.rb /home/mame/work/ruby/tmp/my_gem/m.rb:5:in '': uninitialized constant MyGem::M::X (NameError) SingletonX = X.new ^ from /home/mame/work/ruby/tmp/my_gem/m.rb:2:in '' from /home/mame/work/ruby/tmp/my_gem/m.rb:1:in '' from /home/mame/work/ruby/tmp/my_gem/m/x.rb:2:in 'Kernel#require' from /home/mame/work/ruby/tmp/my_gem/m/x.rb:2:in '' from /home/mame/work/ruby/tmp/my_gem/m/x.rb:1:in '' from base.rb:2:in 'Kernel#require' from base.rb:2:in '
' ``` Here is the sequence of events: * (1) in `main.rb`: `require "my_gem"`. * (2) in `my_gem.rb`: Sets autoload for `MyGem::M`. * (3) in `main.rb`: `require "my_gem/m/x"`. * (4) in `my_gem/m/x.rb`: The definition `module M` triggers the autoload for `MyGem::M`. * (5) in `my_gem/m.rb`: Sets autoload for `MyGem::M::X`. * (6) in `my_gem/m.rb`: `SingletonX = X.new` triggers the autoload for `MyGem::M::X`. However, since `my_gem/m/x.rb` is already in the middle of being required (from step 3), this is treated as a circular require, so Ruby does nothing. * As a result, `MyGem::M::X` remains undefined, raising `uninitialized constant MyGem::M::X`. What is wrong here? I believe the issue arises from performing a "cherry-picking" require (requiring internal files directly) on a library that relies on `autoload`. Therefore: * Users must not perform cherry-picking requires (e.g., `require "my_gem/m/x"`) unless the library author explicitly permits it. * If a library author intends to support cherry-picking requires, they must not use `autoload`. It is important to note that simply replacing `autoload :X, "my_gem/m/x"` with `require "my_gem/m/x"` is not sufficient to fix this. The issue persists unless the parent `autoload :M, "my_gem/m"` is also changed to `require "my_gem/m"`. It is a bit difficult to determine exactly which `autoload` definitions need to be replaced to avoid the issue. I believe the following is a good rule of thumb: Libraries that wish to support cherry-picking requires should not use `autoload` entirely. ---------------------------------------- Misc #21154: Document or change Module#autoload? https://bugs.ruby-lang.org/issues/21154#change-115454 * Author: fxn (Xavier Noria) * Status: Feedback * Assignee: mame (Yusuke Endoh) ---------------------------------------- The documentation of `Module#autoload?` says > Returns filename to be loaded if name is registered as autoload in the namespace of mod or one of its ancestors. Cool, but in the following snippet ```ruby autoload :Foo, 'foo' autoload?(:Foo) ``` the second line could evaluate to `nil`, and this does not seem to agree. I just registered an autoload, therefore (according to the documentation) I should get "foo" back in line 2. I'd like to ask for clarification from the Ruby team: 1. Is the documentation complete? Should that second line always return "foo"? 2. If the answer is no, which is the logic missing in the docs? Thank you! -- 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/