diff options
Diffstat (limited to 'guides/source')
52 files changed, 2267 insertions, 756 deletions
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index e936644daf..15704acefe 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -73,7 +73,7 @@ $ ruby script/plugin install git://github.com/rails/rails_upgrade.git You can see an example of how that works at [Rails Upgrade is now an Official Plugin](http://omgbloglol.com/post/364624593/rails-upgrade-is-now-an-official-plugin) -Aside from Rails Upgrade tool, if you need more help, there are people on IRC and [rubyonrails-talk](http://groups.google.com/group/rubyonrails-talk) that are probably doing the same thing, possibly hitting the same issues. Be sure to blog your own experiences when upgrading so others can benefit from your knowledge! +Aside from Rails Upgrade tool, if you need more help, there are people on IRC and [rubyonrails-talk](https://groups.google.com/group/rubyonrails-talk) that are probably doing the same thing, possibly hitting the same issues. Be sure to blog your own experiences when upgrading so others can benefit from your knowledge! Creating a Rails 3.0 application -------------------------------- @@ -607,6 +607,6 @@ More Information: Credits ------- -See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails 3. Kudos to all of them. +See the [full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails 3. Kudos to all of them. Rails 3.0 Release Notes were compiled by [Mikel Lindsaar](http://lindsaar.net). diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index d6981656ee..09faeea614 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -240,7 +240,7 @@ Action Pack * Added `config.action_controller.include_all_helpers`. By default `helper :all` is done in `ActionController::Base`, which includes all the helpers by default. Setting `include_all_helpers` to `false` will result in including only application_helper and the helper corresponding to controller (like foo_helper for foo_controller). -* `url_for` and named url helpers now accept `:subdomain` and `:domain` as options. +* `url_for` and named URL helpers now accept `:subdomain` and `:domain` as options. * Added `Base.http_basic_authenticate_with` to do simple http basic authentication with a single class method call. @@ -291,9 +291,9 @@ Action Pack end ``` - You can restrict it to some actions by using `:only` or `:except`. Please read the docs at [`ActionController::Streaming`](http://api.rubyonrails.org/v3.1.0/classes/ActionController/Streaming.html) for more information. + You can restrict it to some actions by using `:only` or `:except`. Please read the docs at [`ActionController::Streaming`](https://api.rubyonrails.org/v3.1.0/classes/ActionController/Streaming.html) for more information. -* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused. +* The redirect route method now also accepts a hash of options which will only change the parts of the URL in question, or an object which responds to call, allowing for redirects to be reused. ### Action Dispatch @@ -556,6 +556,6 @@ Deprecations: Credits ------- -See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. +See the [full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. Rails 3.1 Release Notes were compiled by [Vijay Dev](https://github.com/vijaydev) diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index c9bc7f937b..e4b1b04681 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -74,7 +74,7 @@ Major Features * **Routing concerns** ([commit](https://github.com/rails/rails/commit/0dd24728a088fcb4ae616bb5d62734aca5276b1b)) - In the routing DSL, factor out common subroutes (`comments` from `/posts/1/comments` and `/videos/1/comments`). * **ActionController::Live** ([commit](https://github.com/rails/rails/commit/af0a9f9eefaee3a8120cfd8d05cbc431af376da3)) - Stream JSON with `response.stream`. * **Declarative ETags** ([commit](https://github.com/rails/rails/commit/ed5c938fa36995f06d4917d9543ba78ed506bb8d)) - Add controller-level etag additions that will be part of the action etag computation. -* **[Russian doll caching](http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works)** ([commit](https://github.com/rails/rails/commit/4154bf012d2bec2aae79e4a49aa94a70d3e91d49)) - Cache nested fragments of views. Each fragment expires based on a set of dependencies (a cache key). The cache key is usually a template version number and a model object. +* **[Russian doll caching](https://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works)** ([commit](https://github.com/rails/rails/commit/4154bf012d2bec2aae79e4a49aa94a70d3e91d49)) - Cache nested fragments of views. Each fragment expires based on a set of dependencies (a cache key). The cache key is usually a template version number and a model object. * **Turbolinks** ([commit](https://github.com/rails/rails/commit/e35d8b18d0649c0ecc58f6b73df6b3c8d0c6bb74)) - Serve only one initial HTML page. When the user navigates to another page, use pushState to update the URL and use AJAX to update the title and body. * **Decouple ActionView from ActionController** ([commit](https://github.com/rails/rails/commit/78b0934dd1bb84e8f093fb8ef95ca99b297b51cd)) - ActionView was decoupled from ActionPack and will be moved to a separated gem in Rails 4.1. * **Do not depend on ActiveModel** ([commit](https://github.com/rails/rails/commit/166dbaa7526a96fdf046f093f25b0a134b277a68)) - ActionPack no longer depends on ActiveModel. @@ -281,4 +281,4 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/4-0-stable/a Credits ------- -See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. +See the [full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md index b236f7ca24..d481a1f49b 100644 --- a/guides/source/4_1_release_notes.md +++ b/guides/source/4_1_release_notes.md @@ -159,7 +159,7 @@ By default, these preview classes live in `test/mailers/previews`. This can be configured using the `preview_path` option. See its -[documentation](http://api.rubyonrails.org/v4.1.0/classes/ActionMailer/Base.html#class-ActionMailer::Base-label-Previewing+emails) +[documentation](https://api.rubyonrails.org/v4.1.0/classes/ActionMailer/Base.html#class-ActionMailer::Base-label-Previewing+emails) for a detailed write up. ### Active Record enums @@ -231,7 +231,7 @@ extending it with `ActiveSupport::Concern`, then mixing it in to the `Todo` class. See its -[documentation](http://api.rubyonrails.org/v4.1.0/classes/Module/Concerning.html) +[documentation](https://api.rubyonrails.org/v4.1.0/classes/Module/Concerning.html) for a detailed write up and the intended use cases. ### CSRF protection from remote `<script>` tags @@ -727,6 +727,6 @@ Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) for +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 51d06bd07d..6bbd738826 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -154,9 +154,9 @@ remove_foreign_key :accounts, column: :owner_id ``` See the API documentation on -[add_foreign_key](http://api.rubyonrails.org/v4.2.0/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key) +[add_foreign_key](https://api.rubyonrails.org/v4.2.0/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key) and -[remove_foreign_key](http://api.rubyonrails.org/v4.2.0/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key) +[remove_foreign_key](https://api.rubyonrails.org/v4.2.0/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_foreign_key) for a full description. @@ -877,7 +877,7 @@ Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) for +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails the stable and robust framework it is today. Kudos to all of them. diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index d63921507d..b090c71a57 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -150,7 +150,7 @@ The type of an attribute is given the opportunity to change how dirty tracking is performed. See its -[documentation](http://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html) +[documentation](https://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html) for a detailed write up. @@ -1081,7 +1081,7 @@ Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) for +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md index a5a7eb4b2e..e885b1e42e 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -350,9 +350,9 @@ Please refer to the [Changelog][action-pack] for detailed changes. * Removed deprecated methods related to controller filters. ([Commit](https://github.com/rails/rails/commit/d7be30e8babf5e37a891522869e7b0191b79b757)) - + * Removed deprecated support to `:text` and `:nothing` in `render`. - ([Commit](https://github.com/rails/rails/commit/79a5ea9eadb4d43b62afacedc0706cbe88c54496), + ([Commit](https://github.com/rails/rails/commit/79a5ea9eadb4d43b62afacedc0706cbe88c54496), [Commit](https://github.com/rails/rails/commit/57e1c99a280bdc1b324936a690350320a1cd8111)) * Removed deprecated support for calling `HashWithIndifferentAccess` methods on `ActionController::Parameters`. @@ -644,7 +644,7 @@ Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) for +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/5_2_release_notes.md b/guides/source/5_2_release_notes.md index c5b914fffc..ac247bc3f9 100644 --- a/guides/source/5_2_release_notes.md +++ b/guides/source/5_2_release_notes.md @@ -615,6 +615,10 @@ Please refer to the [Changelog][active-record] for detailed changes. the parent class was getting deleted when the child was not. ([Commit](https://github.com/rails/rails/commit/b0fc04aa3af338d5a90608bf37248668d59fc881)) +* Idle database connections (previously just orphaned connections) are now + periodically reaped by the connection pool reaper. + ([Commit](https://github.com/rails/rails/pull/31221/commits/9027fafff6da932e6e64ddb828665f4b01fc8902)) + Active Model ------------ @@ -845,7 +849,7 @@ Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md index f3ed21dc45..14db71f2b5 100644 --- a/guides/source/6_0_release_notes.md +++ b/guides/source/6_0_release_notes.md @@ -5,7 +5,10 @@ Ruby on Rails 6.0 Release Notes Highlights in Rails 6.0: +* Action Mailbox +* Action Text * Parallel Testing +* Action Cable Testing These release notes cover only the major changes. To learn about various bug fixes and changes, please refer to the change logs or check out the [list of @@ -28,6 +31,29 @@ guide. Major Features -------------- +### Action Mailbox + +[Pull Request](https://github.com/rails/rails/pull/34786) + +[Action Mailbox](https://github.com/rails/rails/tree/6-0-stable/actionmailbox) allows you +to route incoming emails to controller-like mailboxes. +You can read more about Action Mailbox in the [Action Mailbox Basics](action_mailbox_basics.html) guide. + +### Action Text + +[Pull Request](https://github.com/rails/rails/pull/34873) + +[Action Text](https://github.com/rails/rails/tree/6-0-stable/actiontext) +brings rich text content and editing to Rails. It includes +the [Trix editor](https://trix-editor.org) that handles everything from formatting +to links to quotes to lists to embedded images and galleries. +The rich text content generated by the Trix editor is saved in its own +RichText model that's associated with any existing Active Record model in the application. +Any embedded images (or other attachments) are automatically stored using +Active Storage and associated with the included RichText model. + +You can read more about Action Text in the [Action Text Overview](action_text_overview.html) guide. + ### Parallel Testing [Pull Request](https://github.com/rails/rails/pull/31900) @@ -37,6 +63,13 @@ test suite. While forking processes is the default method, threading is supported as well. Running tests in parallel reduces the time it takes your entire test suite to run. +### Action Cable Testing + +[Pull Request](https://github.com/rails/rails/pull/33659) + +[Action Cable testing tools](testing.html#testing-action-cable) allow you to test your +Action Cable functionality at any level: connections, channels, broadcasts. + Railties -------- @@ -44,10 +77,73 @@ Please refer to the [Changelog][railties] for detailed changes. ### Removals +* Remove deprecated `after_bundle` helper inside plugins templates. + ([Commit](https://github.com/rails/rails/commit/4d51efe24e461a2a3ed562787308484cd48370c7)) + +* Remove deprecated support to `config.ru` that uses the application + class as argument of `run`. + ([Commit](https://github.com/rails/rails/commit/553b86fc751c751db504bcbe2d033eb2bb5b6a0b)) + +* Remove deprecated `environment` argument from the rails commands. + ([Commit](https://github.com/rails/rails/commit/e20589c9be09c7272d73492d4b0f7b24e5595571)) + +* Remove deprecated `capify!` method in generators and templates. + ([Commit](https://github.com/rails/rails/commit/9d39f81d512e0d16a27e2e864ea2dd0e8dc41b17)) + +* Remove deprecated `config.secret_token`. + ([Commit](https://github.com/rails/rails/commit/46ac5fe69a20d4539a15929fe48293e1809a26b0)) + ### Deprecations +* Deprecate passing Rack server name as a regular argument to `rails server`. + ([Pull Request](https://github.com/rails/rails/pull/32058)) + +* Deprecate support for using `HOST` environment to specify server IP. + ([Pull Request](https://github.com/rails/rails/pull/32540)) + +* Deprecate accessing hashes returned by `config_for` by non-symbol keys. + ([Pull Request](https://github.com/rails/rails/pull/35198)) + ### Notable changes +* Add an explicit option `--using` or `-u` for specifying the server for the + `rails server` command. + ([Pull Request](https://github.com/rails/rails/pull/32058)) + +* Add ability to see the output of `rails routes` in expanded format. + ([Pull Request](https://github.com/rails/rails/pull/32130)) + +* Run the seed database task using inline Active Job adapter. + ([Pull Request](https://github.com/rails/rails/pull/34953)) + +* Add a command `rails db:system:change` to change the database of the application. + ([Pull Request](https://github.com/rails/rails/pull/34832)) + +* Add `rails test:channels` command to test only Action Cable channels. + ([Pull Request](https://github.com/rails/rails/pull/34947)) + +* Introduce guard against DNS rebinding attacks. + ([Pull Request](https://github.com/rails/rails/pull/33145)) + +* Add ability to abort on failure while running generator commands. + ([Pull Request](https://github.com/rails/rails/pull/34420)) + +* Make Webpacker the default JavaScript compiler for Rails 6. + ([Pull Request](https://github.com/rails/rails/pull/33079)) + +* Add multiple database support for `rails db:migrate:status` command. + ([Pull Request](https://github.com/rails/rails/pull/34137)) + +* Add ability to use different migration paths from multiple databases in + the generators. + ([Pull Request](https://github.com/rails/rails/pull/34021)) + +* Add support for multi environment credentials. + ([Pull Request](https://github.com/rails/rails/pull/33521)) + +* Make `null_store` as default cache store in test environment. + ([Pull Request](https://github.com/rails/rails/pull/33773)) + Action Cable ------------ @@ -59,6 +155,22 @@ Please refer to the [Changelog][action-cable] for detailed changes. ### Notable changes +* The ActionCable javascript package has been converted from CoffeeScript + to ES2015, and we now publish the source code in the npm distribution. + + This allows ActionCable users to depend on the javascript source code + rather than the compiled code, which can produce smaller javascript bundles. + + This change includes some breaking changes to optional parts of the + ActionCable javascript API: + + - Configuration of the WebSocket adapter and logger adapter have been moved + from properties of `ActionCable` to properties of `ActionCable.adapters`. + + - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()` + methods have been removed and replaced with the property + `ActionCable.logger.enabled`. + Action Pack ----------- @@ -66,10 +178,78 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Removals +* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`. + ([Commit](https://github.com/rails/rails/commit/e70d3df7c9b05c129b0fdcca57f66eca316c5cfc)) + +* Remove deprecated methods in `ActionDispatch::TestResponse`: + `#success?` in favor of `#successful?`, `#missing?` in favor of `#not_found?`, + `#error?` in favor of `#server_error?` + ([Commit](https://github.com/rails/rails/commit/13ddc92e079e59a0b894e31bf5bb4fdecbd235d1)) + ### Deprecations +* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`. + ([Pull Request](https://github.com/rails/rails/pull/34039)) + +* Deprecate controller level `force_ssl` in favor of `config.force_ssl`. + ([Pull Request](https://github.com/rails/rails/pull/32277)) + ### Notable changes +* Raise an `ArgumentError` if a resource param contains a colon. + ([Pull Request](https://github.com/rails/rails/pull/35236)) + +* Allow `ActionDispatch::SystemTestCase.driven_by` to be called with a block + to define specific browser capabilities. + ([Pull Request](https://github.com/rails/rails/pull/35081)) + +* Add `ActionDispatch::HostAuthorization` middleware that guards against DNS rebinding + attacks. + ([Pull Request](https://github.com/rails/rails/pull/33145)) + +* Allow the use of `parsed_body` in `ActionController::TestCase`. + ([Pull Request](https://github.com/rails/rails/pull/34717)) + +* Raise an `ArgumentError` when multiple root routes exists in the same context + without `as:` naming specifications. + ([Pull Request](https://github.com/rails/rails/pull/34494)) + +* Allow the use of `#rescue_from` for handling parameter parsing errors. + ([Pull Request](https://github.com/rails/rails/pull/34341)) + +* Add `ActionController::Parameters#each_value` for iterating through parameters. + ([Pull Request](https://github.com/rails/rails/pull/33979)) + +* Encode Content-Disposition filenames on `send_data` and `send_file`. + ([Pull Request](https://github.com/rails/rails/pull/33829)) + +* Expose `ActionController::Parameters#each_key`. + ([Pull Request](https://github.com/rails/rails/pull/33758)) + +* Add purpose metadata to signed/encrypted cookies to prevent copying the value of + cookies into one another. + ([Pull Request](https://github.com/rails/rails/pull/32937)) + +* Raise `ActionController::RespondToMismatchError` for conflicting `respond_to` invocations. + ([Pull Request](https://github.com/rails/rails/pull/33446)) + +* Add an explicit error page for when a template is missing for a request format. + ([Pull Request](https://github.com/rails/rails/pull/29286)) + +* Introduce `ActionDispatch::DebugExceptions.register_interceptor`, a way to hook into + DebugExceptions and process the exception, before being rendered. + ([Pull Request](https://github.com/rails/rails/pull/23868)) + +* Output only one Content-Security-Policy nonce header value per request. + ([Pull Request](https://github.com/rails/rails/pull/32602)) + +* Add a module specifically for the Rails default headers configuration + that can be explicitly included in controllers. + ([Pull Request](https://github.com/rails/rails/pull/32484)) + +* Add `#dig` to `ActionDispatch::Request::Session`. + ([Pull Request](https://github.com/rails/rails/pull/32446)) + Action View ----------- @@ -77,10 +257,63 @@ Please refer to the [Changelog][action-view] for detailed changes. ### Removals +* Remove deprecated `image_alt` helper. + ([Commit](https://github.com/rails/rails/commit/60c8a03c8d1e45e48fcb1055ba4c49ed3d5ff78f)) + +* Remove an empty `RecordTagHelper` module from which the functionality + was already moved to the `record_tag_helper` gem. + ([Commit](https://github.com/rails/rails/commit/5c5ddd69b1e06fb6b2bcbb021e9b8dae17e7cb31)) + ### Deprecations +* Deprecate `ActionView::Template.finalize_compiled_template_methods` with + no replacement. + ([Pull Request](https://github.com/rails/rails/pull/35036)) + +* Deprecate `config.action_view.finalize_compiled_template_methods` with + no replacement. + ([Pull Request](https://github.com/rails/rails/pull/35036)) + +* Deprecate calling private model methods from the `options_from_collection_for_select` view helper. + ([Pull Request](https://github.com/rails/rails/pull/33547)) + ### Notable changes +* Clear ActionView cache in development only on file changes, speeding up + development mode. + ([Pull Request](https://github.com/rails/rails/pull/35629)) + +* Move all of the Rails npm packages into a `@rails` scope. + ([Pull Request](https://github.com/rails/rails/pull/34905)) + +* Only accept formats from registered Mime types. + ([Pull Request](https://github.com/rails/rails/pull/35604), [Pull Request](https://github.com/rails/rails/pull/35753)) + +* Add allocations to the template and partial rendering server output. + ([Pull Request](https://github.com/rails/rails/pull/34136)) + +* Add a `year_format` option to `date_select` tag, making it possible to + customize year names. + ([Pull Request](https://github.com/rails/rails/pull/32190)) + +* Add a `nonce: true` option for `javascript_include_tag` helper to + support automatic nonce generation for a Content Security Policy. + ([Pull Request](https://github.com/rails/rails/pull/32607)) + +* Add a `action_view.finalize_compiled_template_methods` configuration to disable or + enable `ActionView::Template` finalizers. + ([Pull Request](https://github.com/rails/rails/pull/32418)) + +* Extract the JS `confirm` call to its own, overridable method in `rails_ujs`. + ([Pull Request](https://github.com/rails/rails/pull/32404)) + +* Add a `action_controller.default_enforce_utf8` configuration option to handle + enforcing UTF-8 encoding. This defaults to `false`. + ([Pull Request](https://github.com/rails/rails/pull/32125)) + +* Add I18n key style support for locale keys to submit tags. + ([Pull Request](https://github.com/rails/rails/pull/26799)) + Action Mailer ------------- @@ -90,8 +323,41 @@ Please refer to the [Changelog][action-mailer] for detailed changes. ### Deprecations +* Deprecate `ActionMailer::Base.receive` in favor of Action Mailbox. + ([Commit](https://github.com/rails/rails/commit/e3f832a7433a291a51c5df397dc3dd654c1858cb)) + +* Deprecate `DeliveryJob` and `Parameterized::DeliveryJob` in favor of + `MailDeliveryJob`. + ([Pull Request](https://github.com/rails/rails/pull/34591)) + ### Notable changes +* Add `MailDeliveryJob` for delivering both regular and parameterized mail. + ([Pull Request](https://github.com/rails/rails/pull/34591)) + +* Allow custom email delivery jobs to work with the Action Mailer test assertions. + ([Pull Request](https://github.com/rails/rails/pull/34339)) + +* Allow specifying a template name for multipart emails with blocks instead of + using just the action name. + ([Pull Request](https://github.com/rails/rails/pull/22534)) + +* Add `perform_deliveries` to payload of `deliver.action_mailer` notification. + ([Pull Request](https://github.com/rails/rails/pull/33824)) + +* Improve the logging message when `perform_deliveries` is false to indicate + that sending of emails was skipped. + ([Pull Request](https://github.com/rails/rails/pull/33824)) + +* Allow calling `assert_enqueued_email_with` without block. + ([Pull Request](https://github.com/rails/rails/pull/33258)) + +* Perform the enqueued mail delivery jobs in the `assert_emails` block. + ([Pull Request](https://github.com/rails/rails/pull/32231)) + +* Allow `ActionMailer::Base` to unregister observers and interceptors. + ([Pull Request](https://github.com/rails/rails/pull/32207)) + Active Record ------------- @@ -99,10 +365,237 @@ Please refer to the [Changelog][active-record] for detailed changes. ### Removals +* Remove deprecated `#set_state` from the transaction object. + ([Commit](https://github.com/rails/rails/commit/6c745b0c5152a4437163a67707e02f4464493983)) + +* Remove deprecated `#supports_statement_cache?` from the database adapters. + ([Commit](https://github.com/rails/rails/commit/5f3ed8784383fb4eb0f9959f31a9c28a991b7553)) + +* Remove deprecated `#insert_fixtures` from the database adapters. + ([Commit](https://github.com/rails/rails/commit/400ba786e1d154448235f5f90183e48a1043eece)) + +* Remove deprecated `ActiveRecord::ConnectionAdapters::SQLite3Adapter#valid_alter_table_type?`. + ([Commit](https://github.com/rails/rails/commit/45b4d5f81f0c0ca72c18d0dea4a3a7b2ecc589bf)) + +* Remove support for passing the column name to `sum` when a block is passed. + ([Commit](https://github.com/rails/rails/commit/91ddb30083430622188d76eb9f29b78131df67f9)) + +* Remove support for passing the column name to `count` when a block is passed. + ([Commit](https://github.com/rails/rails/commit/67356f2034ab41305af7218f7c8b2fee2d614129)) + +* Remove support for delegation of missing methods in a relation to arel. + ([Commit](https://github.com/rails/rails/commit/d97980a16d76ad190042b4d8578109714e9c53d0)) + +* Remove support for delegating missing methods in a relation to private methods of the class. + ([Commit](https://github.com/rails/rails/commit/a7becf147afc85c354e5cfa519911a948d25fc4d)) + +* Remove support for specifying a timestamp name for `#cache_key`. + ([Commit](https://github.com/rails/rails/commit/0bef23e630f62e38f20b5ae1d1d5dbfb087050ea)) + +* Remove deprecated `ActiveRecord::Migrator.migrations_path=`. + ([Commit](https://github.com/rails/rails/commit/90d7842186591cae364fab3320b524e4d31a7d7d)) + +* Remove deprecated `expand_hash_conditions_for_aggregates`. + ([Commit](https://github.com/rails/rails/commit/27b252d6a85e300c7236d034d55ec8e44f57a83e)) + + ### Deprecations +* Deprecate mismatched case-sensitivity collation comparisons for uniqueness validator. + ([Commit](https://github.com/rails/rails/commit/9def05385f1cfa41924bb93daa187615e88c95b9)) + +* Deprecate using class level querying methods if the receiver scope has leaked. + ([Pull Request](https://github.com/rails/rails/pull/35280)) + +* Deprecate `config.activerecord.sqlite3.represent_boolean_as_integer`. + ([Commit](https://github.com/rails/rails/commit/f59b08119bc0c01a00561d38279b124abc82561b)) + +* Deprecate passing `migrations_paths` to `connection.assume_migrated_upto_version`. + ([Commit](https://github.com/rails/rails/commit/c1b14aded27e063ead32fa911aa53163d7cfc21a)) + +* Deprecate `ActiveRecord::Result#to_hash` in favor of `ActiveRecord::Result#to_a`. + ([Commit](https://github.com/rails/rails/commit/16510d609c601aa7d466809f3073ec3313e08937)) + +* Deprecate methods in `DatabaseLimits`: `column_name_length`, `table_name_length`, + `columns_per_table`, `indexes_per_table`, `columns_per_multicolumn_index`, + `sql_query_length`, and `joins_per_query`. + ([Commit](https://github.com/rails/rails/commit/e0a1235f7df0fa193c7e299a5adee88db246b44f)) + +* Deprecate `update_attributes`/`!` in favor of `update`/`!`. + ([Commit](https://github.com/rails/rails/commit/5645149d3a27054450bd1130ff5715504638a5f5)) + ### Notable changes +* Bump the minimum sqlite3 version to 1.4. + ([Pull Request](https://github.com/rails/rails/pull/35844)) + +* Add `rails db:prepare` to create a database if it doesn't exist, and run its migrations. + ([Pull Request](https://github.com/rails/rails/pull/35768)) + +* Add `after_save_commit` callback as shortcut for `after_commit :hook, on: [ :create, :update ]`. + ([Pull Request](https://github.com/rails/rails/pull/35804)) + +* Add `ActiveRecord::Relation#extract_associated` for extracting associated records from a relation. + ([Pull Request](https://github.com/rails/rails/pull/35784)) + +* Add `ActiveRecord::Relation#annotate` for adding SQL comments to ActiveRecord::Relation queries. + ([Pull Request](https://github.com/rails/rails/pull/35617)) + +* Add support for setting Optimizer Hints on databases. + ([Pull Request](https://github.com/rails/rails/pull/35615)) + +* Add `insert_all`/`insert_all!`/`upsert_all` methods for doing bulk inserts. + ([Pull Request](https://github.com/rails/rails/pull/35631)) + +* Add `rails db:seed:replant` that truncates tables of each database + for ther current environment and loads the seeds. + ([Pull Request](https://github.com/rails/rails/pull/34779)) + +* Add `reselect` method, which is a short-hand for `unscope(:select).select(fields)`. + ([Pull Request](https://github.com/rails/rails/pull/33611)) + +* Add negative scopes for all enum values. + ([Pull Request](https://github.com/rails/rails/pull/35381)) + +* Add `#destroy_by` and `#delete_by` for conditional removals. + ([Pull Request](https://github.com/rails/rails/pull/35316)) + +* Add the ability to automatically switch database connections. + ([Pull Request](https://github.com/rails/rails/pull/35073)) + +* Add the ability to prevent writes to a database for the duration of a block. + ([Pull Request](https://github.com/rails/rails/pull/34505)) + +* Add an API for switching connections to support multiple databases. + ([Pull Request](https://github.com/rails/rails/pull/34052)) + +* Make timestamps with precision the default for migrations. + ([Pull Request](https://github.com/rails/rails/pull/34970)) + +* Support `:size` option to change text and blob size in MySQL. + ([Pull Request](https://github.com/rails/rails/pull/35071)) + +* Set both the foreign key and the foreign type columns to NULL for + polymorphic associations on `dependent: :nullify` strategy. + ([Pull Request](https://github.com/rails/rails/pull/28078)) + +* Allow a permitted instance of `ActionController::Parameters` to be passed as an + argument to `ActiveRecord::Relation#exists?`. + ([Pull Request](https://github.com/rails/rails/pull/34891)) + +* Add support in `#where` for endless ranges introduced in Ruby 2.6. + ([Pull Request](https://github.com/rails/rails/pull/34906)) + +* Make `ROW_FORMAT=DYNAMIC` a default create table option for MySQL. + ([Pull Request](https://github.com/rails/rails/pull/34742)) + +* Add the ability to disable scopes generated by `ActiveRecord.enum`. + ([Pull Request](https://github.com/rails/rails/pull/34605/files)) + +* Make implicit ordering configurable for a column. + ([Pull Request](https://github.com/rails/rails/pull/34480)) + +* Bump the minimum PostgreSQL version to 9.3, dropping support for 9.1 and 9.2. + ([Pull Request](https://github.com/rails/rails/pull/34520)) + +* Make the values of an enum frozen, raising an error when attempting to modify them. + ([Pull Request](https://github.com/rails/rails/pull/34517)) + +* Make the SQL of `ActiveRecord::StatementInvalid` errors its own error property + and include SQL binds as a separate error property. + ([Pull Request](https://github.com/rails/rails/pull/34468)) + +* Add an `:if_not_exists` option to `create_table`. + ([Pull Request](https://github.com/rails/rails/pull/31382)) + +* Add support for multiple databases to `rails db:schema:cache:dump` + and `rails db:schema:cache:clear`. + ([Pull Request](https://github.com/rails/rails/pull/34181)) + +* Add support for hash and url configs in database hash of `ActiveRecord::Base.connected_to`. + ([Pull Request](https://github.com/rails/rails/pull/34196)) + +* Add support for default expressions and expression indexes for MySQL. + ([Pull Request](https://github.com/rails/rails/pull/34307)) + +* Add an `index` option for `change_table` migration helpers. + ([Pull Request](https://github.com/rails/rails/pull/23593)) + +* Fix `transaction` reverting for migrations. Previously, commands inside of a `transaction` + in a reverted migration ran uninverted. This change fixes that. + ([Pull Request](https://github.com/rails/rails/pull/31604)) + +* Allow `ActiveRecord::Base.configurations=` to be set with a symbolized hash. + ([Pull Request](https://github.com/rails/rails/pull/33968)) + +* Fix the counter cache to only update if the record is actually saved. + ([Pull Request](https://github.com/rails/rails/pull/33913)) + +* Add expression indexes support for the SQLite adapter. + ([Pull Request](https://github.com/rails/rails/pull/33874)) + +* Allow subclasses to redefine autosave callbacks for associated records. + ([Pull Request](https://github.com/rails/rails/pull/33378)) + +* Bump the minimum MySQL version to 5.5.8. + ([Pull Request](https://github.com/rails/rails/pull/33853)) + +* Use the utf8mb4 character set by default in MySQL. + ([Pull Request](https://github.com/rails/rails/pull/33608)) + +* Add the ability to filter out sensitive data in `#inspect` + ([Pull Request](https://github.com/rails/rails/pull/33756), [Pull Request](https://github.com/rails/rails/pull/34208)) + +* Change `ActiveRecord::Base.configurations` to return an object instead of a hash. + ([Pull Request](https://github.com/rails/rails/pull/33637)) + +* Add database configuration to disable advisory locks. + ([Pull Request](https://github.com/rails/rails/pull/33691)) + +* Update SQLite3 adapter `alter_table` method to restore foreign keys. + ([Pull Request](https://github.com/rails/rails/pull/33585)) + +* Allow the `:to_table` option of `remove_foreign_key` to be invertible. + ([Pull Request](https://github.com/rails/rails/pull/33530)) + +* Fix default value for mysql time types with specified precision. + ([Pull Request](https://github.com/rails/rails/pull/33280)) + +* Fix the `touch` option to behave consistently with `Persistence#touch` method. + ([Pull Request](https://github.com/rails/rails/pull/33107)) + +* Raise an exception for duplicate column definitions in Migrations. + ([Pull Request](https://github.com/rails/rails/pull/33029)) + +* Bump the minimum SQLite version to 3.8. + ([Pull Request](https://github.com/rails/rails/pull/32923)) + +* Fix parent records to not get saved with duplicate children records. + ([Pull Request](https://github.com/rails/rails/pull/32952)) + +* Ensure `Associations::CollectionAssociation#size` and `Associations::CollectionAssociation#empty?` + use loaded association ids if present. + ([Pull Request](https://github.com/rails/rails/pull/32617)) + +* Add support to preload associations of polymorphic associations when not all the records have the requested associations. + ([Commit](https://github.com/rails/rails/commit/75ef18c67c29b1b51314b6c8a963cee53394080b)) + +* Add `touch_all` method to `ActiveRecord::Relation`. + ([Pull Request](https://github.com/rails/rails/pull/31513)) + +* Add `ActiveRecord::Base.base_class?` predicate. + ([Pull Request](https://github.com/rails/rails/pull/32417)) + +* Add custom prefix/suffix options to `ActiveRecord::Store.store_accessor`. + ([Pull Request](https://github.com/rails/rails/pull/32306)) + +* Add `ActiveRecord::Base.create_or_find_by`/`!` to deal with the SELECT/INSERT race condition in + `ActiveRecord::Base.find_or_create_by`/`!` by leaning on unique constraints in the database. + ([Pull Request](https://github.com/rails/rails/pull/31989)) + +* Add `Relation#pick` as short-hand for single-value plucks. + ([Pull Request](https://github.com/rails/rails/pull/31941)) + Active Storage -------------- @@ -114,6 +607,10 @@ Please refer to the [Changelog][active-storage] for detailed changes. ### Notable changes +* Updating an attached model via `update` or `update!` with, say, + `@user.update!(images: [ … ])` now replaces the existing images instead of merely adding to them. + ([Pull Request](https://github.com/rails/rails/pull/33303)) + Active Model ------------ @@ -125,6 +622,18 @@ Please refer to the [Changelog][active-model] for detailed changes. ### Notable changes +* Add a configuration option to customize format of the `ActiveModel::Errors#full_message`. + ([Pull Request](https://github.com/rails/rails/pull/32956)) + +* Add support for configuring attribute name for `has_secure_password`. + ([Pull Request](https://github.com/rails/rails/pull/26764)) + +* Add `#slice!` method to `ActiveModel::Errors`. + ([Pull Request](https://github.com/rails/rails/pull/34489)) + +* Add `ActiveModel::Errors#of_kind?` to check presence of a specific error. + ([Pull Request](https://github.com/rails/rails/pull/34866)) + Active Support -------------- @@ -143,10 +652,56 @@ Please refer to the [Changelog][active-job] for detailed changes. ### Removals +* Remove support for Qu gem. + ([Pull Request](https://github.com/rails/rails/pull/32300)) + ### Deprecations ### Notable changes +* Add support for custom serializers for Active Job arguments. + ([Pull Request](https://github.com/rails/rails/pull/30941)) + +* Add support for executing Active Jobs in the timezone in which + they were enqueued. + ([Pull Request](https://github.com/rails/rails/pull/32085)) + +* Allow passing multiple exceptions to `retry_on`/`discard_on`. + ([Commit](https://github.com/rails/rails/commit/3110caecbebdad7300daaf26bfdff39efda99e25)) + +* Allow calling `assert_enqueued_with` and `assert_enqueued_email_with` without a block. + ([Pull Request](https://github.com/rails/rails/pull/33258)) + +* Wrap the notifications for `enqueue` and `enqueue_at` in the `around_enqueue` + callback instead of `after_enqueue` callback. + ([Pull Request](https://github.com/rails/rails/pull/33171)) + +* Allow calling `perform_enqueued_jobs` without a block. + ([Pull Request](https://github.com/rails/rails/pull/33626)) + +* Allow calling `assert_performed_with` without a block. + ([Pull Request](https://github.com/rails/rails/pull/33635)) + +* Add `:queue` option to job assertions and helpers. + ([Pull Request](https://github.com/rails/rails/pull/33635)) + +* Add hooks to Active Job around retries and discards. + ([Pull Request](https://github.com/rails/rails/pull/33751)) + +* Add a way to test for subset of arguments when performing jobs. + ([Pull Request](https://github.com/rails/rails/pull/33995)) + +* Include deserialized arguments in jobs returned by Active Job + test helpers. + ([Pull Request](https://github.com/rails/rails/pull/34204)) + +* Allow Active Job assertion helpers to accept Proc for `only` + keyword. + ([Pull Request](https://github.com/rails/rails/pull/34339)) + +* Drop microseconds and nanoseconds from the job arguments in assertion helpers. + ([Pull Request](https://github.com/rails/rails/pull/35713)) + Ruby on Rails Guides -------------------- @@ -154,11 +709,20 @@ Please refer to the [Changelog][guides] for detailed changes. ### Notable changes +* Add a section about troubleshooting of autoloading constants. + ([Commit](https://github.com/rails/rails/commit/c03bba4f1f03bad7dc034af555b7f2b329cf76f5)) + +* Add Action Mailbox Basics guide. + ([Pull Request](https://github.com/rails/rails/pull/34812)) + +* Add Action Text Overview guide. + ([Pull Request](https://github.com/rails/rails/pull/34878)) + Credits ------- See the -[full list of contributors to Rails](http://contributors.rubyonrails.org/) +[full list of contributors to Rails](https://contributors.rubyonrails.org/) for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them. diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index 2f602c3e0a..f1e2a0081f 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -27,6 +27,36 @@ client-side JavaScript framework and a server-side Ruby framework. You have access to your full domain model written with Active Record or your ORM of choice. +Terminology +----------- + +A single Action Cable server can handle multiple connection instances. It has one +connection instance per WebSocket connection. A single user may have multiple +WebSockets open to your application if they use multiple browser tabs or devices. +The client of a WebSocket connection is called the consumer. + +Each consumer can in turn subscribe to multiple cable channels. Each channel +encapsulates a logical unit of work, similar to what a controller does in +a regular MVC setup. For example, you could have a `ChatChannel` and +an `AppearancesChannel`, and a consumer could be subscribed to either +or to both of these channels. At the very least, a consumer should be subscribed +to one channel. + +When the consumer is subscribed to a channel, they act as a subscriber. +The connection between the subscriber and the channel is, surprise-surprise, +called a subscription. A consumer can act as a subscriber to a given channel +any number of times. For example, a consumer could subscribe to multiple chat rooms +at the same time. (And remember that a physical user may have multiple consumers, +one per tab/device open to your connection). + +Each channel can then again be streaming zero or more broadcastings. +A broadcasting is a pubsub link where anything transmitted by the broadcaster is +sent directly to the channel subscribers who are streaming that named broadcasting. + +As you can see, this is a fairly deep architectural stack. There's a lot of new +terminology to identify the new pieces, and on top of that, you're dealing +with both client and server side reflections of each unit. + What is Pub/Sub --------------- @@ -147,32 +177,50 @@ established using the following JavaScript, which is generated by default by Rai #### Connect Consumer ```js -// app/assets/javascripts/cable.js -//= require action_cable -//= require_self -//= require_tree ./channels +// app/javascript/channels/consumer.js +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the `rails generate channel` command. -(function() { - this.App || (this.App = {}); +import { createConsumer } from "@rails/actioncable" - App.cable = ActionCable.createConsumer(); -}).call(this); +export default createConsumer() ``` This will ready a consumer that'll connect against `/cable` on your server by default. The connection won't be established until you've also specified at least one subscription you're interested in having. +The consumer can optionally take an argument that specifies the URL to connect to. This +can be a string, or a function that returns a string that will be called when the +WebSocket is opened. + +```js +// Specify a different URL to connect to +createConsumer('https://ws.example.com/cable') + +// Use a function to dynamically generate the URL +createConsumer(getWebSocketURL) + +function getWebSocketURL { + const token = localStorage.get('auth-token') + return `https://ws.example.com/cable?token=${token}` +} +``` + #### Subscriber A consumer becomes a subscriber by creating a subscription to a given channel: -```coffeescript -# app/assets/javascripts/cable/subscriptions/chat.coffee -App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" } +```js +// app/javascript/channels/chat_channel.js +import consumer from "./consumer" + +consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }) -# app/assets/javascripts/cable/subscriptions/appearance.coffee -App.cable.subscriptions.create { channel: "AppearanceChannel" } +// app/javascript/channels/appearance_channel.js +import consumer from "./consumer" + +consumer.subscriptions.create({ channel: "AppearanceChannel" }) ``` While this creates the subscription, the functionality needed to respond to @@ -181,9 +229,12 @@ received data will be described later on. A consumer can act as a subscriber to a given channel any number of times. For example, a consumer could subscribe to multiple chat rooms at the same time: -```coffeescript -App.cable.subscriptions.create { channel: "ChatChannel", room: "1st Room" } -App.cable.subscriptions.create { channel: "ChatChannel", room: "2nd Room" } +```js +// app/javascript/channels/chat_channel.js +import consumer from "./consumer" + +consumer.subscriptions.create({ channel: "ChatChannel", room: "1st Room" }) +consumer.subscriptions.create({ channel: "ChatChannel", room: "2nd Room" }) ``` ## Client-Server Interactions @@ -256,24 +307,31 @@ When a consumer is subscribed to a channel, they act as a subscriber. This connection is called a subscription. Incoming messages are then routed to these channel subscriptions based on an identifier sent by the cable consumer. -```coffeescript -# app/assets/javascripts/cable/subscriptions/chat.coffee -# Assumes you've already requested the right to send web notifications -App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, - received: (data) -> - @appendLine(data) - - appendLine: (data) -> - html = @createLine(data) - $("[data-chat-room='Best Room']").append(html) - - createLine: (data) -> - """ - <article class="chat-line"> - <span class="speaker">#{data["sent_by"]}</span> - <span class="body">#{data["body"]}</span> - </article> - """ +```js +// app/javascript/channels/chat_channel.js +// Assumes you've already requested the right to send web notifications +import consumer from "./consumer" + +consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { + received(data) { + this.appendLine(data) + }, + + appendLine(data) { + const html = this.createLine(data) + const element = document.querySelector("[data-chat-room='Best Room']") + element.insertAdjacentHTML("beforeend", html) + }, + + createLine(data) { + return ` + <article class="chat-line"> + <span class="speaker">${data["sent_by"]}</span> + <span class="body">${data["body"]}</span> + </article> + ` + } +}) ``` ### Passing Parameters to Channels @@ -293,23 +351,30 @@ end An object passed as the first argument to `subscriptions.create` becomes the params hash in the cable channel. The keyword `channel` is required: -```coffeescript -# app/assets/javascripts/cable/subscriptions/chat.coffee -App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, - received: (data) -> - @appendLine(data) - - appendLine: (data) -> - html = @createLine(data) - $("[data-chat-room='Best Room']").append(html) - - createLine: (data) -> - """ - <article class="chat-line"> - <span class="speaker">#{data["sent_by"]}</span> - <span class="body">#{data["body"]}</span> - </article> - """ +```js +// app/javascript/channels/chat_channel.js +import consumer from "./consumer" + +consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { + received(data) { + this.appendLine(data) + }, + + appendLine(data) { + const html = this.createLine(data) + const element = document.querySelector("[data-chat-room='Best Room']") + element.insertAdjacentHTML("beforeend", html) + }, + + createLine(data) { + return ` + <article class="chat-line"> + <span class="speaker">${data["sent_by"]}</span> + <span class="body">${data["body"]}</span> + </article> + ` + } +}) ``` ```ruby @@ -340,13 +405,17 @@ class ChatChannel < ApplicationCable::Channel end ``` -```coffeescript -# app/assets/javascripts/cable/subscriptions/chat.coffee -App.chatChannel = App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }, - received: (data) -> - # data => { sent_by: "Paul", body: "This is a cool chat app." } +```js +// app/javascript/channels/chat_channel.js +import consumer from "./consumer" + +const chatChannel = consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { + received(data) { + // data => { sent_by: "Paul", body: "This is a cool chat app." } + } +} -App.chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." }) +chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." }) ``` The rebroadcast will be received by all connected clients, _including_ the @@ -396,46 +465,69 @@ appear/disappear API could be backed by Redis, a database, or whatever else. Create the client-side appearance channel subscription: -```coffeescript -# app/assets/javascripts/cable/subscriptions/appearance.coffee -App.cable.subscriptions.create "AppearanceChannel", - # Called when the subscription is ready for use on the server. - connected: -> - @install() - @appear() - - # Called when the WebSocket connection is closed. - disconnected: -> - @uninstall() - - # Called when the subscription is rejected by the server. - rejected: -> - @uninstall() - - appear: -> - # Calls `AppearanceChannel#appear(data)` on the server. - @perform("appear", appearing_on: $("main").data("appearing-on")) - - away: -> - # Calls `AppearanceChannel#away` on the server. - @perform("away") - - - buttonSelector = "[data-behavior~=appear_away]" - - install: -> - $(document).on "turbolinks:load.appearance", => - @appear() - - $(document).on "click.appearance", buttonSelector, => - @away() - false - - $(buttonSelector).show() - - uninstall: -> - $(document).off(".appearance") - $(buttonSelector).hide() +```js +// app/javascript/channels/appearance_channel.js +import consumer from "./consumer" + +consumer.subscriptions.create("AppearanceChannel", { + // Called once when the subscription is created. + initialized() { + this.update = this.update.bind(this) + }, + + // Called when the subscription is ready for use on the server. + connected() { + this.install() + this.update() + }, + + // Called when the WebSocket connection is closed. + disconnected() { + this.uninstall() + }, + + // Called when the subscription is rejected by the server. + rejected() { + this.uninstall() + }, + + update() { + this.documentIsActive ? this.appear() : this.away() + }, + + appear() { + // Calls `AppearanceChannel#appear(data)` on the server. + this.perform("appear", { appearing_on: this.appearingOn }) + }, + + away() { + // Calls `AppearanceChannel#away` on the server. + this.perform("away") + }, + + install() { + window.addEventListener("focus", this.update) + window.addEventListener("blur", this.update) + document.addEventListener("turbolinks:load", this.update) + document.addEventListener("visibilitychange", this.update) + }, + + uninstall() { + window.removeEventListener("focus", this.update) + window.removeEventListener("blur", this.update) + document.removeEventListener("turbolinks:load", this.update) + document.removeEventListener("visibilitychange", this.update) + }, + + get documentIsActive() { + return document.visibilityState == "visible" && document.hasFocus() + }, + + get appearingOn() { + const element = document.querySelector("[data-appearing-on]") + return element ? element.getAttribute("data-appearing-on") : null + } +}) ``` ##### Client-Server Interaction @@ -445,16 +537,16 @@ ActionCable.createConsumer("ws://cable.example.com")`. (`cable.js`). The **Server** identifies this connection by `current_user`. 2. **Client** subscribes to the appearance channel via -`App.cable.subscriptions.create(channel: "AppearanceChannel")`. (`appearance.coffee`) +`consumer.subscriptions.create({ channel: "AppearanceChannel" })`. (`appearance_channel.js`) 3. **Server** recognizes a new subscription has been initiated for the appearance channel and runs its `subscribed` callback, calling the `appear` method on `current_user`. (`appearance_channel.rb`) 4. **Client** recognizes that a subscription has been established and calls -`connected` (`appearance.coffee`) which in turn calls `@install` and `@appear`. -`@appear` calls `AppearanceChannel#appear(data)` on the server, and supplies a -data hash of `{ appearing_on: $("main").data("appearing-on") }`. This is +`connected` (`appearance_channel.js`) which in turn calls `install` and `appear`. +`appear` calls `AppearanceChannel#appear(data)` on the server, and supplies a +data hash of `{ appearing_on: this.appearingOn }`. This is possible because the server-side channel instance automatically exposes all public methods declared on the class (minus the callbacks), so that these can be reached as remote procedure calls via a subscription's `perform` method. @@ -488,13 +580,17 @@ end Create the client-side web notifications channel subscription: -```coffeescript -# app/assets/javascripts/cable/subscriptions/web_notifications.coffee -# Client-side which assumes you've already requested -# the right to send web notifications. -App.cable.subscriptions.create "WebNotificationsChannel", - received: (data) -> - new Notification data["title"], body: data["body"] +```js +// app/javascript/channels/web_notifications_channel.js +// Client-side which assumes you've already requested +// the right to send web notifications. +import consumer from "./consumer" + +consumer.subscriptions.create("WebNotificationsChannel", { + received(data) { + new Notification(data["title"], body: data["body"]) + } +}) ``` Broadcast content to a web notification channel instance from elsewhere in your @@ -574,7 +670,7 @@ passed to the server config as an array. The origins can be instances of strings or regular expressions, against which a check for the match will be performed. ```ruby -config.action_cable.allowed_request_origins = ['http://rubyonrails.com', %r{http://ruby.*}] +config.action_cable.allowed_request_origins = ['https://rubyonrails.com', %r{http://ruby.*}] ``` To disable and allow requests from any origin: @@ -592,6 +688,21 @@ To configure the URL, add a call to `action_cable_meta_tag` in your HTML layout HEAD. This uses a URL or path typically set via `config.action_cable.url` in the environment configuration files. +### Worker Pool Configuration + +The worker pool is used to run connection callbacks and channel actions in +isolation from the server's main thread. Action Cable allows the application +to configure the number of simultaneously processed threads in the worker pool. + +```ruby +config.action_cable.worker_pool_size = 4 +``` + +Also, note that your server must provide at least the same number of database +connections as you have workers. The default worker pool size is set to 4, so +that means you have to make at least 4 database connections available. + You can change that in `config/database.yml` through the `pool` attribute. + ### Other Configurations The other common option to configure is the log tags applied to the @@ -609,11 +720,6 @@ config.action_cable.log_tags = [ For a full list of all configuration options, see the `ActionCable::Server::Configuration` class. -Also, note that your server must provide at least the same number of database -connections as you have workers. The default worker pool size is set to 4, so -that means you have to make at least that available. You can change that in -`config/database.yml` through the `pool` attribute. - ## Running Standalone Cable Servers ### In App @@ -629,10 +735,9 @@ class Application < Rails::Application end ``` -You can use `App.cable = ActionCable.createConsumer()` to connect to the cable -server if `action_cable_meta_tag` is invoked in the layout. A custom path is -specified as first argument to `createConsumer` (e.g. `App.cable = -ActionCable.createConsumer("/websocket")`). +You can use `ActionCable.createConsumer()` to connect to the cable +server if `action_cable_meta_tag` is invoked in the layout. Otherwise, A path is +specified as first argument to `createConsumer` (e.g. `ActionCable.createConsumer("/websocket")`). For every instance of your server you create and for every worker your server spawns, you will also have a new instance of Action Cable, but the use of Redis @@ -690,3 +795,8 @@ internally, irrespective of whether the application server is multi-threaded or Accordingly, Action Cable works with popular servers like Unicorn, Puma, and Passenger. + +## Testing + +You can find detailed instructions on how to test your Action Cable functionality in the +[testing guide](testing.html#testing-action-cable). diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index aa746e4731..f8367283fc 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -61,7 +61,7 @@ end The [Layouts & Rendering Guide](layouts_and_rendering.html) explains this in more detail. -`ApplicationController` inherits from `ActionController::Base`, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the [API documentation](http://api.rubyonrails.org/classes/ActionController.html) or in the source itself. +`ApplicationController` inherits from `ActionController::Base`, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the [API documentation](https://api.rubyonrails.org/classes/ActionController.html) or in the source itself. Only public methods are callable as actions. It is a best practice to lower the visibility of methods (with `private` or `protected`) which are not intended to be actions, like auxiliary methods or filters. @@ -157,7 +157,7 @@ And, assuming that you're sending the data to `CompaniesController`, it would th { name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } } ``` -You can customize the name of the key or specific parameters you want to wrap by consulting the [API documentation](http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html) +You can customize the name of the key or specific parameters you want to wrap by consulting the [API documentation](https://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html) NOTE: Support for parsing XML parameters has been extracted into a gem named `actionpack-xml_parser`. @@ -212,7 +212,7 @@ class PeopleController < ActionController::Base end # This will pass with flying colors as long as there's a person key - # in the parameters, otherwise it'll raise a + # in the parameters, otherwise it'll raise an # ActionController::ParameterMissing exception, which will get # caught by ActionController::Base and turned into a 400 Bad # Request error. @@ -469,7 +469,7 @@ To reset the entire session, use `reset_session`. The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for passing error messages etc. -It is accessed in much the same way as the session, as a hash (it's a [FlashHash](http://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html) instance). +It is accessed in much the same way as the session, as a hash (it's a [FlashHash](https://api.rubyonrails.org/classes/ActionDispatch/Flash/FlashHash.html) instance). Let's use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request: @@ -591,7 +591,7 @@ Rails also provides a signed cookie jar and an encrypted cookie jar for storing sensitive data. The signed cookie jar appends a cryptographic signature on the cookie values to protect their integrity. The encrypted cookie jar encrypts the values in addition to signing them, so that they cannot be read by the end user. -Refer to the [API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html) +Refer to the [API documentation](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html) for more details. These special cookie jars use a serializer to serialize the assigned values into @@ -814,7 +814,7 @@ In every controller there are two accessor methods pointing to the request and t ### The `request` Object -The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the [Rails API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Request.html) and [Rack Documentation](http://www.rubydoc.info/github/rack/rack/Rack/Request). Among the properties that you can access on this object are: +The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the [Rails API documentation](https://api.rubyonrails.org/classes/ActionDispatch/Request.html) and [Rack Documentation](https://www.rubydoc.info/github/rack/rack/Rack/Request). Among the properties that you can access on this object are: | Property of `request` | Purpose | | ----------------------------------------- | -------------------------------------------------------------------------------- | @@ -836,7 +836,7 @@ Rails collects all of the parameters sent along with the request in the `params` ### The `response` Object -The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values. To get a full list of the available methods, refer to the [Rails API documentation](http://api.rubyonrails.org/classes/ActionDispatch/Response.html) and [Rack Documentation](http://www.rubydoc.info/github/rack/rack/Rack/Response). +The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values. To get a full list of the available methods, refer to the [Rails API documentation](https://api.rubyonrails.org/classes/ActionDispatch/Response.html) and [Rack Documentation](https://www.rubydoc.info/github/rack/rack/Rack/Response). | Property of `response` | Purpose | | ---------------------- | --------------------------------------------------------------------------------------------------- | diff --git a/guides/source/action_mailbox_basics.md b/guides/source/action_mailbox_basics.md new file mode 100644 index 0000000000..de92401226 --- /dev/null +++ b/guides/source/action_mailbox_basics.md @@ -0,0 +1,376 @@ +**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.** + +Action Mailbox Basics +===================== + +This guide provides you with all you need to get started in receiving +emails to your application. + +After reading this guide, you will know: + +* How to receive email within a Rails application. +* How to configure Action Mailbox. +* How to generate and route emails to a mailbox. +* How to test incoming emails. + +-------------------------------------------------------------------------------- + +Introduction +------------ + +Action Mailbox routes incoming emails to controller-like mailboxes for +processing in Rails. It ships with ingresses for Mailgun, Mandrill, Postmark, +and SendGrid. You can also handle inbound mails directly via the built-in Exim, +Postfix, and Qmail ingresses. + +The inbound emails are turned into `InboundEmail` records using Active Record +and feature lifecycle tracking, storage of the original email on cloud storage +via Active Storage, and responsible data handling with +on-by-default incineration. + +These inbound emails are routed asynchronously using Active Job to one or +several dedicated mailboxes, which are capable of interacting directly +with the rest of your domain model. + +## Setup + +Install migrations needed for `InboundEmail` and ensure Active Storage is set up: + +```bash +$ rails action_mailbox:install +$ rails db:migrate +``` + +## Configuration + +### Exim + +Tell Action Mailbox to accept emails from an SMTP relay: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :relay +``` + +Generate a strong password that Action Mailbox can use to authenticate requests to the relay ingress. + +Use `rails credentials:edit` to add the password to your application's encrypted credentials under +`action_mailbox.ingress_password`, where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + ingress_password: ... +``` + +Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` environment variable. + +Configure Exim to pipe inbound emails to `bin/rails action_mailbox:ingress:exim`, +providing the `URL` of the relay ingress and the `INGRESS_PASSWORD` you +previously generated. If your application lived at `https://example.com`, the +full command would look like this: + +```shell +bin/rails action_mailbox:ingress:exim URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=... +``` + +### Mailgun + +Give Action Mailbox your +[Mailgun API key](https://help.mailgun.com/hc/en-us/articles/203380100-Where-can-I-find-my-API-key-and-SMTP-credentials) +so it can authenticate requests to the Mailgun ingress. + +Use `rails credentials:edit` to add your API key to your application's +encrypted credentials under `action_mailbox.mailgun_api_key`, +where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + mailgun_api_key: ... +``` + +Alternatively, provide your API key in the `MAILGUN_INGRESS_API_KEY` environment +variable. + +Tell Action Mailbox to accept emails from Mailgun: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :mailgun +``` + +[Configure Mailgun](https://documentation.mailgun.com/en/latest/user_manual.html#receiving-forwarding-and-storing-messages) +to forward inbound emails to `/rails/action_mailbox/mailgun/inbound_emails/mime`. +If your application lived at `https://example.com`, you would specify the +fully-qualified URL `https://example.com/rails/action_mailbox/mailgun/inbound_emails/mime`. + +### Mandrill + +Give Action Mailbox your Mandrill API key so it can authenticate requests to +the Mandrill ingress. + +Use `rails credentials:edit` to add your API key to your application's +encrypted credentials under `action_mailbox.mandrill_api_key`, +where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + mandrill_api_key: ... +``` + +Alternatively, provide your API key in the `MANDRILL_INGRESS_API_KEY` +environment variable. + +Tell Action Mailbox to accept emails from Mandrill: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :mandrill +``` + +[Configure Mandrill](https://mandrill.zendesk.com/hc/en-us/articles/205583197-Inbound-Email-Processing-Overview) +to route inbound emails to `/rails/action_mailbox/mandrill/inbound_emails`. +If your application lived at `https://example.com`, you would specify +the fully-qualified URL `https://example.com/rails/action_mailbox/mandrill/inbound_emails`. + +### Postfix + +Tell Action Mailbox to accept emails from an SMTP relay: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :relay +``` + +Generate a strong password that Action Mailbox can use to authenticate requests to the relay ingress. + +Use `rails credentials:edit` to add the password to your application's encrypted credentials under +`action_mailbox.ingress_password`, where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + ingress_password: ... +``` + +Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` environment variable. + +[Configure Postfix](https://serverfault.com/questions/258469/how-to-configure-postfix-to-pipe-all-incoming-email-to-a-script) +to pipe inbound emails to `bin/rails action_mailbox:ingress:postfix`, providing +the `URL` of the Postfix ingress and the `INGRESS_PASSWORD` you previously +generated. If your application lived at `https://example.com`, the full command +would look like this: + +```shell +$ bin/rails action_mailbox:ingress:postfix URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=... +``` + +### Postmark + +Tell Action Mailbox to accept emails from Postmark: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :postmark +``` + +Generate a strong password that Action Mailbox can use to authenticate +requests to the Postmark ingress. + +Use `rails credentials:edit` to add the password to your application's +encrypted credentials under `action_mailbox.ingress_password`, +where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + ingress_password: ... +``` + +Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` +environment variable. + +[Configure Postmark inbound webhook](https://postmarkapp.com/manual#configure-your-inbound-webhook-url) +to forward inbound emails to `/rails/action_mailbox/postmark/inbound_emails` with the username `actionmailbox` +and the password you previously generated. If your application lived at `https://example.com`, you would +configure Postmark with the following fully-qualified URL: + +``` +https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails +``` + +NOTE: When configuring your Postmark inbound webhook, be sure to check the box labeled **"Include raw email content in JSON payload"**. +Action Mailbox needs the raw email content to work. + +### Qmail + +Tell Action Mailbox to accept emails from an SMTP relay: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :relay +``` + +Generate a strong password that Action Mailbox can use to authenticate requests to the relay ingress. + +Use `rails credentials:edit` to add the password to your application's encrypted credentials under +`action_mailbox.ingress_password`, where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + ingress_password: ... +``` + +Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` environment variable. + +Configure Qmail to pipe inbound emails to `bin/rails action_mailbox:ingress:qmail`, +providing the `URL` of the relay ingress and the `INGRESS_PASSWORD` you +previously generated. If your application lived at `https://example.com`, the +full command would look like this: + +```shell +bin/rails action_mailbox:ingress:qmail URL=https://example.com/rails/action_mailbox/relay/inbound_emails INGRESS_PASSWORD=... +``` + +### SendGrid + +Tell Action Mailbox to accept emails from SendGrid: + +```ruby +# config/environments/production.rb +config.action_mailbox.ingress = :sendgrid +``` + +Generate a strong password that Action Mailbox can use to authenticate +requests to the SendGrid ingress. + +Use `rails credentials:edit` to add the password to your application's +encrypted credentials under `action_mailbox.ingress_password`, +where Action Mailbox will automatically find it: + +```yaml +action_mailbox: + ingress_password: ... +``` + +Alternatively, provide the password in the `RAILS_INBOUND_EMAIL_PASSWORD` +environment variable. + +[Configure SendGrid Inbound Parse](https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/) +to forward inbound emails to +`/rails/action_mailbox/sendgrid/inbound_emails` with the username `actionmailbox` +and the password you previously generated. If your application lived at `https://example.com`, +you would configure SendGrid with the following URL: + +``` +https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/inbound_emails +``` + +NOTE: When configuring your SendGrid Inbound Parse webhook, be sure to check the box labeled **“Post the raw, full MIME message.”** Action Mailbox needs the raw MIME message to work. + +## Examples + +Configure basic routing: + +```ruby +# app/mailboxes/application_mailbox.rb +class ApplicationMailbox < ActionMailbox::Base + routing /^save@/i => :forwards + routing /@replies\./i => :replies +end +``` + +Then set up a mailbox: + +```ruby +# Generate new mailbox +$ bin/rails generate mailbox forwards +``` + +```ruby +# app/mailboxes/forwards_mailbox.rb +class ForwardsMailbox < ApplicationMailbox + # Callbacks specify prerequisites to processing + before_processing :require_forward + + def process + if forwarder.buckets.one? + record_forward + else + stage_forward_and_request_more_details + end + end + + private + def require_forward + unless message.forward? + # Use Action Mailers to bounce incoming emails back to sender – this halts processing + bounce_with Forwards::BounceMailer.missing_forward( + inbound_email, forwarder: forwarder + ) + end + end + + def forwarder + @forwarder ||= Person.where(email_address: mail.from) + end + + def record_forward + forwarder.buckets.first.record \ + Forward.new forwarder: forwarder, subject: message.subject, content: mail.content + end + + def stage_forward_and_request_more_details + Forwards::RoutingMailer.choose_project(mail).deliver_now + end +end +``` + +## Incineration of InboundEmails + +By default, an InboundEmail that has been successfully processed will be +incinerated after 30 days. This ensures you're not holding on to people's data +willy-nilly after they may have canceled their accounts or deleted their +content. The intention is that after you've processed an email, you should have +extracted all the data you needed and turned it into domain models and content +on your side of the application. The InboundEmail simply stays in the system +for the extra time to provide debugging and forensics options. + +The actual incineration is done via the `IncinerationJob` that's scheduled +to run after `config.action_mailbox.incinerate_after` time. This value is +by default set to `30.days`, but you can change it in your production.rb +configuration. (Note that this far-future incineration scheduling relies on +your job queue being able to hold jobs for that long.) + +## Working with Action Mailbox in development + +It's helpful to be able to test incoming emails in development without actually +sending and receiving real emails. To accomplish this, there's a conductor +controller mounted at `/rails/conductor/action_mailbox/inbound_emails`, +which gives you an index of all the InboundEmails in the system, their +state of processing, and a form to create a new InboundEmail as well. + +## Testing mailboxes + +Example: + +```ruby +class ForwardsMailboxTest < ActionMailbox::TestCase + test "directly recording a client forward for a forwarder and forwardee corresponding to one project" do + assert_difference -> { people(:david).buckets.first.recordings.count } do + receive_inbound_email_from_mail \ + to: 'save@example.com', + from: people(:david).email_address, + subject: "Fwd: Status update?", + body: <<~BODY + --- Begin forwarded message --- + From: Frank Holland <frank@microsoft.com> + + What's the status? + BODY + end + + recording = people(:david).buckets.first.recordings.last + assert_equal people(:david), recording.creator + assert_equal "Status update?", recording.forward.subject + assert_match "What's the status?", recording.forward.content.to_s + end +end +``` diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 1acb993cad..f600cf29ce 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -3,13 +3,13 @@ Action Mailer Basics ==================== -This guide provides you with all you need to get started in sending and -receiving emails from and to your application, and many internals of Action +This guide provides you with all you need to get started in sending +emails from and to your application, and many internals of Action Mailer. It also covers how to test your mailers. After reading this guide, you will know: -* How to send and receive email within a Rails application. +* How to send email within a Rails application. * How to generate and edit an Action Mailer class and mailer view. * How to configure Action Mailer for your environment. * How to test your Action Mailer classes. @@ -168,7 +168,7 @@ view and sending it over the HTTP protocol, they are just sending it out through the email protocols instead. Due to this, it makes sense to just have your controller tell the Mailer to send an email when a user is successfully created. -Setting this up is painfully simple. +Setting this up is simple. First, let's create a simple `User` scaffold: @@ -427,7 +427,7 @@ If you would like to render a template located outside of the default `app/views ```ruby class UserMailer < ApplicationMailer prepend_view_path "custom/path/to/mailer/view" - + # This will try to load "custom/path/to/mailer/view/welcome_email" template def welcome_email # ... @@ -573,7 +573,7 @@ web addresses. Thus, you should always use the "_url" variant of named route helpers. If you did not configure the `:host` option globally make sure to pass it to the -url helper. +URL helper. ```erb <%= user_url(@user, host: 'example.com') %> @@ -651,48 +651,8 @@ class UserMailer < ApplicationMailer end ``` -Receiving Emails ----------------- - -Receiving and parsing emails with Action Mailer can be a rather complex -endeavor. Before your email reaches your Rails app, you would have had to -configure your system to somehow forward emails to your app, which needs to be -listening for that. So, to receive emails in your Rails app you'll need to: - -* Implement a `receive` method in your mailer. - -* Configure your email server to forward emails from the address(es) you would - like your app to receive to `/path/to/app/bin/rails runner - 'UserMailer.receive(STDIN.read)'`. - -Once a method called `receive` is defined in any mailer, Action Mailer will -parse the raw incoming email into an email object, decode it, instantiate a new -mailer, and pass the email object to the mailer `receive` instance -method. Here's an example: - -```ruby -class UserMailer < ApplicationMailer - def receive(email) - page = Page.find_by(address: email.to.first) - page.emails.create( - subject: email.subject, - body: email.body - ) - - if email.has_attachments? - email.attachments.each do |attachment| - page.attachments.create({ - file: attachment, - description: email.subject - }) - end - end - end -end -``` - Action Mailer Callbacks ---------------------------- +----------------------- Action Mailer allows for you to specify a `before_action`, `after_action` and `around_action`. @@ -786,7 +746,7 @@ files (environment.rb, production.rb, etc...) |`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default `"localhost"` setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain` (will send the password in the clear), `:login` (will send password Base64 encoded) or `:cram_md5` (combines a Challenge/Response mechanism to exchange information and a cryptographic Message Digest 5 algorithm to hash important information)</li><li>`:enable_starttls_auto` - Detects if STARTTLS is enabled in your SMTP server and starts to use it. Defaults to `true`.</li><li>`:openssl_verify_mode` - When using TLS, you can set how OpenSSL checks the certificate. This is really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name of an OpenSSL verify constant ('none' or 'peer') or directly the constant (`OpenSSL::SSL::VERIFY_NONE` or `OpenSSL::SSL::VERIFY_PEER`).</li></ul>| |`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i`.</li></ul>| |`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.| -|`delivery_method`|Defines a delivery method. Possible values are:<ul><li>`:smtp` (default), can be configured by using `config.action_mailer.smtp_settings`.</li><li>`:sendmail`, can be configured by using `config.action_mailer.sendmail_settings`.</li><li>`:file`: save emails to files; can be configured by using `config.action_mailer.file_settings`.</li><li>`:test`: save emails to `ActionMailer::Base.deliveries` array.</li></ul>See [API docs](http://api.rubyonrails.org/classes/ActionMailer/Base.html) for more info.| +|`delivery_method`|Defines a delivery method. Possible values are:<ul><li>`:smtp` (default), can be configured by using `config.action_mailer.smtp_settings`.</li><li>`:sendmail`, can be configured by using `config.action_mailer.sendmail_settings`.</li><li>`:file`: save emails to files; can be configured by using `config.action_mailer.file_settings`.</li><li>`:test`: save emails to `ActionMailer::Base.deliveries` array.</li></ul>See [API docs](https://api.rubyonrails.org/classes/ActionMailer/Base.html) for more info.| |`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing. If this value is `false`, `deliveries` array will not be populated even if `delivery_method` is `:test`.| |`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.| |`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).| @@ -882,7 +842,7 @@ class EmailDeliveryObserver end end ``` -Like interceptors, you need to register observers with the Action Mailer framework. You can do this in an initializer file +Like interceptors, you need to register observers with the Action Mailer framework. You can do this in an initializer file `config/initializers/email_delivery_observer.rb` ```ruby diff --git a/guides/source/action_text_overview.md b/guides/source/action_text_overview.md new file mode 100644 index 0000000000..08ec35e52a --- /dev/null +++ b/guides/source/action_text_overview.md @@ -0,0 +1,99 @@ +**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.** + +Action Text Overview +==================== + +This guide provides you with all you need to get started in handling +rich text content. + +After reading this guide, you will know: + +* How to configure Action Text. +* How to handle rich text content. +* How to style rich text content. + +-------------------------------------------------------------------------------- + +Introduction +------------ + +Action Text brings rich text content and editing to Rails. It includes +the [Trix editor](https://trix-editor.org) that handles everything from formatting +to links to quotes to lists to embedded images and galleries. +The rich text content generated by the Trix editor is saved in its own +RichText model that's associated with any existing Active Record model in the application. +Any embedded images (or other attachments) are automatically stored using +Active Storage and associated with the included RichText model. + +## Trix compared to other rich text editors + +Most WYSIWYG editors are wrappers around HTML’s `contenteditable` and `execCommand` APIs, +designed by Microsoft to support live editing of web pages in Internet Explorer 5.5, +and [eventually reverse-engineered](https://blog.whatwg.org/the-road-to-html-5-contenteditable#history) +and copied by other browsers. + +Because these APIs were never fully specified or documented, +and because WYSIWYG HTML editors are enormous in scope, each +browser's implementation has its own set of bugs and quirks, +and JavaScript developers are left to resolve the inconsistencies. + +Trix sidesteps these inconsistencies by treating contenteditable +as an I/O device: when input makes its way to the editor, Trix converts that input +into an editing operation on its internal document model, then re-renders +that document back into the editor. This gives Trix complete control over what +happens after every keystroke, and avoids the need to use execCommand at all. + +## Installation + +Run `rails action_text:install` to add the Yarn package and copy over the necessary migration. + +## Examples + +Adding a rich text field to an existing model: + +```ruby +# app/models/message.rb +class Message < ApplicationRecord + has_rich_text :content +end +``` + +Then refer to this field in the form for the model: + +```erb +<%# app/views/messages/_form.html.erb %> +<%= form_with(model: message) do |form| %> + <div class="field"> + <%= form.label :content %> + <%= form.rich_text_area :content %> + </div> +<% end %> +``` + +And finally display the sanitized rich text on a page: + +```erb +<%= @message.content %> +``` + +To accept the rich text content, all you have to do is permit the referenced attribute: + +```ruby +class MessagesController < ApplicationController + def create + message = Message.create! params.require(:message).permit(:title, :content) + redirect_to message + end +end +``` + +## Custom styling + +By default, the Action Text editor and content is styled by the Trix defaults. +If you want to change these defaults, you'll want to remove +the `app/assets/stylesheets/actiontext.css` linker and base your stylings on +the [contents of that file](https://raw.githubusercontent.com/basecamp/trix/master/dist/trix.css). + +You can also style the HTML used for embedded images and other attachments (known as blobs). +On installation, Action Text will copy over a partial to +`app/views/active_storage/blobs/_blob.html.erb`, which you can specialize. diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 495ae9d267..a1b69edd22 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -91,7 +91,7 @@ Here are some basic examples: ```ruby xml.em("emphasized") xml.em { xml.b("emph & bold") } -xml.a("A Link", "href" => "http://rubyonrails.org") +xml.a("A Link", "href" => "https://rubyonrails.org") xml.target("name" => "compile", "option" => "fast") ``` @@ -100,7 +100,7 @@ which would produce: ```html <em>emphasized</em> <em><b>emph & bold</b></em> -<a href="http://rubyonrails.org">A link</a> +<a href="https://rubyonrails.org">A link</a> <target option="fast" name="compile" /> ``` @@ -402,9 +402,9 @@ This will add `app/views/direct` to the end of the lookup paths. Overview of helpers provided by Action View ------------------------------------------- -WIP: Not all the helpers are listed here. For a full list see the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers.html) +WIP: Not all the helpers are listed here. For a full list see the [API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers.html) -The following is only a brief overview summary of the helpers available in Action View. It's recommended that you review the [API Documentation](http://api.rubyonrails.org/classes/ActionView/Helpers.html), which covers all of the helpers in more detail, but this should serve as a good starting point. +The following is only a brief overview summary of the helpers available in Action View. It's recommended that you review the [API Documentation](https://api.rubyonrails.org/classes/ActionView/Helpers.html), which covers all of the helpers in more detail, but this should serve as a good starting point. ### AssetTagHelper @@ -1446,7 +1446,7 @@ Sanitizes a block of CSS code. Strips all link tags from text leaving just the link text. ```ruby -strip_links('<a href="http://rubyonrails.org">Ruby on Rails</a>') +strip_links('<a href="https://rubyonrails.org">Ruby on Rails</a>') # => Ruby on Rails ``` diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md index 0ebef46373..0925ad3d74 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -121,7 +121,7 @@ production apps will need to pick a persistent backend. Active Job has built-in adapters for multiple queuing backends (Sidekiq, Resque, Delayed Job, and others). To get an up-to-date list of the adapters -see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). +see the API Documentation for [ActiveJob::QueueAdapters](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). ### Setting the Backend @@ -455,7 +455,7 @@ class RemoteServiceJob < ApplicationJob end ``` -To get more details see the API Documentation for [ActiveJob::Exceptions](http://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html). +To get more details see the API Documentation for [ActiveJob::Exceptions](https://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html). ### Deserialization diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index a67e2924d7..d765e32ac7 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -29,7 +29,7 @@ Object Relational Mapping system. ### The Active Record Pattern -[Active Record was described by Martin Fowler](http://www.martinfowler.com/eaaCatalog/activeRecord.html) +[Active Record was described by Martin Fowler](https://www.martinfowler.com/eaaCatalog/activeRecord.html) in his book _Patterns of Enterprise Application Architecture_. In Active Record, objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring @@ -45,7 +45,7 @@ relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code. -NOTE: If you are not familiar enough with relational database management systems (RDBMS) or structured query language (SQL), please go through [this tutorial](https://www.w3schools.com/sql/default.asp) (or [this one](http://www.sqlcourse.com/)) or study them by other means. Understanding how relational databases work is crucial to understanding Active Records and Rails in general. +NOTE: Basic knowledge of relational database management systems (RDBMS) and structured query language (SQL) is helpful in order to fully understand Active Record. Please refer to [this tutorial](https://www.w3schools.com/sql/default.asp) (or [this one](http://www.sqlcourse.com/)) or study them by other means if you would like to learn more. ### Active Record as an ORM Framework @@ -105,9 +105,9 @@ depending on the purpose of these columns. fields that Active Record will look for when you create associations between your models. * **Primary keys** - By default, Active Record will use an integer column named - `id` as the table's primary key. When using [Active Record - Migrations](active_record_migrations.html) to create your tables, this column will be - automatically created. + `id` as the table's primary key (`bigint` for PostgreSQL and MySQL, `integer` + for SQLite). When using [Active Record Migrations](active_record_migrations.html) + to create your tables, this column will be automatically created. There are also some optional column names that will add additional features to Active Record instances: @@ -117,10 +117,10 @@ to Active Record instances: * `updated_at` - Automatically gets set to the current date and time whenever the record is created or updated. * `lock_version` - Adds [optimistic - locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to + locking](https://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to a model. * `type` - Specifies that the model uses [Single Table - Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance). + Inheritance](https://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance). * `(association_name)_type` - Stores the type for [polymorphic associations](association_basics.html#polymorphic-associations). * `(table_name)_count` - Used to cache the number of belonging objects on @@ -309,12 +309,12 @@ user = User.find_by(name: 'David') user.destroy ``` -If you'd like to delete several records in bulk, you may use `destroy_all` -method: +If you'd like to delete several records in bulk, you may use `destroy_by` +or `destroy_all` method: ```ruby # find and delete all users named David -User.where(name: 'David').destroy_all +User.destroy_by(name: 'David') # delete all users User.destroy_all diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index ebdee446f9..617f81d37b 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -239,13 +239,12 @@ Skipping Callbacks Just as with validations, it is also possible to skip callbacks by using the following methods: -* `decrement` +* `decrement!` * `decrement_counter` * `delete` * `delete_all` -* `increment` +* `increment!` * `increment_counter` -* `toggle` * `update_column` * `update_columns` * `update_all` @@ -310,7 +309,7 @@ end ### Using `:if` and `:unless` with a `Proc` -Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: +It is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: ```ruby class Order < ApplicationRecord @@ -338,6 +337,20 @@ class Comment < ApplicationRecord end ``` +### Combining Callback Conditions + +When multiple conditions define whether or not a callback should happen, an `Array` can be used. Moreover, you can apply both `:if` and `:unless` to the same callback. + +```ruby +class Comment < ApplicationRecord + after_create :send_email_to_author, + if: [Proc.new { |c| c.user.allow_send_email? }, :author_wants_emails?], + unless: Proc.new { |c| c.article.ignore_comments? } +end +``` + +The callback only runs when all the `:if` conditions and none of the `:unless` conditions are evaluated to `true`. + Callback Classes ---------------- @@ -460,10 +473,25 @@ end => User was saved to database ``` -To register callbacks for both create and update actions, use `after_commit` instead. +There is also an alias for using the `after_commit` callback for both create and update together: + +* `after_save_commit` ```ruby class User < ApplicationRecord - after_commit :log_user_saved_to_db, on: [:create, :update] + after_save_commit :log_user_saved_to_db + + private + def log_user_saved_to_db + puts 'User was saved to database' + end end + +# creating a User +>> @user = User.create +=> User was saved to database + +# updating @user +>> @user.save +=> User was saved to database ``` diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index 4d195988f8..270e4a3bf9 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -126,7 +126,7 @@ generator to handle making it for you: $ rails generate migration AddPartNumberToProducts ``` -This will create an empty but appropriately named migration: +This will create an appropriately named empty migration: ```ruby class AddPartNumberToProducts < ActiveRecord::Migration[5.0] @@ -135,9 +135,14 @@ class AddPartNumberToProducts < ActiveRecord::Migration[5.0] end ``` -If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is -followed by a list of column names and types then a migration containing the -appropriate `add_column` and `remove_column` statements will be created. +This generator can do much more than append a timestamp to the file name. +Based on naming conventions and additional (optional) arguments it can +also start fleshing out the migration. + +If the migration name is of the form "AddColumnToTable" or +"RemoveColumnFromTable" and is followed by a list of column names and +types then a migration containing the appropriate `add_column` and +`remove_column` statements will be created. ```bash $ rails generate migration AddPartNumberToProducts part_number:string @@ -247,7 +252,7 @@ end ``` This migration will create a `user_id` column and appropriate index. -For more `add_reference` options, visit the [API documentation](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference). +For more `add_reference` options, visit the [API documentation](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference). There is also a generator which will produce join tables if `JoinTable` is part of the name: @@ -460,7 +465,6 @@ number of digits after the decimal point. * `default` Allows to set a default value on the column. Note that if you are using a dynamic value (such as a date), the default will only be calculated the first time (i.e. on the date the migration is applied). -* `index` Adds an index for the column. * `comment` Adds a comment for the column. Some adapters may support additional options; see the adapter specific API docs @@ -479,7 +483,7 @@ add_foreign_key :articles, :authors This adds a new foreign key to the `author_id` column of the `articles` table. The key references the `id` column of the `authors` table. If the -column names can not be derived from the table names, you can use the +column names cannot be derived from the table names, you can use the `:column` and `:primary_key` options. Rails will generate a name for every foreign key starting with @@ -491,9 +495,6 @@ NOTE: Active Record only supports single column foreign keys. `execute` and `structure.sql` are required to use composite foreign keys. See [Schema Dumping and You](#schema-dumping-and-you). -NOTE: The SQLite3 adapter doesn't support `add_foreign_key` since SQLite supports -only [a limited subset of ALTER TABLE](https://www.sqlite.org/lang_altertable.html). - Removing a foreign key is easy as well: ```ruby @@ -518,12 +519,12 @@ Product.connection.execute("UPDATE products SET price = 'free' WHERE 1=1") For more details and examples of individual methods, check the API documentation. In particular the documentation for -[`ActiveRecord::ConnectionAdapters::SchemaStatements`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html) +[`ActiveRecord::ConnectionAdapters::SchemaStatements`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html) (which provides the methods available in the `change`, `up` and `down` methods), -[`ActiveRecord::ConnectionAdapters::TableDefinition`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html) +[`ActiveRecord::ConnectionAdapters::TableDefinition`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html) (which provides the methods available on the object yielded by `create_table`) and -[`ActiveRecord::ConnectionAdapters::Table`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html) +[`ActiveRecord::ConnectionAdapters::Table`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html) (which provides the methods available on the object yielded by `change_table`). ### Using the `change` Method @@ -946,7 +947,7 @@ If `:ruby` is selected, then the schema is stored in `db/schema.rb`. If you look at this file you'll find that it looks an awful lot like one very big migration: ```ruby -ActiveRecord::Schema.define(version: 20080906171750) do +ActiveRecord::Schema.define(version: 2008_09_06_171750) do create_table "authors", force: true do |t| t.string "name" t.datetime "created_at" diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 536a7138e9..12115d01ef 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -150,7 +150,7 @@ Event.where("payload->>'kind' = ?", "user_renamed") * [type definition](https://www.postgresql.org/docs/current/static/rangetypes.html) * [functions and operators](https://www.postgresql.org/docs/current/static/functions-range.html) -This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.2.2/Range.html) objects. +This type is mapped to Ruby [`Range`](https://ruby-doc.org/core-2.2.2/Range.html) objects. ```ruby # db/migrate/20130923065404_create_events.rb @@ -367,7 +367,7 @@ user.save! * [type definition](https://www.postgresql.org/docs/current/static/datatype-net-types.html) The types `inet` and `cidr` are mapped to Ruby -[`IPAddr`](http://www.ruby-doc.org/stdlib-2.2.2/libdoc/ipaddr/rdoc/IPAddr.html) +[`IPAddr`](https://ruby-doc.org/stdlib-2.2.2/libdoc/ipaddr/rdoc/IPAddr.html) objects. The `macaddr` type is mapped to normal text. ```ruby diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index fd1dcf22c0..e40f16e62d 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -59,11 +59,13 @@ To retrieve objects from the database, Active Record provides several finder met The methods are: +* `annotate` * `find` * `create_with` * `distinct` * `eager_load` * `extending` +* `extract_associated` * `from` * `group` * `having` @@ -74,11 +76,13 @@ The methods are: * `lock` * `none` * `offset` +* `optimizer_hints` * `order` * `preload` * `readonly` * `references` * `reorder` +* `reselect` * `reverse_order` * `select` * `where` @@ -611,7 +615,8 @@ If you want to call `order` multiple times, subsequent orders will be appended t Client.order("orders_count ASC").order("created_at DESC") # SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC ``` -WARNING: If you are using **MySQL 5.7.5** and above, then on selecting fields from a result set using methods like `select`, `pluck` and `ids`; the `order` method will raise an `ActiveRecord::StatementInvalid` exception unless the field(s) used in `order` clause are included in the select list. See the next section for selecting fields from the result set. + +WARNING: In most database systems, on selecting fields with `distinct` from a result set using methods like `select`, `pluck` and `ids`; the `order` method will raise an `ActiveRecord::StatementInvalid` exception unless the field(s) used in `order` clause are included in the select list. See the next section for selecting fields from the result set. Selecting Specific Fields ------------------------- @@ -623,6 +628,8 @@ To select only a subset of fields from the result set, you can specify the subse For example, to select only `viewable_by` and `locked` columns: ```ruby +Client.select(:viewable_by, :locked) +# OR Client.select("viewable_by, locked") ``` @@ -805,6 +812,32 @@ SELECT * FROM articles WHERE id > 10 ORDER BY id DESC LIMIT 20 ``` +### `reselect` + +The `reselect` method overrides an existing select statement. For example: + +```ruby +Post.select(:title, :body).reselect(:created_at) +``` + +The SQL that would be executed: + +```sql +SELECT `posts`.`created_at` FROM `posts` +``` + +In case the `reselect` clause is not used, + +```ruby +Post.select(:title, :body).select(:created_at) +``` + +the SQL executed would be: + +```sql +SELECT `posts`.`title`, `posts`.`body`, `posts`.`created_at` FROM `posts` +``` + ### `reorder` The `reorder` method overrides the default scope order. For example: @@ -1524,7 +1557,7 @@ book.available? # => false ``` Read the full documentation about enums -[in the Rails API docs](http://api.rubyonrails.org/classes/ActiveRecord/Enum.html). +[in the Rails API docs](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html). Understanding The Method Chaining --------------------------------- @@ -1702,10 +1735,13 @@ Client.find_by_sql("SELECT * FROM clients ### `select_all` -`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. This method will return an instance of `ActiveRecord::Result` class and calling `to_hash` on this object would return you an array of hashes where each hash indicates a record. +`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve +objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. +This method will return an instance of `ActiveRecord::Result` class and calling `to_a` on this +object would return you an array of hashes where each hash indicates a record. ```ruby -Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_hash +Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_a # => [ # {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, # {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"} @@ -2035,9 +2071,9 @@ under MySQL and MariaDB. Interpretation of the output of EXPLAIN is beyond the scope of this guide. The following pointers may be helpful: -* SQLite3: [EXPLAIN QUERY PLAN](http://www.sqlite.org/eqp.html) +* SQLite3: [EXPLAIN QUERY PLAN](https://www.sqlite.org/eqp.html) -* MySQL: [EXPLAIN Output Format](http://dev.mysql.com/doc/refman/5.7/en/explain-output.html) +* MySQL: [EXPLAIN Output Format](https://dev.mysql.com/doc/refman/5.7/en/explain-output.html) * MariaDB: [EXPLAIN](https://mariadb.com/kb/en/mariadb/explain/) diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 0fda7c5cfd..f904d4de65 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -639,7 +639,7 @@ class Holiday < ApplicationRecord message: "should happen once per year" } end ``` -Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](https://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns. +Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](https://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](https://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns. There is also a `:case_sensitive` option that you can use to define whether the uniqueness constraint will be case sensitive or not. This option defaults to @@ -664,7 +664,7 @@ This helper passes the record to a separate class for validation. class GoodnessValidator < ActiveModel::Validator def validate(record) if record.first_name == "Evil" - record.errors[:base] << "This person is evil" + record.errors.add :base, "This person is evil" end end end @@ -692,7 +692,7 @@ validator class as `options`: class GoodnessValidator < ActiveModel::Validator def validate(record) if options[:fields].any?{|field| record.send(field) == "Evil" } - record.errors[:base] << "This person is evil" + record.errors.add :base, "This person is evil" end end end @@ -723,7 +723,7 @@ class GoodnessValidator def validate if some_complex_condition_involving_ivars_and_private_methods? - @person.errors[:base] << "This person is evil" + @person.errors.add :base, "This person is evil" end end @@ -934,7 +934,7 @@ end ### Using a Proc with `:if` and `:unless` -Finally, it's possible to associate `:if` and `:unless` with a `Proc` object +It is possible to associate `:if` and `:unless` with a `Proc` object which will be called. Using a `Proc` object gives you the ability to write an inline condition instead of a separate method. This option is best suited for one-liners. @@ -1004,7 +1004,7 @@ and performs the validation on it. The custom validator is called using the class MyValidator < ActiveModel::Validator def validate(record) unless record.name.starts_with? 'X' - record.errors[:name] << 'Need a name starting with X please!' + record.errors.add :name, "Need a name starting with X please!" end end end @@ -1026,7 +1026,7 @@ instance. class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i - record.errors[attribute] << (options[:message] || "is not an email") + record.errors.add attribute, (options[:message] || "is not an email") end end end @@ -1044,7 +1044,7 @@ own custom validators. You can also create methods that verify the state of your models and add messages to the `errors` collection when they are invalid. You must then register these methods by using the `validate` -([API](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate)) +([API](https://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate)) class method, passing in the symbols for the validation methods' names. You can pass more than one symbol for each class method and the respective @@ -1203,7 +1203,7 @@ You can add error messages that are related to the object's state as a whole, in ```ruby class Person < ApplicationRecord def a_method_used_for_validation_purposes - errors[:base] << "This person is invalid because ..." + errors.add :base, "This person is invalid because ..." end end ``` diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md index 65d4e2934f..615f576797 100644 --- a/guides/source/active_storage_overview.md +++ b/guides/source/active_storage_overview.md @@ -421,7 +421,7 @@ Transforming Images To create a variation of the image, call `variant` on the `Blob`. You can pass any transformation to the method supported by the processor. The default processor is [MiniMagick](https://github.com/minimagick/minimagick), but you -can also use [Vips](http://www.rubydoc.info/gems/ruby-vips/Vips/Image). +can also use [Vips](https://www.rubydoc.info/gems/ruby-vips/Vips/Image). To enable variants, add the `image_processing` gem to your `Gemfile`: @@ -434,7 +434,7 @@ original blob into the specified format and redirect to its new service location. ```erb -<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %> +<%= image_tag user.avatar.variant(resize_to_limit: [100, 100]) %> ``` To switch to the Vips processor, you would add the following to @@ -489,8 +489,7 @@ directly from the client to the cloud. Using the npm package: ```js - import * as ActiveStorage from "activestorage" - ActiveStorage.start() + require("@rails/activestorage").start() ``` 2. Annotate file inputs with the direct upload URL. @@ -616,7 +615,7 @@ of choice, instantiate a DirectUpload and call its create method. Create takes a callback to invoke when the upload completes. ```js -import { DirectUpload } from "activestorage" +import { DirectUpload } from "@rails/activestorage" const input = document.querySelector('input[type=file]') @@ -664,7 +663,7 @@ will call the object's `directUploadWillStoreFileWithXHR` method. You can then bind your own progress handler on the XHR. ```js -import { DirectUpload } from "activestorage" +import { DirectUpload } from "@rails/activestorage" class Uploader { constructor(file, url) { @@ -742,16 +741,22 @@ during the test are complete and you won't receive an error from Active Storage saying it can't find a file. ```ruby +module RemoveUploadedFiles + def after_teardown + super + remove_uploaded_files + end + + private + + def remove_uploaded_files + FileUtils.rm_rf(Rails.root.join('tmp', 'storage')) + end +end + module ActionDispatch class IntegrationTest - def remove_uploaded_files - FileUtils.rm_rf(Rails.root.join('tmp', 'storage')) - end - - def after_teardown - super - remove_uploaded_files - end + prepend RemoveUploadedFiles end end ``` diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 6b0554bb5f..1a057832d4 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -1201,6 +1201,8 @@ The `inquiry` method converts a string into a `StringInquirer` object making equ "active".inquiry.inactive? # => false ``` +NOTE: Defined in `active_support/core_ext/string/inquiry.rb`. + ### `starts_with?` and `ends_with?` Active Support defines 3rd person aliases of `String#start_with?` and `String#end_with?`: @@ -1339,7 +1341,7 @@ The method `pluralize` returns the plural of its receiver: "equipment".pluralize # => "equipment" ``` -As the previous example shows, Active Support knows some irregular plurals and uncountable nouns. Built-in rules can be extended in `config/initializers/inflections.rb`. That file is generated by the `rails` command and has instructions in comments. +As the previous example shows, Active Support knows some irregular plurals and uncountable nouns. Built-in rules can be extended in `config/initializers/inflections.rb`. This file is generated by default, by the `rails new` command and has instructions in comments. `pluralize` can also take an optional `count` parameter. If `count == 1` the singular form will be returned. For any other value of `count` the plural form will be returned: @@ -1999,7 +2001,7 @@ Addition only assumes the elements respond to `+`: ```ruby [[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4] %w(foo bar baz).sum # => "foobarbaz" -{a: 1, b: 2, c: 3}.sum # => [:b, 2, :c, 3, :a, 1] +{a: 1, b: 2, c: 3}.sum # => [:a, 1, :b, 2, :c, 3] ``` The sum of an empty collection is zero by default, but this is customizable: @@ -2132,30 +2134,6 @@ The methods `second`, `third`, `fourth`, and `fifth` return the corresponding el NOTE: Defined in `active_support/core_ext/array/access.rb`. -### Adding Elements - -#### `prepend` - -This method is an alias of `Array#unshift`. - -```ruby -%w(a b c d).prepend('e') # => ["e", "a", "b", "c", "d"] -[].prepend(10) # => [10] -``` - -NOTE: Defined in `active_support/core_ext/array/prepend_and_append.rb`. - -#### `append` - -This method is an alias of `Array#<<`. - -```ruby -%w(a b c d).append('e') # => ["a", "b", "c", "d", "e"] -[].append([1,2]) # => [[1, 2]] -``` - -NOTE: Defined in `active_support/core_ext/array/prepend_and_append.rb`. - ### Extracting The method `extract!` removes and returns the elements for which the block returns a true value. @@ -2646,48 +2624,6 @@ There's also the bang variant `except!` that removes keys in the very receiver. NOTE: Defined in `active_support/core_ext/hash/except.rb`. -#### `transform_keys` and `transform_keys!` - -The method `transform_keys` accepts a block and returns a hash that has applied the block operations to each of the keys in the receiver: - -```ruby -{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase } -# => {"" => nil, "1" => 1, "A" => :a} -``` - -In case of key collision, one of the values will be chosen. The chosen value may not always be the same given the same hash: - -```ruby -{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase } -# The result could either be -# => {"A"=>2} -# or -# => {"A"=>1} -``` - -This method may be useful for example to build specialized conversions. For instance `stringify_keys` and `symbolize_keys` use `transform_keys` to perform their key conversions: - -```ruby -def stringify_keys - transform_keys { |key| key.to_s } -end -... -def symbolize_keys - transform_keys { |key| key.to_sym rescue key } -end -``` - -There's also the bang variant `transform_keys!` that applies the block operations to keys in the very receiver. - -Besides that, one can use `deep_transform_keys` and `deep_transform_keys!` to perform the block operation on all the keys in the given hash and all the hashes nested into it. An example of the result is: - -```ruby -{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys { |key| key.to_s.upcase } -# => {""=>nil, "1"=>1, "NESTED"=>{"A"=>3, "5"=>5}} -``` - -NOTE: Defined in `active_support/core_ext/hash/keys.rb`. - #### `stringify_keys` and `stringify_keys!` The method `stringify_keys` returns a hash that has a stringified version of the keys in the receiver. It does so by sending `to_s` to them: @@ -2795,26 +2731,7 @@ NOTE: Defined in `active_support/core_ext/hash/keys.rb`. ### Slicing -Ruby has built-in support for taking slices out of strings and arrays. Active Support extends slicing to hashes: - -```ruby -{a: 1, b: 2, c: 3}.slice(:a, :c) -# => {:a=>1, :c=>3} - -{a: 1, b: 2, c: 3}.slice(:b, :X) -# => {:b=>2} # non-existing keys are ignored -``` - -If the receiver responds to `convert_key` keys are normalized: - -```ruby -{a: 1, b: 2}.with_indifferent_access.slice("a") -# => {:a=>1} -``` - -NOTE. Slicing may come in handy for sanitizing option hashes with a white list of keys. - -There's also `slice!` which in addition to perform a slice in place returns what's removed: +The method `slice!` replaces the hash with only the given keys and returns a hash containing the removed key/value pairs. ```ruby hash = {a: 1, b: 2} @@ -2872,10 +2789,10 @@ Regexp.new('.', Regexp::MULTILINE).multiline? # => true Rails uses this method in a single place, also in the routing code. Multiline regexps are disallowed for route requirements and this flag eases enforcing that constraint. ```ruby -def assign_route_options(segments, defaults, requirements) +def verify_regexp_requirements(requirements) ... if requirement.multiline? - raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}" + raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}" end ... end diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 64db141381..4868b00bbe 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -203,6 +203,15 @@ INFO. Additional keys may be added by the caller. | ------- | ---------------- | | `:keys` | Unpermitted keys | +Action Dispatch +--------------- + +### process_middleware.action_dispatch + +| Key | Value | +| ------------- | ---------------------- | +| `:middleware` | Name of the middleware | + Action View ----------- @@ -255,13 +264,16 @@ Active Record ### sql.active_record -| Key | Value | -| ---------------- | ---------------------------------------- | -| `:sql` | SQL statement | -| `:name` | Name of the operation | -| `:connection_id` | `self.object_id` | -| `:binds` | Bind parameters | -| `:cached` | `true` is added when cached queries used | +| Key | Value | +| -------------------- | ---------------------------------------- | +| `:sql` | SQL statement | +| `:name` | Name of the operation | +| `:connection_id` | Object ID of the connection object | +| `:connection` | Connection object | +| `:binds` | Bind parameters | +| `:type_casted_binds` | Typecasted bind parameters | +| `:statement_name` | SQL Statement name | +| `:cached` | `true` is added when cached queries used | INFO. The adapters will add their own data as well. @@ -270,7 +282,10 @@ INFO. The adapters will add their own data as well. sql: "SELECT \"posts\".* FROM \"posts\" ", name: "Post Load", connection_id: 70307250813140, - binds: [] + connection: #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f7a838850>, + binds: [#<ActiveModel::Attribute::WithCastValue:0x00007fe19d15dc00>], + type_casted_binds: [11], + statement_name: nil } ``` @@ -291,32 +306,6 @@ INFO. The adapters will add their own data as well. Action Mailer ------------- -### receive.action_mailer - -| Key | Value | -| ------------- | -------------------------------------------- | -| `:mailer` | Name of the mailer class | -| `:message_id` | ID of the message, generated by the Mail gem | -| `:subject` | Subject of the mail | -| `:to` | To address(es) of the mail | -| `:from` | From address of the mail | -| `:bcc` | BCC addresses of the mail | -| `:cc` | CC addresses of the mail | -| `:date` | Date of the mail | -| `:mail` | The encoded form of the mail | - -```ruby -{ - mailer: "Notification", - message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail", - subject: "Rails Guides", - to: ["users@rails.com", "dhh@rails.com"], - from: ["me@rails.com"], - date: Sat, 10 Mar 2012 14:18:09 +0100, - mail: "..." # omitted for brevity -} -``` - ### deliver.action_mailer | Key | Value | @@ -444,7 +433,7 @@ INFO. Cache stores may add their own keys ``` Active Job --------- +---------- ### enqueue_at.active_job @@ -556,6 +545,14 @@ Active Storage | `:key` | Secure token | | `:service` | Name of the service | +### service_download_chunk.active_storage + +| Key | Value | +| ------------ | ------------------------------- | +| `:key` | Secure token | +| `:service` | Name of the service | +| `:range` | Byte range attempted to be read | + ### service_download.active_storage | Key | Value | @@ -591,7 +588,24 @@ Active Storage | ------------ | ------------------- | | `:key` | Secure token | | `:service` | Name of the service | -| `:url` | Generated url | +| `:url` | Generated URL | + +### service_update_metadata.active_storage + +| Key | Value | +| --------------- | ------------------------------ | +| `:key` | Secure token | +| `:service` | Name of the service | +| `:content_type` | HTTP Content-Type field | +| `:disposition` | HTTP Content-Disposition field | + +INFO. The only ActiveStorage service that provides this hook so far is GCS. + +### preview.active_storage + +| Key | Value | +| ------------ | ------------------- | +| `:key` | Secure token | Railties -------- @@ -648,6 +662,18 @@ ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*a end ``` +You may also pass block with only one argument, it will yield an event object to the block: + +```ruby +ActiveSupport::Notifications.subscribe "process_action.action_controller" do |event| + event.name # => "process_action.action_controller" + event.duration # => 10 (in milliseconds) + event.payload # => {:extra=>information} + + Rails.logger.info "#{event} Received!" +end +``` + Most times you only care about the data itself. Here is a shortcut to just get the data. ```ruby @@ -672,7 +698,7 @@ Creating custom events Adding your own events is easy as well. `ActiveSupport::Notifications` will take care of all the heavy lifting for you. Simply call `instrument` with a `name`, `payload` and a block. The notification will be sent after the block returns. `ActiveSupport` will generate the start and end times -and add the instrumenter's unique ID. All data passed into the `instrument` call will make +and add the instrumenter's unique ID. All data passed into the `instrument` call will make it into the payload. Here's an example: @@ -691,5 +717,16 @@ ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, fini end ``` +You also have the option to call instrument without passing a block. This lets you leverage the +instrumentation infrastructure for other messaging uses. + +```ruby +ActiveSupport::Notifications.instrument "my.custom.event", this: :data + +ActiveSupport::Notifications.subscribe "my.custom.event" do |name, started, finished, unique_id, data| + puts data.inspect # {:this=>:data} +end +``` + You should follow Rails conventions when defining your own events. The format is: `event.library`. If your application is sending Tweets, you should create an event named `tweet.twitter`. diff --git a/guides/source/api_app.md b/guides/source/api_app.md index 85367c50e7..181d39e7e0 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -76,7 +76,7 @@ Handled at the middleware layer: - Conditional GETs: Rails handles conditional `GET` (`ETag` and `Last-Modified`) processing request headers and returning the correct response headers and status code. All you need to do is use the - [`stale?`](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F) + [`stale?`](https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F) check in your controller, and Rails will handle all of the HTTP details for you. - HEAD requests: Rails will transparently convert `HEAD` requests into `GET` ones, and return just the headers on the way out. This makes `HEAD` work reliably in @@ -287,7 +287,7 @@ environment's configuration file. You can learn more about how to use `Rack::Sendfile` with popular front-ends in [the Rack::Sendfile -documentation](http://rubydoc.info/github/rack/rack/master/Rack/Sendfile). +documentation](https://www.rubydoc.info/github/rack/rack/master/Rack/Sendfile). Here are some values for this header for some popular servers, once these servers are configured to support accelerated file sending: @@ -374,7 +374,7 @@ controller modules by default: - `ActionController::Renderers::All`: Support for `render :json` and friends. - `ActionController::ConditionalGet`: Support for `stale?`. - `ActionController::BasicImplicitRender`: Makes sure to return an empty response, if there isn't an explicit one. -- `ActionController::StrongParameters`: Support for parameters white-listing in combination with Active Model mass assignment. +- `ActionController::StrongParameters`: Support for parameters filtering in combination with Active Model mass assignment. - `ActionController::DataStreaming`: Support for `send_file` and `send_data`. - `AbstractController::Callbacks`: Support for `before_action` and similar helpers. @@ -420,6 +420,15 @@ Some common modules you might want to add: - `ActionController::MimeResponds`: Support for `respond_to`. - `ActionController::Cookies`: Support for `cookies`, which includes support for signed and encrypted cookies. This requires the cookies middleware. +- `ActionController::Caching`: Support view caching for the API controller. Please notice that + you will need to manually specify cache store inside the controller like: + ```ruby + class ApplicationController < ActionController::API + include ::ActionController::Caching + self.cache_store = :mem_cached_store + end + ``` + Rails does *not* pass this configuration automatically. The best place to add a module is in your `ApplicationController`, but you can also add modules to individual controllers. diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index b6ee7354f9..a7ffa4fbd4 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -15,7 +15,7 @@ After reading this guide, you will know: RDoc ---- -The [Rails API documentation](http://api.rubyonrails.org) is generated with +The [Rails API documentation](https://api.rubyonrails.org) is generated with [RDoc](https://ruby.github.io/rdoc/). To generate it, make sure you are in the rails root directory, run `bundle install` and execute: diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 500e230ff9..d853559440 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -33,13 +33,11 @@ passing the `--skip-sprockets` option. rails new appname --skip-sprockets ``` -Rails automatically adds the `sass-rails`, `coffee-rails` and `uglifier` -gems to your `Gemfile`, which are used by Sprockets for asset compression: +Rails automatically adds the `sass-rails` gem to your `Gemfile`, which is used +by Sprockets for asset compression: ```ruby gem 'sass-rails' -gem 'uglifier' -gem 'coffee-rails' ``` Using the `--skip-sprockets` option will prevent Rails from adding @@ -126,7 +124,7 @@ The query string strategy has several disadvantages: 1. **Not all caches will reliably cache content where the filename only differs by query parameters** - [Steve Souders recommends](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/), + [Steve Souders recommends](https://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/), "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation. @@ -176,8 +174,7 @@ in `app/assets` are never served directly in production. ### Controller Specific Assets -When you generate a scaffold or a controller, Rails also generates a JavaScript -file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a +When you generate a scaffold or a controller, Rails also generates a Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`) for that controller. Additionally, when generating a scaffold, Rails generates the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the @@ -434,9 +431,8 @@ one file rather than many, the load time of pages can be greatly reduced because the browser makes fewer requests. Compression also reduces file size, enabling the browser to download them faster. - -For example, a new Rails application includes a default -`app/assets/javascripts/application.js` file containing the following lines: +For example, with a `app/assets/javascripts/application.js` file containing the +following lines: ```js // ... @@ -476,8 +472,7 @@ which contains these lines: */ ``` -Rails creates both `app/assets/javascripts/application.js` and -`app/assets/stylesheets/application.css` regardless of whether the +Rails create `app/assets/stylesheets/application.css` regardless of whether the --skip-sprockets option is used when creating a new Rails application. This is so you can easily add asset pipelining later if you like. @@ -489,7 +484,7 @@ one, requiring all stylesheets from the current directory. In this example, `require_self` is used. This puts the CSS contained within the file (if any) at the precise location of the `require_self` call. -NOTE. If you want to use multiple Sass files, you should generally use the [Sass `@import` rule](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import) +NOTE. If you want to use multiple Sass files, you should generally use the [Sass `@import` rule](https://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import) instead of these Sprockets directives. When using Sprockets directives, Sass files exist within their own scope, making variables or mixins only available within the document they were defined in. @@ -517,8 +512,7 @@ The file extensions used on an asset determine what preprocessing is applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file are generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which -generated an `app/assets/javascripts/projects.coffee` and an -`app/assets/stylesheets/projects.scss` file. +generated an `app/assets/stylesheets/projects.scss` file. In development mode, or if the asset pipeline is disabled, when these files are requested they are processed by the processors provided by the `coffee-script` @@ -745,7 +739,7 @@ mapping requests back to Sprockets. A typical manifest file looks like: The default location for the manifest is the root of the location specified in `config.assets.prefix` ('/assets' by default). -NOTE: If there are missing precompiled files in production you will get an +NOTE: If there are missing precompiled files in production you will get a `Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError` exception indicating the name of the missing file(s). @@ -961,7 +955,7 @@ is present. ##### CDN Header Debugging One way to check the headers are cached properly in your CDN is by using [curl]( -http://explainshell.com/explain?cmd=curl+-I+http%3A%2F%2Fwww.example.com). You +https://explainshell.com/explain?cmd=curl+-I+http%3A%2F%2Fwww.example.com). You can request the headers from both your server and your CDN to verify they are the same: @@ -1009,7 +1003,7 @@ such as `X-Cache` or for any additional headers they may add. ##### CDNs and the Cache-Control Header The [cache control -header](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) is a W3C +header](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) is a W3C specification that describes how a request can be cached. When no CDN is used, a browser will use this information to cache contents. This is very helpful for assets that are not modified so that a browser does not need to re-download a @@ -1083,7 +1077,7 @@ Possible options for JavaScript compression are `:closure`, `:uglifier` and `:yui`. These require the use of the `closure-compiler`, `uglifier` or `yui-compressor` gems, respectively. -The default `Gemfile` includes [uglifier](https://github.com/lautis/uglifier). +Take the `uglifier` gem, for example. This gem wraps [UglifyJS](https://github.com/mishoo/UglifyJS) (written for NodeJS) in Ruby. It compresses your code by removing white space and comments, shortening local variable names, and performing other micro-optimizations such @@ -1101,7 +1095,7 @@ Windows you have a JavaScript runtime installed in your operating system. -### Serving GZipped version of assets +### GZipping your assets By default, gzipped version of compiled assets will be generated, along with the non-gzipped version of assets. Gzipped assets help reduce the transmission @@ -1111,6 +1105,8 @@ of data over the wire. You can configure this by setting the `gzip` flag. config.assets.gzip = false # disable gzipped assets generation ``` +Refer to your web server's documentation for instructions on how to serve gzipped assets. + ### Using Your Own Compressor The compressor config settings for CSS and JavaScript also take any object. @@ -1152,7 +1148,7 @@ The X-Sendfile header is a directive to the web server to ignore the response from the application, and instead serve a specified file from disk. This option is off by default, but can be enabled if your server supports it. When enabled, this passes responsibility for serving the file to the web server, which is -faster. Have a look at [send_file](http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file) +faster. Have a look at [send_file](https://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file) on how to use this feature. Apache and NGINX support this option, which can be enabled in @@ -1170,7 +1166,7 @@ and any other environments you define with production behavior (not TIP: For further details have a look at the docs of your production web server: - [Apache](https://tn123.org/mod_xsendfile/) -- [NGINX](http://wiki.nginx.org/XSendfile) +- [NGINX](https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/) Assets Cache Store ------------------ @@ -1228,4 +1224,3 @@ it as a preprocessor for your mime type. ```ruby Sprockets.register_preprocessor 'text/css', AddComment ``` - diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 4f3e8b2cff..62e9270539 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -384,7 +384,7 @@ end The corresponding migration might look like this: ```ruby -class CreateSuppliers < ActiveRecord::Migration[5.0] +class CreateSuppliers < ActiveRecord::Migration[5.2] def change create_table :suppliers do |t| t.string :name @@ -392,7 +392,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0] end create_table :accounts do |t| - t.integer :supplier_id + t.bigint :supplier_id t.string :account_number t.timestamps end @@ -402,7 +402,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0] end ``` -NOTE: Using `t.integer :supplier_id` makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using `t.references :supplier` instead. +NOTE: Using `t.bigint :supplier_id` makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using `t.references :supplier` instead. ### Choosing Between `has_many :through` and `has_and_belongs_to_many` @@ -466,11 +466,11 @@ Similarly, you can retrieve `@product.pictures`. If you have an instance of the `Picture` model, you can get to its parent via `@picture.imageable`. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface: ```ruby -class CreatePictures < ActiveRecord::Migration[5.0] +class CreatePictures < ActiveRecord::Migration[5.2] def change create_table :pictures do |t| t.string :name - t.integer :imageable_id + t.bigint :imageable_id t.string :imageable_type t.timestamps end @@ -619,11 +619,11 @@ end These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key: ```ruby -class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.2] def change create_table :assemblies_parts, id: false do |t| - t.integer :assembly_id - t.integer :part_id + t.bigint :assembly_id + t.bigint :part_id end add_index :assemblies_parts, :assembly_id @@ -1231,6 +1231,7 @@ The `has_one` association supports these options: * `:source` * `:source_type` * `:through` +* `:touch` * `:validate` ##### `:as` @@ -1257,7 +1258,7 @@ Controls what happens to the associated object when its owner is destroyed: * `:destroy` causes the associated object to also be destroyed * `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute) -* `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed. +* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed. * `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there is an associated record * `:restrict_with_error` causes an error to be added to the owner if there is an associated object @@ -1305,10 +1306,47 @@ The `:source` option specifies the source association name for a `has_one :throu The `:source_type` option specifies the source association type for a `has_one :through` association that proceeds through a polymorphic association. +```ruby +class Book < ApplicationRecord + has_one :format, polymorphic: true + has_one :dust_jacket, through: :format, source: :dust_jacket, source_type: "Hardback" +end + +class Paperback < ApplicationRecord; end + +class Hardback < ApplicationRecord + has_one :dust_jacket +end + +class DustJacket < ApplicationRecord; end +``` + ##### `:through` The `:through` option specifies a join model through which to perform the query. `has_one :through` associations were discussed in detail [earlier in this guide](#the-has-one-through-association). +##### `:touch` + +If you set the `:touch` option to `true`, then the `updated_at` or `updated_on` timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: + +```ruby +class Supplier < ApplicationRecord + has_one :account, touch: true +end + +class Account < ApplicationRecord + belongs_to :supplier +end +``` + +In this case, saving or destroying a supplier will update the timestamp on the associated account. You can also specify a particular timestamp attribute to update: + +```ruby +class Supplier < ApplicationRecord + has_one :account, touch: :suppliers_updated_at +end +``` + ##### `:validate` If you set the `:validate` option to `true`, then associated objects will be validated whenever you save this object. By default, this is `false`: associated objects will not be validated when this object is saved. @@ -1544,7 +1582,7 @@ The `collection.size` method returns the number of objects in the collection. ##### `collection.find(...)` The `collection.find` method finds objects within the collection. It uses the same syntax and options as -[`ActiveRecord::Base.find`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find). +[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find). ```ruby @available_book = @author.books.find(1) @@ -1563,7 +1601,7 @@ The `collection.where` method finds objects within the collection based on the c The `collection.exists?` method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as -[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F). +[`ActiveRecord::Base.exists?`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F). ##### `collection.build(attributes = {}, ...)` @@ -1658,7 +1696,7 @@ Controls what happens to the associated objects when their owner is destroyed: * `:destroy` causes all the associated objects to also be destroyed * `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute) -* `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed. +* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed. * `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there are any associated records * `:restrict_with_error` causes an error to be added to the owner if there are any associated objects @@ -1717,6 +1755,20 @@ The `:source` option specifies the source association name for a `has_many :thro The `:source_type` option specifies the source association type for a `has_many :through` association that proceeds through a polymorphic association. +```ruby +class Author < ApplicationRecord + has_many :books + has_many :paperbacks, through: :books, source: :format, source_type: "Paperback" +end + +class Book < ApplicationRecord + has_one :format, polymorphic: true +end + +class Hardback < ApplicationRecord; end +class Paperback < ApplicationRecord; end +``` + ##### `:through` The `:through` option specifies a join model through which to perform the query. `has_many :through` associations provide a way to implement many-to-many relationships, as discussed [earlier in this guide](#the-has-many-through-association). @@ -2077,7 +2129,7 @@ The `collection.size` method returns the number of objects in the collection. ##### `collection.find(...)` The `collection.find` method finds objects within the collection. It uses the same syntax and options as -[`ActiveRecord::Base.find`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find). +[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find). ```ruby @assembly = @part.assemblies.find(1) @@ -2095,7 +2147,7 @@ The `collection.where` method finds objects within the collection based on the c The `collection.exists?` method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as -[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F). +[`ActiveRecord::Base.exists?`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F). ##### `collection.build(attributes = {})` @@ -2350,6 +2402,17 @@ end If a `before_add` callback throws an exception, the object does not get added to the collection. Similarly, if a `before_remove` callback throws an exception, the object does not get removed from the collection. +NOTE: These callbacks are called only when the associated objects are added or removed through the association collection: + +```ruby +# Triggers `before_add` callback +author.books << book +author.books = [book, book2] + +# Does not trigger the `before_add` callback +book.update(author_id: 1) +``` + ### Association Extensions You're not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example: diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md index b3f923a017..7dfc39e192 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -798,7 +798,7 @@ to load a file using the current [loading mechanism](#loading-mechanism), and keeping track of constants defined in that file as if they were autoloaded to have them reloaded as needed. -`require_dependency` is rarely needed, but see a couple of use-cases in +`require_dependency` is rarely needed, but see a couple of use cases in [Autoloading and STI](#autoloading-and-sti) and [When Constants aren't Triggered](#when-constants-aren-t-missed). diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 3ac3f8fa8b..3f013fff3a 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -51,10 +51,10 @@ For instance, it will not impact low-level caching, that we address ### Page Caching Page caching is a Rails mechanism which allows the request for a generated page -to be fulfilled by the webserver (i.e. Apache or NGINX) without having to go +to be fulfilled by the web server (i.e. Apache or NGINX) without having to go through the entire Rails stack. While this is super fast it can't be applied to every situation (such as pages that need authentication). Also, because the -webserver is serving a file directly from the filesystem you will need to +web server is serving a file directly from the filesystem you will need to implement cache expiration. INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_caching gem](https://github.com/rails/actionpack-page_caching). @@ -63,7 +63,7 @@ INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_cachi Page Caching cannot be used for actions that have before filters - for example, pages that require authentication. This is where Action Caching comes in. Action Caching works like Page Caching except the incoming web request hits the Rails stack so that before filters can be run on it before the cache is served. This allows authentication and other restrictions to be run while still serving the result of the output from a cached copy. -INFO: Action Caching has been removed from Rails 4. See the [actionpack-action_caching gem](https://github.com/rails/actionpack-action_caching). See [DHH's key-based cache expiration overview](http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works) for the newly-preferred method. +INFO: Action Caching has been removed from Rails 4. See the [actionpack-action_caching gem](https://github.com/rails/actionpack-action_caching). See [DHH's key-based cache expiration overview](https://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works) for the newly-preferred method. ### Fragment Caching @@ -302,7 +302,7 @@ class Product < ApplicationRecord end ``` -NOTE: Notice that in this example we used the `cache_key_with_version` method, so the resulting cache key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key_with_version` generates a string based on the model's `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. +NOTE: Notice that in this example we used the `cache_key_with_version` method, so the resulting cache key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key_with_version` generates a string based on the model's class name, `id`, and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key. ### SQL Caching diff --git a/guides/source/command_line.md b/guides/source/command_line.md index bbebf97c3f..55f8c84b66 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -238,17 +238,20 @@ Usage: ... -Active Record options: - [--migration] # Indicates when to generate migration - # Default: true +ActiveRecord options: + [--migration], [--no-migration] # Indicates when to generate migration + # Default: true ... Description: - Create rails files for model generator. + Stubs out a new model. Pass the model name, either CamelCased or + under_scored, and an optional list of attribute pairs as arguments. + +... ``` -NOTE: For a list of available field types for the `type` parameter, refer to the [API documentation](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column) for the add_column method for the `SchemaStatements` module. The `index` parameter generates a corresponding index for the column. +NOTE: For a list of available field types for the `type` parameter, refer to the [API documentation](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column) for the add_column method for the `SchemaStatements` module. The `index` parameter generates a corresponding index for the column. But instead of generating a model directly (which we'll be doing later), let's set up a scaffold. A **scaffold** in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above. @@ -257,7 +260,7 @@ We will set up a simple resource called "HighScore" that will keep track of our ```bash $ rails generate scaffold HighScore game:string score:integer invoke active_record - create db/migrate/20130717151933_create_high_scores.rb + create db/migrate/20190416145729_create_high_scores.rb create app/models/high_score.rb invoke test_unit create test/models/high_score_test.rb @@ -275,20 +278,19 @@ $ rails generate scaffold HighScore game:string score:integer create app/views/high_scores/_form.html.erb invoke test_unit create test/controllers/high_scores_controller_test.rb + create test/system/high_scores_test.rb invoke helper create app/helpers/high_scores_helper.rb + invoke test_unit invoke jbuilder create app/views/high_scores/index.json.jbuilder create app/views/high_scores/show.json.jbuilder - invoke test_unit - create test/system/high_scores_test.rb + create app/views/high_scores/_high_score.json.jbuilder invoke assets - invoke coffee - create app/assets/javascripts/high_scores.coffee invoke scss create app/assets/stylesheets/high_scores.scss invoke scss - identical app/assets/stylesheets/scaffolds.scss + create app/assets/stylesheets/scaffolds.scss ``` The generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the **resource**, and new tests for everything. @@ -344,7 +346,7 @@ irb(main):001:0> Inside the `rails console` you have access to the `app` and `helper` instances. -With the `app` method you can access url and path helpers, as well as do requests. +With the `app` method you can access URL and path helpers, as well as do requests. ```bash >> app.root_path @@ -481,6 +483,22 @@ lib/school.rb: * [ 17] [FIXME] ``` +#### Tags + +You can add more default tags to search for by using `config.annotations.register_tags`. It receives a list of tags. + +```ruby +config.annotations.register_tags("DEPRECATEME", "TESTME") +``` + +```bash +$ rails notes +app/controllers/admin/users_controller.rb: + * [ 20] [TODO] do A/B testing on this + * [ 42] [TESTME] this needs more functional tests + * [132] [DEPRECATEME] ensure this method is deprecated in next release +``` + #### Directories You can add more default directories to search from by using `config.annotations.register_directories`. It receives a list of directory names. @@ -606,7 +624,7 @@ $ rails "task_name[value 1]" # entire argument string should be quoted $ rails db:nothing ``` -NOTE: If your need to interact with your application models, perform database queries, and so on, your task should depend on the `environment` task, which will load your application code. +NOTE: If you need to interact with your application models, perform database queries, and so on, your task should depend on the `environment` task, which will load your application code. The Rails Advanced Command Line ------------------------------- diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 029ae1a5ff..3863323bd2 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -64,6 +64,8 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`. It is no longer recommended to adjust this. See [Autoloading and Reloading Constants](autoloading_and_reloading_constants.html#autoload-paths-and-eager-load-paths) +* `config.add_autoload_paths_to_load_path` says whether autoload paths have to be added to `$LOAD_PATH`. This flag is `true` by default, but it is recommended to be set to `false` in `:zeitwerk` mode early, in `config/application.rb`. Zeitwerk uses absolute paths internally, and applications running in `:zeitwerk` mode do not need `require_dependency`, so models, controllers, jobs, etc. do not need to be in `$LOAD_PATH`. Setting this to `false` saves Ruby from checking these directories when resolving `require` calls with relative paths, and saves Bootsnap work and RAM, since it does not need to build an index for them. + * `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to `false` in development mode, and `true` in test and production modes. * `config.beginning_of_week` sets the default beginning of week for the @@ -86,6 +88,8 @@ application. Accepts a valid week day symbol (e.g. `:monday`). end ``` +* `config.disable_sandbox` controls whether or not someone can start a console in sandbox mode. This is helpful to avoid a long running session of sandbox console, that could lead a database server to run out of memory. Defaults to false. + * `config.eager_load` when `true`, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks, and any other registered namespace. * `config.eager_load_namespaces` registers namespaces that are eager loaded when `config.eager_load` is `true`. All namespaces in the list must respond to the `eager_load!` method. @@ -106,7 +110,7 @@ application. Accepts a valid week day symbol (e.g. `:monday`). you don't want shown in the logs, such as passwords or credit card numbers. It also filters out sensitive values of database columns when call `#inspect` on an Active Record object. By default, Rails filters out passwords by adding `Rails.application.config.filter_parameters += [:password]` in `config/initializers/filter_parameter_logging.rb`. Parameters filter works by partial matching regular expression. -* `config.force_ssl` forces all requests to be served over HTTPS by using the `ActionDispatch::SSL` middleware, and sets `config.action_mailer.default_url_options` to be `{ protocol: 'https' }`. This can be configured by setting `config.ssl_options` - see the [ActionDispatch::SSL documentation](http://api.rubyonrails.org/classes/ActionDispatch/SSL.html) for details. +* `config.force_ssl` forces all requests to be served over HTTPS by using the `ActionDispatch::SSL` middleware, and sets `config.action_mailer.default_url_options` to be `{ protocol: 'https' }`. This can be configured by setting `config.ssl_options` - see the [ActionDispatch::SSL documentation](https://api.rubyonrails.org/classes/ActionDispatch/SSL.html) for details. * `config.log_formatter` defines the formatter of the Rails logger. This option defaults to an instance of `ActiveSupport::Logger::SimpleFormatter` for all modes. If you are setting a value for `config.logger` you must manually pass the value of your formatter to your logger before it is wrapped in an `ActiveSupport::TaggedLogging` instance, Rails will not do it for you. @@ -135,6 +139,10 @@ defaults to `:debug` for all environments. The available log levels are: `:debug * `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to `true`. If `config.cache_classes` is `true`, this option is ignored. +* `config.credentials.content_path` configures lookup path for encrypted credentials. + +* `config.credentials.key_path` configures lookup path for encryption key. + * `secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get a random generated key in test and development environments, other environments should set one in `config/credentials.yml.enc`. * `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory. @@ -304,7 +312,7 @@ All these configuration options are delegated to the `I18n` library. ### Configuring Active Model -* `config.active_model.i18n_full_message` is a boolean value which controls whether the `full_message` error format can be overridden at the attribute or model level in the locale files. This is `false` by default. +* `config.active_model.i18n_customize_full_message` is a boolean value which controls whether the `full_message` error format can be overridden at the attribute or model level in the locale files. This is `false` by default. ### Configuring Active Record @@ -387,28 +395,6 @@ The PostgreSQL adapter adds one additional configuration option: highly recommended that you do not enable this in a production environment. Defaults to `false` in all environments. -The SQLite3Adapter adapter adds one additional configuration option: - -* `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` -indicates whether boolean values are stored in sqlite3 databases as 1 and 0 or -'t' and 'f'. Leaving `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` -set to false is deprecated. SQLite databases have used 't' and 'f' to serialize -boolean values and must have old data converted to 1 and 0 (its native boolean -serialization) before setting this flag to true. Conversion can be accomplished -by setting up a Rake task which runs - - ```ruby - ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1) - ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0) - ``` - - for all models and all boolean columns, after which the flag must be set to true -by adding the following to your `application.rb` file: - - ```ruby - Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - ``` - The schema dumper adds two additional configuration options: * `ActiveRecord::SchemaDumper.ignore_tables` accepts an array of tables that should _not_ be included in any generated schema file. @@ -425,7 +411,7 @@ The schema dumper adds two additional configuration options: * `config.action_controller.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself. -* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to `false` in development mode, `true` in production. +* `config.action_controller.perform_caching` configures whether the application should perform the caching features provided by the Action Controller component or not. Set to `false` in development mode, `true` in production. If it's not specified, the default will be `true`. * `config.action_controller.default_static_extension` configures the extension used for cached pages. Defaults to `.html`. @@ -441,7 +427,7 @@ The schema dumper adds two additional configuration options: * `config.action_controller.per_form_csrf_tokens` configures whether CSRF tokens are only valid for the method/action they were generated for. -* `config.action_controller.default_protect_from_forgery` determines whether forgery protection is added on `ActionController:Base`. This is false by default, but enabled when loading defaults for Rails 5.2. +* `config.action_controller.default_protect_from_forgery` determines whether forgery protection is added on `ActionController:Base`. This is false by default. * `config.action_controller.relative_url_root` can be used to tell Rails that you are [deploying to a subdirectory](configuring.html#deploy-to-a-subdirectory-relative-url-root). The default is `ENV['RAILS_RELATIVE_URL_ROOT']`. @@ -599,7 +585,7 @@ Defaults to `'signed cookie'`. The default setting is `true`, which uses the partial at `/admin/articles/_article.erb`. Setting the value to `false` would render `/articles/_article.erb`, which is the same behavior as rendering from a non-namespaced controller such as `ArticlesController`. * `config.action_view.raise_on_missing_translations` determines whether an - error should be raised for missing translations. + error should be raised for missing translations. This defaults to `false`. * `config.action_view.automatically_disable_submit_tag` determines whether `submit_tag` should automatically disable on click, this defaults to `true`. @@ -608,16 +594,32 @@ Defaults to `'signed cookie'`. * `config.action_view.form_with_generates_remote_forms` determines whether `form_with` generates remote forms or not. This defaults to `true`. -* `config.action_view.form_with_generates_ids` determines whether `form_with` generates ids on inputs. This defaults to `true`. +* `config.action_view.form_with_generates_ids` determines whether `form_with` generates ids on inputs. This defaults to `false`. * `config.action_view.default_enforce_utf8` determines whether forms are generated with a hidden tag that forces older versions of Internet Explorer to submit forms encoded in UTF-8. This defaults to `false`. -* `config.action_view.finalize_compiled_template_methods` determines - whether the methods on `ActionView::CompiledTemplates` that templates - compile themselves to are removed when template instances are - destroyed by the garbage collector. This helps prevent memory leaks in - development mode, but for large test suites, disabling this option in - the test environment can improve performance. This defaults to `true`. + +### Configuring Action Mailbox + +`config.action_mailbox` provides the following configuration options: + +* `config.action_mailbox.logger` contains the logger used by Action Mailbox. It accepts a logger conforming to the interface of Log4r or the default Ruby Logger class. The default is `Rails.logger`. + + ```ruby + config.action_mailbox.logger = ActiveSupport::Logger.new(STDOUT) + ``` + +* `config.action_mailbox.incinerate_after` accepts an `ActiveSupport::Duration` indicating how long after processing `ActionMailbox::InboundEmail` records should be destroyed. It defaults to `30.days`. + + ```ruby + # Incinerate inbound emails 14 days after processing. + config.action_mailbox.incinerate_after = 14.days + ``` + +* `config.action_mailbox.queues.incineration` accepts a symbol indicating the Active Job queue to use for incineration jobs. It defaults to `:action_mailbox_incineration`. + +* `config.action_mailbox.queues.routing` accepts a symbol indicating the Active Job queue to use for routing jobs. It defaults to `:action_mailbox_routing`. + ### Configuring Action Mailer @@ -696,7 +698,9 @@ There are a number of settings available on `config.action_mailer`: * `config.action_mailer.deliver_later_queue_name` specifies the queue name for mailers. By default this is `mailers`. -* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. By default this is `false` in all environments. +* `config.action_mailer.perform_caching` specifies whether the mailer templates should perform fragment caching or not. If it's not specified, the default will be `true`. + +* `config.action_mailer.delivery_job` specifies delivery job for mail. Defaults to `ActionMailer::DeliveryJob`. ### Configuring Active Support @@ -715,7 +719,7 @@ There are a few configuration options available in Active Support: * `config.active_support.use_sha1_digests` specifies whether to use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. Defaults to false. -* `config.active_support.use_authenticated_message_encryption` specifies whether to use AES-256-GCM authenticated encryption as the default cipher for encrypting messages instead of AES-256-CBC. This is false by default, but enabled when loading defaults for Rails 5.2. +* `config.active_support.use_authenticated_message_encryption` specifies whether to use AES-256-GCM authenticated encryption as the default cipher for encrypting messages instead of AES-256-CBC. This is false by default. * `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`. @@ -725,13 +729,13 @@ There are a few configuration options available in Active Support: * `ActiveSupport::Deprecation.silence` takes a block in which all deprecation warnings are silenced. -* `ActiveSupport::Deprecation.silenced` sets whether or not to display deprecation warnings. +* `ActiveSupport::Deprecation.silenced` sets whether or not to display deprecation warnings. The default is `false`. ### Configuring Active Job `config.active_job` provides the following configuration options: -* `config.active_job.queue_adapter` sets the adapter for the queuing backend. The default adapter is `:async`. For an up-to-date list of built-in adapters see the [ActiveJob::QueueAdapters API documentation](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). +* `config.active_job.queue_adapter` sets the adapter for the queuing backend. The default adapter is `:async`. For an up-to-date list of built-in adapters see the [ActiveJob::QueueAdapters API documentation](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). ```ruby # Be sure to have the adapter's gem in your Gemfile @@ -795,6 +799,9 @@ main application. You can set this as nil to not mount Action Cable as part of your normal Rails server. +You can find more detailed configuration options in the +[Action Cable Overview](action_cable_overview.html#configuration). + ### Configuring Active Storage @@ -815,15 +822,21 @@ normal Rails server. config.active_storage.paths[:ffprobe] = '/usr/local/bin/ffprobe' ``` -* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/vnd.adobe.photoshop image/vnd.microsoft.icon)`. +* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/pjpeg image/tiff image/vnd.adobe.photoshop image/vnd.microsoft.icon)`. * `config.active_storage.content_types_to_serve_as_binary` accepts an array of strings indicating the content types that Active Storage will always serve as an attachment, rather than inline. The default is `%w(text/html text/javascript image/svg+xml application/postscript application/x-shockwave-flash text/xml application/xml application/xhtml+xml)`. -* `config.active_storage.queue` can be used to set the name of the Active Job queue used to perform jobs like analyzing the content of a blob or purging a blog. +* `config.active_storage.queues.analysis` accepts a symbol indicating the Active Job queue to use for analysis jobs. When this option is `nil`, analysis jobs are sent to the default Active Job queue (see `config.active_job.default_queue_name`). + + ```ruby + config.active_storage.queues.analysis = :low_priority + ``` + +* `config.active_storage.queues.purge` accepts a symbol indicating the Active Job queue to use for purge jobs. When this option is `nil`, purge jobs are sent to the default Active Job queue (see `config.active_job.default_queue_name`). ```ruby - config.active_storage.queue = :low_priority + config.active_storage.queues.purge = :low_priority ``` * `config.active_storage.logger` can be used to set the logger used by Active Storage. Accepts a logger conforming to the interface of Log4r or the default Ruby Logger class. @@ -847,6 +860,39 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla The default is `/rails/active_storage` +### Results of `load_defaults` + +#### With '5.0': + +- `config.action_controller.per_form_csrf_tokens`: `true` +- `config.action_controller.forgery_protection_origin_check`: `true` +- `ActiveSupport.to_time_preserves_timezone`: `true` +- `config.active_record.belongs_to_required_by_default`: `true` +- `config.ssl_options`: `{ hsts: { subdomains: true } }` + +#### With '5.1': + +- `config.assets.unknown_asset_fallback`: `false` +- `config.action_view.form_with_generates_remote_forms`: `true` + +#### With '5.2': + +- `config.active_record.cache_versioning`: `true` +- `action_dispatch.use_authenticated_cookie_encryption`: `true` +- `config.active_support.use_authenticated_message_encryption`: `true` +- `config.active_support.use_sha1_digests`: `true` +- `config.action_controller.default_protect_from_forgery`: `true` +- `config.action_view.form_with_generates_ids`: `true` + +#### With '6.0': + +- `config.action_view.default_enforce_utf8`: `false` +- `config.action_dispatch.use_cookies_with_metadata`: `true` +- `config.action_mailer.delivery_job`: `"ActionMailer::MailDeliveryJob"` +- `config.active_job.return_false_on_aborted_enqueue`: `true` +- `config.active_storage.queues.analysis`: `:active_storage_analysis` +- `config.active_storage.queues.purge`: `:active_storage_purge` + ### Configuring a Database Just about every Rails application will interact with a database. You can connect to the database by setting an environment variable `ENV['DATABASE_URL']` or by using a configuration file called `config/database.yml`. @@ -1118,7 +1164,7 @@ Imagine you have a server which mirrors the production environment but is only u That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console -e staging`, `Rails.env.staging?` works, etc. -### Deploy to a subdirectory (relative url root) +### Deploy to a subdirectory (relative URL root) By default Rails expects that your application is running at the root (eg. `/`). This section explains how to run your application inside a directory. @@ -1197,6 +1243,8 @@ Using Initializer Files After loading the framework and any gems in your application, Rails turns to loading initializers. An initializer is any Ruby file stored under `config/initializers` in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and gems are loaded, such as options to configure settings for these parts. +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. + NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down. TIP: While Rails supports numbering of initializer file names for load ordering purposes, a better technique is to place any code that need to load in a specific order within the same file. This reduces file name churn, makes dependencies more explicit, and can help surface new concepts within your application. @@ -1404,7 +1452,7 @@ Custom configuration You can configure your own code through the Rails configuration object with custom configuration under either the `config.x` namespace, or `config` directly. The key difference between these two is that you should be using `config.x` if you -are defining _nested_ configuration (ex: `config.x.nested.nested.hi`), and just +are defining _nested_ configuration (ex: `config.x.nested.hi`), and just `config` for _single level_ configuration (ex: `config.hello`). ```ruby diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 709a5146e9..f86589bdf1 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -16,7 +16,7 @@ After reading this guide, you will know: Ruby on Rails is not "someone else's framework." Over the years, thousands of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches. As mentioned in [Rails' -README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/). +README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](https://rubyonrails.org/conduct/). -------------------------------------------------------------------------------- @@ -53,7 +53,7 @@ You can then share your executable test case as a [gist](https://gist.github.com ### Special Treatment for Security Issues -WARNING: Please do not report security vulnerabilities with public GitHub issue reports. The [Rails security policy page](http://rubyonrails.org/security) details the procedure to follow for security issues. +WARNING: Please do not report security vulnerabilities with public GitHub issue reports. The [Rails security policy page](https://rubyonrails.org/security) details the procedure to follow for security issues. ### What about Feature Requests? @@ -247,7 +247,7 @@ Rails follows a simple set of coding style conventions: * Two spaces, no tabs (for indentation). * No trailing whitespace. Blank lines should not have any spaces. -* Indent after private/protected. +* Indent and no blank line after private/protected. * Use Ruby >= 1.9 syntax for hashes. Prefer `{ a: :b }` over `{ :a => :b }`. * Prefer `&&`/`||` over `and`/`or`. * Prefer class << self over self.method for class methods. @@ -302,7 +302,7 @@ the recommended workflow with the [rails-dev-box](https://github.com/rails/rails As a compromise, test what your code obviously affects, and if the change is not in railties, run the whole test suite of the affected component. If all tests are passing, that's enough to propose your contribution. We have -[Travis CI](https://travis-ci.org/rails/rails) as a safety net for catching +[Buildkite](https://buildkite.com/rails/rails) as a safety net for catching unexpected breakages elsewhere. #### Entire Rails: @@ -418,7 +418,7 @@ To run a single test against all adapters, use: $ bundle exec rake TEST=test/cases/associations/has_many_associations_test.rb ``` -You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` also. See the file `activerecord/RUNNING_UNIT_TESTS.rdoc` for information on running more targeted database tests, or the file `ci/travis.rb` for the test suite run by the continuous integration server. +You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` also. See the file `activerecord/RUNNING_UNIT_TESTS.rdoc` for information on running more targeted database tests. ### Warnings @@ -677,11 +677,11 @@ $ git apply ~/my_changes.patch This works well for simple changes. However, if your changes are complicated or if the code in master has deviated significantly from your target branch, it might require more work on your part. The difficulty of a backport varies greatly from case to case, and sometimes it is simply not worth the effort. -Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the Ruby versions listed in `.travis.yml` before submitting your pull request. +Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the oldest Ruby version permitted by the target branch's `rails.gemspec` before submitting your pull request. And then... think about your next contribution! Rails Contributors ------------------ -All contributions get credit in [Rails Contributors](http://contributors.rubyonrails.org). +All contributions get credit in [Rails Contributors](https://contributors.rubyonrails.org). diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 3a383cbd4d..170c22905b 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -147,7 +147,7 @@ TIP: The default Rails log level is `debug` in all environments. ### Sending Messages -To write in the current log use the `logger.(debug|info|warn|error|fatal)` method from within a controller, model, or mailer: +To write in the current log use the `logger.(debug|info|warn|error|fatal|unknown)` method from within a controller, model, or mailer: ```ruby logger.debug "Person attributes hash: #{@person.attributes.inspect}" @@ -962,15 +962,8 @@ Plugins for Debugging There are some Rails plugins to help you to find errors and debug your application. Here is a list of useful plugins for debugging: -* [Footnotes](https://github.com/josevalim/rails-footnotes) Every Rails page has -footnotes that give request information and link back to your source via -TextMate. * [Query Trace](https://github.com/ruckus/active-record-query-trace/tree/master) Adds query origin tracing to your logs. -* [Query Reviewer](https://github.com/nesquena/query_reviewer) This Rails plugin -not only runs "EXPLAIN" before each of your select queries in development, but -provides a small DIV in the rendered output of each page with the summary of -warnings for each query that it analyzed. * [Exception Notifier](https://github.com/smartinez87/exception_notification/tree/master) Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index b3baf726e3..e84e5561f2 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -227,7 +227,6 @@ If you're using another database, check the file `activerecord/test/config.yml` If you installed Yarn, you will need to install the javascript dependencies: ```bash -$ cd activestorage $ yarn install ``` diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 25c159d471..1e67b2bce7 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -74,7 +74,17 @@ - name: Action Mailer Basics url: action_mailer_basics.html - description: This guide describes how to use Action Mailer to send and receive emails. + description: This guide describes how to use Action Mailer to send emails. + - + name: Action Mailbox Basics + work_in_progress: true + url: action_mailbox_basics.html + description: This guide describes how to use Action Mailbox to receive emails. + - + name: Action Text Overview + work_in_progress: true + url: action_text_overview.html + description: This guide describes how to use Action Text to handle rich text content. - name: Active Job Basics url: active_job_basics.html @@ -145,6 +155,11 @@ name: Using Rails for API-only Applications url: api_app.html description: This guide explains how to effectively use Rails to develop a JSON API application. + - + name: Active Record and PostgreSQL + work_in_progress: true + url: active_record_postgresql.html + description: This guide covers PostgreSQL specific usage of Active Record. - name: Extending Rails diff --git a/guides/source/engines.md b/guides/source/engines.md index 1e93a19c84..3031c62928 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -217,10 +217,8 @@ important parts about namespacing, and is discussed later in the #### `app` Directory Inside the `app` directory are the standard `assets`, `controllers`, `helpers`, -`mailers`, `models` and `views` directories that you should be familiar with -from an application. The `helpers`, `mailers` and `models` directories are -empty, so they aren't described in this section. We'll look more into models in -a future section, when we're writing the engine. +`jobs`, `mailers`, `models`, and `views` directories that you should be familiar with +from an application. We'll look more into models in a future section, when we're writing the engine. Within the `app/assets` directory, there are the `images`, `javascripts` and `stylesheets` directories which, again, you should be familiar with due to their @@ -261,6 +259,30 @@ WARNING: Don't use `require` because it will break the automatic reloading of cl in the development environment - using `require_dependency` ensures that classes are loaded and unloaded in the correct manner. +Within the `app/helpers` directory there is a `blorgh` directory that +contains a file called `application_helper.rb`. This file will provide any +common functionality for the helpers of the engine. The `blorgh` directory +is where the other helpers for the engine will go. By placing them within +this namespaced directory, you prevent them from possibly clashing with +identically-named helpers within other engines or even within the +application. + +Within the `app/jobs` directory there is a `blorgh` directory that +contains a file called `application_job.rb`. This file will provide any +common functionality for the jobs of the engine. The `blorgh` directory +is where the other jobs for the engine will go. By placing them within +this namespaced directory, you prevent them from possibly clashing with +identically-named jobs within other engines or even within the +application. + +Within the `app/mailers` directory there is a `blorgh` directory that +contains a file called `application_mailer.rb`. This file will provide any +common functionality for the mailers of the engine. The `blorgh` directory +is where the other mailers for the engine will go. By placing them within +this namespaced directory, you prevent them from possibly clashing with +identically-named mailers within other engines or even within the +application. + Lastly, the `app/views` directory contains a `layouts` folder, which contains a file at `blorgh/application.html.erb`. This file allows you to specify a layout for the engine. If this engine is to be used as a stand-alone engine, then you @@ -347,14 +369,11 @@ create app/views/blorgh/articles/new.html.erb create app/views/blorgh/articles/_form.html.erb invoke test_unit create test/controllers/blorgh/articles_controller_test.rb +create test/system/blorgh/articles_test.rb invoke helper create app/helpers/blorgh/articles_helper.rb -invoke test_unit -create test/application_system_test_case.rb -create test/system/articles_test.rb +invoke test_unit invoke assets -invoke js -create app/assets/javascripts/blorgh/articles.js invoke css create app/assets/stylesheets/blorgh/articles.css invoke css @@ -394,9 +413,8 @@ be isolated from those routes that are within the application. The Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::ArticlesController` (at `app/controllers/blorgh/articles_controller.rb`) and its related views at -`app/views/blorgh/articles`. This generator also generates a test for the -controller (`test/controllers/blorgh/articles_controller_test.rb`) and a helper -(`app/helpers/blorgh/articles_helper.rb`). +`app/views/blorgh/articles`. This generator also generates tests for the +controller (`test/controllers/blorgh/articles_controller_test.rb` and `test/system/blorgh/articles_test.rb`) and a helper (`app/helpers/blorgh/articles_helper.rb`). Everything this generator has created is neatly namespaced. The controller's class is defined within the `Blorgh` module: @@ -425,10 +443,7 @@ end This helps prevent conflicts with any other engine or application that may have an article resource as well. -Finally, the assets for this resource are generated in two files: -`app/assets/javascripts/blorgh/articles.js` and -`app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little -later. +Finally, the assets for this resource are generated in one file: `app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little later. You can see what the engine has so far by running `rails db:migrate` at the root of our engine to run the migration generated by the scaffold generator, and then @@ -579,9 +594,8 @@ invoke test_unit create test/controllers/blorgh/comments_controller_test.rb invoke helper create app/helpers/blorgh/comments_helper.rb +invoke test_unit invoke assets -invoke js -create app/assets/javascripts/blorgh/comments.js invoke css create app/assets/stylesheets/blorgh/comments.css ``` @@ -1091,16 +1105,15 @@ main Rails application. Engine model and controller classes can be extended by open classing them in the main Rails application (since model and controller classes are just Ruby classes that inherit Rails specific functionality). Open classing an Engine class -redefines it for use in the main application. This is usually implemented by -using the decorator pattern. +redefines it for use in the main application. For simple class modifications, use `Class#class_eval`. For complex class modifications, consider using `ActiveSupport::Concern`. -#### A note on Decorators and Loading Code +#### A note on Overriding and Loading Code -Because these decorators are not referenced by your Rails application itself, -Rails' autoloading system will not kick in and load your decorators. This means +Because these overrides are not referenced by your Rails application itself, +Rails' autoloading system will not kick in and load your overrides. This means that you need to require them yourself. Here is some sample code to do this: @@ -1112,7 +1125,7 @@ module Blorgh isolate_namespace Blorgh config.to_prepare do - Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| + Dir.glob(Rails.root + "app/overrides/**/*_override*.rb").each do |c| require_dependency(c) end end @@ -1120,15 +1133,15 @@ module Blorgh end ``` -This doesn't apply to just Decorators, but anything that you add in an engine +This doesn't apply to just overrides, but anything that you add in an engine that isn't referenced by your main application. -#### Implementing Decorator Pattern Using Class#class_eval +#### Reopening existing classes using Class#class_eval **Adding** `Article#time_since_created`: ```ruby -# MyApp/app/decorators/models/blorgh/article_decorator.rb +# MyApp/app/overrides/models/blorgh/article_override.rb Blorgh::Article.class_eval do def time_since_created @@ -1138,10 +1151,11 @@ end ``` ```ruby -# Blorgh/app/models/article.rb - -class Article < ApplicationRecord - has_many :comments +# Blorgh/app/models/blorgh/article.rb +module Blorgh + class Article < ApplicationRecord + has_many :comments + end end ``` @@ -1149,7 +1163,7 @@ end **Overriding** `Article#summary`: ```ruby -# MyApp/app/decorators/models/blorgh/article_decorator.rb +# MyApp/app/overrides/models/blorgh/article_override.rb Blorgh::Article.class_eval do def summary @@ -1159,21 +1173,22 @@ end ``` ```ruby -# Blorgh/app/models/article.rb - -class Article < ApplicationRecord - has_many :comments - def summary - "#{title}" +# Blorgh/app/models/blorgh/article.rb +module Blorgh + class Article < ApplicationRecord + has_many :comments + def summary + "#{title}" + end end end ``` -#### Implementing Decorator Pattern Using ActiveSupport::Concern +#### Reopening existing classes using ActiveSupport::Concern Using `Class#class_eval` is great for simple adjustments, but for more complex class modifications, you might want to consider using [`ActiveSupport::Concern`] -(http://api.rubyonrails.org/classes/ActiveSupport/Concern.html). +(https://api.rubyonrails.org/classes/ActiveSupport/Concern.html). ActiveSupport::Concern manages load order of interlinked dependent modules and classes at run time allowing you to significantly modularize your code. @@ -1196,10 +1211,11 @@ end ``` ```ruby -# Blorgh/app/models/article.rb - -class Article < ApplicationRecord - include Blorgh::Concerns::Models::Article +# Blorgh/app/models/blorgh/article.rb +module Blorgh + class Article < ApplicationRecord + include Blorgh::Concerns::Models::Article + end end ``` @@ -1402,7 +1418,7 @@ s.add_development_dependency "moo" Both kinds of dependencies will be installed when `bundle install` is run inside of the application. The development dependencies for the gem will only be used -when the tests for the engine are running. +when the development and tests for the engine are running. Note that if you want to immediately require dependencies when the engine is required, you should require them before the engine's initialization. For @@ -1498,6 +1514,8 @@ To hook into the initialization process of one of the following classes use the | Class | Available Hooks | | --------------------------------- | ------------------------------------ | | `ActionCable` | `action_cable` | +| `ActionCable::Channel::Base` | `action_cable_channel` | +| `ActionCable::Connection::Base` | `action_cable_connection` | | `ActionController::API` | `action_controller_api` | | `ActionController::API` | `action_controller` | | `ActionController::Base` | `action_controller_base` | @@ -1505,13 +1523,20 @@ To hook into the initialization process of one of the following classes use the | `ActionController::TestCase` | `action_controller_test_case` | | `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` | | `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` | +| `ActionMailbox::Base` | `action_mailbox` | +| `ActionMailbox::InboundEmail` | `action_mailbox_inbound_email` | +| `ActionMailbox::TestCase` | `action_mailbox_test_case` | | `ActionMailer::Base` | `action_mailer` | | `ActionMailer::TestCase` | `action_mailer_test_case` | +| `ActionText::Content` | `action_text_content` | +| `ActionText::RichText` | `action_text_rich_text` | | `ActionView::Base` | `action_view` | | `ActionView::TestCase` | `action_view_test_case` | | `ActiveJob::Base` | `active_job` | | `ActiveJob::TestCase` | `active_job_test_case` | | `ActiveRecord::Base` | `active_record` | +| `ActiveStorage::Attachment` | `active_storage_attachment` | +| `ActiveStorage::Blob` | `active_storage_blob` | | `ActiveSupport::TestCase` | `active_support_test_case` | | `i18n` | `i18n` | @@ -1528,4 +1553,6 @@ These are the available configuration hooks. They do not hook into any particula ### Example -`config.before_configuration { puts 'I am called before any initializers' }` +```ruby +config.before_configuration { puts 'I am called before any initializers' } +``` diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index b5e2c49487..6418005921 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -17,7 +17,7 @@ After reading this guide, you will know: -------------------------------------------------------------------------------- -NOTE: This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit [the Rails API documentation](http://api.rubyonrails.org/) for a complete reference. +NOTE: This guide is not intended to be a complete documentation of available form helpers and their arguments. Please visit [the Rails API documentation](https://api.rubyonrails.org/) for a complete reference. Dealing with Basic Forms ------------------------ @@ -89,7 +89,7 @@ value entered by the user for that field. For example, if the form contains `<%= text_field_tag(:query) %>`, then you would be able to get the value of this field in the controller with `params[:query]`. -When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in `params`. You can read more about them in chapter [Understanding Parameter Naming Conventions](#understanding-parameter-naming-conventions) of this guide. For details on the precise usage of these helpers, please refer to the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html). +When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in `params`. You can read more about them in chapter [Understanding Parameter Naming Conventions](#understanding-parameter-naming-conventions) of this guide. For details on the precise usage of these helpers, please refer to the [API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html). #### Checkboxes @@ -248,7 +248,7 @@ There are a few things to note here: * `@article` is the actual object being edited. * There is a single hash of options. HTML options (except `id` and `class`) are passed in the `:html` hash. Also you can provide a `:namespace` option for your form to ensure uniqueness of id attributes on form elements. The scope attribute will be prefixed with underscore on the generated HTML id. * The `form_with` method yields a **form builder** object (the `f` variable). -* If you wish to direct your form request to a particular url, you would use `form_with url: my_nifty_url_path` instead. To see more in depth options on what `form_with` accepts be sure to [check out the API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with). +* If you wish to direct your form request to a particular URL, you would use `form_with url: my_nifty_url_path` instead. To see more in depth options on what `form_with` accepts be sure to [check out the API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with). * Methods to create form controls are called **on** the form builder object `f`. The resulting HTML is: @@ -527,13 +527,13 @@ NOTE: Pairs passed to `options_for_select` should have the text first and the va ### Time Zone and Country Select -To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined [`ActiveSupport::TimeZone`](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this: +To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined [`ActiveSupport::TimeZone`](https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this: ```erb <%= time_zone_select(:person, :time_zone) %> ``` -There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-time_zone_options_for_select) to learn about the possible arguments for these two methods. +There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the [API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-time_zone_options_for_select) to learn about the possible arguments for these two methods. Rails _used_ to have a `country_select` helper for choosing countries, but this has been extracted to the [country_select plugin](https://github.com/stefanpenner/country_select). @@ -604,7 +604,7 @@ When this is passed to `Person.new` (or `update`), Active Record spots that thes ### Common Options -Both families of helpers use the same core set of functions to generate the individual select tags and so both accept largely the same options. In particular, by default Rails will generate year options 5 years either side of the current year. If this is not an appropriate range, the `:start_year` and `:end_year` options override this. For an exhaustive list of the available options, refer to the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html). +Both families of helpers use the same core set of functions to generate the individual select tags and so both accept largely the same options. In particular, by default Rails will generate year options 5 years either side of the current year. If this is not an appropriate range, the `:start_year` and `:end_year` options override this. For an exhaustive list of the available options, refer to the [API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html). As a rule of thumb you should be using `date_select` when working with model objects and `select_date` in other cases, such as a search form which filters results by date. @@ -642,7 +642,7 @@ Rails provides the usual pair of helpers: the barebones `file_field_tag` and the ### What Gets Uploaded -The object in the `params` hash is an instance of [`ActionDispatch::Http::UploadedFile`](http://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html). The following snippet saves the uploaded file in `#{Rails.root}/public/uploads` under the same name as the original file. +The object in the `params` hash is an instance of [`ActionDispatch::Http::UploadedFile`](https://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html). The following snippet saves the uploaded file in `#{Rails.root}/public/uploads` under the same name as the original file. ```ruby def upload @@ -658,7 +658,7 @@ Once a file has been uploaded, there are a multitude of potential tasks, ranging Customizing Form Builders ------------------------- -The object yielded by `form_with` and `fields_for` is an instance of [`ActionView::Helpers::FormBuilder`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html). Form builders encapsulate the notion of displaying form elements for a single object. While you can write helpers for your forms in the usual way, you can also create subclass `ActionView::Helpers::FormBuilder` and add the helpers there. For example: +The object yielded by `form_with` and `fields_for` is an instance of [`ActionView::Helpers::FormBuilder`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html). Form builders encapsulate the notion of displaying form elements for a single object. While you can write helpers for your forms in the usual way, you can also create subclass `ActionView::Helpers::FormBuilder` and add the helpers there. For example: ```erb <%= form_with model: @person do |f| %> diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index e2f558d74c..1f6744caba 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -90,7 +90,7 @@ $ ruby -v ruby 2.5.0 ``` -Rails requires Ruby version 2.4.1 or later. If the version number returned is +Rails requires Ruby version 2.5.0 or later. If the version number returned is less than that number, you'll need to install a fresh copy of Ruby. TIP: To quickly install Ruby and Ruby on Rails on your system in Windows, you can use @@ -126,7 +126,7 @@ run the following: $ rails --version ``` -If it says something like "Rails 5.2.1", you are ready to continue. +If it says something like "Rails 6.0.0", you are ready to continue. ### Creating the Blog Application @@ -205,12 +205,10 @@ $ rails server TIP: If you are using Windows, you have to pass the scripts under the `bin` folder directly to the Ruby interpreter e.g. `ruby bin\rails server`. -TIP: Compiling CoffeeScript and JavaScript asset compression requires you +TIP: Compiling JavaScript asset compression requires you have a JavaScript runtime available on your system, in the absence of a runtime you will see an `execjs` error during asset compilation. Usually macOS and Windows come with a JavaScript runtime installed. -Rails adds the `mini_racer` gem to the generated `Gemfile` in a -commented line for new apps and you can uncomment if you need it. `therubyrhino` is the recommended runtime for JRuby users and is added by default to the `Gemfile` in apps generated under JRuby. You can investigate all the supported runtimes at [ExecJS](https://github.com/rails/execjs#readme). @@ -461,22 +459,19 @@ available, Rails will raise an exception. Let's look at the full error message again: ->ArticlesController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not… nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot. +>ArticlesController#new is missing a template for request formats: text/html -That's quite a lot of text! Let's quickly go through and understand what each -part of it means. +>NOTE! +>Unless told otherwise, Rails expects an action to render a template with the same name, contained in a folder named after its controller. If this controller is an API responding with 204 (No Content), which does not require a template, then this error will occur when trying to access it via browser, since we expect an HTML template to be rendered for such requests. If that's the case, carry on. -The first part identifies which template is missing. In this case, it's the +The message identifies which template is missing. In this case, it's the `articles/new` template. Rails will first look for this template. If not found, -then it will attempt to load a template called `application/new`. It looks for -one here because the `ArticlesController` inherits from `ApplicationController`. +then it will attempt to load a template called `application/new`, because the +`ArticlesController` inherits from `ApplicationController`. -The next part of the message contains `request.formats` which specifies -the format of template to be served in response. It is set to `text/html` as we -requested this page via browser, so Rails is looking for an HTML template. -`request.variant` specifies what kind of physical devices would be served by -the response and helps Rails determine which template to use in the response. -It is empty because no information has been provided. +Next the message contains `request.formats` which specifies the format of +template to be served in response. It is set to `text/html` as we requested +this page via browser, so Rails is looking for an HTML template. The simplest template that would work in this case would be one located at `app/views/articles/new.html.erb`. The extension of this file name is important: @@ -686,7 +681,7 @@ If you look in the `db/migrate/YYYYMMDDHHMMSS_create_articles.rb` file (remember, yours will have a slightly different name), here's what you'll find: ```ruby -class CreateArticles < ActiveRecord::Migration[5.0] +class CreateArticles < ActiveRecord::Migration[6.0] def change create_table :articles do |t| t.string :title @@ -1212,7 +1207,7 @@ view above, will cause form helpers to fill in form fields with the correspondin values of the object. Passing in a symbol scope such as `scope: :article`, as was done in the new view, only creates empty form fields. More details can be found in [form_with documentation] -(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with). +(https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with). Next, we need to create the `update` action in `app/controllers/articles_controller.rb`. @@ -1348,7 +1343,7 @@ to stand in for either of the other forms is that `@article` is a *resource* corresponding to a full set of RESTful routes, and Rails is able to infer which URI and method to use. For more information about this use of `form_with`, see [Resource-oriented style] -(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with-label-Resource-oriented+style). +(https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with-label-Resource-oriented+style). Now, let's update the `app/views/articles/new.html.erb` view to use this new partial, rewriting it completely: @@ -1558,12 +1553,12 @@ In addition to the model, Rails has also made a migration to create the corresponding database table: ```ruby -class CreateComments < ActiveRecord::Migration[5.0] +class CreateComments < ActiveRecord::Migration[6.0] def change create_table :comments do |t| t.string :commenter t.text :body - t.references :article, foreign_key: true + t.references :article, null: false, foreign_key: true t.timestamps end @@ -1655,7 +1650,7 @@ controller. Again, we'll use the same generator we used before: $ rails generate controller Comments ``` -This creates five files and one empty directory: +This creates four files and one empty directory: | File/Directory | Purpose | | -------------------------------------------- | ---------------------------------------- | diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 10b1a6de7e..5d91a6df33 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -139,10 +139,12 @@ Note that appending directly to `I18n.load_paths` instead of to the application' ### Managing the Locale across Requests -The default locale is used for all translations unless `I18n.locale` is explicitly set. - A localized application will likely need to provide support for multiple locales. To accomplish this, the locale should be set at the beginning of each request so that all strings are translated using the desired locale during the lifetime of that request. +The default locale is used for all translations unless `I18n.locale=` or `I18n.with_locale` is used. + +`I18n.locale` can leak into subsequent requests served by the same thread/process if it is not consistently set in every controller. For example executing `I18n.locale = :es` in one POST requests will have effects for all later requests to controllers that don't set the locale, but only in that particular thread/process. For that reason, instead of `I18n.locale =` you can use `I18n.with_locale` which does not have this leak issue. + The locale can be set in an `around_action` in the `ApplicationController`: ```ruby @@ -220,7 +222,7 @@ This approach has almost the same set of advantages as setting the locale from t Getting the locale from `params` and setting it accordingly is not hard; including it in every URL and thus **passing it through the requests** is. To include an explicit option in every URL, e.g. `link_to(books_url(locale: I18n.locale))`, would be tedious and probably impossible, of course. -Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its [`ApplicationController#default_url_options`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Base.html#method-i-default_url_options), which is useful precisely in this scenario: it enables us to set "defaults" for [`url_for`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for) and helper methods dependent on it (by implementing/overriding `default_url_options`). +Rails contains infrastructure for "centralizing dynamic decisions about the URLs" in its [`ApplicationController#default_url_options`](https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Base.html#method-i-default_url_options), which is useful precisely in this scenario: it enables us to set "defaults" for [`url_for`](https://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for) and helper methods dependent on it (by implementing/overriding `default_url_options`). We can include something like this in our `ApplicationController` then: @@ -235,7 +237,7 @@ Every helper method dependent on `url_for` (e.g. helpers for named routes like ` You may be satisfied with this. It does impact the readability of URLs, though, when the locale "hangs" at the end of every URL in your application. Moreover, from the architectural standpoint, locale is usually hierarchically above the other parts of the application domain: and URLs should reflect this. -You probably want URLs to look like this: `http://www.example.com/en/books` (which loads the English locale) and `http://www.example.com/nl/books` (which loads the Dutch locale). This is achievable with the "over-riding `default_url_options`" strategy from above: you just have to set up your routes with [`scope`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html): +You probably want URLs to look like this: `http://www.example.com/en/books` (which loads the English locale) and `http://www.example.com/nl/books` (which loads the Dutch locale). This is achievable with the "over-riding `default_url_options`" strategy from above: you just have to set up your routes with [`scope`](https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html): ```ruby # config/routes.rb @@ -299,7 +301,7 @@ A trivial implementation of using an `Accept-Language` header would be: def switch_locale(&action) logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}" locale = extract_locale_from_accept_language_header - logger.debug "* Locale set to '#{I18n.locale}'" + logger.debug "* Locale set to '#{locale}'" I18n.with_locale(locale, &action) end @@ -594,7 +596,7 @@ You should have a good understanding of using the i18n library now and know how to internationalize a basic Rails application. In the following chapters, we'll cover its features in more depth. -These chapters will show examples using both the `I18n.translate` method as well as the [`translate` view helper method](http://api.rubyonrails.org/classes/ActionView/Helpers/TranslationHelper.html#method-i-translate) (noting the additional feature provide by the view helper method). +These chapters will show examples using both the `I18n.translate` method as well as the [`translate` view helper method](https://api.rubyonrails.org/classes/ActionView/Helpers/TranslationHelper.html#method-i-translate) (noting the additional feature provide by the view helper method). Covered are features like these: @@ -990,27 +992,6 @@ So, for example, instead of the default error message `"cannot be blank"` you co | numericality | :odd | :odd | - | | numericality | :even | :even | - | -#### Translations for the Active Record `error_messages_for` Helper - -If you are using the Active Record `error_messages_for` helper, you will want to add -translations for it. - -Rails ships with the following translations: - -```yaml -en: - activerecord: - errors: - template: - header: - one: "1 error prohibited this %{model} from being saved" - other: "%{count} errors prohibited this %{model} from being saved" - body: "There were problems with the following fields:" -``` - -NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form) -gem by adding this line to your `Gemfile`: `gem 'dynamic_form'`. - ### Translations for Action Mailer E-Mail Subjects If you don't pass a subject to the `mail` method, Action Mailer will try to find @@ -1204,7 +1185,7 @@ The I18n API described in this guide is primarily intended for translating inter Several gems can help with this: * [Globalize](https://github.com/globalize/globalize): Store translations on separate translation tables, one for each translated model -* [Mobility](https://github.com/shioyama/mobility): Provides support for storing translations in many formats, including translation tables, json columns (Postgres), etc. +* [Mobility](https://github.com/shioyama/mobility): Provides support for storing translations in many formats, including translation tables, json columns (PostgreSQL), etc. * [Traco](https://github.com/barsoom/traco): Translatable columns for Rails 3 and 4, stored in the model table itself Conclusion diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb index 76f01fea0a..10e388774c 100644 --- a/guides/source/index.html.erb +++ b/guides/source/index.html.erb @@ -1,6 +1,5 @@ -<% content_for :page_title do %> -Ruby on Rails Guides -<% end %> +<% content_for :page_title, "Ruby on Rails Guides" %> +<% content_for :description, "Ruby on Rails Guides" %> <% content_for :header_section do %> <%= render 'welcome' %> diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 1f42d72756..3ffd7ff1ac 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -3,7 +3,7 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title> + <title><%= yield(:page_title) %></title> <link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbolinks-track="reload"> <link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print"> <link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" data-turbolinks-track="reload"> @@ -14,6 +14,13 @@ <script src="javascripts/turbolinks.js" data-turbolinks-track="reload"></script> <script src="javascripts/guides.js" data-turbolinks-track="reload"></script> <script src="javascripts/responsive-tables.js" data-turbolinks-track="reload"></script> + <meta property="og:title" content="<%= yield(:page_title) %>" /> + <meta name="description" content="<%= yield(:description) %>" /> + <meta property="og:description" content="<%= yield(:description) %>" /> + <meta property="og:locale" content="en_US" /> + <meta property="og:site_name" content="Ruby on Rails Guides" /> + <meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" /> + <meta property="og:type" content="website" /> </head> <body class="guide"> <% if @edge %> @@ -23,14 +30,14 @@ <% end %> <div id="topNav"> <div class="wrapper"> - <strong class="more-info-label">More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong> + <strong class="more-info-label">More at <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong> <span class="red-button more-info-button"> More Ruby on Rails </span> <ul class="more-info-links s-hidden"> <li class="more-info"><a href="https://weblog.rubyonrails.org/">Blog</a></li> <li class="more-info"><a href="https://guides.rubyonrails.org/">Guides</a></li> - <li class="more-info"><a href="http://api.rubyonrails.org/">API</a></li> + <li class="more-info"><a href="https://api.rubyonrails.org/">API</a></li> <li class="more-info"><a href="https://stackoverflow.com/questions/tagged/ruby-on-rails">Ask for help</a></li> <li class="more-info"><a href="https://github.com/rails/rails">Contribute on GitHub</a></li> </ul> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index ad08e5a5a9..39935cd2ef 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -97,7 +97,7 @@ If we want to display the properties of all the books in our view, we can do so <%= link_to "New book", new_book_path %> ``` -NOTE: The actual rendering is done by nested classes of the module [`ActionView::Template::Handlers`](http://api.rubyonrails.org/classes/ActionView/Template/Handlers.html). This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. +NOTE: The actual rendering is done by nested classes of the module [`ActionView::Template::Handlers`](https://api.rubyonrails.org/classes/ActionView/Template/Handlers.html). This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. ### Using `render` @@ -296,6 +296,7 @@ Calls to the `render` method generally accept five options: * `:location` * `:status` * `:formats` +* `:variants` ##### The `:content_type` Option @@ -417,6 +418,44 @@ render formats: [:json, :xml] If a template with the specified format does not exist an `ActionView::MissingTemplate` error is raised. +##### The `:variants` Option + +This tells Rails to look for template variations of the same format. +You can specify a list of variants by passing the `:variants` option with a symbol or an array. + +An example of use would be this. + +```ruby +# called in HomeController#index +render variants: [:mobile, :desktop] +``` + +With this set of variants Rails will look for the following set of templates and use the first that exists. + +- `app/views/home/index.html+mobile.erb` +- `app/views/home/index.html+desktop.erb` +- `app/views/home/index.html.erb` + +If a template with the specified format does not exist an `ActionView::MissingTemplate` error is raised. + +Instead of setting the variant on the render call you may also set it on the request object in your controller action. + +```ruby +def index + request.variant = determine_variant +end + +private + +def determine_variant + variant = nil + # some code to determine the variant(s) to use + variant = :mobile if session[:use_mobile] + + variant +end +``` + #### Finding Layouts To find the current layout, Rails first looks for a file in `app/views/layouts` with the same base name as the controller. For example, rendering actions from the `PhotosController` class will use `app/views/layouts/photos.html.erb` (or `app/views/layouts/photos.builder`). If there is no such controller-specific layout, Rails will use `app/views/layouts/application.html.erb` or `app/views/layouts/application.builder`. If there is no `.erb` layout, Rails will use a `.builder` layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. @@ -650,7 +689,7 @@ Just like the `:status` option for `render`, `:status` for `redirect_to` accepts #### The Difference Between `render` and `redirect_to` -Sometimes inexperienced developers think of `redirect_to` as a sort of `goto` command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. +Sometimes inexperienced developers think of `redirect_to` as a sort of `goto` command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request from the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. Consider these actions to see the difference: diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index bc68a555c5..982df26987 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -195,6 +195,12 @@ You can also run commands as a super-user: rails_command "log:clear", sudo: true ``` +You can also run commands that should abort application generation if they fail: + +```ruby +rails_command "db:migrate", abort_on_failure: true +``` + ### route(routing_code) Adds a routing entry to the `config/routes.rb` file. In the steps above, we generated a person scaffold and also removed `README.rdoc`. Now, to make `PeopleController#index` the default page for the application: diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index c33851a0f9..d60e53b052 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -13,7 +13,7 @@ After reading this guide, you will know: -------------------------------------------------------------------------------- -WARNING: This guide assumes a working knowledge of Rack protocol and Rack concepts such as middlewares, url maps, and `Rack::Builder`. +WARNING: This guide assumes a working knowledge of Rack protocol and Rack concepts such as middlewares, URL maps, and `Rack::Builder`. Introduction to Rack -------------------- @@ -35,7 +35,7 @@ application. Any Rack compliant web server should be using ### `rails server` -`rails server` does the basic job of creating a `Rack::Server` object and starting the webserver. +`rails server` does the basic job of creating a `Rack::Server` object and starting the web server. Here's how `rails server` creates an instance of `Rack::Server` diff --git a/guides/source/routing.md b/guides/source/routing.md index 0a0f1b6754..e3a6bbb138 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -260,7 +260,7 @@ In each of these cases, the named routes remain the same as if you did not use ` | PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) | | DELETE | /admin/articles/:id | articles#destroy | article_path(:id) | -TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo' => '/foo#index'`._ +TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo', to: '/foo#index'`._ ### Nested Resources @@ -519,7 +519,7 @@ resources :photos do end ``` -You can leave out the `:on` option, this will create the same member route except that the resource id value will be available in `params[:photo_id]` instead of `params[:id]`. +You can leave out the `:on` option, this will create the same member route except that the resource id value will be available in `params[:photo_id]` instead of `params[:id]`. Route helpers will also be renamed from `preview_photo_url` and `preview_photo_path` to `photo_preview_url` and `photo_preview_path`. #### Adding Collection Routes @@ -1190,6 +1190,33 @@ For example, here's a small section of the `rails routes` output for a RESTful r edit_user GET /users/:id/edit(.:format) users#edit ``` +You can also use the `--expanded` option to turn on the expanded table formatting mode. + +``` +$ rails routes --expanded + +--[ Route 1 ]---------------------------------------------------- +Prefix | users +Verb | GET +URI | /users(.:format) +Controller#Action | users#index +--[ Route 2 ]---------------------------------------------------- +Prefix | +Verb | POST +URI | /users(.:format) +Controller#Action | users#create +--[ Route 3 ]---------------------------------------------------- +Prefix | new_user +Verb | GET +URI | /users/new(.:format) +Controller#Action | users#new +--[ Route 4 ]---------------------------------------------------- +Prefix | edit_user +Verb | GET +URI | /users/:id/edit(.:format) +Controller#Action | users#edit +``` + You can search through your routes with the grep option: -g. This outputs any routes that partially match the URL helper method name, the HTTP verb, or the URL path. ``` @@ -1207,11 +1234,11 @@ $ rails routes -c Comments $ rails routes -c Articles::CommentsController ``` -TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. You can also use --expanded option to turn on the expanded table formatting mode. +TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. ### Testing Routes -Routes should be included in your testing strategy (just like the rest of your application). Rails offers three [built-in assertions](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html) designed to make testing routes simpler: +Routes should be included in your testing strategy (just like the rest of your application). Rails offers three [built-in assertions](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html) designed to make testing routes simpler: * `assert_generates` * `assert_recognizes` diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index 4b56cf6296..67b0e523a7 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -58,7 +58,7 @@ Links to the API (`api.rubyonrails.org`) are processed by the guides generator i Links that include a release tag are left untouched. For example ``` -http://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html +https://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html ``` is not modified. @@ -68,25 +68,25 @@ Please use these in release notes, since they should point to the corresponding If the link does not include a release tag and edge guides are being generated, the domain is replaced by `edgeapi.rubyonrails.org`. For example, ``` -http://api.rubyonrails.org/classes/ActionDispatch/Response.html +https://api.rubyonrails.org/classes/ActionDispatch/Response.html ``` becomes ``` -http://edgeapi.rubyonrails.org/classes/ActionDispatch/Response.html +https://edgeapi.rubyonrails.org/classes/ActionDispatch/Response.html ``` If the link does not include a release tag and release guides are being generated, the Rails version is injected. For example, if we are generating the guides for v5.1.0 the link ``` -http://api.rubyonrails.org/classes/ActionDispatch/Response.html +https://api.rubyonrails.org/classes/ActionDispatch/Response.html ``` becomes ``` -http://api.rubyonrails.org/v5.1.0/classes/ActionDispatch/Response.html +https://api.rubyonrails.org/v5.1.0/classes/ActionDispatch/Response.html ``` Please don't link to `edgeapi.rubyonrails.org` manually. diff --git a/guides/source/security.md b/guides/source/security.md index dbec3cdd2d..22c122d4b9 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -32,27 +32,17 @@ In order to develop secure web applications you have to keep up to date on all l Sessions -------- -A good place to start looking at security is with sessions, which can be vulnerable to particular attacks. +This chapter describes some particular attacks related to sessions, and security measures to protect your session data. ### What are Sessions? -NOTE: _HTTP is a stateless protocol. Sessions make it stateful._ +INFO: Sessions enable the application to maintain user-specific state, while users interact with the application. For example, sessions allow users to authenticate once and remain signed in for future requests. -Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request. -Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application. +Most applications need to keep track of state for users that interact with the application. This could be the contents of a shopping basket, or the user id of the currently logged in user. This kind of user-specific state can be stored in the session. -A session usually consists of a hash of values and a session ID, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session ID. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method: +Rails provides a session object for each user that accesses the application. If the user already has an active session, Rails uses the existing session. Otherwise a new session is created. -```ruby -session[:user_id] = @current_user.id -User.find(session[:user_id]) -``` - -### Session ID - -NOTE: _The session ID is a 32-character random hex string._ - -The session ID is generated using `SecureRandom.hex` which generates a random hex string using platform specific methods (such as OpenSSL, /dev/urandom or Win32 CryptoAPI) for generating cryptographically secure random numbers. Currently it is not feasible to brute-force Rails' session IDs. +NOTE: Read more about sessions and how to use them in [Action Controller Overview Guide](action_controller_overview.html#session). ### Session Hijacking @@ -76,70 +66,43 @@ Hence, the cookie serves as temporary authentication for the web application. An The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from 0.5%-10% of account balance, $0.5-$30 for credit card numbers ($20-$60 with full details), $0.1-$1.5 for identities (Name, SSN & DOB), $20-$50 for retailer accounts, and $6-$10 for cloud service provider accounts, according to the [Symantec Internet Security Threat Report (2017)](https://www.symantec.com/content/dam/symantec/docs/reports/istr-22-2017-en.pdf). -### Session Guidelines - -Here are some general guidelines on sessions. - -* _Do not store large objects in a session_. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below). -This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate. - -* _Critical data should not be stored in session_. If the user clears their cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data. +### Session Storage -### Encrypted Session Storage +NOTE: Rails uses `ActionDispatch::Session::CookieStore` as the default session storage. -NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._ +TIP: Learn more about other session storages in [Action Controller Overview Guide](action_controller_overview.html#session). -The `CookieStore` saves the session hash directly in a cookie on the -client-side. The server retrieves the session hash from the cookie and +Rails `CookieStore` saves the session hash in a cookie on the client-side. +The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications and storage limitations of it: -* Cookies imply a strict size limit of 4kB. This is fine as you should - not store large amounts of data in a session anyway, as described - before. Storing the current user's database id in a session is common - practice. +* Cookies have a size limit of 4kB. Use cookies only for data which is relevant for the session. + +* Cookies are stored on the client-side. The client may preserve cookie contents even for expired cookies. The client may copy cookies to other machines. Avoid storing sensitive data in cookies. + +* Cookies are temporary by nature. The server can set expiration time for the cookie, but the client may delete the cookie and its contents before that. Persist all data that is of more permanent nature on the server side. * Session cookies do not invalidate themselves and can be maliciously reused. It may be a good idea to have your application invalidate old session cookies using a stored timestamp. +* Rails encrypts cookies by default. The client cannot read or edit the contents of the cookie, without breaking encryption. If you take appropriate care of your secrets, you can consider your cookies to be generally secured. + The `CookieStore` uses the -[encrypted](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted) +[encrypted](https://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted) cookie jar to provide a secure, encrypted location to store session data. Cookie-based sessions thus provide both integrity as well as confidentiality to their contents. The encryption key, as well as the verification key used for -[signed](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed) +[signed](https://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed) cookies, is derived from the `secret_key_base` configuration value. -As of Rails 5.2 encrypted cookies and sessions are protected using AES -GCM encryption. This form of encryption is a type of Authenticated -Encryption and couples authentication and encryption in single step -while also producing shorter ciphertexts as compared to other -algorithms previously used. The key for cookies encrypted with AES GCM -are derived using a salt value defined by the -`config.action_dispatch.authenticated_encrypted_cookie_salt` -configuration value. - -Prior to this version, encrypted cookies were secured using AES in CBC -mode with HMAC using SHA1 for authentication. The keys for this type of -encryption and for HMAC verification were derived via the salts defined -by `config.action_dispatch.encrypted_cookie_salt` and -`config.action_dispatch.encrypted_signed_cookie_salt` respectively. - -Prior to Rails version 4 in both versions 2 and 3, session cookies were -protected using only HMAC verification. As such, these session cookies -only provided integrity to their content because the actual session data -was stored in plaintext encoded as base64. This is how `signed` cookies -work in the current version of Rails. These kinds of cookies are still -useful for protecting the integrity of certain client-stored data and -information. - -__Do not use a trivial secret for the `secret_key_base`, i.e. a word -from a dictionary, or one which is shorter than 30 characters! Instead -use `rails secret` to generate secret keys!__ +TIP: Secrets must be long and random. Use `rails secret` to get new unique secrets. + +INFO: Learn more about [managing credentials later in this guide](security.html#custom-credentials) It is also important to use different salt values for encrypted and signed cookies. Using the same value for different salt configuration @@ -150,7 +113,7 @@ In test and development applications get a `secret_key_base` derived from the ap secret_key_base: 492f... -If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. +WARNING: If your application's secrets may have been exposed, strongly consider changing them. Changing `secret_key_base` will expire currently active sessions. ### Rotating Encrypted and Signed Cookies Configurations @@ -192,9 +155,9 @@ rotations going at any one time. For more details on key rotation with encrypted and signed messages as well as the various options the `rotate` method accepts, please refer to the -[MessageEncryptor API](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) +[MessageEncryptor API](https://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html) and -[MessageVerifier API](http://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) +[MessageVerifier API](https://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) documentation. ### Replay Attacks for CookieStore Sessions @@ -1188,7 +1151,7 @@ The same works with `javascript_include_tag`: <%= javascript_include_tag "script", nonce: true %> ``` -Use [`csp_meta_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/CspHelper.html#method-i-csp_meta_tag) +Use [`csp_meta_tag`](https://api.rubyonrails.org/classes/ActionView/Helpers/CspHelper.html#method-i-csp_meta_tag) helper to create a meta tag "csp-nonce" with the per-session nonce value for allowing inline `<script>` tags. @@ -1204,23 +1167,18 @@ loaded inline `<script>` elements. Environmental Security ---------------------- -It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. +It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, master key for `credentials.yml`, and other unencrypted secrets. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. ### Custom credentials -Rails generates a `config/credentials.yml.enc` to store third-party credentials -within the repo. This is only viable because Rails encrypts the file with a master -key that's generated into a version control ignored `config/master.key` — Rails -will also look for that key in `ENV["RAILS_MASTER_KEY"]`. Rails also requires the -key to boot in production, so the credentials can be read. +Rails stores secrets in `config/credentials.yml.enc`, which is encrypted and hence cannot be edited directly. Rails uses `config/master.key` or alternatively looks for environment variable `ENV["RAILS_MASTER_KEY"]` to encrypt the credentials file. The credentials file can be stored in version control, as long as master key is kept safe. -To edit stored credentials use `rails credentials:edit`. +To add new secret to credentials, first run `rails secret` to get a new secret. Then run `rails credentials:edit` to edit credentials, and add the secret. Running `credentials:edit` creates new credentials file and master key, if they did not already exist. By default, this file contains the application's -`secret_key_base`, but it could also be used to store other credentials such as -access keys for external APIs. +`secret_key_base`, but it could also be used to store other credentials such as access keys for external APIs. -The credentials added to this file are accessible via `Rails.application.credentials`. +The secrets kept in credentials file are accessible via `Rails.application.credentials`. For example, with the following decrypted `config/credentials.yml.enc`: secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 @@ -1235,6 +1193,11 @@ version: Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank ``` + +TIP: Learn more about credentials with `rails credentials:help`. + +WARNING: Keep your master key safe. Do not commit your master key. + Dependency Management and CVEs ------------------------------ diff --git a/guides/source/testing.md b/guides/source/testing.md index 9541598b26..18eecf49fa 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -33,11 +33,11 @@ Rails creates a `test` directory for you as soon as you create a Rails project u ```bash $ ls -F test -application_system_test_case.rb fixtures/ integration/ models/ test_helper.rb -controllers/ helpers/ mailers/ system/ +application_system_test_case.rb controllers/ helpers/ mailers/ system/ +channels/ fixtures/ integration/ models/ test_helper.rb ``` -The `helpers`, `mailers`, and `models` directories are meant to hold tests for view helpers, mailers, and models, respectively. The `controllers` directory is meant to hold tests for controllers, routes, and views. The `integration` directory is meant to hold tests for interactions between controllers. +The `helpers`, `mailers`, and `models` directories are meant to hold tests for view helpers, mailers, and models, respectively. The `channels` directory is meant to hold tests for Action Cable connection and channels. The `controllers` directory is meant to hold tests for controllers, routes, and views. The `integration` directory is meant to hold tests for interactions between controllers. The system test directory holds system tests, which are used for full browser testing of your application. System tests allow you to test your application @@ -350,15 +350,15 @@ Rails adds some custom assertions of its own to the `minitest` framework: | Assertion | Purpose | | --------------------------------------------------------------------------------- | ------- | -| [`assert_difference(expressions, difference = 1, message = nil) {...}`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference) | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.| -| [`assert_no_difference(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference) | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.| -| [`assert_changes(expressions, message = nil, from:, to:, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes) | Test that the result of evaluating an expression is changed after invoking the passed in block.| -| [`assert_no_changes(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes) | Test the result of evaluating an expression is not changed after invoking the passed in block.| -| [`assert_nothing_raised { block }`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised) | Ensures that the given block doesn't raise any exceptions.| -| [`assert_recognizes(expected_options, path, extras={}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes) | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| -| [`assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates) | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| -| [`assert_response(type, message = nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response) | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| -| [`assert_redirected_to(options = {}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to) | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| +| [`assert_difference(expressions, difference = 1, message = nil) {...}`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference) | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.| +| [`assert_no_difference(expressions, message = nil, &block)`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference) | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.| +| [`assert_changes(expressions, message = nil, from:, to:, &block)`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes) | Test that the result of evaluating an expression is changed after invoking the passed in block.| +| [`assert_no_changes(expressions, message = nil, &block)`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes) | Test the result of evaluating an expression is not changed after invoking the passed in block.| +| [`assert_nothing_raised { block }`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised) | Ensures that the given block doesn't raise any exceptions.| +| [`assert_recognizes(expected_options, path, extras={}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes) | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| +| [`assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates) | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| +| [`assert_response(type, message = nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response) | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](https://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| +| [`assert_redirected_to(options = {}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to) | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| You'll see the usage of some of these assertions in the next chapter. @@ -366,13 +366,13 @@ You'll see the usage of some of these assertions in the next chapter. All the basic assertions such as `assert_equal` defined in `Minitest::Assertions` are also available in the classes we use in our own test cases. In fact, Rails provides the following classes for you to inherit from: -* [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html) -* [`ActionMailer::TestCase`](http://api.rubyonrails.org/classes/ActionMailer/TestCase.html) -* [`ActionView::TestCase`](http://api.rubyonrails.org/classes/ActionView/TestCase.html) -* [`ActiveJob::TestCase`](http://api.rubyonrails.org/classes/ActiveJob/TestCase.html) -* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html) -* [`ActionDispatch::SystemTestCase`](http://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html) -* [`Rails::Generators::TestCase`](http://api.rubyonrails.org/classes/Rails/Generators/TestCase.html) +* [`ActiveSupport::TestCase`](https://api.rubyonrails.org/classes/ActiveSupport/TestCase.html) +* [`ActionMailer::TestCase`](https://api.rubyonrails.org/classes/ActionMailer/TestCase.html) +* [`ActionView::TestCase`](https://api.rubyonrails.org/classes/ActionView/TestCase.html) +* [`ActiveJob::TestCase`](https://api.rubyonrails.org/classes/ActiveJob/TestCase.html) +* [`ActionDispatch::IntegrationTest`](https://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html) +* [`ActionDispatch::SystemTestCase`](https://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html) +* [`Rails::Generators::TestCase`](https://api.rubyonrails.org/classes/Rails/Generators/TestCase.html) Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests. @@ -473,8 +473,8 @@ takes your entire test suite to run. ### Parallel testing with processes The default parallelization method is to fork processes using Ruby's DRb system. The processes -are forked based on the number of workers provided. The default is 2, but can be changed by the -number passed to the parallelize method. +are forked based on the number of workers provided. The default number is the actual core count +on the machine you are on, but can be changed by the number passed to the parallelize method. To enable parallelization add the following to your `test_helper.rb`: @@ -516,7 +516,7 @@ class ActiveSupport::TestCase # cleanup databases end - parallelize(workers: 2) + parallelize(workers: :number_of_processors) end ``` @@ -531,7 +531,7 @@ To change the parallelization method to use threads over forks put the following ```ruby class ActiveSupport::TestCase - parallelize(workers: 2, with: :threads) + parallelize(workers: :number_of_processors, with: :threads) end ``` @@ -572,7 +572,7 @@ be rebuilt. This can be done by executing `rails db:test:prepare`. For good tests, you'll need to give some thought to setting up test data. In Rails, you can handle this by defining and customizing fixtures. -You can find comprehensive documentation in the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). +You can find comprehensive documentation in the [Fixtures API documentation](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). #### What Are Fixtures? @@ -621,7 +621,7 @@ first: Notice the `category` key of the `first` article found in `fixtures/articles.yml` has a value of `about`. This tells Rails to load the category `about` found in `fixtures/categories.yml`. -NOTE: For associations to reference one another by name, you can use the fixture name instead of specifying the `id:` attribute on the associated fixtures. Rails will auto assign a primary key to be consistent between runs. For more information on this association behavior please read the [Fixtures API documentation](http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). +NOTE: For associations to reference one another by name, you can use the fixture name instead of specifying the `id:` attribute on the associated fixtures. Rails will auto assign a primary key to be consistent between runs. For more information on this association behavior please read the [Fixtures API documentation](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). #### ERB'in It Up @@ -684,7 +684,7 @@ create test/models/article_test.rb create test/fixtures/articles.yml ``` -Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html). +Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from [`ActiveSupport::TestCase`](https://api.rubyonrails.org/classes/ActiveSupport/TestCase.html). System Testing -------------- @@ -932,11 +932,11 @@ Here the test is inheriting from `ActionDispatch::IntegrationTest`. This makes s In addition to the standard testing helpers, inheriting from `ActionDispatch::IntegrationTest` comes with some additional helpers available when writing integration tests. Let's get briefly introduced to the three categories of helpers we get to choose from. -For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). +For dealing with the integration test runner, see [`ActionDispatch::Integration::Runner`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/Runner.html). -When performing requests, we will have [`ActionDispatch::Integration::RequestHelpers`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for our use. +When performing requests, we will have [`ActionDispatch::Integration::RequestHelpers`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html) available for our use. -If we need to modify the session, or state of our integration test, take a look at [`ActionDispatch::Integration::Session`](http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. +If we need to modify the session, or state of our integration test, take a look at [`ActionDispatch::Integration::Session`](https://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html) to help. ### Implementing an integration test @@ -1012,7 +1012,7 @@ Finally we can assert that our response was successful and our new article is re #### Taking it further -We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use-cases for our applications. +We were able to successfully test a very small workflow for visiting our blog and creating a new article. If we wanted to take this further we could add tests for commenting, removing articles, or editing comments. Integration tests are a great place to experiment with all kinds of use cases for our applications. Functional Tests for Your Controllers @@ -1212,7 +1212,7 @@ Let's start by adding this assertion to our `test_should_create_article` test: ```ruby test "should create article" do assert_difference('Article.count') do - post article_url, params: { article: { title: 'Some title' } } + post articles_url, params: { article: { title: 'Some title' } } end assert_redirected_to article_path(Article.last) @@ -1404,7 +1404,7 @@ If you find your helpers are cluttering `test_helper.rb`, you can extract them i ```ruby # lib/test/multiple_assertions.rb module MultipleAssertions - def assert_multiple_of_fourty_two(number) + def assert_multiple_of_forty_two(number) assert (number % 42 == 0), 'expected #{number} to be a multiple of 42' end end @@ -1419,8 +1419,8 @@ require 'test/multiple_assertions' class NumberTest < ActiveSupport::TestCase include MultipleAssertions - test '420 is a multiple of fourty two' do - assert_multiple_of_fourty_two 420 + test '420 is a multiple of forty two' do + assert_multiple_of_forty_two 420 end end ``` @@ -1454,7 +1454,7 @@ Like everything else in your Rails application, you can test your routes. Route NOTE: If your application has complex routes, Rails provides a number of useful helpers to test them. -For more information on routing assertions available in Rails, see the API documentation for [`ActionDispatch::Assertions::RoutingAssertions`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html). +For more information on routing assertions available in Rails, see the API documentation for [`ActionDispatch::Assertions::RoutingAssertions`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html). Testing Views ------------- @@ -1712,7 +1712,7 @@ no jobs have already been executed in the scope of each test. ### Custom Assertions And Testing Jobs Inside Other Components -Active Job ships with a bunch of custom assertions that can be used to lessen the verbosity of tests. For a full list of available assertions, see the API documentation for [`ActiveJob::TestHelper`](http://api.rubyonrails.org/classes/ActiveJob/TestHelper.html). +Active Job ships with a bunch of custom assertions that can be used to lessen the verbosity of tests. For a full list of available assertions, see the API documentation for [`ActiveJob::TestHelper`](https://api.rubyonrails.org/classes/ActiveJob/TestHelper.html). It's a good practice to ensure that your jobs correctly get enqueued or performed wherever you invoke them (e.g. inside your controllers). This is precisely where @@ -1722,7 +1722,9 @@ within a model: ```ruby require 'test_helper' -class ProductTest < ActiveJob::TestCase +class ProductTest < ActiveSupport::TestCase + include ActiveJob::TestHelper + test 'billing job scheduling' do assert_enqueued_with(job: BillingJob) do product.charge(account) @@ -1731,6 +1733,158 @@ class ProductTest < ActiveJob::TestCase end ``` +### Asserting Time Arguments in Jobs + +When serializing job arguments, `Time`, `DateTime`, and `ActiveSupport::TimeWithZone` lose microsecond precision. This means comparing deserialized time with actual time doesn't always work. To compensate for the loss of precision, `assert_enqueued_with` and `assert_performed_with` will remove microseconds from time objects in argument assertions. + +```ruby +require 'test_helper' + +class ProductTest < ActiveSupport::TestCase + include ActiveJob::TestHelper + + test 'that product is reserved at a given time' do + now = Time.now + assert_performed_with(job: ReservationJob, args: [product, now]) do + product.reserve(now) + end + end +end +``` + +Testing Action Cable +-------------------- + +Since Action Cable is used at different levels inside your application, +you'll need to test both the channels, connection classes themselves, and that other +entities broadcast correct messages. + +### Connection Test Case + +By default, when you generate new Rails application with Action Cable, a test for the base connection class (`ApplicationCable::Connection`) is generated as well under `test/channels/application_cable` directory. + +Connection tests aim to check whether a connection's identifiers get assigned properly +or that any improper connection requests are rejected. Here is an example: + +```ruby +class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase + test "connects with params" do + # Simulate a connection opening by calling the `connect` method + connect params: { user_id: 42 } + + # You can access the Connection object via `connection` in tests + assert_equal connection.user_id, "42" + end + + test "rejects connection without params" do + # Use `assert_reject_connection` matcher to verify that + # connection is rejected + assert_reject_connection { connect } + end +end +``` + +You can also specify request cookies the same way you do in integration tests: + +```ruby +test "connects with cookies" do + cookies.signed[:user_id] = "42" + + connect + + assert_equal connection.user_id, "42" +end +``` + +See the API documentation for [`ActionCable::Connection::TestCase`](https://api.rubyonrails.org/classes/ActionCable/Connection/TestCase.html) for more information. + +### Channel Test Case + +By default, when you generate a channel, an associated test will be generated as well +under the `test/channels` directory. Here's an example test with a chat channel: + +```ruby +require "test_helper" + +class ChatChannelTest < ActionCable::Channel::TestCase + test "subscribes and stream for room" do + # Simulate a subscription creation by calling `subscribe` + subscribe room: "15" + + # You can access the Channel object via `subscription` in tests + assert subscription.confirmed? + assert_has_stream "chat_15" + end +end +``` + +This test is pretty simple and only asserts that the channel subscribes the connection to a particular stream. + +You can also specify the underlying connection identifiers. Here's an example test with a web notifications channel: + +```ruby +require "test_helper" + +class WebNotificationsChannelTest < ActionCable::Channel::TestCase + test "subscribes and stream for user" do + stub_connection current_user: users(:john) + + subscribe + + assert_has_stream_for users(:john) + end +end +``` + +See the API documentation for [`ActionCable::Channel::TestCase`](https://api.rubyonrails.org/classes/ActionCable/Channel/TestCase.html) for more information. + +### Custom Assertions And Testing Broadcasts Inside Other Components + +Action Cable ships with a bunch of custom assertions that can be used to lessen the verbosity of tests. For a full list of available assertions, see the API documentation for [`ActionCable::TestHelper`](https://api.rubyonrails.org/classes/ActionCable/TestHelper.html). + +It's a good practice to ensure that the correct message has been broadcasted inside other components (e.g. inside your controllers). This is precisely where +the custom assertions provided by Action Cable are pretty useful. For instance, +within a model: + +```ruby +require 'test_helper' + +class ProductTest < ActionCable::TestCase + test "broadcast status after charge" do + assert_broadcast_on("products:#{product.id}", type: "charged") do + product.charge(account) + end + end +end +``` + +If you want to test the broadcasting made with `Channel.broadcast_to`, you shoud use +`Channel.broadcasting_for` to generate an underlying stream name: + +```ruby +# app/jobs/chat_relay_job.rb +class ChatRelayJob < ApplicationJob + def perform_later(room, message) + ChatChannel.broadcast_to room, text: message + end +end + +# test/jobs/chat_relay_job_test.rb +require 'test_helper' + +class ChatRelayJobTest < ActiveJob::TestCase + include ActionCable::TestHelper + + test "broadcast message to room" do + room = rooms(:all) + + assert_broadcast_on(ChatChannel.broadcasting_for(room), text: "Hi!") do + ChatRelayJob.perform_now(room, "Hi!") + end + end +end +``` + Additional Testing Resources ---------------------------- @@ -1738,7 +1892,7 @@ Additional Testing Resources Rails provides built-in helper methods that enable you to assert that your time-sensitive code works as expected. -Here is an example using the [`travel_to`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to) helper: +Here is an example using the [`travel_to`](https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to) helper: ```ruby # Lets say that a user is eligible for gifting a month after they register. @@ -1751,5 +1905,5 @@ end assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block. ``` -Please see [`ActiveSupport::Testing::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html) +Please see [`ActiveSupport::Testing::TimeHelpers` API Documentation](https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html) for in-depth information about the available time helpers. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index a0553c1ccc..7e4152aa51 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -35,7 +35,7 @@ You can find a list of all released Rails versions [here](https://rubygems.org/g Rails generally stays close to the latest released Ruby version when it's released: -* Rails 6 requires Ruby 2.4.1 or newer. +* Rails 6 requires Ruby 2.5.0 or newer. * Rails 5 requires Ruby 2.2.2 or newer. * Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer. * Rails 3.2.x is the last branch to support Ruby 1.8.7. @@ -85,18 +85,54 @@ 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 in the cookies values +### Purpose in signed or encrypted cookie is now embedded within cookies -To improve security, Rails now embeds the purpose information in encrypted or signed cookies value. -Rails can now thwart attacks that attempt to copy signed/encrypted 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. -If you require your cookies to be read by 5.2 and older, or you are still validating your 6.0 deploy and want -to allow you to rollback set +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 `Rails.application.config.action_dispatch.use_cookies_with_metadata` to `false`. +### Action Cable JavaScript API Changes + +The Action Cable JavaScript package has been converted from CoffeeScript +to ES2015, and we now publish the source code in the npm distribution. + +This release includes some breaking changes to optional parts of the +Action Cable JavaScript API: + +- Configuration of the WebSocket adapter and logger adapter have been moved + from properties of `ActionCable` to properties of `ActionCable.adapters`. + If you are configuring these adapters you will need to make + these changes: + + ```diff + - ActionCable.WebSocket = MyWebSocket + + ActionCable.adapters.WebSocket = MyWebSocket + ``` + ```diff + - ActionCable.logger = myLogger + + ActionCable.adapters.logger = myLogger + ``` + +- The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()` + methods have been removed and replaced with the property + `ActionCable.logger.enabled`. If you are using these methods you + will need to make these changes: + + ```diff + - ActionCable.startDebugging() + + ActionCable.logger.enabled = true + ``` + ```diff + - ActionCable.stopDebugging() + + ActionCable.logger.enabled = false + ``` + Upgrading from Rails 5.1 to Rails 5.2 ------------------------------------- @@ -407,7 +443,7 @@ want to add this feature it will need to be turned on in an initializer. Rails 5 now supports per-form CSRF tokens to mitigate against code-injection attacks with forms created by JavaScript. With this option turned on, forms in your application will each have their -own CSRF token that is specified to the action and method for that form. +own CSRF token that is specific to the action and method for that form. config.action_controller.per_form_csrf_tokens = true @@ -622,7 +658,7 @@ gem 'rails-deprecated_sanitizer' ### Rails DOM Testing -The [`TagAssertions` module](http://api.rubyonrails.org/v4.1/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing). +The [`TagAssertions` module](https://api.rubyonrails.org/v4.1/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing). ### Masked Authenticity Tokens diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index c36b3faa6c..8cf8efefd0 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -160,7 +160,7 @@ remote elements inside your application. #### form_with -[`form_with`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with) +[`form_with`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with) is a helper that assists with writing forms. By default, `form_with` assumes that your form will be using Ajax. You can opt out of this behavior by passing the `:local` option `form_with`. @@ -204,7 +204,7 @@ have been bundled into `event.detail`. For information about the previously used #### link_to -[`link_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) +[`link_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) is a helper that assists with generating links. It has a `:remote` option you can use like this: @@ -236,7 +236,7 @@ $ -> #### button_to -[`button_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this: +[`button_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this: ```erb <%= button_to "An article", @article, remote: true %> |