What is a Websocket?
A brief history before we delve into WebSockets, but before they existed, if we wanted something like “real-time” data, we would have to continuous fetches to the server at regular intervals.
This would have been called polling. But because this means that the frontend would have to make continuous fetches, sometimes including fetches with no data whatsoever, this would clutter the call stack with unnecessary requests to the database.
A websocket solves this by keeping the connection open on the client and server side until one or both sides disconnects.
Implementing Real-Time with ActionCable Overview
We will have to create a Channel to make a chat room app.
The process is:
The client initiates a connection with a server via a WebSocket request. This will allow two-way communication.
The server handles this by a specific feature called a channel. For our example, we can create a ‘chatroom_channel’
After this connection is established (full-duplex communication), we can use this channel to broadcast messages to all users who are ‘subscribed’ to the channel (this, in our context, means that they have a chat window open).
Steps:
Create a chatroom channel on the server side
Browser-side, we’ll use JavaScript (or React.js for a frontend)
Then we:
Broadcast the message to the frontend side where it is received.
Then we display this data to the chat window or wherever we want it displayed.
Here are some more abstract steps:
- Generate chatroom channel
- Update messages_controller and create broadcast data to chatroom channel
- Write some JS to handle the data and receive data and append to chat window
- Update styling (such as scrolling) We would generate our channel inside the app/channels folder with the name chatroom_channel.rb or like so _channel.rb
Generate a Chatroom Channel
We need to run this command: $ rails generate channel
So for us this would be: $ rails generate channel chatroom
Which creates app/channels/chatroom_channel.rb which looks like this:
class ChatroomChannel < ApplicationCable::Channel
def subscribed
# establishing a connection to a channel
# make sure you stream from the channel name for the channel
stream_from “chatroom_channel”
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
But the next thing we have to do is mount this to some route in routes.rb so our frontend can actually communicate with this channel in some way.
So now, in our routes.rb, the way to mount this to a route:
mount ActionCable.server, at: ‘/cable’ → “/cable”
being the route
So now communication to the socket via that route, “/cable”, will be possible.
Modify and Broadcast Messages
Now let’s figure out how to broadcast our messages to everybody connected to the channel:
So now we need to go to our messages_controller.rb to alter the create route (this is the route that handles the POST of a new message to the messages table in the database) so that all new messages are broadcast.
In your app/config/environments folder there are these three files: development.rb, production.rb, and test.rb…for the sake of production builds and deployment, we will need to use this:
config.action_cable.allowed_request_origins = [‘https:// of your deployed app or something, this could be something in heroku-app or whatever’]
Displaying Messages by Appending new Messages to the screen when they are sent
We need to handle this in the Messages Controller and inside the chatroom_channel.js
Instead of partials which is what we would’ve used with Rails’s traditional MVC model, we’ll use frontend components in React. For this you need to append the message.body to the specific container you want.
class MessagesController < ApplicationController
before_action :require_user
def create
message = current_user.messages.build(message_params)
if message.save
# broadcasts to the specified channel that we made in the previous section
# this takes and sends a hash (object), whatever is sent here is received inside our chatroom_channel.js in the received(data) {} section of that file
# inside that chatroom_channel.js, we would reference this as data.mod_message
ActionCable.server.broadcast “chatroom_channel”,
mod_message: message.body
end
end
private
def message_params
params.require(:message).permit(:body)
end
end
Top comments (0)