aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/upgrading_ruby_on_rails.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/upgrading_ruby_on_rails.md')
-rw-r--r--guides/source/upgrading_ruby_on_rails.md321
1 files changed, 317 insertions, 4 deletions
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 7e4152aa51..05980d1614 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -85,13 +85,14 @@ Rails 6.1. You are encouraged to enable `config.force_ssl` to enforce HTTPS
connections throughout your application. If you need to exempt certain endpoints
from redirection, you can use `config.ssl_options` to configure that behavior.
-### Purpose in signed or encrypted cookie is now embedded within cookies
+### Purpose and expiry metadata is now embedded inside signed and encrypted cookies for increased security
+
+To improve security, Rails embeds the purpose and expiry metadata inside encrypted or signed cookies value.
-To improve security, Rails embeds the purpose information in encrypted or signed cookies value.
Rails can then thwart attacks that attempt to copy the signed/encrypted value
of a cookie and use it as the value of another cookie.
-This new embed information make those cookies incompatible with versions of Rails older than 6.0.
+This new embed metadata make those cookies incompatible with versions of Rails older than 6.0.
If you require your cookies to be read by Rails 5.2 and older, or you are still validating your 6.0 deploy and want
to be able to rollback set
@@ -133,6 +134,318 @@ Action Cable JavaScript API:
+ ActionCable.logger.enabled = false
```
+### `ActionDispatch::Response#content_type` now returns the Content-Type header without modification
+
+Previously, the return value of `ActionDispatch::Response#content_type` did NOT contain the charset part.
+This behavior has changed to include the previously omitted charset part as well.
+
+If you want just the MIME type, please use `ActionDispatch::Response#media_type` instead.
+
+Before:
+
+```ruby
+resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
+resp.content_type #=> "text/csv; header=present"
+```
+
+After:
+
+```ruby
+resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
+resp.content_type #=> "text/csv; header=present; charset=utf-16"
+resp.media_type #=> "text/csv"
+```
+
+### Autoloading
+
+The default configuration for Rails 6
+
+```ruby
+# config/application.rb
+
+config.load_defaults "6.0"
+```
+
+enables `zeitwerk` autoloading mode on CRuby. In that mode, autoloading, reloading, and eager loading are managed by [Zeitwerk](https://github.com/fxn/zeitwerk).
+
+#### Public API
+
+In general, applications do not need to use the API of Zeitwerk directly. Rails sets things up according to the existing contract: `config.autoload_paths`, `config.cache_classes`, etc.
+
+While applications should stick to that interface, the actual Zeitwerk loader object can be accessed as
+
+```ruby
+Rails.autoloaders.main
+```
+
+That may be handy if you need to preload STIs or configure a custom inflector, for example.
+
+#### Project Structure
+
+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 with the `zeitwerk:check` task:
+
+```
+$ bin/rails zeitwerk:check
+Hold on, I am eager loading the application.
+All is good!
+```
+
+#### require_dependency
+
+All known use cases of `require_dependency` have been eliminated, you should grep the project and delete them.
+
+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
+
+You can now robustly use constant paths in class and module definitions:
+
+```ruby
+# Autoloading in this class' body matches Ruby semantics now.
+class Admin::UsersController < ApplicationController
+ # ...
+end
+```
+
+A gotcha to be aware of is that, depending on the order of execution, the classic autoloader could sometimes be able to autoload `Foo::Wadus` in
+
+```ruby
+class Foo::Bar
+ Wadus
+end
+```
+
+That does not match Ruby semantics because `Foo` is not in the nesting, and won't work at all in `zeitwerk` mode. If you find such corner case you can use the qualified name `Foo::Wadus`:
+
+```ruby
+class Foo::Bar
+ Foo::Wadus
+end
+```
+
+or add `Foo` to the nesting:
+
+```ruby
+module Foo
+ class Bar
+ Wadus
+ end
+end
+```
+
+#### Concerns
+
+You can autoload and eager load from a standard structure like
+
+```
+app/models
+app/models/concerns
+```
+
+In that case, `app/models/concerns` is assumed to be a root directory (because it belongs to the autoload paths), and it is ignored as namespace. So, `app/models/concerns/foo.rb` should define `Foo`, not `Concerns::Foo`.
+
+The `Concerns::` namespace worked with the classic autoloader as a side-effect of the implementation, but it was not really an intended behavior. An application using `Concerns::` needs to rename those classes and modules to be able to run in `zeitwerk` mode.
+
+#### Having `app` in the autoload paths
+
+Some projects want something like `app/api/base.rb` to define `API::Base`, and add `app` to the autoload paths to accomplish that in `classic` mode. Since Rails adds all subdirectories of `app` to the autoload paths automatically, we have another situation in which there are nested root directories, so that setup no longer works. Similar principle we explained above with `concerns`.
+
+If you want to keep that structure, you'll need to delete the subdirectory from the autoload paths in an initializer:
+
+```ruby
+ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
+```
+
+#### Autoloaded Constants and Explicit Namespaces
+
+If a namespace is defined in a file, as `Hotel` is here:
+
+```
+app/models/hotel.rb # Defines Hotel.
+app/models/hotel/pricing.rb # Defines Hotel::Pricing.
+```
+
+the `Hotel` constant has to be set using the `class` or `module` keywords. For example:
+
+```ruby
+class Hotel
+end
+```
+
+is good.
+
+Alternatives like
+
+```ruby
+Hotel = Class.new
+```
+
+or
+
+```ruby
+Hotel = Struct.new
+```
+
+won't work, child objects like `Hotel::Pricing` won't be found.
+
+This restriction only applies to explicit namespaces. Classes and modules not defining a namespace can be defined using those idioms.
+
+#### One file, one constant (at the same top-level)
+
+In `classic` mode you could technically define several constants at the same top-level and have them all reloaded. For example, given
+
+```ruby
+# app/models/foo.rb
+
+class Foo
+end
+
+class Bar
+end
+```
+
+while `Bar` could not be autoloaded, autoloading `Foo` would mark `Bar` as autoloaded too. This is not the case in `zeitwerk` mode, you need to move `Bar` to its own file `bar.rb`. One file, one constant.
+
+This affects only to constants at the same top-level as in the example above. Inner classes and modules are fine. For example, consider
+
+```ruby
+# app/models/foo.rb
+
+class Foo
+ class InnerClass
+ end
+end
+```
+
+If the application reloads `Foo`, it will reload `Foo::InnerClass` too.
+
+#### Spring and the `test` Environment
+
+Spring reloads the application code if something changes. In the `test` environment you need to enable reloading for that to work:
+
+```ruby
+# config/environments/test.rb
+
+config.cache_classes = false
+```
+
+Otherwise you'll get this error:
+
+```
+reloading is disabled because config.cache_classes is true
+```
+
+#### Bootsnap
+
+Bootsnap should be at least version 1.4.2.
+
+In addition to that, Bootsnap needs to disable the iseq cache due to a bug in the interpreter if running Ruby 2.5. Please make sure to depend on at least Bootsnap 1.4.4 in that case.
+
+#### `config.add_autoload_paths_to_load_path`
+
+The new configuration point
+
+```ruby
+config.add_autoload_paths_to_load_path
+```
+
+is `true` by default for backwards compatibility, but allows you to opt-out from adding the autoload paths to `$LOAD_PATH`.
+
+This makes sense in most applications, since you never should require a file in `app/models`, for example, and Zeitwerk only uses absolute file names internally.
+
+By opting-out you optimize `$LOAD_PATH` lookups (less directories to check), and save Bootsnap work and memory consumption, since it does not need to build an index for these directories.
+
+#### Thread-safety
+
+In classic mode, constant autoloading is not thread-safe, though Rails has locks in place for example to make web requests thread-safe when autoloading is enabled, as it is common in `development` mode.
+
+Constant autoloading is thread-safe in `zeitwerk` mode. For example, you can now autoload in multi-threaded scripts executed by the `runner` command.
+
+#### Globs in config.autoload_paths
+
+Beware of configurations like
+
+```ruby
+config.autoload_paths += Dir["#{config.root}/lib/**/"]
+```
+
+Every element of `config.autoload_paths` should represent the top-level namespace (`Object`) and they cannot be nested in consequence (with the exception of `concerns` directories explained above).
+
+To fix this, just remove the wildcards:
+
+```ruby
+config.autoload_paths << "#{config.root}/lib"
+```
+
+#### Eager loading and autoloading are consistent
+
+In `classic` mode, if `app/models/foo.rb` defines `Bar`, you won't be able to autoload that file, but eager loading will work because it loads files recursively blindly. This can be a source of errors if you test things first eager loading, execution may fail later autoloading.
+
+In `zeitwerk` mode both loading modes are consistent, they fail and err in the same files.
+
+#### How to Use the Classic Autoloader in Rails 6
+
+Applications can load Rails 6 defaults and still use the classic autoloader by setting `config.autoloader` this way:
+
+```ruby
+# config/application.rb
+
+config.load_defaults "6.0"
+config.autoloader = :classic
+```
+
+### Active Storage assignment behavior change
+
+In Rails 5.2, assigning to a collection of attachments declared with `has_many_attached` appended new files:
+
+```ruby
+class User < ApplicationRecord
+ has_many_attached :highlights
+end
+
+user.highlights.attach(filename: "funky.jpg", ...)
+user.higlights.count # => 1
+
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.update!(highlights: [ blob ])
+
+user.highlights.count # => 2
+user.highlights.first.filename # => "funky.jpg"
+user.highlights.second.filename # => "town.jpg"
+```
+
+With the default configuration for Rails 6.0, assigning to a collection of attachments replaces existing files
+instead of appending to them. This matches Active Record behavior when assigning to a collection association:
+
+```ruby
+user.highlights.attach(filename: "funky.jpg", ...)
+user.highlights.count # => 1
+
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.update!(highlights: [ blob ])
+
+user.highlights.count # => 1
+user.highlights.first.filename # => "town.jpg"
+```
+
+`#attach` can be used to add new attachments without removing the existing ones:
+
+```ruby
+blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
+user.highlights.attach(blob)
+
+user.highlights.count # => 2
+user.highlights.first.filename # => "funky.jpg"
+user.highlights.second.filename # => "town.jpg"
+```
+
+Opt in to the new default behavior by setting `config.active_storage.replace_on_assign_to_many` to `true`.
+The old behavior will be deprecated in Rails 6.1 and removed in a subsequent release.
+
Upgrading from Rails 5.1 to Rails 5.2
-------------------------------------
@@ -1557,7 +1870,7 @@ config.assets.enabled = true
config.assets.version = '1.0'
```
-If your application is using an "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
+If your application is using an "/assets" route for a resource you may want to change the prefix used for assets to avoid conflicts:
```ruby
# Defaults to '/assets'