Event Notifications in Rails 5 with ActionCable!

activity-02-2016

Here at Onehub, we have an activity log for actions users perform. That way you can see when Workspaces, files and folders are created, moved, deleted, or downloaded. You can also see when comments and messages are made as well as many other events.

Currently, we maintain a separate project that handles streaming these events to your activity log. This project is a piece of legacy software (read: hard to maintain and confusing to trace). With the release of Rails 5.0.0beta2, we took a dive into one of Rails 5 most exciting new features, ActionCable.

ActionCable greatly simplifies the complexity of getting websockets up and running to stream real-time event notifications to the activity log. We can now have our activity log logic live right alongside our application logic.

Based on a video tutorial by DHH for a chat app, I put together a short tutorial on how to get an event system up and running in the new Rails 5 app. This tutorial assumes you have created a new Rails 5.0.0beta2 (installing Rails 5 here) project and have a redis server up and running (installing redis).

Lets make some events!

First we’re going to make our event model. An event will have a message, and we’ll use it’s created_at field for the timestamp

This will create our migration and our model as well as some test files. We’re going to skip testing in this tutorial, but you shouldn’t in your application!

Now we get to run a command new to Rails!

It feels weird not typing rake db:migrate but you’ll get over that.

Now let’s create a controller and view so we can look at our events.

In our events_controller.rb we’ll fetch our events and display them in reverse chronological order.

Next we’ll create a partial in views/events/_event.html.erb that displays the timestamp of the event and the message.

And we’ll create the index view.

And while we’re at it we’ll setup our routes to point to this activity log.

Notice a little hint of where we’re headed on that last line?

Now let’s start up our rails server. By default it’s Puma in Rails 5! Talk about speed!

Go to your http://localhost:3000. OK nothing interesting there yet. Let’s pop into the rails console and create ourselves a test event.

Refresh the browser and…. ok that’s better. But having to refresh is annoying, what if we could just handle this via… oh I don’t know maybe… ActionCable?

Enter our superhero ActionCable!

Before unpacking (our Action Cables), we need to turn on ActionCable in cable.coffee by uncommenting the following lines:

We’ll also need to uncomment that line in routes.rb so we can connect to the ActionCable server. So make sure you have the following line in routes.

Now we’re going to generate a channel, which defines our websocket and how our series of tubes connects the client to the application.

The first file here handles our server-side logic while the second is our client-side javascript. Let’s setup our activity_channel.rb. Since we want to stream from our activity channel it’s pretty clear what we can do here.

Because we probably don’t want everyones’ activity through the whole app you’d want to do something like “activity_channel-#{account.id}” to quiet down the noise. But we’re going to skip that to simplify things.

Now on the client-side when we’re going to receive a message containing an event and then we’ll prepend that event to the events in the activity log. So let’s setup some basic javascript to do that in our activity.coffee.

Sweet… but none of this actually works yet.

One thing DHH covers in his tutorial is the fact that in a production application you’ll want to use jobs to handle the broadcasting of messages. This is true for Onehub because we have A LOT of events and we don’t want to block up our app with them. So let’s create a background job that gets queued up when we create an event that’s tasked with broadcasting our message.

Our job will use ActionCable.server.broadcast with our ‘activity_channel’ and send our a message with our rendered partial we created earlier. (Thanks to DHH for that suggestion, you can see his comments on caching the template in the video linked earlier.)

Lastly, we’ll add in an after_create_commit hook to perform this job after an event has been successfully saved to our database.

Great now that that’s all piped together, let’s hook this up to something more interesting, like a comment. We’ll take the easy approach here and just scaffold it.

Great! Now let’s use that fancy new rails migrate command.

And let’s go hook an event to a comment in the comment model.

Start up your server, go to the activity log in few browsers (just so you can see the magic of all the connections). And open a tab to http://localhost:3000/comments/new and create a new comment and watch all the activity logs get updated via the magic of ActionCable!

Screen Shot 2016-02-17 at 2.18.25 PM

Great! That’s just the tip of the ice berg with Rails 5’s new ActionCable. Here we’ve shown a one-way push from the server to the connected clients to dynamically update content in an activity log. It’s also possible to push events from the client to the server via a channel and trigger broadcasts (much like a chat app). But we’ll save that for extra-credit and/or another tutorial.

Hope you enjoyed this tutorial. We’ve uploaded this code to github so you can download it and play around.