Getting started with Rails 5's ActionCable and websockets
ActionCable is the websockets framework that ships with Rails 5. It aims to simplify the addition of realtime features to Rails apps. In this tutorial, we will explore ActionCable by building a simple chat application.
Let’s get started by installing the pre-release version of Rails (5.0.0.beta).
Once the final 5.0.0 version of Rails is released,
we won’t have to add the --pre
flag to the gem install command.
(ActionCable isn’t working well with Spring in 5.0.0.beta1,
which is why I’m using the --skip-spring
option.
This should be fixed in future versions of Rails.)
Adding the views
We will be displaying the chat messages in messages#index
.
Before joining the chat room, we will ask users to set a username
that will be displayed along with the chat messages.
The sessions#new
view will contain the form to pick a username,
and we will also set it as the home page.
Let’s add these routes to routes.rb:
Next, we add a SessionsController.
The #new
method only renders a view, so we can omit it here,
and just add the view template.
The #create
method sets a signed cookie, username
that we will use to identify the user.
We’ll also add the erb template for sessions#new
at
app/views/sessions/new.html.erb
:
In MessagesController
, the #index
method only renders the view,
so we don’t need to do anything there:
The messages/index
template looks like this:
The empty #messages
div is where we’ll append
all the chat messages coming through the channel.
Adding a channel
The next thing we will do is generate a channel that we can use to communicate via websockets between the client and the server.
This will generate a ChatChannel
with an action called speak
.
It also generates a chat.coffee
file in
app/assets/javascripts/channels
.
After creating our first channel, we need to turn on the cable connection in
app/assets/javascript/cable.coffee
by uncommenting these lines:
We also need to uncomment a line in config/routes.rb
so that
ActionCable runs in the same process when we run rails server
.
Setting up ChatChannel
First, we need to handle the actions present in ChatChannel
.
When a client connects to the channel, the #subscribed
action is called.
This subscribes the client to a stream called messages
.
Whenever data is broadcast to the messages stream, it is pushed to the clients.
The #speak
action corresponds to a method in the client side code.
When a user types a message a hits enter, we can call App.chat.speak
on the client side, which in turn invokes this action on the server.
When we receive a message in the #speak
action,
we will render the HTML for that message to the messages
stream.
We’re using the newly introduced ApplicationController.render
method
which allows us to render a partial into a string.
Let’s create the partial:
app/views/messages/_message.html.erb
:
When we broadcast the HTML to the messages
stream,
all the clients connected to the channel will receive the data
and call the App.chat.received
method on the client side.
Nest stop: writing the client side code corresponding to this.
Client side code
Update: This code uses jquery, which is not included by default in Rails 5.1 and above. Refer to Adam Narel’s comment below on how to include jquery.
Rails has already generated some client side code for us.
Let’s start by handling the event when enter is pressed in the chat input field.
Add this at the end of app/assets/javascripts/channels/chat.coffee
:
When you hit enter in the #chat-speak
input field,
this pushes the content of the field to the chat channel
by calling App.chat.speak
, which in turn sends it to the cable server.
Let’s also handle the two main actions in App.chat
:
With this, our chat app is ready. Try submitting a message in the input field, and you’ll see it appear on the page. There is one problem with this, though. We aren’t showing the name of the person sending the message. Let’s fix that.
Setting current_user
In the messages/_message
partial, we want to be able to
access the username of the current user.
To do this, we will add a #current_user
method in
ApplicationCable::Connection
.
The next thing to do is to use this ChatChannel#render_message
:
In app/views/messages/_message.html.erb
:
Restart the rails server and open the chat in two different browsers. Now, you can see the messages being rendered with the username.
Next steps
- Code for this example
- ActionCable screencast by DHH
- ActionCable examples from the Rails core team have some very nice examples that show off the more advanced features of ActionCable.
- GoRails: ActionCable and Websockets Introduction explores the code from the ActionCable examples linked above in a great screencast.
If you want to look at how similar features are implemented in other languages and frameworks, I recently wrote about building a similar chat app using the Phoenix framework for the Elixir language. Because of Elixir’s similarity with Ruby in terms of syntax, it should be easy for Rubyists to follow along.