Rails real-time chat application using Action Cable.

Dean Raguso
7 min readJun 20, 2021
Ruby on Rails logo. https://upload.wikimedia.org/wikipedia/commons/6/62/Ruby_On_Rails_Logo.svg

A user can go to this application, join or create a room, and have their messages and anyone else update without a page refresh!

If you want an out-of-the-box working solution that does this, feel free to clone this repo. Note, I am using Rails 6.1.3 and Ruby 2.7.2.

I will be leaving user authentication/authorization for another day. This is intended to be a bare-bones Action Cable tutorial that works out of the box and can be modified to your needs.

Step 1: Create the back-end functionality.

We need to create the required Models to make this project work and set the project up at the application level. Creating a model for the chat-room, and a model for each message, which belongs to the chat room.

You probably already know how to do this part, if so, skip to part 2.

A) Create the Rails application.

rails new chat-room

From here, create the database (if needed) and ensure the application is visible from localHost.

B) Create the required models.

For this project, we need 2 models (ignoring users)

  1. Room Model: The page that will contain the conversations.
  2. Message Model: The messages a user can create.

I generated both of these things using a scaffold, to speed up the development process.

rails g scaffold room name:string topic:stringrails g scaffold message content:text room:references

After this, you’ll want to check your migration files to ensure they look as they should, then go ahead and migrate.

Migration file for the Rooms Table.
Migration file for the Messages table.
rails db:migrate

Since you created Room first, ensure you add the has_many association with messages.

Room Model file with has_many association to messages.

C) Modify the views so you can view and create messages from each Rooms show page.

The reason I used scaffolds, is because most of the code is already done for us! To avoid making this article way too long, I’ll keep this part brief, know there will be a follow-up video that goes into more detail.

You modify the show action within the Rooms controller to have a “@message” and “@messages” instance variable, for use in the view.

The show action within the Rooms controller.

You take the form partial from the Messages’ view and insert it into Room’s show view.

You will need to modify it, by replacing “message” with “@message” everywhere it occurs in the form.

Location of the Room’s show view within the file structure.
The message from within the Room’s show view.

Finally, create an ERB loop through all messages with a wrapping div, to display all messages available from the controller.

A loop using each method within Room’s show view.

D) Hide the room_id field and add its value automatically.

This was visible in the previous screenshot, but here it is again.

This isn’t the best way of doing it because it assumes that a “params[:id]” exists, but the key point is giving the room Id to the hidden field, do it as you like.

At this point, your Room’s show page should look similar to this and be functional.

Show page for a sample “Room A” with Topic test and one dummy “Hello” message.

Note: I changed the text area to a text field, which means pressing enter will automatically hit the submit button.

Step 2: Create the back-end part of the configuration for Action Cable.

In this step, we’ll get Action Cable working. The DOM won't be updating on changes, but we’ll know it’s working with a simple test.

A) Generate the Room Channel.

rails g channel room

This creates 2 main files.

  1. “room_channel.rb” in the channels directory.
  2. “room_channel.js” in the JavaScript channels directory.

B) Configure the room_channel.js file.

We have to accomplish a few things in this file.

We need to create the “subscription” for the room that matches the page we’re on. Figure out what room we’re on (you can do this many ways), I did it using the URL and reading it from the string. Finally, we must pass an object containing the channel name and room_id to the “subscriptions.create” method that got automatically generated.

Once you have implemented these steps, the file should look something like this.

room_channel.js after configuration.

Note also, that I added a console log within the received data method. This will print the data object to the console whenever incoming data is received.

console.log(`Subscribed to ${room_id}`)

C) Configure the room_channel.rb file.

You will have access to “room_id” here because you passed it through the js file in the first parameter of the “subscription.create” method.

room_channel.rb after configuartion.

I then use the “stream_for” function, which automatically does what the commented out code does above the reject method. It opens up a stream to the room saved in the local “room” variable”.

D) Broadcast messages upon their creation.

To accomplish this, you can do it directly as I have. But, you really should do this using a Job, which would turn the action into an asynchronous event, that won't collide and cause potential errors.

But, for demonstration purposes, all you NEED to do is this.

Modified Messages controller for the create action.

Modify the create action within the message controller to broadcast the message to its associated room, if the message saves to the database correctly.

E) IMPORTANT STEP

There may be a clever workaround that I don’t know about, but Turbolinks tends to prevent the javascript from reloading on pages. So unless you know that work-around, simply remove the Turbolinks gem, and any instance of it being called within the application.

You will not notice any difference within the application, only that the JavaScript files will re-load on every page you visit, giving it the opportunity to disconnect and reconnect from rooms with the script in the channel file.

At this stage, Action Cable is working. If you go to a particular room, you’ll see in your server terminal that RoomChannel is streaming.

Step 3: Final Step — Have the DOM update when message data is received.

For this really simple implementation, all I have to do is insert the adjacent HTML for a new list item, adding the content within.

A) Update room_channel.js to modify the DOM when messages are received.

Within the room_channel.js file.

You will likely need to do modification here to suit your styling options.

B) Prevent the message create action defaults.

You also need to prevent the default behavior of the submit button, which we already have when we changed the Messages’ create action.

Once changed, the form will be usable, but it will appear as though nothing is happening. If you look at the server console, you’ll see the message went through, the data was received by the consumer, and the page updates.

But it will look odd, because the submit button will remain grey, and you won't be able to submit again.

B) Add and link a JavaScript file to make the submit button clickable multiple times without a page refresh.

Within javascript/packs, create a file called “message_submit.js”.

message_submit.js file.

It basically finds the submit button and puts an event listener on it. Now, whenever the click event is detected, the site waits 100ms and then clears the message content, and un-disables the submit button.

I found making this asynchronous with the setTimeout function to be a must, I ran into many issues otherwise.

Unless I’ve missed something, it should now be working!

I will be putting a video on YouTube soon, showing me creating this project from scratch, though the video is a little long right now and may need some editing.

You can find this project on my GitHub linked here.

This is my first article so take it easy on me, kind regards :D

Cheers from Dean.

--

--