Introduce config.autoload_lib_once(ignore:)

This commit is contained in:
Xavier Noria 2023-06-29 09:15:17 +02:00
parent de29ff225e
commit 0568bc9a90
5 changed files with 106 additions and 46 deletions

@ -197,6 +197,35 @@ INFO: Technically, you can autoload classes and modules managed by the `once` au
The autoload once paths are managed by `Rails.autoloaders.once`.
config.autoload_lib_once(ignore:)
---------------------------------
The method `config.autoload_lib_once` is similar to `config.autoload_lib`, except that it adds `lib` to `config.autoload_once_paths` instead. It has to be invoked from `config/application.rb` or `config/environments/*.rb`, and it is not available for engines.
By calling `config.autoload_lib_once`, classes and modules in `lib` can be autoloaded, even from application initializers, but won't be reloaded.
`config.autoload_lib_once` is not available before 7.1, but you can still emulate it as long as the application uses Zeitwerk:
```ruby
# config/application.rb
module MyApp
class Application < Rails::Application
lib = root.join("lib")
config.autoload_once_paths << lib
config.eager_load_paths << lib
Rails.autoloaders.once.ignore(
lib.join("assets"),
lib.join("tasks"),
lib.join("generators")
)
...
end
end
```
$LOAD_PATH{#load_path}
----------

@ -228,6 +228,12 @@ config.autoload_lib(ignore: %w(assets tasks generators))
Please, see more details in the [autoloading guide](autoloading_and_reloading_constants.html).
#### `config.autoload_lib_once(ignore:)`
The method `config.autoload_lib_once` is similar to `config.autoload_lib`, except that it adds `lib` to `config.autoload_once_paths` instead.
By calling `config.autoload_lib_once`, classes and modules in `lib` can be autoloaded, even from application initializers, but won't be reloaded.
#### `config.beginning_of_week`
Sets the default beginning of week for the

@ -1,3 +1,14 @@
* The new `config.autoload_lib_once` is similar to `config.autoload_lib`,
except that it adds `lib` to `config.autoload_once_paths` instead.
By calling `config.autoload_lib_once`, classes and modules in `lib` can be
autoloaded, even from application initializers, but won't be reloaded.
Please, see further details in the [autoloading
guide](https://guides.rubyonrails.org/v7.1/autoloading_and_reloading_constants.html).
*Xavier Noria*
* Add `config.action_dispatch.debug_exception_log_level` to configure the log
level used by `ActionDispatch::DebugExceptions`.

@ -464,6 +464,18 @@ def autoload_lib(ignore:)
Rails.autoloaders.main.ignore(ignored_abspaths)
end
def autoload_lib_once(ignore:)
lib = root.join("lib")
# Set as a string to have the same type as default autoload paths, for
# consistency.
autoload_once_paths << lib.to_s
eager_load_paths << lib.to_s
ignored_abspaths = Array.wrap(ignore).map { lib.join(_1) }
Rails.autoloaders.once.ignore(ignored_abspaths)
end
def colorize_logging
ActiveSupport::LogSubscriber.colorize_logging
end

@ -2151,74 +2151,76 @@ def index
end
end
test "config.autoload_lib adds lib to the autoload and eager load paths (array ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
app_file "lib/generators/x.rb", "Generators::X = true"
[%w(autoload_lib autoload_paths), %w(autoload_lib_once autoload_once_paths)].each do |method_name, paths|
test "config.#{method_name} adds lib to the expected paths (array ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
app_file "lib/generators/x.rb", "Generators::X = true"
add_to_config "config.autoload_lib(ignore: %w(tasks generators))"
add_to_config "config.#{method_name}(ignore: %w(tasks generators))"
app "development"
app "development"
Rails.application.config.tap do |config|
assert_includes config.autoload_paths, "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
Rails.application.config.tap do |config|
assert_includes config.send(paths), "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
end
assert X
assert_raises(NameError) { Tasks }
assert_raises(NameError) { Generators }
end
assert X
assert_raises(NameError) { Tasks }
assert_raises(NameError) { Generators }
end
test "config.#{method_name} adds lib to the expected paths (empty array ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
test "config.autoload_lib adds lib to the autoload and eager load paths (empty array ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
add_to_config "config.#{method_name}(ignore: [])"
add_to_config "config.autoload_lib(ignore: [])"
app "development"
app "development"
Rails.application.config.tap do |config|
assert_includes config.send(paths), "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
end
Rails.application.config.tap do |config|
assert_includes config.autoload_paths, "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
assert X
assert Tasks::X
end
assert X
assert Tasks::X
end
test "config.#{method_name} adds lib to the expected paths (scalar ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
test "config.autoload_lib adds lib to the autoload and eager load paths (scalar ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
add_to_config "config.#{method_name}(ignore: 'tasks')"
add_to_config "config.autoload_lib(ignore: 'tasks')"
app "development"
app "development"
Rails.application.config.tap do |config|
assert_includes config.send(paths), "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
end
Rails.application.config.tap do |config|
assert_includes config.autoload_paths, "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
assert X
assert_raises(NameError) { Tasks }
end
assert X
assert_raises(NameError) { Tasks }
end
test "config.#{method_name} adds lib to the expected paths (nil ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
test "config.autoload_lib adds lib to the autoload and eager load paths (nil ignore)" do
app_file "lib/x.rb", "X = true"
app_file "lib/tasks/x.rb", "Tasks::X = true"
add_to_config "config.#{method_name}(ignore: nil)"
add_to_config "config.autoload_lib(ignore: nil)"
app "development"
app "development"
Rails.application.config.tap do |config|
assert_includes config.send(paths), "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
end
Rails.application.config.tap do |config|
assert_includes config.autoload_paths, "#{app_path}/lib"
assert_includes config.eager_load_paths, "#{app_path}/lib"
assert X
assert Tasks::X
end
assert X
assert Tasks::X
end
test "load_database_yaml returns blank hash if configuration file is blank" do