WebSockets is a beautiful way to allow the client-server communication using a persistent bidirectional channel without the client having to poll the server.
In this article I’d like to explain how to get started with websocket in Ruby on Rails from a generic application. I wont’ dive into what is a Websocket or how to create a Rails project. I’ll just leave some links at the end of this article.
First of all, WebSocket in Rails are called ActionCable
.
The problem
Tutorials from the official documentation or others like this one explain how to configure the server part and show the code to subscribe and communicate from a Javascript frontend using the rails/actioncable
node module. But what if you have an API-only Rails project or you have a frontend other than JS, like an Android or an iOS app? This is why I wrote this article.
Get started!
Let’s suppose we’ve a Rails project and you run the command below
rails generate channel Chat
it will generate a file called <your_name>_channel.rb
where we can handle the client subscriptions or/and we can write custom methods that will be called by the clients… To keep the things simple we’ll implement the subscribed
method and we write from scratch a custom dummy method (my_method
) that receive a data
parameter. We’ll see later what data
is. Let’s replace <your_name>
with ChatChannel
for this example. Here’s how file looks like.
class ChatChannel < ApplicationCable::Channel
def subscribed
logger.info "subscribed"
stream_from "chat_channel"
end
def my_method(data)
puts data
end
end
Our goal is reach the
subscribed
and themy_method
methods from an external client other than JS or out of our Rails project. Let’s start!!!
Photo by Braden Collum on Unsplash
First question: how to create a WebSocket client? Or can we use a “Postman” like client already available?
There are many ways and libraries to establish a WebSocket connection for many platforms, but to get started I suggest this ready to use WebSocket clients
Second question: which URL should I call? In my project routes I can’t see anything related to ActionCable
- Firstly we have to change the protocol from
http/https
tows
. - Let’s suppose that our Rails project runs on
localhost
, port 3000. - By default without specifying anything else, Rails expose the WebSocket service on
/cable
path
Putting these things together the URL is ws://localhost:3000/cable
Third question: how we communicate?
Basically with JSON messages as specified by WebSocket protocol. Like the rails/actioncable
JS module, libraries provides an “SDK way” to manage these messages, but in our simple web clients we have a “Message field” to write the command in, nothing more. I strived to find what to format the request and at the time I write I guess there is a lot more to find out. To guide me to the right direction i found these two articles (1, 2).The 2 also explain how to authenticate the user. To see the things working for a “step 0” in a simple way I’ll ignore this step. From the 1 I tried to copy/paste the string in the sendTXT
method in the web client Message field as is (even with begin/end double quotes and all escape characters: “{\\”command\\”:\\”subscribe\\”,\\”identifier\\”:\\”{\\\\\\”channel\\\\\\”:\\\\\\”ArduinoChannel\\\\\\”}\\”}”
) but it didn’t work (I changed the name to my channel name first). I tried to remove the begin/end quotes and I thought that a problem was that the escape characters were too many. Hence I tried to serialize a dictionary representing the JSON above with the help of the Rails console . I finally obtained this JSON:
{"command": "subscribe","identifier": "{\"channel\": \"ChatChannel\"}"}
It works!!!! The web console answerd me “welcome”! Just take a look at the Rails log to be sure that the subscribed
method had been called and printed “subscribed”. Yes!!! All right, then: How to call my_method
method now?
Here’s the best part: we can pass as many parameters we want and we can call any methods with a very similar JSON
{"command": "message","identifier": "{\"channel\": \"ChatChannel\"}", "data":"{\"action\": \"my_method\", \"code\":\"a\"}"}
The differences:
- the command type has changed from “subscribe” to “message”
- identifier must remain the same as the subscription
- we added the
data
key with a JSON string (with all the escape characters)
Let’s send this message from the console and have a look at the Rails log. We’ll see these lines
ChatChannel#my_method({“code”=>”a”}).
It recognized theaction
keyword and interpreted the JSON as an hash parameter. If we print thedata
parameter we obtain indeed{“action”=>”my\_method”, “code”=>”a”}
Our goal has reached!!! Great!
Just a little last thing: in a normal Rails (http) controller method you can write this code:
ActionCable.server.broadcast("chat_channel","An update has arrived...")
You’re dispatching via WebSocket on the chat_channel
the “An update has arrived…” string: this will be printed even on your web console. Doesn’t it look like a notification
Further references
To learn about WebSocket or creating a Rails project
Thanks for reading…