Skip to content

Commit 2b5d784

Browse files
author
David Heinemeier Hansson
committed
Further cleanup of the cable guide
1 parent 4c43a10 commit 2b5d784

File tree

1 file changed

+51
-100
lines changed

1 file changed

+51
-100
lines changed

guides/source/action_cable_overview.md

Lines changed: 51 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ What is Pub/Sub
2424

2525
Pub/Sub, or Publish-Subscribe, refers to a message queue paradigm whereby senders
2626
of information (publishers), send data to an abstract class of recipients (subscribers),
27-
without specifying individual recipients.
27+
without specifying individual recipients. Action Cable uses this approach to communicate
28+
between the server and many clients.
2829

2930
What is Action Cable
3031
--------------------
@@ -35,19 +36,20 @@ client-server connection instance established per WebSocket connection.
3536
## Server-Side Components
3637

3738
### Connections
39+
3840
Connections form the foundation of the client-server relationship. For every WebSocket
39-
the cable server is accepting, a Connection object will be instantiated. This instance
40-
becomes the parent of all the channel subscriptions that are created from there on.
41+
the cable server is accepting, a Connection object will be instantiated on the server side.
42+
This instance becomes the parent of all the channel subscriptions that are created from there on.
4143
The Connection itself does not deal with any specific application logic beyond authentication
42-
and authorization. The client of a WebSocket connection is called the consumer.
43-
A single consumer may have multiple WebSockets open to your application if they
44-
use multiple browser tabs or devices.
44+
and authorization. The client of a WebSocket connection is called a consumer. An individual
45+
user will create one consumer-connection pair per browser tab, window, or device they have open.
4546

4647
Connections are instantiated via the `ApplicationCable::Connection` class in Ruby.
4748
In this class, you authorize the incoming connection, and proceed to establish it
4849
if the user can be identified.
4950

5051
#### Connection Setup
52+
5153
```ruby
5254
# app/channels/application_cable/connection.rb
5355
module ApplicationCable
@@ -69,34 +71,38 @@ module ApplicationCable
6971
end
7072
end
7173
```
74+
7275
Here `identified_by` is a connection identifier that can be used to find the
73-
specific connection again or later.
74-
Note that anything marked as an identifier will automatically create a delegate
75-
by the same name on any channel instances created off the connection.
76+
specific connection later. Note that anything marked as an identifier will automatically
77+
create a delegate by the same name on any channel instances created off the connection.
7678

77-
This relies on the fact that you will already have handled authentication of the user,
78-
and that a successful authentication sets a signed cookie with the `user_id`.
79-
This cookie is then automatically sent to the connection instance when a new connection
79+
This example relies on the fact that you will already have handled authentication of the user
80+
somewhere else in your application, and that a successful authentication sets a signed
81+
cookie with the `user_id`.
82+
83+
The cookie is then automatically sent to the connection instance when a new connection
8084
is attempted, and you use that to set the `current_user`. By identifying the connection
8185
by this same current_user, you're also ensuring that you can later retrieve all open
8286
connections by a given user (and potentially disconnect them all if the user is deleted
8387
or deauthorized).
8488

8589
### Channels
90+
8691
A channel encapsulates a logical unit of work, similar to what a controller does in a
87-
regular MVC setup.
88-
By default, Rails creates a parent `ApplicationCable::Channel` class for encapsulating
89-
shared logic between your channels.
92+
regular MVC setup. By default, Rails creates a parent `ApplicationCable::Channel` class
93+
for encapsulating shared logic between your channels.
9094

9195
#### Parent Channel Setup
96+
9297
```ruby
9398
# app/channels/application_cable/channel.rb
9499
module ApplicationCable
95100
class Channel < ActionCable::Channel::Base
96101
end
97102
end
98103
```
99-
Then you would create child channel classes. For example, you could have a
104+
105+
Then you would create your own channel classes. For example, you could have a
100106
**ChatChannel** and an **AppearanceChannel**:
101107

102108
```ruby
@@ -109,7 +115,7 @@ class AppearanceChannel < ApplicationCable::Channel
109115
end
110116
```
111117

112-
A consumer could then be subscribed to either or both of these channels.
118+
A consumer could then be subscribed to either or both of these channels.
113119

114120
#### Subscriptions
115121

@@ -121,42 +127,47 @@ an identifier sent by the cable consumer.
121127
```ruby
122128
# app/channels/application_cable/chat_channel.rb
123129
class ChatChannel < ApplicationCable::Channel
130+
# Called when the consumer has successfully become a subscriber of this channel
124131
def subscribed
125132
end
126133
end
127134
```
128135

129136
## Client-Side Components
137+
130138
### Connections
139+
131140
Consumers require an instance of the connection on their side. This can be
132-
established using the following Javascript:
141+
established using the following Javascript, which is generated by default in Rails:
133142

134143
#### Connect Consumer
144+
135145
```coffeescript
136146
# app/assets/javascripts/cable.coffee
137147
#= require action_cable
138148

139149
@App = {}
140-
App.cable = ActionCable.createConsumer("ws://cable.example.com")
150+
App.cable = ActionCable.createConsumer()
141151
```
142-
The `ws://cable.example.com` address must point to your set of Action Cable servers, and it
143-
must share a cookie namespace with the rest of the application (which may live under http://example.com).
144-
This ensures that the signed cookie will be correctly sent.
152+
153+
This will ready a consumer that'll connect against /cable on your server by default.
154+
The connection won't be established until you've also specified at least one subscription
155+
you're interested in having.
145156

146157
#### Subscriber
158+
147159
When a consumer is subscribed to a channel, they act as a subscriber. A
148160
consumer can act as a subscriber to a given channel any number of times.
149161
For example, a consumer could subscribe to multiple chat rooms at the same time.
150162
(remember that a physical user may have multiple consumers, one per tab/device open to your connection).
151163

152164
A consumer becomes a subscriber, by creating a subscription to a given channel:
165+
153166
```coffeescript
154167
# app/assets/javascripts/cable/subscriptions/chat.coffee
155-
# Assumes you've already requested the right to send web notifications
156168
App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" }
157169

158170
# app/assets/javascripts/cable/subscriptions/appearance.coffee
159-
# Assumes you've already requested the right to send web notifications
160171
App.cable.subscriptions.create { channel: "AppearanceChannel" }
161172
```
162173

@@ -166,6 +177,7 @@ received data will be described later on.
166177
## Client-Server Interactions
167178

168179
### Streams
180+
169181
Streams provide the mechanism by which channels route published content
170182
(broadcasts) to its subscribers.
171183

@@ -190,8 +202,8 @@ class CommentsChannel < ApplicationCable::Channel
190202
end
191203
end
192204
```
193-
You can then broadcast to this channel using:
194-
`CommentsChannel.broadcast_to(@post, @comment)`
205+
206+
You can then broadcast to this channel using: `CommentsChannel.broadcast_to(@post, @comment)`
195207

196208
### Broadcastings
197209

@@ -261,7 +273,6 @@ will become your params hash in your cable channel. The keyword `channel` is req
261273

262274
```coffeescript
263275
# app/assets/javascripts/cable/subscriptions/chat.coffee
264-
# Client-side, which assumes you've already requested the right to send web notifications
265276
App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
266277
received: (data) ->
267278
@appendLine(data)
@@ -281,7 +292,7 @@ App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
281292

282293
```ruby
283294
# Somewhere in your app this is called, perhaps from a NewCommentJob
284-
ChatChannel.broadcast_to chat_#{room}, sent_by: 'Paul', body: 'This is a cool chat app.'
295+
ChatChannel.broadcast_to "chat_#{room}", sent_by: 'Paul', body: 'This is a cool chat app.'
285296
```
286297

287298

@@ -305,7 +316,6 @@ end
305316

306317
```coffeescript
307318
# app/assets/javascripts/cable/subscriptions/chat.coffee
308-
# Client-side which assumes you've already requested the right to send web notifications
309319
App.chatChannel = App.cable.subscriptions.create { channel: "ChatChannel", room: "Best Room" },
310320
received: (data) ->
311321
# data => { sent_by: "Paul", body: "This is a cool chat app." }
@@ -425,7 +435,7 @@ web notifications when you broadcast to the right streams:
425435
# app/channels/web_notifications_channel.rb
426436
class WebNotificationsChannel < ApplicationCable::Channel
427437
def subscribed
428-
stream_from "web_notifications_#{current_user.id}"
438+
stream_for current_user
429439
end
430440
end
431441
```
@@ -450,6 +460,7 @@ The `WebNotificationsChannel.broadcast_to` call places a message in the current
450460
subscription adapter (Redis by default)'s pubsub queue under a separate
451461
broadcasting name for each user. For a user with an ID of 1, the broadcasting
452462
name would be `web_notifications_1`.
463+
453464
The channel has been instructed to stream everything that arrives at
454465
`web_notifications_1` directly to the client by invoking the `#received(data)`
455466
callback. The data is the hash sent as the second parameter to the server-side
@@ -463,8 +474,7 @@ repository for a full example of how to setup Action Cable in a Rails app and ad
463474

464475
## Configuration
465476

466-
Action Cable has three required configurations: a subscription adapter,
467-
allowed request origins, and the cable server URL.
477+
Action Cable has two required configurations: a subscription adapter and allowed request origins.
468478

469479
### Subscription Adapter
470480

@@ -475,10 +485,10 @@ additional information on adapters.
475485

476486
```yaml
477487
production: &production
478-
adapter: redis # Optional as default is redis
488+
adapter: redis
479489
url: redis://10.10.3.153:6381
480490
development: &development
481-
url: redis://localhost:6379
491+
adapter: async
482492
test: *development
483493
```
484494
@@ -512,41 +522,8 @@ in the development environment.
512522

513523
### Consumer Configuration
514524

515-
Once you have decided how to run your cable server (see below), you must provide
516-
the server url (or path) to your client-side setup. There are two ways you can do this.
517-
518-
The first is to simply pass it in when creating your consumer. For a standalone server,
519-
this would be something like: `App.cable = ActionCable.createConsumer("ws://example.com:28080")`,
520-
and for an in-app server, something like: `App.cable = ActionCable.createConsumer("/cable")`.
521-
522-
The second option is to pass the server url through the `action_cable_meta_tag` in your layout.
523-
This uses a url or path typically set via `config.action_cable.url`
524-
in the environment configuration files, or defaults to "/cable".
525-
526-
This method is especially useful if your WebSocket url might change
527-
between environments. If you host your production server via https,
528-
you will need to use the wss scheme for your ActionCable server, but
529-
development might remain http and use the ws scheme. You might use localhost
530-
in development and your domain in production.
531-
532-
In any case, to vary the WebSocket url between environments, add the following
533-
configuration to each environment:
534-
535-
```ruby
536-
config.action_cable.url = "ws://example.com:28080"
537-
```
538-
539-
Then add the following line to your layout before your JavaScript tag:
540-
541-
```erb
542-
<%= action_cable_meta_tag %>
543-
```
544-
545-
And finally, create your consumer like so:
546-
547-
```coffeescript
548-
App.cable = ActionCable.createConsumer()
549-
```
525+
To configure the URL, add a call to `action_cable_meta_tag` in your HTML layout HEAD.
526+
This uses a url or path typically set via `config.action_cable.url` in the environment configuration files.
550527

551528
### Other Configurations
552529

@@ -560,38 +537,16 @@ Rails.application.config.action_cable.log_tags = [
560537
]
561538
```
562539

563-
Your WebSocket URL might change between environments. If you host your
564-
production server via https, you will need to use the wss scheme
565-
for your ActionCable server, but development might remain http and
566-
use the ws scheme. You might use localhost in development and your
567-
domain in production. In any case, to vary the WebSocket URL between
568-
environments, add the following configuration to each environment:
569-
570-
```ruby
571-
config.action_cable.url = "ws://example.com:28080"
572-
```
573-
574-
Then add the following line to your layout before your JavaScript tag:
575-
576-
```erb
577-
<%= action_cable_meta_tag %>
578-
```
579-
580-
And finally, create your consumer like so:
581-
582-
```coffeescript
583-
App.cable = Cable.createConsumer()
584-
```
585-
586540
For a full list of all configuration options, see the `ActionCable::Server::Configuration` class.
587541

588542
Also note that your server must provide at least the same number of
589543
database connections as you have workers. The default worker pool is
590544
set to 100, so that means you have to make at least that available.
591545
You can change that in `config/database.yml` through the `pool` attribute.
592546

593-
## Running the cable server
547+
## Running standalone cable servers
594548

549+
<<<<<<< HEAD
595550
### In App
596551

597552
Action Cable can run alongside your Rails application. For example, to
@@ -615,6 +570,9 @@ but the use of Redis keeps messages synced across connections.
615570

616571
### Standalone
617572
The cable server(s) can be separated from your normal application server.
573+
=======
574+
The cable servers can be separated from your normal application server.
575+
>>>>>>> Further cleanup of the cable guide
618576
It's still a Rack application, but it is its own Rack application.
619577
The recommended basic setup is as follows:
620578

@@ -636,13 +594,6 @@ The above will start a cable server on port 28080.
636594

637595
### Notes
638596

639-
Beware that currently the cable server will _not_ auto-reload any
640-
changes in the framework. As we've discussed, long-running cable
641-
connections mean long-running objects. We don't yet have a way of
642-
reloading the classes of those objects in a safe manner. So when
643-
you change your channels, or the model your channels use, you must
644-
restart the cable server.
645-
646597
The WebSocket server doesn't have access to the session, but it has
647598
access to the cookies. This can be used when you need to handle
648599
authentication. You can see one way of doing that with Devise in this [article](http://www.rubytutorial.io/actioncable-devise-authentication).

0 commit comments

Comments
 (0)