rails: Rails.application.eager_load! regression in Zeitwerk autoloader mode
https://github.com/rails/rails/blob/master/railties/lib/rails/engine.rb#L474 there is a note
Already done by Zeitwerk::Loader.eager_load_all in the finisher.
This doesn’t appear to be true outside of production modes.
It’s fairly common to eager load (Rails.application.eager_load!
) for rake tasks or other reason in lower environments but the behavior with zeitwerk is different than in classic mode.
I believe adjusting the line beneath the comment from return if Rails.autoloaders.zeitwerk_enabled?
to return Zeitwerk::Loader.eager_load_all if Rails.autoloaders.zeitwerk_enabled?
returns the behavior to the previous, but I’m unsure of other implications.
Steps to reproduce
rails new autoload
cd autoload
rails c
>
Rails.application.eager_load!
> nil
ActiveRecord::Base.descendants
> []
Expected behavior
in same app as above - edit config/application.rb
add config.autoloader = :classic
directly beneath config.load_defaults 6.0
save & exit
rails c
>
Rails.application.eager_load!
> ["/home/tongboy/autoload/autoload/app/channels", "/home/tongboy/autoload/autoload/app/controllers", "/home/tongboy/autoload/autoload/app/controllers/concerns", "/home/tongboy/autoload/autoload/app/helpers", "/home/tongboy/autoload/autoload/app/jobs", "/home/tongboy/autoload/autoload/app/mailers", "/home/tongboy/autoload/autoload/app/models", "/home/tongboy/autoload/autoload/app/models/concerns"]
ActiveRecord::Base.descendants
> [ApplicationRecord(abstract)]
similarly - if classic mode is not enabled and instead just Zeitwerk::Loader.eager_load_all
is called rather than the Rails.application.eager_load!
then the ‘good’ behavior is also seen.
To me this looks like a regression in the behavior of Rails.application.eager_load!
in non-classic mode - matching the long-standing behavior seems to make the most sense, but I’m far from an expert in auto loading and just working on fixing up some gems for rails 6 support.
Please let me know if pulling together a PR or any additional information around this would be helpful.
System configuration
Rails version: 6.0.0
Ruby version: 2.6.3p62
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 14
- Comments: 21 (18 by maintainers)
Just to add some context, this is important if you have anything that does app-wide reflection (like rebuilding search indexes or reflecting on associations).
Bump on this. We used (past tense!) reflection quite a lot but on moving to Rails 6 under Zeitwerk, it all went wrong (dev mode - not in Production, thankfully). We even had cases as severe as this:
.descendants
is used in a test which instantiates the subclasses and calls one of the methods under test.descendants
had enumerated the base class not a descendant, indicating a pretty horrible Zeitwerk failure.We could’ve just eager-loaded the whole application, but that’s very slow. It’s when we first realised that
Rails.application.eager_load!
didn’t work anymore, too; fortunately this very bug report led us to the solution of usingZeitwerk::Loader.eager_load_all
. We tried usingrequire
and a few other tricks that worked under the classic autoloader, but it just didn’t help. In the end we resolved it by losing a benefit of Ruby - we stopped doing reflection, instead hand-maintaining lists of subclasses in constants which are iterated over instead. This works reliably.We are sticking with Zeitwerk since it’s recommended for Rails 6, but only with a sense of unease - we’ve seen too many weird bugs in dev that don’t happen if you change to the classic autoloader. This is a new application built under Rails 5.2 just a couple of months before 6.0 was released and I’m pretty sure we’re not doing anything particularly strange 😃 - just feels like Zeitwerk wasn’t quite ready for prime-time.
Fingers crossed for some fixes soon. If there’s anything that could be added here which might aid debugging at least this core issue with
Rails.application.eager_load!
, let me know.@fxn No problem 🤝 Anyway, thanks for you hard work in Zeitwerk and its integration in Rails 🏆 It`s a great gem
You can put this in an initializer and not change any of your code:
It’s not an ideal solution, but it should ease migration without you having to change a bunch of reflection.
No activity in a year. I’ll close.
This is on our production system, so I won’t be able to for a few days, but I added this workaround for the time being:
This then loads all my models, but is a pretty messy solution 😦
@pedrofurtado yeah, sorry but took a Christmas hiatus. It’s in my plate.