From c0d91a4f9da10094ccdb80e34d1be42ce1016c9a Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 7 Jan 2020 15:13:02 +0100 Subject: [PATCH] restores the ability to manually eager load applications The main interface to eager loading is config.eager_load. The logic that implies happens during the boot process. With the introduction of Zeitwerk, application code is loaded in the finisher as everything else, but in previous versions of Rails users could eager load the application code regardless of config.eager_load. Use cases: * Some gems like indexers need to have everything in memory and would be a bad user experience to ask users to conditionally set the eager load flag. * Some tests may need to have everything in memory and would be a bad experience to have the flag enabled globally in the test environment. I personally feel that the contract between this method and the entire eager loading process is ill-defined. I believe this method is essentially internal. The purpose of this patch is simply to restore this functionality emulating what it did before because rethinking the design of this interface may need time. --- railties/lib/rails/application.rb | 9 +++++++++ railties/lib/rails/engine.rb | 3 ++- .../application/zeitwerk_integration_test.rb | 20 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index ed7bca4e3c..93524ae147 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -497,6 +497,15 @@ module Rails ordered_railties.flatten - [self] end + # Eager loads the application code. + def eager_load! + if Rails.autoloaders.zeitwerk_enabled? + Rails.autoloaders.each(&:eager_load) + else + super + end + end + protected alias :build_middleware_stack :app diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index d7dc9869b0..93940e5a08 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -471,7 +471,8 @@ module Rails end def eager_load! - # Already done by Zeitwerk::Loader.eager_load_all in the finisher. + # Already done by Zeitwerk::Loader.eager_load_all. We need this guard to + # easily provide a compatible API for both zeitwerk and classic modes. return if Rails.autoloaders.zeitwerk_enabled? config.eager_load_paths.each do |load_path| diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb index ab8dbe64d3..af230f73c3 100644 --- a/railties/test/application/zeitwerk_integration_test.rb +++ b/railties/test/application/zeitwerk_integration_test.rb @@ -192,6 +192,26 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase assert $zeitwerk_integration_test_post end + test "eager loading loads the application code if invoked manually too (regression test)" do + $zeitwerk_integration_test_user = false + $zeitwerk_integration_test_post = false + + app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true" + app_file "app/models/post.rb", "class Post; end; $zeitwerk_integration_test_post = true" + + boot + + # Preconditions. + assert !$zeitwerk_integration_test_user + assert !$zeitwerk_integration_test_post + + Rails.application.eager_load! + + # Postconditions. + assert $zeitwerk_integration_test_user + assert $zeitwerk_integration_test_post + end + test "reloading is enabled if config.cache_classes is false" do boot