Recently I was watching a David Heinemeier Hansson (DHH) (founder of rails, basecamp) tutorial on implementing a chat app using the new and improved rails 5 beta release which makes working with web sockets and real-time apps a breeze - seriously, itās fantastic. I was really impressed with how easy it was and so I wanted to take that DHH tutorial (here) and provide a written tutoral with the steps DHH outlined for implementing it, but I also wanted to include some additional steps I needed to take to get it to work in my environment. I hope this is helpful for anyone out there looking to implement real-time functionalties using the rails 5 framework. All the credit goes to DHH. His video provides much more detail and explanation, so I would not consider this an alternative, simply a place where itās all laid out on paper. I mostly needed a reference for myself and wanted to solidify the concepts in my head.
Note: I am running vagrant via virtual box on an ubuntu 14.04 box. This tutorial assumes that you are running ruby > 2.2.2.
First letās start by cloning the rails 5 github source code into a directory. This will initialize an empty rails5 directory, where we will download the rails 5 source code.
If you are already using a rails environment, you are probably running a rails version < 5, so this will allow you to spawn a new rails project using the freshly minted rails 5 source. Once this downloads, you should create a new project directory. First navigate back a level in your directory, running the following:
This code block will initialize the application using the rails 5 source and then we will verify we are runnning rails 5. If it works, you should see āRails 5.0.0.beta1ā.
Before we can get started building the application, there were a couple of gems I needed to add to get the application working (redis-rails via bundle install, for whatever reason, was giving me the 0.0.0.0 version, so before I ran the bundle install below, I did a manual install by running: gem install redis-rails, which gave me 4.0.0):
Run bundle install to download the redis (more information about redis here) gems:
Letās start building the application. First we will use a command line generator to develop a controller called rooms with a method called show:
This will generate a scaffold with several files for you. Modify the config/routes.rb to point the root to the show method you just created. You will also want to enable the Action Cable route, which will be needed later on.
Once you do this, you should be able to start your rails server and visit the page:
I bind to 0.0.0.0 since my environment sits in a virtual box with a private network IP assigned to the box. By binding my rails server to 0.0.0.0, I am able to see the app when I visit it in my browser. If you are not runnning this environment, you can probably leave off the -b flag.
Now, modify the rooms controller to the following:
Once we have the controller setup to grab all the messages in our model, we want to setup the model by running this in the command prompt (visiting the rails site at this point would cause an error, since we have not yet initialized the data model we are calling in the controller):
This creates a model called Message with a text field called content. This will be used to store our messages in the chat room. Then we run a migration to get the table created. Letās initialize a value in our model, manually for the time being:
In order to see this message we need to pass the model attribute to the view. We will need to update the following files:
This view will display all messages in the Message model and the input text box will be linked to some client-side javascript attribute (ādata-behaviorā) that will pass our text messages into the messages channel. More on that soon.
This will create a channel called room which will contain a method called speak. Modify the following file to match this:
This file indicates that you would like to stream messages from the room_channel and that any data passed to speak (e.g. your message content) will be saved to the database.
Letās create a āmessagesā folder in app/views and within this folder create a partial called ā_message.html.erb. Modify a message partial which we will use to display the messages, later on:
This will display the content of your messages. Running the rails server, should show the hello world message you created earlier. In order to finish this tutorial up, we need to tell the room controller how to interact with the client side input. This can be done by modifying the room js coffee file:
This js file does a couple of things, but primarily accepts input from the user in our view, binds the data-behavior attribute in the view to an enter/return keypress event and then calls the App.room.speak(āyour message hereā) method, which we define just before it. Perform is then called and message is passed to it in a jobs file we have yet to create. This jobs file will do the magic rendering as the input is recorded by our users.
Before we continue, we will need to enable Action Cable in the following file:
This chunk of code initializes a consumer. More information on the terminology is located here. I suggest reading through this. I was having trouble with this because I was making invalid requests to the site since I was operating within virtualbox. In order to allow this I had to add the following to the app/controllers/application_controller.rb:
I setup a windows host file that creates an alias for the network IP assigned by virtual box. That way I can visit ruby.app instead of localhost or 127.0.0.1 or 193.122.12.1 in my browser. Because rails 5 is setup to point to localhost in development for Action Cable, I had to explicity allow request origins from a different name, hence the code above.
Alright, letās get back on track. Next we need to update the model to ensure that the message can be rendered through a template. Remember - we already have a means to save the record; now we just need to transmit or broadcast the message to a viewer without refreshing the page. This will require that we display a transmitted message continuously as new messages are submitted through the channel. This isnāt really something you would execute in a model, so we will create a MessageBroadcast Job and call it in the model after the message is committed, as follows:
We of course, need to generate the message broadcast job we refer to in this model using a generator:
This generates a file in the jobs folder, which should be modified as follows:
This perform method is called in the room channel we created earlier and has one parameter, a message. This message is then rendered through render_message using a partial (which we created earlier). Itās rails magic!
The flow, as I understand it, is as follows: The user enters input into the text field (client side) via App.room.speak which is sent through the Channel. This message then gets commited to the database through the model via the create methods within the channel, and then after the message is created, the broadcast job above is called which renders a ārevolving doorā partial template containing our message(s)! Now anyone who subscribes to the channel (e.g. at this point, itās anyone who visits this root page, should be able to see the chat conversation). At this point if you run the rails server and you also open another window (incognito if you have chrome), you can start typing back and forth (e.g. across multiple connections) in real-time! Thatās it.
If you have any thoughts, please leave them below. Always looking to improve the delivery of my content and Iād like to hear if I was helpful. Again all the credit goes to DHH. Rails 5 is stellar.