Skip to content

Commit 39f15e1

Browse files
committed
Iterates docs about autoloading during boot
1 parent 326d886 commit 39f15e1

File tree

1 file changed

+51
-12
lines changed

1 file changed

+51
-12
lines changed

guides/source/autoloading_and_reloading_constants.md

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ Reloading
176176

177177
Rails automatically reloads classes and modules if application files in the autoload paths change.
178178

179-
More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants just before the next request is processed. That way, application classes or modules used during that request will be autoloaded again, thus picking up their current implementation in the file system.
179+
More precisely, if the web server is running and application files have been modified, Rails unloads all autoloaded constants managed by the `main` autoloader just before the next request is processed. That way, application classes or modules used during that request will be autoloaded again, thus picking up their current implementation in the file system.
180180

181181
Reloading can be enabled or disabled. The setting that controls this behavior is `config.cache_classes`, which is false by default in `development` mode (reloading enabled), and true by default in `production` mode (reloading disabled).
182182

@@ -226,34 +226,73 @@ if `User` is reloaded, since `VipUser` is not, the superclass of `VipUser` is th
226226

227227
Bottom line: **do not cache reloadable classes or modules**.
228228

229-
### Autoloading when the application boots
229+
## Autoloading when the application boots
230230

231-
Applications can safely autoload constants during boot using a reloader callback:
231+
While booting, applications can autoload from the autoload once paths, which are managed by the `once` autoloader. Please check the section [`config.autoload_once_paths`](#config-autoload-once-paths) above.
232+
233+
However, you cannot autoload from the autoload paths, which are managed by the `main` autoloader. This applies to code in `config/initializers` as well as application or engines initializers.
234+
235+
Why? Initializers only run once, when the application boots. If you reboot the server, they run again in a new process, but reloading does not reboot the server, and initializers don't run again. Let's see the two main use cases.
236+
237+
### Use case 1: During boot, load reloadable code
238+
239+
Let's imagine `ApiGateway` is a reloadable class from `app/services` managed by the `main` autoloader and you need to configure its endpoint while the application boots:
232240

233241
```ruby
242+
# config/initializers/api_gateway_setup.rb
243+
ApiGateway.endpoint = "https://example.com" # DO NOT DO THIS
244+
```
245+
246+
a reloaded `ApiGateway` would have a `nil` endpoint, because the code above does not run again.
247+
248+
You can still set things up during boot, but you need to wrap them in a `to_prepare` block, which is runs on boot, and after each reload:
249+
250+
```ruby
251+
# config/initializers/api_gateway_setup.rb
234252
Rails.application.config.to_prepare do
235-
ApiGateway.endpoint = "https://example.com"
253+
ApiGateway.endpoint = "https://example.com" # CORRECT
236254
end
237255
```
238256

239-
That block runs when the application boots, and every time code is reloaded.
240-
241257
NOTE: For historical reasons, this callback may run twice. The code it executes must be idempotent.
242258

243-
However, if you do not need to reload the class, it is easier to define it in a directory which does not belong to the autoload paths. For instance, `lib` is an idiomatic choice. It does not belong to the autoload paths by default, but it does belong to `$LOAD_PATH`. Then, in the place the class is needed at boot time, just perform a regular `require` to load it.
259+
### Use case 2: During boot, load code that remains cached
244260

245-
For example, there is no point in defining reloadable Rack middleware, because changes would not be reflected in the instance stored in the middleware stack anyway. If `lib/my_app/middleware/foo.rb` defines a middleware class, then in `config/application.rb` you write:
261+
Some configurations take a class or module object, and they store it in a place that is not reloaded, for example, within the state of the framework.
262+
263+
One example is middleware:
246264

247265
```ruby
248-
require "my_app/middleware/foo"
249-
...
250266
config.middleware.use MyApp::Middleware::Foo
251267
```
252268

253-
To have changes in that middleware reflected, you need to restart the server.
269+
When you reload, the middleware stack is not affected, so, whatever object was stored in `MyApp::Middleware::Foo` at boot time remains there stale.
270+
271+
Another example is Active Job serializers:
272+
273+
```ruby
274+
# config/initializers/custom_serializers.rb
275+
Rails.application.config.active_job.custom_serializers << MoneySerializer
276+
```
277+
278+
Whatever `MoneySerializer` evaluates to during initialization gets pushed to the custom serializers. If that was reloadable, the initial object would be still within Active Job, not reflecting your changes.
279+
280+
Corollary: those classes or modules **cannot be reloadable**.
281+
282+
The easiest way to refer to those classes or modules during boot is to have them defined in a directory which does not belong to the autoload paths. For instance, `lib` is an idiomatic choice. It does not belong to the autoload paths by default, but it does belong to `$LOAD_PATH`. Just perform a regular `require` to load it and done.
254283

255-
Another possibility is to autoload from the autoload once paths. Please check the section [`config.autoload_once_paths`](#config-autoload-once-paths) above.
284+
As noted above, another option is to have the directory that defines them in the autoload once paths:
285+
286+
```
287+
# config/application.rb
288+
module YourApp
289+
class Application < Rails::Application
290+
config.autoload_once_paths << Rails.root.join('app', 'serializers')
291+
end
292+
end
293+
```
256294

295+
in this option, you can autoload serializers during initialization, because they are managed by the `once` autoloader.
257296

258297
Eager Loading
259298
-------------

0 commit comments

Comments
 (0)