Feature #19006
closedInconsistent behaviour of autoload in wrapped script
Description
Suppose I have two files, foo.rb
and bar.rb
:
# foo.rb
puts "loading Foo..."
module Foo
autoload :Bar, "foo/bar"
end
and
# foo/bar.rb
puts "loading Foo::Bar..."
module Foo
module Bar
end
end
I can require "foo"
and access both Foo
and Foo::Bar
:
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
However, if I load foo
under a wrap module with load
:
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
... I'm now unable to access Foo::Bar
anywhere, because whereas the constant is autoloaded from MyModule::Foo::Bar
, it is required from the top-level as Foo::Bar
:
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
This means that autoload
is basically useless inside anything loaded with the wrap
argument to load
, because the file being autoloaded can't know in advance what the base namespace will be.
I would argue that it makes much more sense to apply the wrap module (top_wrapper
) to any autoloaded file loaded when top_wrapper
is set. In the example above, this would mean that accessing MyModule::Foo::Bar
would work, since MyModule
would apply when the autoload triggers to load foo/bar
.
Updated by shioyama (Chris Salzberg) over 1 year ago
This is related to: https://bugs.ruby-lang.org/issues/10320#note-13
Updated by jeremyevans0 (Jeremy Evans) over 1 year ago
When loading foo.rb
with the MyModule
wrapper, you are loading the equivalent of:
module MyModule
module Foo
autoload :Bar, "foo/bar"
end
end
foo/bar.rb
defines ::Foo::Bar
, not ::MyModule::Foo::Bar
, so I think the error you receive when you attempt to load MyModule::Foo::Bar
is expected.
load
's wrapped module is not transitive to load
/require
/autoload
calls inside the loaded file. It was never designed to be transitive, nor documented as being transitive, so I don't think the current behavior is a bug. Making the behavior transitive would be a feature request, IMO.
FWIW, I agree that the lack of transitivity makes load
's wrapped module not very useful in practice. However, I don't see that as a problem.
Updated by shioyama (Chris Salzberg) over 1 year ago
- Tracker changed from Bug to Feature
- Backport deleted (
2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN)
It was never designed to be transitive, nor documented as being transitive, so I don't think the current behavior is a bug. Making the behavior transitive would be a feature request, IMO.
Agree, I've changed the tracker to "feature" to reflect this.
FWIW, I agree that the lack of transitivity makes load's wrapped module not very useful in practice. However, I don't see that as a problem.
Agree with the first part, not sure though if I agree with the last part. This is really a larger discussion, but making load
's wrapped module transitive to require
, autoload
, etc would open the door to a lot of interesting things (https://bugs.ruby-lang.org/issues/10320#note-13 etc.)
My goal is to leave Ruby itself as much unchanged as possible, but without transitivity at the language level it's virtually impossible to implement true namespace isolation at the gem level, which is my original goal. I also think there is a strong argument to be made that transitivity is at least as "natural" as the current implementation. e.g. require
currently has to reset top_wrapper
before doing its requiring; making require
transitive actually entails removing code, not adding it.
Not suggesting we just suddenly change require
to remove that line (obviously backwards compatibility is a thing), but providing something that would allow that to happen — maybe a flag? — would unlock a lot of latent potential in Ruby.
Updated by shioyama (Chris Salzberg) over 1 year ago
Rethinking this, autoload
can fairly easily be patched at the gem level to make this work, without changes at the language level, so this isn't strictly necessary. In terms of transitivity, require
is the more important one.
I'm happy to close this, thanks for the feedback!
Updated by jeremyevans0 (Jeremy Evans) over 1 year ago
- Status changed from Open to Closed