diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index 76c29c6426942..8e5f6e168ca8f 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -71,6 +71,16 @@ class Railtie < Rails::Railtie # :nodoc: initializer "action_mailer.set_autoload_paths", before: :set_autoload_paths do |app| options = app.config.action_mailer app.config.paths["test/mailers/previews"].concat(options.preview_paths) + + # Preview paths configuration needs a pass. + # + # config.paths is cached as soon as it is accessed. Therefore, mutating + # paths["test/mailers/previews"] does not guarantee config.autoload_paths + # is going to include them. + # + # If config.paths was accessed before, config.autoload_paths is going to + # have whatever paths["test/mailers/previews"] had when cached. + app.config.autoload_paths.concat(options.preview_paths) end initializer "action_mailer.compile_config_methods" do diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 719e5dbea5419..f02771b467ff8 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,13 @@ +* `config/application.rb` now includes + + ```ruby + config.autoload_lib(ignore: %w(assets tasks)) + ``` + + In practice, this means that new 7.1 applications autoload from `lib` out of the box. + + *Xavier Noria* + * Add an option to start rails console in sandbox mode by default `sandbox_by_default` option is added to start rails console in sandbox diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb index 51ffeea826acd..c3efc74e69256 100644 --- a/railties/lib/rails/engine/configuration.rb +++ b/railties/lib/rails/engine/configuration.rb @@ -50,6 +50,9 @@ def paths paths.add "app/mailers", eager_load: true paths.add "app/views" + # If you add more lib subdirectories here that should not be managed + # by the main autoloader, please update the config.autoload_lib call + # in the template that generates config/application.rb accordingly. paths.add "lib", load_path: true paths.add "lib/assets", glob: "*" paths.add "lib/tasks", glob: "**/*.rake" diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt index a03c289d4019d..ca466a5ecdb0a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb.tt @@ -15,6 +15,9 @@ module <%= app_const_base %> config.load_defaults Rails::VERSION::STRING.to_f <%- end -%> + # Please, see https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#config-autoload-lib-ignore. + config.autoload_lib(ignore: %w(assets tasks)) + # Configuration for the application, engines, and railties goes here. # # These settings can be overridden in specific environments using the files diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index f0a67dafcf757..526863279db20 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -2119,8 +2119,10 @@ def index end end - test "autoload paths will exclude the configured javascript_path" do - add_to_config "config.javascript_path = 'webpack'" + test "autoload paths do not include custom config.javascript_paths" do + # The config.javascript_path assignment has to be in place before + # config.paths is accessed, since their compilation uses the value. + add_to_top_of_config "config.javascript_path = 'webpack'" app_dir("app/webpack") app "development" @@ -2148,10 +2150,10 @@ def index assert_empty Rails.configuration.paths.load_paths - $LOAD_PATH end - test "autoload paths are not added to $LOAD_PATH by default" do + test "autoload paths are not added to $LOAD_PATH by default, except for lib" do app "development" - assert_empty ActiveSupport::Dependencies.autoload_paths & $LOAD_PATH + assert_equal ["#{app_path}/lib"], ActiveSupport::Dependencies.autoload_paths & $LOAD_PATH # Precondition, ensure we are testing something next. assert_not_empty Rails.configuration.paths.load_paths @@ -2170,6 +2172,23 @@ def index assert_includes $LOAD_PATH, lib end + test "config.autoload_lib(...) is generated by default" do + app_file "lib/x.rb", "X = true" + app_file "lib/m/x.rb", "M::X = true" + app_file "lib/assets/x.rb", "Assets::X = true" + app_file "lib/tasks/x.rb", "Tasks::X = true" + + app "development" + + assert_includes Rails.application.config.autoload_paths, "#{app_path}/lib" + assert_includes Rails.application.config.eager_load_paths, "#{app_path}/lib" + + assert X + assert M::X + assert_raises(NameError) { Assets } + assert_raises(NameError) { Tasks } + end + test "autoload paths can be set in the config file of the environment" do app_dir "custom_autoload_path" app_dir "custom_autoload_once_path" @@ -2197,6 +2216,7 @@ def index app_file "lib/tasks/x.rb", "Tasks::X = true" app_file "lib/generators/x.rb", "Generators::X = true" + remove_from_config "config\\.#{method_name}.*" add_to_config "config.#{method_name}(ignore: %w(tasks generators))" app "development" @@ -2215,6 +2235,7 @@ def index app_file "lib/x.rb", "X = true" app_file "lib/tasks/x.rb", "Tasks::X = true" + remove_from_config "config\\.#{method_name}.*" add_to_config "config.#{method_name}(ignore: [])" app "development" @@ -2232,6 +2253,7 @@ def index app_file "lib/x.rb", "X = true" app_file "lib/tasks/x.rb", "Tasks::X = true" + remove_from_config "config\\.#{method_name}.*" add_to_config "config.#{method_name}(ignore: 'tasks')" app "development" @@ -2249,6 +2271,7 @@ def index app_file "lib/x.rb", "X = true" app_file "lib/tasks/x.rb", "Tasks::X = true" + remove_from_config "config\\.#{method_name}.*" add_to_config "config.#{method_name}(ignore: nil)" app "development" diff --git a/railties/test/application/zeitwerk_integration_test.rb b/railties/test/application/zeitwerk_integration_test.rb index 37c1723dc2b34..63c45bc915c35 100644 --- a/railties/test/application/zeitwerk_integration_test.rb +++ b/railties/test/application/zeitwerk_integration_test.rb @@ -234,27 +234,17 @@ module ZeitwerkIntegrationTestExtras; end $zeitwerk_integration_test_user = false app_file "app/models/user.rb", "class User; end; $zeitwerk_integration_test_user = true" - $zeitwerk_integration_test_lib = false - app_dir "lib" - app_file "lib/webhook_hacks.rb", "WebhookHacks = 1; $zeitwerk_integration_test_lib = true" - $zeitwerk_integration_test_extras = false app_dir "extras" app_file "extras/websocket_hacks.rb", "WebsocketHacks = 1; $zeitwerk_integration_test_extras = true" - - add_to_config "config.autoload_paths << '#{app_path}/lib'" add_to_config "config.autoload_once_paths << '#{app_path}/extras'" boot("production") assert $zeitwerk_integration_test_user - assert_not $zeitwerk_integration_test_lib assert_not $zeitwerk_integration_test_extras - assert WebhookHacks assert WebsocketHacks - - assert $zeitwerk_integration_test_lib assert $zeitwerk_integration_test_extras end