ActionCable: A Simple Example
Rails 5 is released a few days ago, and ActionCable is one of the most notable features. The following is a very simple but workable example.
Installation
Add gems to the Gemfile
:
# Rails must be newer than 5.0
gem 'rails', '>= 5.0.0'
# ActionCable uses redis to store and retrieve data
gem 'redis'
# A server for websocket connection
gem 'puma'
In addition, Redis requires configurations in config/cable.yml
. It uses async
as default in development environment. In other words, it requires redis only in production environment. If you want to use redis locally, make sure it is installed by running:
brew install redis
Or sudo apt get install redis-server
if you're on a Ubuntu machine. After that, run:
redis-server
in order to run the server on port 6379
.
Server Side Setup
This demo will not use any model. Instead, it simply allows you to establish a websocket connection, and from which you will know what to do next. If you don't know how websocket works, it establishes a HTTP connection from the client to the server, and upgrades that connection to websocket afterwards. Thus it does not need to reconnect anymore like using AJAX.
If you're upgrading from Rails 4, it is necessary to have app/channels/application_cable
and two files in it. Follow this example directory on GitHub and copy the files if you need it.
Before defining any channel, we mount ActionCable to a specific route in config/routes.rb
:
Rails.application.routes.draw do
mount ActionCable.server => '/channels'
end
And add the following to the config/environments/development.rb
:
Rails.application.configure do
config.action_cable.url = "ws://localhost:3000/cable"
end
Apparently you will have to do the same in production environment before deployment. And add the meta tags in views/layouts/application.html.erb
:
<%= action_cable_meta_tag %>
Skip this step if you're hosting an API server.
Now let's create a new channel file app/channels/messages_channel.rb
as if we're creating a real-time messaging app. It goes like this:
class MessagesChannel < ApplicationCable::Channel
def subscribed
stream_from 'MessageChannel'
end
def receive
ActionCable.server.broadcast('MessageChannel', message: 'message received!', head: :ok)
end
end
- The
subscribed
method defines the channel name that we're subscribing to from the browser. - The
receive
method is called when this channel receives any message from the browser. - The
ActionCable.server.broadcast
method is for sending messages to the browser through its subscription. Themessage
attribute can by any attribute.
Also, we have to create a page for testing the connection. Create a app/controllers/messages_controller.rb
:
Rails.application.configure do
get "/" => "messages#index"
end
Add app/views/messages/index.html.erb
:
<button id="subscribe">Subscribe</button>
<button id="send">Send</button>
That's it!
Client Side Setup
Most tutorials have gone through the client-side setup. However, this tutorial is not using any JavaScript library from ActionCable. We're establishing the connection from scratch. First, let's take a look at the app/assets/javascripts/application.js
and see there's a basic setup for our application:
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
Remove these lines and add:
$(document).ready(function(){
var ws = new WebSocket("ws://localhost:3000/channels");
$("#subscribe").click(function(){
ws.send(JSON.stringify({
command: "subscribe",
identifier: "{\"channel\":\"MatchesChannel\"}"
}));
});
$("#send").click(function(){
ws.send(JSON.stringify({
command: "message",
identifier: "{\"channel\":\"MatchesChannel\"}",
data: JSON.stringify({'foo': 'bar'})
}));
});
});