Build A Real-Time Chat App: ActionCable/Rails

In this blog post, I am going to teach you what websockets are and how to build a real-time chat web app with ActionCable in Rails 5.

A little bit about why

I first heard of websockets when I was Googling around about real-time web applications becuase I was curious about real-time notifications and updates as features for web apps and how they were implemented. I initially learned what they were and how they came to be for my talk for Flatiron Presents: Websockets && ActionCable.

Since then, I’ve been reading up on it and became interested in implementing my own chat app. So, I’ve read a few tutorials, but this is the best in my opinion.

Before we get started building, let’s explore a few terms.

What are Websockets? What is ActionCable?

The main distinction between the HTTP protocol and Websockets is the same as the difference between “half-duplex” and “full-duplex”.

“Half-duplex” refers to the ability to communicate with the server one request at a time, whereas ‘full-duplex’ refers to the ability to communicate more than one request at a time with a bi-directional connection setup between the server and client that remains in constant communication.

Websockets are ‘full-duplex’, meaning that one client can send a message at the same time as another client and both messages will be rendered in real-time.

ActionCable is simply Rails’ implementation of Websockets protocol that was released by the HyPy group from Google, Inc. in 2011.

For more information, see my other post on Websockets && ActionCable or these resources (Real-Time Rails, ActionCable Rails Guides, Rails 5 Tutorial, DHH ActionCable Demo).

How long will it take to build this chat app?

I’d allow about half a day to get familiar with the tutorial and build it if you have some familiarity with MVC and Rails. It took me about that much time to build.

What was a challenge about building this chat app?

One of the challenges with building this app was reading the error messages coming from the heroku logs on the server. I had to debug those error messages for the first time and they show up in a different syntax, as well as use the CLI to debug deployment issues.

For example:

2018-06-08T20:24:15.745182+00:00 app[web.1]: I, [2018-06-08T20:24:15.745070 #4]  INFO -- : [0a978a8a-103a-4e3c-8f0d-e4d584e37f29] Started POST "/messages" for 65.207.79.74 at 2018-06-08 20:24:15 +0000

Instead of:

Started GET "/cable" for ::1 at 2018-06-11 11:55:44 -0400
Started GET "/cable/" [WebSocket] for ::1 at 2018-06-11 11:55:44 -0400
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
Registered connection (Z2lkOi8vY2hhdC1hcHAvVXNlci8x)
RoomChannel is streaming from room_channel
RoomChannel is transmitting the subscription confirmation
RoomChannel is streaming from room_channel_user_1

Otherwise, the tutorial is fairly straightforward to follow so long as you are familiar with MVC organizations for web apps. Even if you are not, Hartl does a great job of making it accessible and highlights key changes that occur at every step.

See What We're Building First

It is under current development. In the meantime, see the app working in production here on Heroku.

You can sign up as any user with any password (atleast 6 characters) or use ‘bob’ (pwd:asdfasdf) or “alice” (pwd: wonderland).

Let's Get Started!

Step 1) Download the base app ActionCable Base Chat App.

Fork it and then clone the fork on your local repo.

To clone it, use this command in your CLI: git clone https://github.com/<username>/action_cable_chat_app.git

Step 2) `cd` into your local repo

cd action_cable_chat_app/

Step 3) Run the necessaries

Run the following commands: bundle install
rails db:migrate rails db:seed

Then: rails test (all tests should be green)

Step 4) Go to the `app/controllers/messages_controller.rb` file

Go to def create and add the following just below the definition of the action:

message = current_user.messages.build(message_params)

I chose to skip most of the javascript in the tutorial to the “Upgrading to ActionCable” section. Section 4. The purpose of the Javascript portion of the tutorial is to teach one why exactly websockets are an upgrade. Let’s assume we feel the pain of polling and move forward.

Step 5) Create a new branch

Enter git checkout -b action-cable into the CLI. This tells git that we are working on a new branch and we are now committing changes in that branch to be merged with master later.

Step 6) Create the Rooms Channel

Run rails generate channel Room.

If you want to get back to a blank slate for the app chat room, then run this : rails db:migrate:reset && rails db:seed

This command resets the database and reintegrates the seeds for the database.

Go to app/channels/room_channel.rb file in your text editor. I use Sublime. You should see something like this:

# Be sure to restart your server when you modify this file.
# Action Cable runs in a loop that does not support auto reloading.
class RoomChannel < ApplicationCable::Channel
  def subscribed
    # stream_from "some_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

We want to subscribe to the Room channel that we just generated.

So, we want to add this: stream_from "room_channel" to the #subscribed action.

Our RoomChannel should look like this now:

# Be sure to restart your server when you modify this file.
# Action Cable runs in a loop that does not support auto reloading.
class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "room_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

Step 7) Edit the Messages Controller

Edit your MessagesController #create to look like this:

def create
message = current_user.messages.build(message_params)
if message.save
  ActionCable.server.broadcast 'room_channel',
                               content:  message.content,
                               username: message.user.username
end

This allows ActionCable to send the content of the messages that a user enters as well as a username in the room_channel.

Step 8) Editing Room.coffee

Go to app/assets/javascripts/channels/room.coffee

Here is what your file should look like before we edit it:

App.room = App.cable.subscriptions.create "RoomChannel",
connected: ->
# Called when the subscription is ready for use on the server

disconnected: ->
# Called when the subscription has been terminated by the server

received: (data) ->

To test that we received the data object, we will update the received: (data) -> function to add an alert alert data.content like this:

App.room = App.cable.subscriptions.create "RoomChannel",
connected: ->
# Called when the subscription is ready for use on the server

disconnected: ->
# Called when the subscription has been terminated by the server

received: (data) ->
    alert data.content

To get this alert to work, we need to do one more step.

Step 9) Mounting the ActionCable Server in Your Routes

Go to config/routes.rb. It should look like this:

Rails.application.routes.draw do
  root 'messages#index'
  resources :users
  resources :messages
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
end

Let’s mount our ActionCable server in our routes:

Rails.application.routes.draw do
  root 'messages#index'
  resources :users
  resources :messages
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

  mount ActionCable.server, at: '/cable'
end

This allows the application to know about our ActionCable server so that it can transfer information between your local system and the main Rails server.

Step 9a) Using a Cloud IDE? Add this to your `config/environments/development.rb` file

config.action_cable.allowed_request_origins = [
    'insertyourURLhere' ]

Step 10) REJOICE!

You have now built an ActionCable chat app that works on your local machine!

To learn how to deploy it to production and add extended features such as login protection and SSL in production, follow 4.2 - 7 of the Hartl tutorial and follow Hartl’s Professional Grade Deployment guidelines.

What did you specifically did you learn while building this chat app?

The main key takeway from this tutorial is that there are three main steps to setting up a Websocket.

  1. Setting up the channel.
  2. Making the coffeescript file for the rooms channel to function.
  3. Making edits to the rooms controller #create action to get the ActionCable to function properly.

Or in the words of Hartl:

"There are only three main steps needed to convert the base app to a working Action Cable chat app:

Make a channel to handle WebSocket connections on the server side (local server in development, Heroku in production).

Make a CoffeeScript program (room.coffee) for the chat room on the client side (web browser).

Update the Messages controller create action to broadcast changes to the channel instead of redirecting or rendering.”

4 Upgrading to Action Cable by Michael Hartl

I want to see your code! Where is your code repo?

ActionCable Chat App

Is there screenflow of this app working in production?

It is under current development. In the meantime, see the app working in production here on Heroku.

Update: here is the screenflow of the app!

Questions or comments?

Feel free to reach out with any questions or comments max.goodman@flatironschool.com or find me on Twitter here to DM me about your own implementation of ActionCable Websockets.

Published 11 Jun 2018

full stack && ethereum engineer.
Max Goodman on Twitter