aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source')
-rw-r--r--guides/source/6_0_release_notes.md49
-rw-r--r--guides/source/action_cable_overview.md307
-rw-r--r--guides/source/action_mailbox_basics.md398
-rw-r--r--guides/source/action_mailer_basics.md52
-rw-r--r--guides/source/action_text_overview.md99
-rw-r--r--guides/source/active_record_basics.md6
-rw-r--r--guides/source/active_record_migrations.md13
-rw-r--r--guides/source/active_storage_overview.md29
-rw-r--r--guides/source/active_support_core_extensions.md87
-rw-r--r--guides/source/active_support_instrumentation.md62
-rw-r--r--guides/source/asset_pipeline.md4
-rw-r--r--guides/source/association_basics.md4
-rw-r--r--guides/source/configuring.md77
-rw-r--r--guides/source/documents.yaml12
-rw-r--r--guides/source/engines.md27
-rw-r--r--guides/source/getting_started.md2
-rw-r--r--guides/source/i18n.md8
-rw-r--r--guides/source/index.html.erb5
-rw-r--r--guides/source/layout.html.erb9
-rw-r--r--guides/source/routing.md2
-rw-r--r--guides/source/testing.md120
-rw-r--r--guides/source/upgrading_ruby_on_rails.md38
22 files changed, 1074 insertions, 336 deletions
diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md
index f3ed21dc45..e6fb15c09c 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
--------
@@ -59,6 +92,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
-----------
diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md
index 2f602c3e0a..8f5c44849a 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,16 +177,13 @@ 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.
@@ -167,12 +194,16 @@ you're interested in having.
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 +212,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 +290,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 +334,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 +388,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 +448,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 +520,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 +563,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
@@ -629,10 +708,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 +768,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_mailbox_basics.md b/guides/source/action_mailbox_basics.md
new file mode 100644
index 0000000000..c90892d456
--- /dev/null
+++ b/guides/source/action_mailbox_basics.md
@@ -0,0 +1,398 @@
+**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 Amazon SES, 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
+
+### Amazon SES
+
+Install the [`aws-sdk-sns`](https://rubygems.org/gems/aws-sdk-sns) gem:
+
+```ruby
+# Gemfile
+gem "aws-sdk-sns", ">= 1.9.0", require: false
+```
+
+Tell Action Mailbox to accept emails from SES:
+
+```ruby
+# config/environments/production.rb
+config.action_mailbox.ingress = :amazon
+```
+
+[Configure SES](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications.html)
+to deliver emails to your application via POST requests to
+`/rails/action_mailbox/amazon/inbound_emails`. If your application lived at
+`https://example.com`, you would specify the fully-qualified URL
+`https://example.com/rails/action_mailbox/amazon/inbound_emails`.
+
+### 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..16db433bd4 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.
@@ -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
# ...
@@ -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`.
@@ -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/active_record_basics.md b/guides/source/active_record_basics.md
index a67e2924d7..b0d4bbd2c0 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -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 Postgres 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:
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 4d195988f8..905c76e5c1 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
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
index 65d4e2934f..474a93c83e 100644
--- a/guides/source/active_storage_overview.md
+++ b/guides/source/active_storage_overview.md
@@ -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..3db46bc42e 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2132,30 +2132,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 +2622,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 +2729,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}
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 64db141381..c1c3832b79 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -255,13 +255,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 +273,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 +297,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 |
@@ -648,6 +628,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 +664,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:
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 500e230ff9..e7faa5c330 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -1101,7 +1101,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 +1111,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.
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 4f3e8b2cff..38e46e4bf8 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1257,7 +1257,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
@@ -1658,7 +1658,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
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 029ae1a5ff..32682fb91f 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -441,7 +441,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']`.
@@ -619,6 +619,29 @@ Defaults to `'signed cookie'`.
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
There are a number of settings available on `config.action_mailer`:
@@ -698,6 +721,8 @@ There are a number of settings available on `config.action_mailer`:
* `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.delivery_job` specifies delivery job for mail. Defaults to `ActionMailer::DeliveryJob`.
+
### Configuring Active Support
@@ -715,7 +740,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`.
@@ -815,15 +840,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 +878,40 @@ 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`
+- `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer`: `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`.
@@ -1404,7 +1469,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/documents.yaml b/guides/source/documents.yaml
index 25c159d471..25e4fdb4e6 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
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 1e93a19c84..f15383e3f1 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -1091,16 +1091,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 +1111,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 +1119,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
@@ -1149,7 +1148,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
@@ -1169,7 +1168,7 @@ class Article < ApplicationRecord
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`]
@@ -1505,13 +1504,19 @@ 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::Blob` | `active_storage_blob` |
| `ActiveSupport::TestCase` | `active_support_test_case` |
| `i18n` | `i18n` |
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index e2f558d74c..264c94326e 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
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 10b1a6de7e..d146685675 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
@@ -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
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..65a003fceb 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 %>
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 0a0f1b6754..a33ac6a589 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -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
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 9541598b26..1a2f480407 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
@@ -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
```
@@ -1731,6 +1731,112 @@ class ProductTest < ActiveJob::TestCase
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 [`AcionCable::Connection::TestCase`](http://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 [`AcionCable::Channel::TestCase`](http://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`](http://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
+```
+
Additional Testing Resources
----------------------------
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index e74985c5b0..1f30ce1971 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.
@@ -97,6 +97,42 @@ If you require your cookies to be read by 5.2 and older, or you are still valida
to allow you to rollback set
`Rails.application.config.action_dispatch.use_cookies_with_metadata` to `false`.
+### ActionCable javascript API Changes
+
+The ActionCable javascript package has been converted from CoffeeScript
+to ES2015, and we now publish the source code in the npm distribution.
+
+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`.
+ If you are currently configuring these adapters you will need to make
+ these changes when upgrading:
+
+ ```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 currently using these methods you
+ will need to make these changes when upgrading:
+
+ ```diff
+ - ActionCable.startDebugging()
+ + ActionCable.logger.enabled = true
+ ```
+ ```diff
+ - ActionCable.stopDebugging()
+ + ActionCable.logger.enabled = false
+ ```
+
Upgrading from Rails 5.1 to Rails 5.2
-------------------------------------