ActionCable: 101 — Ruby on Rails

(Before we start keep in mind that this is not a content about 100% of this feature or even covers half of all the documentation, It is just some kind of “Action Cable in a nutshell”.)
‘Action Cable seamlessly integrates WebSockets with the rest of your Rails application. It allows for real-time features to be written in Ruby in the same style and form as the rest of your Rails application, while still being performant and scalable. It’s a full-stack offering that provides both a client-side JavaScript framework and a server-side Ruby framework. You have access to your full domain model written with Active Record or your ORM of choice.’
Basically, ActionCable is the new “good way” (I would to say ~hype~ ;D) to handle asynchronous changes in our DOM! That means after our view is rendered, we cannot see any changes on the screen without refreshing the page but ActionCable ‘listen’ to those changes and re-render our DOM with no hard work.
Okay, first of all, lets create our new rails project:
rails new cable-in-actionokay, now, lets create a Home controller just to see what is about to happen!
rails g controller Home indexand then we define our root path on our routes.rb: (just add the following line)
root 'home#index'Our localhost:3000 should look like this by now:

Let’s create a controller to add some messages (to be rendered on our home page)
rails g controller Messages create(I’m using only a create method because I don’t feel like I need to use anything else than that — at least for now :D)
and inside our create method (in app/messages_controller.rb):
message = Message.create!(params.permit(:message))and of course, lets create our model Message to have our messages that have been sent, stored in our database.
rails g model Message message:textNow we add a little form on our root index view, just to write and send our message:
<%= form_with url: 'messages' do |form| %> <%= form.text_field :message %> <%= form.submit %><% end %>
(don’t forget to)
rake db:migrate(quick note: if you are having any issue with version, try just to prepend the ~bundle exec ~on your command)
and to make that work we add the following line in our config/routes.rb
resources :messagesand our page look like this now:

Now lets get all messages and render them on our index page. Just add the following line on our home/index.html.erb
in our action index (app/home_controller.rb):
def index @messages = Message.allend
and in our view:
<%= @messages.each do |message|%> <div><%= message %></div><% end %>
Now, to check if there is any new message sent, we should refresh the page and the new messages will show up:

the problem is, the page is being refreshed automatically every time we click the save button by our message controller
.
.
.
redirect_to root_path(if we take out that line, new messages only show up if we refresh the page manually)
That said, Action Cable comes to our rescue, and helps us to create a Really efficient Single Page Application. In our case its a very simple app, so lets go do our first step. (FINALLY!)
A few things we should keep in mind, ActionCable needs ~redis~ and ~jquery~ installed so:
you need to install JQUERY:
$ yarn add jqueryand after that we should require it on our application.js
//= require jqueryWith that done, install redis on your machine.
brew install redisthen add the gem on your gemfille
gem 'redis'then, bundle it:
$ bundle installAll that set, lets create our ~channel~:
rails g channel WebMessagesThat last command line generated two filles for us: one is the
1 - app/channels/web_messages_channel.rband the other one is
2 - app/assets/javascripts/channels/web_messages.coffee. In our app/channels/web_messages_channel.rb (1) we should uncomment the line inside the subscribed method and stream the channel we just created:
def subscribed stream_from "web_messages_channel"end
In our channels/web_messages.coffee (2) we are going to treat all the data exchange. Take a look at the structure of that fille:
App.web_messages = App.cable.subscriptions.create "WebMessagesChannel",connected: -> # Called when the subscription is ready for use on the serverdisconnected: -> # Called when the subscription has been terminated by the serverreceived: (data) -> # Called when there's incoming data on the websocket for this channel
So, every time a ~connection~ is made, wether connecting or not, we could do something!
So basically, ActionCable works upon javascript! so, in our rails app, using coffescript, we should add a div with an id in our view (just so our js could find where to go and what to do with that):
<div id="messages"></div>and, in our web_messages.coffee, within the received method we add the following line:
received: (data) ->
$('#messages').append data['message']and now, to see our view being render we just add a method from action cable when a new message is saved:
class MessagesController < ApplicationController def create
message = Message.create!(params.permit(:message))
if message.save
ActionCable.server.broadcast 'web_messages_channel', message: "<div> <h2>#{message.message}</h2> </div>"
end
endend
we are passing ‘message:…’ because we are receiving ‘message’ in our ‘received: (data)’ method.
Now, this our app should works like this:

Although, its still weird that our input doesn’t be blank after we pressed the send button. so lets add this to our app.
within our form tag, added a id=”messagesInput”:
<%= form_with url: 'messages' do |form| %> <%= form.text_field :message, id: "messageInput"%>
<%= form.submit %><% end %>
and then, inside our web_messages.coffee we add:
$('#messageInput').val('')so, by now, your files should look like this:
- app/assets/javascripts/channels/web_messages.coffe
App.web_messages = App.cable.subscriptions.create "WebMessagesChannel",connected: ->
# Called when the subscription is ready for use on the serverdisconnected: ->
# Called when the subscription has been terminated by the serverreceived: (data) ->
$('#messages').append data['message']
$('#messageInput').val('')
# Called when there's incoming data on the websocket for this channel
- app/channels/web_messages_channel.rb
class WebMessagesChannel < ApplicationCable::Channel def subscribed
stream_from "web_messages_channel"
end def unsubscribed
# Any cleanup needed when channel is unsubscribed
endend
and
- app/views/home/index.html.erb
<h1>Home#index</h1><p>Find me in app/views/home/index.html.erb</p><%= form_with url: 'messages' do |form| %>
<%= form.text_field :message, id: "messageInput" %>
<%= form.submit %>
<% end %><div id="messages">
<% @messages.each do |message|%>
<div>
<h2> <%= message.message %> </h2>
</div>
<% end %>
</div>
How our app looks like now:

- There is also a youtube video about the basics: https://www.youtube.com/watch?v=5IHxPhAFjEQ&t=
Take a Look at the documentation: https://guides.rubyonrails.org/action_cable_overview.htmlIn Addition, Action Cable currently is the fastest way to handle asynchronous changes on our DOM and It’s a very easy tool to configure. That’s it for now! you can get in touch with through twitter.
