Move load hooks section to the configuration guide

Those also applies to applications, so they should not be in a guide
specific for engines.
This commit is contained in:
Rafael Mendonça França 2024-03-07 22:03:04 +00:00
parent 8922180af5
commit 247c892150
No known key found for this signature in database
GPG Key ID: FC23B6D0F1EEE948
2 changed files with 129 additions and 154 deletions

@ -3453,6 +3453,135 @@ NOTE: There is no guarantee that your initializers will run after all the gem
initializers, so any initialization code that depends on a given gem having been
initialized should go into a `config.after_initialize` block.
Load Hooks
----------------------------
Rails code can often be referenced on load of an application. Rails is responsible for the load order of these frameworks, so when you load frameworks, such as `ActiveRecord::Base`, prematurely you are violating an implicit contract your application has with Rails. Moreover, by loading code such as `ActiveRecord::Base` on boot of your application you are loading entire frameworks which may slow down your boot time and could cause conflicts with load order and boot of your application.
Load and configuration hooks are the API that allow you to hook into this initialization process without violating the load contract with Rails. This will also mitigate boot performance degradation and avoid conflicts.
### Avoid Loading Rails Frameworks
Since Ruby is a dynamic language, some code will cause different Rails frameworks to load. Take this snippet for instance:
```ruby
ActiveRecord::Base.include(MyActiveRecordHelper)
```
This snippet means that when this file is loaded, it will encounter `ActiveRecord::Base`. This encounter causes Ruby to look for the definition of that constant and will require it. This causes the entire Active Record framework to be loaded on boot.
`ActiveSupport.on_load` is a mechanism that can be used to defer the loading of code until it is actually needed. The snippet above can be changed to:
```ruby
ActiveSupport.on_load(:active_record) do
include MyActiveRecordHelper
end
```
This new snippet will only include `MyActiveRecordHelper` when `ActiveRecord::Base` is loaded.
### When are Hooks called?
In the Rails framework these hooks are called when a specific library is loaded. For example, when `ActionController::Base` is loaded, the `:action_controller_base` hook is called. This means that all `ActiveSupport.on_load` calls with `:action_controller_base` hooks will be called in the context of `ActionController::Base` (that means `self` will be an `ActionController::Base`).
### Modifying Code to Use Load Hooks
Modifying code is generally straightforward. If you have a line of code that refers to a Rails framework such as `ActiveRecord::Base` you can wrap that code in a load hook.
**Modifying calls to `include`**
```ruby
ActiveRecord::Base.include(MyActiveRecordHelper)
```
becomes
```ruby
ActiveSupport.on_load(:active_record) do
# self refers to ActiveRecord::Base here,
# so we can call .include
include MyActiveRecordHelper
end
```
**Modifying calls to `prepend`**
```ruby
ActionController::Base.prepend(MyActionControllerHelper)
```
becomes
```ruby
ActiveSupport.on_load(:action_controller_base) do
# self refers to ActionController::Base here,
# so we can call .prepend
prepend MyActionControllerHelper
end
```
**Modifying calls to class methods**
```ruby
ActiveRecord::Base.include_root_in_json = true
```
becomes
```ruby
ActiveSupport.on_load(:active_record) do
# self refers to ActiveRecord::Base here
self.include_root_in_json = true
end
```
### Available Load Hooks
These are the load hooks you can use in your own code. To hook into the initialization process of one of the following classes use the available hook.
| Class | Hook |
| -------------------------------------| ------------------------------------ |
| `ActionCable` | `action_cable` |
| `ActionCable::Channel::Base` | `action_cable_channel` |
| `ActionCable::Connection::Base` | `action_cable_connection` |
| `ActionCable::Connection::TestCase` | `action_cable_connection_test_case` |
| `ActionController::API` | `action_controller_api` |
| `ActionController::API` | `action_controller` |
| `ActionController::Base` | `action_controller_base` |
| `ActionController::Base` | `action_controller` |
| `ActionController::TestCase` | `action_controller_test_case` |
| `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` |
| `ActionDispatch::Response` | `action_dispatch_response` |
| `ActionDispatch::Request` | `action_dispatch_request` |
| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` |
| `ActionMailbox::Base` | `action_mailbox` |
| `ActionMailbox::InboundEmail` | `action_mailbox_inbound_email` |
| `ActionMailbox::Record` | `action_mailbox_record` |
| `ActionMailbox::TestCase` | `action_mailbox_test_case` |
| `ActionMailer::Base` | `action_mailer` |
| `ActionMailer::TestCase` | `action_mailer_test_case` |
| `ActionText::Content` | `action_text_content` |
| `ActionText::Record` | `action_text_record` |
| `ActionText::RichText` | `action_text_rich_text` |
| `ActionText::EncryptedRichText` | `action_text_encrypted_rich_text` |
| `ActionView::Base` | `action_view` |
| `ActionView::TestCase` | `action_view_test_case` |
| `ActiveJob::Base` | `active_job` |
| `ActiveJob::TestCase` | `active_job_test_case` |
| `ActiveModel::Model` | `active_model` |
| `ActiveRecord::Base` | `active_record` |
| `ActiveRecord::TestFixtures` | `active_record_fixtures` |
| `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter` | `active_record_postgresqladapter` |
| `ActiveRecord::ConnectionAdapters::Mysql2Adapter` | `active_record_mysql2adapter` |
| `ActiveRecord::ConnectionAdapters::TrilogyAdapter` | `active_record_trilogyadapter` |
| `ActiveRecord::ConnectionAdapters::SQLite3Adapter` | `active_record_sqlite3adapter` |
| `ActiveStorage::Attachment` | `active_storage_attachment` |
| `ActiveStorage::VariantRecord` | `active_storage_variant_record` |
| `ActiveStorage::Blob` | `active_storage_blob` |
| `ActiveStorage::Record` | `active_storage_record` |
| `ActiveSupport::TestCase` | `active_support_test_case` |
| `i18n` | `i18n` |
Initialization Events
---------------------

@ -1369,157 +1369,3 @@ module MyEngine
end
end
```
Load and Configuration Hooks
----------------------------
Rails code can often be referenced on load of an application. Rails is responsible for the load order of these frameworks, so when you load frameworks, such as `ActiveRecord::Base`, prematurely you are violating an implicit contract your application has with Rails. Moreover, by loading code such as `ActiveRecord::Base` on boot of your application you are loading entire frameworks which may slow down your boot time and could cause conflicts with load order and boot of your application.
Load and configuration hooks are the API that allow you to hook into this initialization process without violating the load contract with Rails. This will also mitigate boot performance degradation and avoid conflicts.
### Avoid Loading Rails Frameworks
Since Ruby is a dynamic language, some code will cause different Rails frameworks to load. Take this snippet for instance:
```ruby
ActiveRecord::Base.include(MyActiveRecordHelper)
```
This snippet means that when this file is loaded, it will encounter `ActiveRecord::Base`. This encounter causes Ruby to look for the definition of that constant and will require it. This causes the entire Active Record framework to be loaded on boot.
`ActiveSupport.on_load` is a mechanism that can be used to defer the loading of code until it is actually needed. The snippet above can be changed to:
```ruby
ActiveSupport.on_load(:active_record) do
include MyActiveRecordHelper
end
```
This new snippet will only include `MyActiveRecordHelper` when `ActiveRecord::Base` is loaded.
### When are Hooks called?
In the Rails framework these hooks are called when a specific library is loaded. For example, when `ActionController::Base` is loaded, the `:action_controller_base` hook is called. This means that all `ActiveSupport.on_load` calls with `:action_controller_base` hooks will be called in the context of `ActionController::Base` (that means `self` will be an `ActionController::Base`).
### Modifying Code to Use Load Hooks
Modifying code is generally straightforward. If you have a line of code that refers to a Rails framework such as `ActiveRecord::Base` you can wrap that code in a load hook.
**Modifying calls to `include`**
```ruby
ActiveRecord::Base.include(MyActiveRecordHelper)
```
becomes
```ruby
ActiveSupport.on_load(:active_record) do
# self refers to ActiveRecord::Base here,
# so we can call .include
include MyActiveRecordHelper
end
```
**Modifying calls to `prepend`**
```ruby
ActionController::Base.prepend(MyActionControllerHelper)
```
becomes
```ruby
ActiveSupport.on_load(:action_controller_base) do
# self refers to ActionController::Base here,
# so we can call .prepend
prepend MyActionControllerHelper
end
```
**Modifying calls to class methods**
```ruby
ActiveRecord::Base.include_root_in_json = true
```
becomes
```ruby
ActiveSupport.on_load(:active_record) do
# self refers to ActiveRecord::Base here
self.include_root_in_json = true
end
```
### Available Load Hooks
These are the load hooks you can use in your own code. To hook into the initialization process of one of the following classes use the available hook.
| Class | Hook |
| -------------------------------------| ------------------------------------ |
| `ActionCable` | `action_cable` |
| `ActionCable::Channel::Base` | `action_cable_channel` |
| `ActionCable::Connection::Base` | `action_cable_connection` |
| `ActionCable::Connection::TestCase` | `action_cable_connection_test_case` |
| `ActionController::API` | `action_controller_api` |
| `ActionController::API` | `action_controller` |
| `ActionController::Base` | `action_controller_base` |
| `ActionController::Base` | `action_controller` |
| `ActionController::TestCase` | `action_controller_test_case` |
| `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` |
| `ActionDispatch::Response` | `action_dispatch_response` |
| `ActionDispatch::Request` | `action_dispatch_request` |
| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` |
| `ActionMailbox::Base` | `action_mailbox` |
| `ActionMailbox::InboundEmail` | `action_mailbox_inbound_email` |
| `ActionMailbox::Record` | `action_mailbox_record` |
| `ActionMailbox::TestCase` | `action_mailbox_test_case` |
| `ActionMailer::Base` | `action_mailer` |
| `ActionMailer::TestCase` | `action_mailer_test_case` |
| `ActionText::Content` | `action_text_content` |
| `ActionText::Record` | `action_text_record` |
| `ActionText::RichText` | `action_text_rich_text` |
| `ActionText::EncryptedRichText` | `action_text_encrypted_rich_text` |
| `ActionView::Base` | `action_view` |
| `ActionView::TestCase` | `action_view_test_case` |
| `ActiveJob::Base` | `active_job` |
| `ActiveJob::TestCase` | `active_job_test_case` |
| `ActiveModel::Model` | `active_model` |
| `ActiveRecord::Base` | `active_record` |
| `ActiveRecord::TestFixtures` | `active_record_fixtures` |
| `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter` | `active_record_postgresqladapter` |
| `ActiveRecord::ConnectionAdapters::Mysql2Adapter` | `active_record_mysql2adapter` |
| `ActiveRecord::ConnectionAdapters::TrilogyAdapter` | `active_record_trilogyadapter` |
| `ActiveRecord::ConnectionAdapters::SQLite3Adapter` | `active_record_sqlite3adapter` |
| `ActiveStorage::Attachment` | `active_storage_attachment` |
| `ActiveStorage::VariantRecord` | `active_storage_variant_record` |
| `ActiveStorage::Blob` | `active_storage_blob` |
| `ActiveStorage::Record` | `active_storage_record` |
| `ActiveSupport::TestCase` | `active_support_test_case` |
| `i18n` | `i18n` |
### Available Configuration Hooks
Configuration hooks do not hook into any particular framework, but instead they run in context of the entire application.
| Hook | Use Case |
| ---------------------- | ---------------------------------------------------------------------------------- |
| `before_configuration` | First configurable block to run. Called before any initializers are run. |
| `before_initialize` | Second configurable block to run. Called before frameworks initialize. |
| `before_eager_load` | Third configurable block to run. Does not run if [`config.eager_load`][] set to false. |
| `after_initialize` | Last configurable block to run. Called after frameworks initialize. |
Configuration hooks can be called in the Engine class.
```ruby
module Blorgh
class Engine < ::Rails::Engine
config.before_configuration do
puts 'I am called before any initializers'
end
end
end
```
[`config.eager_load`]: configuring.html#config-eager-load