Channels 101

While stream_from is great, there are many techniques only possible if you leverage ActionCable Connections, Channels and Subscriptions.

CableReady v5 introduced the stream_from helper, which was covered in the previous Hello World chapter.

So, what's a channel?

There's an entire chapter dedicated to explaining ActionCable, but this should be enough to get started. 😅

An ActionCable Channel is a Ruby class you create with a goal in mind. Achieving that goal revolves around sending and receiving messages from a list of subscribers that are connected over WebSockets.

Channels define one or more unique identifiers. These are string patterns which map subscribers to Channels, similarly to how routes.rb maps request paths to Controllers. The way you structure these identifiers decides whether messages sent to the Channel are delivered to every subscriber, or just a subset that matches a given pattern.

On the client, you use an ActionCable Connection consumer to subscribe to Channels. The subscription provides access to data that is received from the server, as well as a mechanism for sending data to the sever.

The subscription attempt can optionally include params, similar to POSTing a form to a controller action. The Channel class can access these params and use these values to compute and return a subscription identifier.

Don't worry if this sounds complicated, because we're going to create a Channel together, now.

Basic Channel Setup

Use the Rails channel generator to create an ActionCable Channel class called ExampleChannel. If this is the first time you've generated a Channel, a number of important files and folders will be created.

rails g channel example

In this configuration, every client that subscribes to ExampleChannel will receive any broadcasts sent to to the identifier visitors. Operations broadcast there will be sent to everyone looking at your site. They will be automatically subscribed to your channel.

app/channels/example_channel.rb
class ExampleChannel < ApplicationCable::Channel
def subscribed
stream_from "visitors"
end
end

The generator also creates a JavaScript channel subscriber. Import CableReady and modify the received method to check incoming data for CableReady broadcasts.

app/javascript/channels/example_channel.js
import CableReady from 'cable_ready'
import consumer from './consumer'
consumer.subscriptions.create('ExampleChannel', {
received (data) {
if (data.cableReady) CableReady.perform(data.operations)
}
})

Thanks to Turbo Drive / Turbolinks, subscriptions created in this manner will remain active until the user refreshes the page or leaves the site.

Broadcasting operations

Now that we have created a Channel, it's time to send commands to the client.

Make CableReady available

You can use CableReady almost anywhere in your application, so long as you include it in the class that you're working in:

include CableReady::Broadcaster

On the CableReady Everywhere page, you'll learn how to broadcast from (pretty much) anywhere in your application.

The 3 parts of a CableReady command

With few exceptions, all CableReady invocations have three predictable segments:

  1. Stream identifier(s): who (or what) will receive operations

  2. Operation queueing: one or more operations to broadcast

  3. Broadcast: deliver all queued operations immediately

class User < ApplicationRecord
include CableReady::Broadcaster
after_create do
cable_ready["visitors"] # send to everyone subscribed to the channel streaming from "visitors"
.console_log(message: "Welcome #{self.name} to the site!") # all users will see a message appear in their browser's Console Inspector
.broadcast # send all queued operations to all ExampleChannel subscribers
end
end

The ExampleChannel that you created will send any operations broadcast to visitors to all currently subscribed clients. In the code above, everyone on the site will see a Console Inspector message welcoming the latest member.

ActionCable can deduce ExampleChannel from visitors because only one Channel can stream from a given identifier. It is conceptually similar to Rails request routing, except that identifiers are defined inside of your Channel classes.

Queueing operations

CableReady maintains a queue of operations for every identifier. You can call cable_ready multiple times to add more operations to these queues.

cable_ready is a singleton instance, which means that you can keep adding operations to a queue across multiple method calls.

cable_ready["visitors"].console_log(message: "We have more salad than we can eat.")
cable_ready["visitors"].set_style(selector: "body", name: "color", value: "red")

You can use different operations together, and each operation can have completely different options. The most common option is selector, which is how you identify the target DOM element(s) for an operation. In fact, it's so common that you can just pass it as the first parameter, without a key.

cable_ready["visitors"].set_style("#foo", name: "color", value: "blue")

Operations will continue to accumulate for all stream identifier queues until you call broadcast.

Method chaining

When you call cable_ready["visitors"], it returns a CableReady::Channels object, which supports method chaining. That is, you can link up as many operations in sequence as you want, and they will be broadcast in the order that they were created.

cable_ready["visitors"].console_log(message: "1").console_log(message: "2")

The broadcast method concludes the chain. After the operations have been dispatched, the queues are emptied.

cable_ready["visitors"].console_log(message: "Welcome!").broadcast

Ready to rumble!

That's really all you need to get started with CableReady.

You can look over the next sections to learn more techniques, such as broadcasting to resources, or jump to the Operations reference and see everything CableReady can do.