A simple guide to Action Cable 
Tags: ruby, rails, tutorial
Published: 2021-05-06 02:00:19

Action cable is the Rails way of implementing WebSockets - with some Rails magic. 

Repository for this app [here](https://github.com/LucasKuhn/actioncable-app)

## Why use it
Usually your client connects with your server by making requests: 
![953F8214-7173-4755-B16E-AB6FC9983223](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbkmkjgf1l8yh2cjbp9h.png)

With ActionCable, you create an open connection between your client and your server, allowing a communication flow: 
![65223B82-BB95-4D36-8FC0-7EE6B1227ED4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9wdg07e6kawodmz9tqo.png)
 

### Example: 
You have a simple blog - posts and comments - and multiple users reading that post. If one user adds a comment, the others will never know: 
![Kapture 2021-05-05 at 22.53.50](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l38e37j4mkyo32myub12.gif)
 
But with the open connection from ActionCable, they will get instant updates for that post 🔥
![Kapture 2021-05-06 at 09.57.29](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65lycq6sw22jofcaqxqo.gif)
 
## How to do it 

First of all, generate a _channel_ for your Posts. This class will be able to broadcast updates to all clients listening: 
```
rails generate channel posts
```

Which will create some files for you:
```
      create    test/channels/posts_channel_test.rb
      create  app/channels/posts_channel.rb
   identical  app/javascript/channels/index.js
   identical  app/javascript/channels/consumer.js
      create  app/javascript/channels/posts_channel.js
```

---

## Sending messages
We will work with our newly generated `posts_channel.rb`

We want to specify from which channel to stream, so we can pass an `id` params and ask rails to make a stream for that post:

```ruby 
class PostsChannel < ApplicationCable::Channel
  def subscribed
    post = Post.find(params[:id])
    stream_for post
  end
end
```

And now, from anywhere in our app, we can call PostsChannel and ask it to broadcast  something to anyone listening to that post: 
```ruby
PostsChannel.broadcast_to(@post, “hello world”)
```

We will add this to our create action, to broadcast the comment to the post channel every time a comment is created: 
```ruby 
# app/controllers/comments_controller.rb

def create
  @comment = @post.comments.new(comment_params)
    if @comment.save
      PostsChannel.broadcast_to(@post, @comment.body)
      redirect_to @post, notice: "Comment was successfully created."
    else
      render :new
    end
end
```
And that does nothing so far since no one is listening to this broadcast. Moving forward! 

## Receiving messages

#### Opinionated setup:

I do not like to create a separate file for every consumer, I prefer to do the connection in script tags in the view. It feels more like a separate front end, where only the view that needs a connection creates one. 
To do so, add this snippet in `app/javascript/channels/index.js`: 
```js 
// Expose action cable
import * as ActionCable from '@rails/actioncable'
window.App || (window.App = {});
window.App.cable = ActionCable.createConsumer();
```
*Note:* Exposing the cable [was the default according to official docs](https://guides.rubyonrails.org/v5.0/action_cable_overview.html#client-side-components) until Rails 6, where Webpacker was introduced

The rails generator we used before created a file in `app/javascript/channels/posts_channel.js` 
Here’s why we won’t use it:
* It is always required, so whatever we put in it will run on every page of our app 
* We don’t want everyone opening a connection to get updates, just the people on our post show page

So you can go ahead and delete the created posts_channel.js 🗑

And add a code to listen to our broadcast on the post show page: 
```erb
<!-- app/views/posts/show.html.erb -->

<script>
  App.cable.subscriptions.create({ channel: "PostsChannel", id: "<%= @post.id %>" }, {
    connected() {
      console.log("Connected to the channel:", this);
    },
    disconnected() {
      console.log("Disconnected");
    },
    received(data) {
      console.log("Received some data:", data);
    }
  });
</script>
```

And now, upon opening our blog post page, we can see the connected message on our terminal, and some Rails magic that enabled this connection: 
![82E5203A-8D8C-494E-8810-0CEACBF4AEAB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3kf26kih020li4qfmyi.png) 

The `posts:Z2lkOi8vYWN0aW9uY2FibGUtYXBwL1Bvc3QvMg` is the name of the channel created by rails when we told it to `stream_for post` in our posts_channel file.

And you’re done! 🎉 
~Almost~

While the above script received data, it doesn’t show it on the page. We can   update it to add the comment to our list upon receiving them: 
```js
App.cable.subscriptions.create({ channel: "PostsChannel", id: "<%= @post.id %>" }, {
  received(comment) {
    el = document.createElement('li');
    el.innerHTML = comment;
    document.querySelector('ul').appendChild(el)
  }
});
```
(All functions are optional, I removed the disconnected and connected ones) 

And there you go, your app now talks to any browser listening to it via Action Cable: 
![Kapture 2021-05-06 at 09.57.29](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/65lycq6sw22jofcaqxqo.gif)

Full code repo: [GitHub: actioncable-app](https://github.com/LucasKuhn/actioncable-app)

#### References 
Rails Guide: 
 [https://guides.rubyonrails.org/action_cable_overview.html](https://guides.rubyonrails.org/action_cable_overview.html?fbclid=IwAR1e2hlrvUB0qPzmFZ1KB7JfkseRSAJUckbMkHV-caaBu-yH1LNxCI9fZEE) 
Heroku Guide: 
 [https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable](https://l.facebook.com/l.php?u=https%3A%2F%2Fblog.heroku.com%2Freal_time_rails_implementing_websockets_in_rails_5_with_action_cable%3Ffbclid%3DIwAR3xePBKYPjiptHNHhW520hfxddI5moPOgCechqi3cGqFLXgRAUzU0c1s3w&h=AT1gDytX1Q7SvWag-FK-6TRL3ZQqfNAEMKx2s_0LuFMUBdFPhsoYAWTlAceA9-gF16zcVF0GsuYb5GIXMbhQoDs8S54CmrioiOYTDTVhXCXo-Mp1hFZuNVads7wETfNrJkJNtQQe) 
Cable.yml Config: 
 [https://github.com/rails/rails/issues/28118](https://l.facebook.com/l.php?u=https%3A%2F%2Fgithub.com%2Frails%2Frails%2Fissues%2F28118%3Ffbclid%3DIwAR0hiW4E9dEvz4Awl5EDeDXmePJW8acG05dn-9lporNyWngNjr5PxRC_82c&h=AT1gDytX1Q7SvWag-FK-6TRL3ZQqfNAEMKx2s_0LuFMUBdFPhsoYAWTlAceA9-gF16zcVF0GsuYb5GIXMbhQoDs8S54CmrioiOYTDTVhXCXo-Mp1hFZuNVads7wETfNrJkJNtQQe) 
Cable for specific pages: 
 [https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages](https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages?fbclid=IwAR0_Nz3ICdHB5JOE896e0HG51yhGiluGjzLuWCrlK2HecXVC545IMGJDk24) 
 [https://stackoverflow.com/questions/36438323/page-specific-actioncable](https://l.facebook.com/l.php?u=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F36438323%2Fpage-specific-actioncable%3Ffbclid%3DIwAR3DZ3CO7Biofa1DXfiWFXOurHdNbTrinUF_h5D8Bg6d_VT7jvrJIoADpxY&h=AT1gDytX1Q7SvWag-FK-6TRL3ZQqfNAEMKx2s_0LuFMUBdFPhsoYAWTlAceA9-gF16zcVF0GsuYb5GIXMbhQoDs8S54CmrioiOYTDTVhXCXo-Mp1hFZuNVads7wETfNrJkJNtQQe) 
Good JS subscription examples: 
 [https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages](https://stackoverflow.com/questions/39597665/rails-actioncable-for-specific-pages?fbclid=IwAR11aMICYd5jBjdPylyMmmNUFSvx16sHLjhBjD-zbn7rPKFsQgEg3KRmV2M) 
 [https://samuelmullen.com/articles/introduction-to-actioncable-in-rails-5/](https://samuelmullen.com/articles/introduction-to-actioncable-in-rails-5/?fbclid=IwAR0NZcr4c9vk-B9avdHWabUCreEj5sF1mf8F_n48SWdsgvzS48on-nV1e3c) 
 [https://stackoverflow.com/questions/36266746/actioncable-not-receiving-data](https://l.facebook.com/l.php?u=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F36266746%2Factioncable-not-receiving-data%3Ffbclid%3DIwAR11aMICYd5jBjdPylyMmmNUFSvx16sHLjhBjD-zbn7rPKFsQgEg3KRmV2M&h=AT1gDytX1Q7SvWag-FK-6TRL3ZQqfNAEMKx2s_0LuFMUBdFPhsoYAWTlAceA9-gF16zcVF0GsuYb5GIXMbhQoDs8S54CmrioiOYTDTVhXCXo-Mp1hFZuNVads7wETfNrJkJNtQQe) 
Usage with ActiveJob 
 [https://www.pluralsight.com/guides/creating-a-chat-using-rails-action-cable](https://www.pluralsight.com/guides/creating-a-chat-using-rails-action-cable?fbclid=IwAR2BNC0JG3-ADFvZn5nC9OxiFnTHWBX5cMPPyYIWbSI4Npwc4Jav6lr1tr4) 
Cable on ReactNative 
 [https://stackoverflow.com/questions/43510021/action-cable-not-receiving-response-on-client](https://stackoverflow.com/questions/43510021/action-cable-not-receiving-response-on-client?fbclid=IwAR3DZ3CO7Biofa1DXfiWFXOurHdNbTrinUF_h5D8Bg6d_VT7jvrJIoADpxY) 
AnyCable
[Action Cable vs AnyCable: fight! | Nebulab](https://nebulab.com/blog/actioncable-vs-anycable-fight)