There are times when one as a Ruby on Rails developer wants to implement a real-time feature, like a chat application, and after digging a bit you find a framework's feature named ActionCable, sounds new and scary, right? Fear not, I'll try to explain it as simple as possible so that at the end of this article you'll feel comfortable with the subject.
At the core, ActionCable uses the WebSocket connection protocol. The WebSocket protocol enables full-duplex connections between the client and the server to broadcast changes that occur in the server to the client as soon as it happens.
Because we are really busy and our only interest is how to implement the real-time features to our Ruby on Rails application I'll continue with the steps required to set up ActionCable. The following steps assume that you are familiar with Ruby on Rails and you are using a version of it greater than 5.2.
1. In your Gemfile, add the redis gem and run bundle install.
2. In your routes, mount the route where WebSocket requests are going to be delivered. You can do this with
mount ActionCable.server, at: '/cable'
, you guessed it, '/cable' is the default path.3. Insert
<%= action_cable_meta_tag %>
at the head of your main layout template.4. Create a channel that will stream from a given WebSocket identifier, as an example, below is a code snippet of a file generated after running
rails g channel messages
on the terminal.# app/channels/messages_channel.rb
class MessagesChannel < ApplicationCable::Channel
def subscribed
stream_from 'messages'
end
end
5. Broadcast to the channel, you can do this from any other part of your application, it usually happens in a job or a controller. The last argument to the
broadcast
method is a hash that will be serialized as JSON and received at the side of the client.# app/controllers/messages_controller.rb
class MessagesController < ApplicationController
def create
# ...
if message.save
ActionCable.server.broadcast(
'messages',
body: message.body,
author: message.author
)
#...
end
# ...
end
end
6. Ruby on Rails uses Redis to store and maintain synced the messages sent over the channel streams, if you have Redis installed locally you can start the server at some port and specify the port to listen in your ActionCable configuration file.
# config/cable.yml
production:
adapter: redis
url: redis://localhost:6478/1
# ...
7. When we generated the channel, a file named
messages_channel.js
was created under the channels folder inside of the directory holding the javascript files (it is different between Rails 5 and Rails 6), this file is the client-side of the WebSocket connection and is there where the data will be received.// Rails 5
// app/assets/javascripts/channels/messages.js
App.messages = App.cable.subscriptions.create(
'MessagesChannel',
{
received: function(data) {
document.getElementById('messages').innerHTML += '<div><strong>' + data['author'] + '</strong>: ' + data['body'] + '</div>';
},
},
);
// Rails 6
// app/javascript/channels/messages_channel.js
import consumer from './consumer';
consumer.subscriptions.create(
'MessagesChannel',
{
received(data) {
document.getElementById('messages').innerHTML += `<div><strong>${data.author}</strong>: ${data.body}</div>`
},
},
);
And that's it! It can get complicated quickly, but these are the basics. In summary, this is what you have to do, first generate the channel, then specify from where to stream in your channel file (the Ruby one), after that broadcast to the channel at the specified stream anywhere else in your application (a controller or a job), and finally receive the data at the side of the client and display it using JavaScript DOM manipulation.
I'm a Full-stack Ruby on Rails developer with many projects in the pocket that put into practice what I write about, if you want to reach out to me here you have my email and my LinkedIn profile, just in case here is my GitHub profile too.
Happy coding!