Skip to main content
Realtime Multi-user Spreadsheets:

Multi-User Spreadsheets with ActionCable: Part 1

Episode 137 · September 12, 2016

Build a reactive multi-user spreadsheet web app with ActionCable and RethinkDB



Earn a free month

What's up guys? This episode I want to do something a little bit different, and what we're going to do is just follow a tutorial together. So fusion, the company that creates Passenger, they posted a tutorial basically showing you how you could build something like Google SpreadSheets, where you have a table, and users can be clicking on cells, and everyone else will see where they're at, and that outline that border will move around, so it will look a little bit like this, and you can see when people type and it keeps all that stuff in sync, which is pretty interesting, so in theory it can create a very minimal version of Google SpreadSheets using this tutorial as a foundation. That sounded pretty cool to me, and uses a technology called "RethinkDB" which is an open source database for the real time web, but the cool piece about this is that it's a NoSQL database, so it's kind of like MongoDB where you don't have columns and it's kind of focused around json and stuff like that, but the interesting thing is that you can run queries, and you can say: Let's do our typical query like: Let's look for all the games, the tables, order by score, and limit the number of results to three, but you can call changes on it, which will give you a real-time stream of changes in that database table, so this example here is basically generating records in RethinkDB and they're just watching that stream happen. So any time that one comes up that's higher that the other player's scores, it will go put that inside the top player's scores there which is pretty interesting. So the idea of this is kind of that you can have your database understand that real time is more important these days, and so you can use this database in order to power your real time updates, whereas right now, chances are what you will have to do is insert the record into PostgreSQL, then you broadcast it through a relay job with your redis broadcast happening in there in order to send that out to ActionCable and so on. There's like a few steps and you kind of have to send that out manually, whereas RethinkDB would basically just say: Insert the record into the database, and just because you did that, the stuff monitoring that streaming list of results will automatically go and make those changes on the front end. So it can automatically go and push that stuff over the websocket without you having to tell it to go do that. That's pretty interesting, and I wanted to check it out, but since I haven't used any of these stuff, I figured why not get it on a video and we can kind of walk through it all together, and take a look and see what we think. The first thing they mentioned in this article is basically by using that real time feed of changes. We can kind of eliminate some of the reason for using redis in our rails application, like we'll still need redis for background workers if you're doing anything that needs that, like sending emails or whatever. But this is going to simplify a lot of what you're doing because you don't need to create those background relay jobs because this can kind of be a little bit more automatic in theory. We'll see how to actually implement that, and see what we think as we go along. So like I said, this is what we're going to be building, it will basically use this JavaScript library to create a table, and then we're going to be going and adding our CSS to draw those borders and whatever, and doing a bunch of other stuff there. I didn't notice this until just now that they linked out to gorails inside this tutorial as well, so that's pretty cool, that makes me proud. Let's just dive in.

The first thing is first, is that we need to create a rails new spreadsheets. We'll let this install, and then we cab basically set up the simple ActionCable stuff in order to make sure that that's connected. I believe that this mount ActionCable server is automatic now, and it should be automatic as long as you have channels. This was more of a recent change in rails 5 I believe but I could be wrong. It doesn't hurt to also mount this, it would just override if you do mount this, and it is automatically set up then this would just kind of override that, which is totally fine. We'll just go ahead and just follow their tutorial like step by step, and do the exact same thing and yea.


Rails.application.routes.draw do 
    mount ActionCable.server => '/cable'

railg g channel active_users

I'm guessing this will keep track of all the active users who are currently on your site, because it goes and assigns a color for each user, and then, that list of users that you have will show up with different colors so that nobody is overlapping on the same color, or anything like that. Ok, so we have that generated, that's just the empty templates for all of that stuff. We're going to do the same thing and create a spreadsheets controller with an index action, so we'll have that, and finally we want to set that route routes so that we have access to that by default. I'm going to get rid of that GET route because we're not going to ever access it through that. We just need the controller action in order to set it as the homepage, and so yeah. We want to run rails server, and because we have that channel created, we should have that action cable connection automatically connecting when we start this up, and there we go. Let's take a look at our network tab and set this to the websocket one and let's refresh the page and make sure that we're getting an ActionCable connection, and we see pings coming through, so the subscription has been confirmed, and it is active, which is good which means that that is working. While I'm thinking about it, we can see about disabling that route, removing that route and then seeing if this still works. That was the thing that I mention where the ActionCable route, I believe is kind of automatic now, and so it will just boot up as a future, as required if you use it or need it, and it appears that that is still working if we delete that route, which is cool. This is like upgraded to the websocket connection and everything, it seems to be working. You don't actually need to specify ActionCable anymore explicitly, because that will automatically come in there, and I believe the application.rb file-- One of them, I remember seeing some ActionCable stuff in here that-- The environments file, you can set the mounts path to nil if you want to disable it, but by default, I believe that it goes to /cable as the mount point, and that is the same thing as specifying the mount ActionCable server in the url, so you don't have to do that anymore because it's the default. You can disable, and then you can set your url if you want to change that, but you can also do your allowed request for production, so that you can verify which domains you're going to allow to connect over those websockets. Everything seems to be up and running as we want, and is up to date with the tutorial, so that has all gone successfully as it said it was just basic rails set up, but you can run into problems with that, so RethinkDB, we basically need to install this now, and what we're going to do is we're going to-- you can check out the installation guide for it, you can go to their website or whatever, but I a going to make sure that we just install this with homebrew, so if you have homebrew installed, if you don't, install homebrew, it's fantastic. brew install rethinkdb. You can run this command, I already have it installed, and basically it will show you some information on it once it's installed, so you can basically start the service by running brew services start rethinkdb, which will set it up to automatically run on your machine, and restart every time that you re log in. So it will be there, just like PostgreSQL, like permanently running, if you want to do that. If you don't, you can just say: rethinkdb That will run the server in your terminal, and you can cancel it at any time and shut it down or whatever, and it won't be running permanently. You can also just kind of uninstall it, if you run it permanently, you can turn off the services. You can say services stop rethinkdb and you can manage it with that. I'm just going to have it setup to automatically be running, because I may come back to this later or whatever, and we'll see, once that's running, that the rethinkdb console is open and you can take a look at this, which is pretty nifty because that's not something you get with PostgreSQL or MySQL, you do not get a web interface for it, so you can't actually take a look at what's going on in your database easily, but this does ship with that out of the box, and you can see how many servers are connected, how many tables there are and indexes and resources, all that stuff. Super duper handy, because this can be very useful to monitor once you have your applications running in production and your kind of trying to figure out why are your performance problems, how much storage is it taking up, and you don't have to be a Linux person to poke around at all that stuff like you would with PostgreSQL or MySQL, you can kind of just use it from a really nifty web interface and not have to do all those things. That's pretty cool, looks like a very modern database, seems really good. I am excited for it, and it appears like this NoBrainer gem is what we'll be using to communicate with it, it's an ORM for the RethinkDB, so it's kind of like ActiveRecord, but basically, it's not using ActiveRecord, we're going to be using NoBrainer and this is sort of just a replacement for that, because the way that ActiveRecord works, it works really well for structured data, so you have your PostgreSQL columns and your tables and it can look those up, but with flexible databases, the NoSQL ones, you have a lot of interesting things that you can do with them, but they're not all structured, so the ORM styles need to change quite a bit in order to accommodate for that. NoBrainer seems pretty cool, looks cool. I did notice before, if you use devise, or carrierwave or that sort of thing, like they've already got some notes in here saying that they either work out of the box, or you can make them work nicely with things like devise which is like a huge plus, because a lot of people would be using those gems. This seems pretty cool, let's take a look and see how it goes. I don't know what NoBrainer streams is. This is likely those change feeds, those feeds are going to be a temporary gem that implements streams and NoBrainer. Chances are they are hoping to get this merged into the official gem then, and I believe that this is for "Add Module for Rails 5.0 ActionCable streams". This would kind of be adding overrides or whatever for those streams that you could use RethinkDB instead of redis. We actually won't need redis for this application because it will instead, these stream from, you normally configure your cable.yml file to point to redis. It looks like you would just be pointing this to your database, and it would be using your database configuration instead, which is like pretty cool. Then you have one dependency or RethinkDB instead of two where you'd have PostgreSQL and redis server, whatever you were using for that. That's pretty cool. I believe PostgreSQL also has a pub sub thing, so you could kind of do the same things in pub sub with PostgreSQL, but I haven't done it, so I'd be curious to see how that goes as well.

Anyways, let's grab these two gems, put them in our Gemfile, paste that in, and we'll cancel our rails server, run bundle install and looks like because we don't need to create migrations, we don't really need to go and do the generators for models, you can just edit the files and all of the fields will be defined inside the user class, or your model class. You can simply just go edit app/models/user.rb, paste that in, and you're done. You don't have to run any migrations or anything like that. It will automatically instantiate the table, and the field, and all of that stuff for you, because that's what NoSQL databases do, and you can simply delete this field or rename it or whatever you want, and it will pick it up accordingly. It doesn't like automatically transfer the data if you rename this field, but it would create a new column in or a new key and value in RethinkDB for you, which is nifty.

Transcript written by Miguel


Subscribe to the newsletter

Join 31,152+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.