Skip to content

Latest commit

 

History

History
265 lines (212 loc) · 7.42 KB

action-cable.adoc

File metadata and controls

265 lines (212 loc) · 7.42 KB

Action Cable

Most modern webpages are not just static. They often get updates from the server without interaction from the user. Your Twitter or GMail browser client will display new Tweets or E-Mails without you reloading the page. The server pushes the information via WebSockets (https://en.wikipedia.org/wiki/WebSocket). Action Cable provides the tools you need to use these mechanisms without diving deep into the technical aspects of WebSockets.

The use of Action Cable always includes JavaScript and this book is about Ruby and Ruby on Rails. So I will only show you a minimal Hello World example of how Action Cable works to give you an idea how to proceed.

Hello World Action Cable Example

In our first example we are going to push content from the Rails console into a browser which shows the page#index view.

The Rails Application

Please create the following Rails application:

$ rails new hello-world-action-cable
  [...]
$ cd hello-world-action-cable
$ rails db:migrate
$ rails generate controller page index
  [...]

Add a root route so that we can access the page at http://localhost:3000

config/routes.rb
Rails.application.routes.draw do
  get 'page/index'
  root 'page#index'
end

The content of the view:

app/views/page/index.html.erb
<h1>Action Cable Example</h1>

<div id="messages"></div>

Setting up jQuery

We are going to append HTML to <div id="messages"></div> in the DOM. To do that we use jQuery which is not installed by default in Rails any more. There are two ways of doing this. The old way was to add gem 'jquery-rails' followed by a bundle. This still works but Rails 5.2 has yarn build in and that is the new way. If you haven’t installed yarn yet have a look at https://yarnpkg.com/en/docs/install

If you are using macOS and Homebrew you can install it via brew install yarn:

brew install yarn

Within Rails 5.2 you can use yarn now to install jQuery:

$ bin/yarn add jquery
yarn add v1.3.2
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
└─ jquery@3.3.1
✨  Done in 0.52s.

To load 'jQuery' we have to add it in the app/assets/javascripts/application.js file:

app/assets/javascripts/application.js
//= require jquery
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .

Creating a Channel

Rails provides a handy generator to create a new WebSockets channel which we need to push information to the client. We call our channel "WebNotifications".

$ rails generate channel WebNotifications
Running via Spring preloader in process 13267
      create  app/channels/web_notifications_channel.rb
   identical  app/assets/javascripts/cable.js
      create  app/assets/javascripts/channels/web_notifications.coffee

When ever somebody requests the page#index view we want him/her to automatically subscribe to the WebNotificationsChannel channel. We do this by adding this piece of CoffeeScript:

app/assets/javascripts/page.coffee
App.room = App.cable.subscriptions.create "WebNotificationsChannel",
  received: (data) ->
    $('#messages').append data['message']

Lastly we have to add this code to the channel.

app/channels/web_notifications_channel.rb
class WebNotificationsChannel < ApplicationCable::Channel
  def subscribed
    stream_from "web_notifications_channel"
  end

  def unsubscribed
  end
end

We are going to start a rails server and a rails console in separate terminals. We need to use the Redis gem to make this work. This is not the default in the development setup.

To activate the Redis gem by include this line in the 'Gemfile':

Gemfile
gem 'redis', '~> 4.0'

After that change you have to run 'bundle' once more:

$ bundle

Obviously you need a running Redis server. If you are running macOS with Homebrew you can install Redis with brew install redis and start it with brew services start redis but don’t forget to stop it with brew services stop redis after using it.

And we have to configure the use of Redis:

config/cable.yml
redis: &redis
  adapter: redis
  url: redis://localhost:6379/1

production: *redis
development: *redis
test: *redis

To make things a little bit more complicated we have to configure the Content-Security-Policy in config/initializers/content_security_policy.rb to allow the use of Action Cable in the development environment by adding p.connect_src :self, :https, 'ws://localhost:3000':

config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |p|
  p.default_src :self, :https
  p.font_src    :self, :https, :data
  p.img_src     :self, :https, :data
  p.object_src  :none
  p.script_src  :self, :https
  p.style_src   :self, :https, :unsafe_inline
  p.connect_src :self, :https, 'ws://localhost:3000'
end
Tip
Have a look at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy for more info about Content-Security-Policy (CSP).

Finally it’s time to start up our development rails server in the first terminal:

$ rails server

And load http://localhost:3000 in your webbrowser. In the log you’ll see this entry:

Started GET "/" for 127.0.0.1 at 2018-01-27 23:30:56 +0100
Processing by PageController#index as HTML
  Rendering page/index.html.erb within layouts/application
  Rendered page/index.html.erb within layouts/application (1.5ms)
Completed 200 OK in 236ms (Views: 221.8ms | ActiveRecord: 0.0ms)


Finished "/cable/" [WebSocket] for 127.0.0.1 at 2018-01-27 23:30:56 +0100
WebNotificationsChannel stopped streaming from web_notifications_channel
Started GET "/cable" for 127.0.0.1 at 2018-01-27 23:30:56 +0100
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2018-01-27 23:30:56 +0100
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
WebNotificationsChannel is transmitting the subscription confirmation
WebNotificationsChannel is streaming from web_notifications_channel

Now start a second terminal and go to the directory where your Rails project is locate. Fire up the console and use ActionCable.server.broadcast to broadcast a message to web_notifications_channel:

$ rails console
Running via Spring preloader in process 19706
Loading development environment (Rails 5.2.0)
>> ActionCable.server.broadcast 'web_notifications_channel',
message: '<p>Hello World!</p>'
[ActionCable] Broadcasting to web_notifications_channel:
{:message=>"<p>Hello World!</p>"}
=> 1

Now you can see the update in your browser window.

hello world example

You can add other messages by calling ActionCable.server.broadcast 'web_notifications_channel', message: '<p>Hello World!</p>' again.

Congratulation! You have your first working Action Cable application.

Tip
By using $('#messages').replaceWith data['message'] in app/assets/javascripts/page.coffee you can replace the HTML content instead of appending it. See http://api.jquery.com/replaceWith/