Skip to content

Commit

Permalink
Introduce config.autoload_lib_once(ignore:)
Browse files Browse the repository at this point in the history
  • Loading branch information
fxn committed Jun 29, 2023
1 parent de29ff2 commit 0568bc9
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 46 deletions.
29 changes: 29 additions & 0 deletions guides/source/autoloading_and_reloading_constants.md
Expand Up @@ -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}
----------

Expand Down
6 changes: 6 additions & 0 deletions guides/source/configuring.md
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions railties/CHANGELOG.md
@@ -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`.

Expand Down
12 changes: 12 additions & 0 deletions railties/lib/rails/application/configuration.rb
Expand Up @@ -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
Expand Down
94 changes: 48 additions & 46 deletions railties/test/application/configuration_test.rb
Expand Up @@ -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
Expand Down

0 comments on commit 0568bc9

Please sign in to comment.