aboutsummaryrefslogtreecommitdiffstats
path: root/guides
diff options
context:
space:
mode:
Diffstat (limited to 'guides')
-rw-r--r--guides/source/autoloading_and_reloading_constants.md92
-rw-r--r--guides/source/documents.yaml2
-rw-r--r--guides/source/layouts_and_rendering.md46
-rw-r--r--guides/source/upgrading_ruby_on_rails.md32
4 files changed, 103 insertions, 69 deletions
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 444f241afc..8cd2d353de 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -9,13 +9,10 @@ After reading this guide, you will know:
* Autoloading modes
* Related Rails configuration
-* File system conventions
-* Autoloading in `zeitwerk` mode
-* Reloading in `zeitwerk` mode
-* Eager loading in `zeitwerk` mode
+* Project structure
+* Autoloading, reloading, and eager loading
* Single Table Inheritance
-* File path to constant path inflection
-
+* And more
--------------------------------------------------------------------------------
@@ -67,6 +64,22 @@ In `zeitwerk` mode, Rails uses [Zeitwerk](https://github.com/fxn/zeitwerk) inter
INFO. You do not configure Zeitwerk manually in a Rails application. Rather, you configure the application using the portable configuration points explained in this guide, and Rails translates that to Zeitwerk on your behalf.
+Project Structure
+-----------------
+
+In a Rails application file names have to match the constants they define, with directories acting as namespaces.
+
+For example, the file `app/helpers/users_helper.rb` should define `UsersHelper` and the file `app/controllers/admin/payments_controller.rb` should define `Admin::PaymentsController`.
+
+Rails configures Zeitwerk to inflect file names with `String#camelize`. For example, it expects that `app/controllers/users_controller.rb` defines the constant `UsersController` because
+
+```ruby
+"users_controller".camelize # => UsersController
+```
+
+If you need to customize any of these inflections, for example to add an acronym, please have a look at `config/initializers/inflections.rb`.
+
+Please, check the [Zeitwerk documentation](https://github.com/fxn/zeitwerk#file-structure) for further details.
Autoload paths
--------------
@@ -188,20 +201,71 @@ if the `Zeitwerk` constant is defined, Rails invokes `Zeitwerk::Loader.eager_loa
Single Table Inheritance
------------------------
-TODO
-
+Single Table Inheritance is a feature that doesn't play well with lazy loading. Reason is, its API generally needs to be able to enumerate the STI hierarchy to work correctly, whereas lazy loading defers loading classes until they are referenced. You can't enumerate what you haven't referenced yet.
-Inflector
----------
+In a sense, applications need to eager load STI hierarchies regardless of the loading mode.
-Rails configures Zeitwerk to inflect file names with `String#camelize`. For example, it expects that `users_controller.rb` defines the constant `UsersController` because
+Of course, if the application eager loads on boot, that is already accomplished. When it does not, it is in practice enough to instantiate the existing types in the database, which in development or test modes is usually fine. One way to do that is to throw this module into the `lib` directory:
```ruby
-"users_controller".camelize # => UsersController
+module StiPreload
+ unless Rails.application.config.eager_load
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :preloaded, instance_accessor: false
+ end
+
+ class_methods do
+ def descendants
+ preload_sti unless preloaded
+ super
+ end
+
+ # Constantizes all types present in the database. There might be more on
+ # disk, but that does not matter in practice as far as the STI API is
+ # concerned.
+ #
+ # Assumes store_full_sti_class is true, the default.
+ def preload_sti
+ types_in_db = \
+ base_class.
+ select(inheritance_column).
+ distinct.
+ pluck(inheritance_column).
+ compact.
+ each(&:constantize)
+
+ types_in_db.each do |type|
+ logger.debug("Preloading STI type #{type}")
+ type.constantize
+ end
+
+ self.preloaded = true
+ end
+ end
+ end
+end
```
-If you need to customize any of these inflections, for example to add an acronym, please have a look at `config/initializers/inflections.rb`.
+and then include it in the STI root classes of your project:
+
+```ruby
+# app/models/shape.rb
+require "sti_preload"
+class Shape < ApplicationRecord
+ include StiPreload # Only in the root class.
+end
+
+# app/models/polygon.rb
+class Polygon < Shape
+end
+
+# app/models/triangle.rb
+class Triangle < Polygon
+end
+```
Rails.autoloaders
-----------------
@@ -224,7 +288,7 @@ Rails.autoloaders.zeitwerk_enabled?
Opting Out
----------
-You can load Rails 6 defaults and still use the classic autoloader this way:
+Applications can load Rails 6 defaults and still use the classic autoloader this way:
```ruby
# config/application.rb
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index cbb65408d6..da11236064 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -145,7 +145,7 @@
-
name: Autoloading and Reloading Constants (Classic Mode)
url: autoloading_and_reloading_constants_classic_mode.html
- description: This guide documents how autoloading and reloading constants work in (Classic mode).
+ description: This guide documents how autoloading and reloading constants work (Classic mode).
-
name: "Caching with Rails: An Overview"
url: caching_with_rails.html
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 39935cd2ef..ce90a60e36 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -149,25 +149,6 @@ Rails knows that this view belongs to a different controller because of the embe
render template: "products/show"
```
-#### Rendering an Arbitrary File
-
-The `render` method can also use a view that's entirely outside of your application:
-
-```ruby
-render file: "/u/apps/warehouse_app/current/app/views/products/show"
-```
-
-The `:file` option takes an absolute file-system path. Of course, you need to have rights
-to the view that you're using to render the content.
-
-NOTE: Using the `:file` option in combination with users input can lead to security problems
-since an attacker could use this action to access security sensitive files in your file system.
-
-NOTE: By default, the file is rendered using the current layout.
-
-TIP: If you're running Rails on Microsoft Windows, you should use the `:file` option to
-render a file, because Windows filenames do not have the same format as Unix filenames.
-
#### Wrapping it up
The above three ways of rendering (rendering another template within the controller, rendering a template within another controller, and rendering an arbitrary file on the file system) are actually variants of the same action.
@@ -178,17 +159,9 @@ In fact, in the BooksController class, inside of the update action where we want
render :edit
render action: :edit
render "edit"
-render "edit.html.erb"
render action: "edit"
-render action: "edit.html.erb"
render "books/edit"
-render "books/edit.html.erb"
render template: "books/edit"
-render template: "books/edit.html.erb"
-render "/path/to/rails/app/views/books/edit"
-render "/path/to/rails/app/views/books/edit.html.erb"
-render file: "/path/to/rails/app/views/books/edit"
-render file: "/path/to/rails/app/views/books/edit.html.erb"
```
Which one you use is really a matter of style and convention, but the rule of thumb is to use the simplest one that makes sense for the code you are writing.
@@ -287,6 +260,23 @@ time.
NOTE: Unless overridden, your response returned from this render option will be
`text/plain`, as that is the default content type of Action Dispatch response.
+#### Rendering raw file
+
+Rails can render a raw file from an absolute path. This is useful for
+conditionally rendering static files like error pages.
+
+```ruby
+render file: "#{Rails.root}/public/404.html", layout: false
+```
+
+This renders the raw file (it doesn't support ERB or other handlers). By
+default it is rendered within the current layout.
+
+WARNING: Using the `:file` option in combination with users input can lead to security problems
+since an attacker could use this action to access security sensitive files in your file system.
+
+TIP: `send_file` is often a faster and better option if a layout isn't required.
+
#### Options for `render`
Calls to the `render` method generally accept five options:
@@ -303,7 +293,7 @@ Calls to the `render` method generally accept five options:
By default, Rails will serve the results of a rendering operation with the MIME content-type of `text/html` (or `application/json` if you use the `:json` option, or `application/xml` for the `:xml` option.). There are times when you might like to change this, and you can do so by setting the `:content_type` option:
```ruby
-render file: filename, content_type: "application/rss"
+render template: "feed", content_type: "application/rss"
```
##### The `:layout` Option
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 4192063e9b..f17955e022 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -184,41 +184,21 @@ That may be handy if you need to preload STIs or configure a custom inflector, f
If the application being upgraded autoloads correctly, the project structure should be already mostly compatible.
-However, `classic` mode infers file names from missing constant names (`underscore`), whereas `zeitwerk` mode infers constant names from file names (`camelize`). These helpers are not always inverse of each other, in particular if acronyms are involved. For instance, `"FOO".underscore` is `"foo"`, but `"foo".camelize` is `"Foo"`, not `"FOO"`. Compatibility can be checked by setting `classic` mode first temporarily:
+However, `classic` mode infers file names from missing constant names (`underscore`), whereas `zeitwerk` mode infers constant names from file names (`camelize`). These helpers are not always inverse of each other, in particular if acronyms are involved. For instance, `"FOO".underscore` is `"foo"`, but `"foo".camelize` is `"Foo"`, not `"FOO"`.
-```ruby
-# config/application.rb
-
-config.load_defaults "6.0"
-config.autoloader = :classic
-```
-
-and then running
+Compatibility can be checked with the `zeitwerk:check` task:
```
-bin/rails zeitwerk:check
+$ bin/rails zeitwerk:check
+Hold on, I am eager loading the application.
+All is good!
```
-When all is good, you can delete `config.autoloader = :classic`.
-
#### require_dependency
All known use cases of `require_dependency` have been eliminated, you should grep the project and delete them.
-In the case of STIs with a hierarchy of more than two levels, you can preload the leaves of the hierarchy in an initializer:
-
-```ruby
-# config/initializers/preload_stis.rb
-
-# By preloading leaves, the hierarchy is loaded upwards following
-# the references to superclasses in the class definitions.
-sti_leaves = %w(
- app/models/leaf1.rb
- app/models/leaf2.rb
- app/models/leaf3.rb
-)
-Rails.autoloaders.main.preload(sti_leaves)
-```
+If your application has STIs, please check their section in the guide [Autoloading and Reloading Constants (Zeitwerk Mode)](autoloading_and_reloading_constants.html#single-table-inheritance).
#### Qualified names in class and module definitions