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.
In our first example we are going to push content from the Rails console
into a browser which shows the page#index
view.
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
Rails.application.routes.draw do
get 'page/index'
root 'page#index'
end
The content of the view:
<h1>Action Cable Example</h1>
<div id="messages"></div>
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.
└─ [email protected]
✨ Done in 0.52s.
To load 'jQuery' we have to add it in the
app/assets/javascripts/application.js
file:
//= require jquery
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .
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.room = App.cable.subscriptions.create "WebNotificationsChannel",
received: (data) ->
$('#messages').append data['message']
Lastly we have to add this code to the channel.
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':
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:
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'
:
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.
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/
|