bolt githublinkedinPage 1Created with Sketch. spots swoosh target triangle-icon twilio twitterweave

Action Cable Feature in Rails 5

VitaliiFebruary 01, 2018

Rails 5.0.0 was released just over a year ago, on June 30, 2016. It includes lots of major changes but, probably the most interesting feature is Action Cable. It is a new framework in Rails 5. It seamlessly integrates WebSockets with the rest of your Rails application.

Action Cable is the new feature which Rails 5 provides from the box. It allows developers to create real-time features using Ruby syntax in the same style and form as common Rails application, while still being performant and scalable. Action Cable is a full-stack offering that provides both a client-side JavaScript framework and a server-side Ruby framework.

Because I think it’s a great upgrade I’d like to show you a simple example of how can it be used. The most common use case is real time chat, which works without page reloading.

Preparation

First of all let’s create a new Rails application.
In this step we create the new application with a postgresql database:

$ rails new chat —database=postgresql

Next, let’s create our first controller:

$ rails generate controller rooms show

And set show action as the root path:

# config/routes.rb

Rails.application.routes.draw do
  root to: 'rooms#show'
end

Now we need a model which will store all the messages. It will have only one field - content

$ rails g model message content:text

Also, we will need to migrate the DB:

$ rails db:migrate

Let's go back to RoomsController and its one action show. We will have a simple chat with one room so that it will show all messages on one page.

class RoomsController < ApplicationController
  def show
    @messages = Message.all
  end
end

After this we will need to customize the

app/views/rooms/show.html.erb view

<h1>Chat room</h1>

<div class="js-messages">
  <%= render @messages %>
</div>

<form>
  <label>Say</label><br>
  <input type="text" data-behavior="room_speaker">
</form>

And app/views/messages/_message.html.erb

<div class="message">
  <p><%= message.content %></p>
</div>

The next step is to establish the WebSocket connection. First, we need to mount the Action Cable server onto a sub-URI of our main application. For this we have to add following line to routes.rb file.

mount ActionCable.server => '/cable'

Now Action Cable will be listening for WebSocket requests on ws://localhost:3000/cable.
Also we have to set up an Action Cable url for our environment: in our case it will be “development”:

# config/development.rb

Rails.application.configure do 
  config.action_cable.url = "ws://localhost:3000/cable"
end

Main Part

Before we define our own Messages Channel and start working directly with Action Cable code, let's take a quick tour of the Action Cable infrastructure that Rails 5 provides for us.
When we generated our new Rails 5 application, the following directory was generated for us:


icons

Our ApplicationCable module has a Channel and a Connection class defined for us by the Rails framework. The Connection class is where we would authorize the incoming connection — for example, establishing a channel to a given user’s inbox, which requires user authorization. We'll leave this class without changes, as any user can join our chat at any time. However the Messages Channel, that we will define shortly, will be inherited from ApplicationCable::Channel.

$ rails generate channel room speak

In this way we’ve generated the room channel. As you can see, it created 3 files.


green

  • cable.js: This file defines the client-side instance of our WebSocket connections. It’s autogenerated and we won’t change anything there.
//= require action_cable
//= require_self
//= require_tree ./channels

(function() {
  this.App || (this.App = {});

  App.cable = ActionCable.createConsumer();

}).call(this);

  • room.coffee: This file has callbacks for when the connection is made to the server, when it’s disconnected and when it receives the answer, which you can customise with your own actions. There is also our speak action which we are going to use in our chat. You will also see here an action for the key press event, this will serve as the submit action on pressing enter (return). Within the key press action we call the speak method to push the user’s message to the server. You will notice that we have customized received callback. We will append new messages to the common chat block.
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) ->
    # Called when there's incoming data on the WebSocket for this channel
    $('.js-messages').append data['message']

  speak: (message) ->
    @perform 'speak', message: message

  $(document).on 'keypress', '[data-behavior~=room_speaker]', (event) ->
    if event.keyCode is 13 # return = send
      App.room.speak event.target.value
      event.target.value = ""
      event.preventDefault()

room_channel.rb: Here we will define our channel to inherit from the ApplicationCable::Channel class that we described earlier. Our Room Channel needs only a few methods for our purposes: i.e. the #subscribed and #speak methods:

  • #subscribed method is responsible for subscribing to and streaming messages that are broadcast to this channel.
  • #speak is our own method. It broadcasts messages immediately after they are created and persisted to the database. So we'll define our broadcasting code within the #speak method.
class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'room_channel'
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    message = Message.create! content: data['message']
    ActionCable.server.broadcast(
      'room_channel',
      message: render_message(message)
    )
  end

  private

  def render_message(message)
    ApplicationController.renderer.render(
      partial: 'messages/message',
      locals: { message: message }
    )
  end
end

As you can see we broadcast html code which will be appended on client side (see room.coffee file).

Final Touches

To make all of this code work you need to add a few more details.

  • Add Action Cable meta tag to your layout:
# app/chat/layouts/application.html.erb

<%= action_cable_meta_tag %>
  • Add redis to your Gemfile and install it:

gem 'redis', '~> 3.0'

  • Change your Action Cable configs in config/cable.yml
development:
  adapter: 'redis'
  url: 'redis://localhost:6379/1'

That’s it. Now you are ready to launch! . You can also check our live heroku application on https://rails-cable-chat.herokuapp.com


chatroom

Summary

As you can see Action Cable is really useful feature which can be used in lots of places. Our team used it on real projects: it was implemented to show the name of all participants attending classes on our fitness training projects. This information updates in realtime after updating class participants info, so coaches can see this info on the kiosks.

Action Cable adds really big advantages to the Rails framework. It provides a simple and clear interface similar to the rest of the rails application, which means that you don’t need to spend months learning how to use it.

THINK

out loud

We think out loud on our blog. We share our views on coding, remote work and post regular insights into the freshest tech industry news.

Want more? Join our mailing list.

    DVELP

    WORK WITH US.

    Client Engagement

    +44 (0) 20 31 37 63 39

    [email protected]

    So, how can we help you?

    Live chat