00:00:16.259
Um, hello! My name is Mike Moore, and I'm going to talk about real-time Rails with Sync.
00:00:21.730
So, hi! Hello, how's RailsConf?
00:00:28.689
I am Mike Moore. You might know me as Blue Maj or Blow Mitch, depending on how you want to pronounce it. It doesn't matter.
00:00:35.580
I am very happy to be here. I'm leaving in about an hour to fly home, so I really appreciate being here for this.
00:00:41.350
And I'm 100% totally prepared for this! I was not up all night; I did not make these slides 20 minutes ago.
00:00:46.870
Yeah, so who likes live coding? Woot! Three people? All right, okay!
00:00:54.879
We're going to talk about Sync. Sync is a fantastic little Rails engine written by Chris McCord. Is Chris here? Where's Chris?
00:01:06.160
If you don't like it, you can find Chris right there! You can find out more on GitHub at Chris McCord/sync.
00:01:11.229
So, let's do this! I have an app that I'll just load up here. It's a very simple app.
00:01:21.780
I was going to have a slightly more interesting app to demonstrate this, but I ran into an issue.
00:01:29.740
Maybe we'll talk about that at the end if we have time, but this is a very simple blog-ish type of application.
00:01:42.340
We have posts, comments, users, and some tags. Oh, and also, this is the Git repo for Sync, so please check it out.
00:01:54.090
Okay, so I want to demonstrate this very simple Rails application. We'll look at a little bit of code, and we're going to add Sync to this application.
00:02:05.890
We will turn it from a very classic kind of CRUD Rails application into a real-time app.
00:02:11.140
A little note before we start: the code quality of this app is intentionally subpar. There are lots of places where you might apply some design.
00:02:24.640
However, I have not done that for this demonstration. The reason for this is to make it easier to refactor it to use Sync.
00:02:31.239
We have a series of posts, and when you click on a post, you go to the post-show action. On that post show action, we have a series of comments.
00:02:47.040
This is no different than going to /comments, other than it just looks a little different. We won't be looking at comments on the actual resource.
00:03:01.150
Instead, we will be looking at comments associated with the post resource. And that's it!
00:03:08.019
You can say hello to RailsConf! Also, a disclaimer: I cannot type in public, so this will be interesting to watch.
00:03:22.290
I can go ahead and add a comment or delete a comment because it's owned by me. I can also go into posts that I own and edit those posts.
00:03:39.099
So, if you like that, it's pretty simple. Okay, let's expand this a little bit. Here is our application, the same thing as we saw before.
00:03:56.349
Our home controller is the homepage that shows the jumbotron. This is all very Bootstrap-oriented. I'm sorry. Here's our posts controller.
00:04:08.430
It's almost straight out of the box Rails resource. Our comments controller is nested underneath our posts controller.
00:04:24.729
We also have a couple of additional calls defined in helpers. This may not be how you would do this in a real application, but for demonstration purposes, it fits the need.
00:04:39.020
I can use these methods in both the controller and the views. Now, let's take a look at the routes to ensure we're not cheating.
00:04:53.390
We have nested comments under posts; we also have tags. Let's take a quick look at tags.
00:05:01.910
Tags are just a string attached to these various posts. If you click on the Rails tag, you see three of the four posts are tagged with Rails.
00:05:09.680
Let's say that we have this application, and we want to make it even more awesome than it is today.
00:05:21.650
One of the things we really want is to approximate what some apps using heavy JavaScript MVC frameworks accomplish.
00:05:27.740
We want to achieve their responsiveness and the ability to update the UI whenever something changes.
00:05:39.500
Instead of rewriting our entire front end and creating an API to support that JavaScript presentation layer, I believe we can use Rails as intended.
00:05:45.440
We can still gain a significant portion of this kind of functionality. So, let's go ahead and jump in!
00:06:15.950
The first thing we want to do is open the Gemfile and add a few gems. The first one is Faye.
00:06:22.490
We need to add Faye for development. We'll use Faye to communicate over WebSockets back to the server.
00:06:35.510
We also need Thin, but not very much, so we won't require it by default. Lastly, we'll add Sync.
00:06:49.620
So let's run 'bundle install'. There we go! Now we've added Sync to the application.
00:07:03.780
Next, we need to go a couple of steps further. In our application file, we need to add the JavaScript for Sync.
00:07:10.380
This will be loaded as part of our normal application JavaScript. Everything will be pulled in.
00:07:15.570
We also need to go into our layout, our main application layout, and add another JavaScript tag.
00:07:21.690
We'll be using a little helper from Sync to use the adapter Javascript URL, and we'll discuss this more by the end.
00:07:29.640
Now, before we do anything else, we need to start Faye to run our WebSocket connections. We can do that easily by running 'rack up'.
00:07:51.780
Oh, I'm sorry; one more thing. Let's take a look at our generators. There's now an installed generator added by Sync.
00:08:06.750
Let's go ahead and run that generator, which will create a rack-up file and a configuration file.
00:08:13.310
Now that we have that, we can run Faye in the background, and here we can just run our application.
00:08:28.409
Refreshing the page, nothing has changed, but it all continues to work.
00:08:35.310
Faye is running, but we aren't actually communicating with it yet. That’s the important first step.
00:08:43.140
I don't particularly like having to open up two consoles, so I'll create a new file called a Procfile. Inside, I can add an entry for Web.
00:09:07.510
And now, I can easily manage all the processes needed.
00:09:14.140
The other thing we need to do is come back over here and add Foreman to help manage running both Faye and Rails.
00:09:22.580
Foreman is a gem by Heroku, allowing coordination of multiple services. Now, instead of going to multiple terminals, I can just run 'Foreman start'.
00:09:40.820
That makes things much handier.
00:09:47.810
Now, let's look at one of these pages. I want to be able to visit this page and add a new comment.
00:09:59.300
When I add a comment, I want everyone to see it as soon as it appears. If I say, 'See me!', my browser refreshes.
00:10:09.840
However, the other browsers won't necessarily refresh to show the new comment.
00:10:21.650
So, if I delete a comment, it still shows up in those other browsers. I want it to disappear immediately.
00:10:40.210
To do that, we’re going to register our ActiveRecord models to be synced in browsers.
00:10:47.970
Sync will take care of all the communication from our Rails application to the browsers.
00:11:03.690
To implement this, we'll open up our Comment model and add a little declaration called 'sync all'.
00:11:15.660
This will insert the Sync DSL into the model. Now, whenever the model changes, it will try to notify the browsers of the change.
00:11:26.180
We also need to open our controller to enable Sync here as well.
00:11:38.580
This allows the controller to listen for messages from the models regarding updates and respond appropriately.
00:11:53.710
Let’s take a look at the post-show action. As I mentioned, this is a mess of HTML, and it’s not how I would usually do it.
00:12:09.690
We have two main areas on the page: the first contains the content of the blog post, including the title, user, tags, and the body markdown.
00:12:21.660
After that, we have the comments section, which iterates through the comments.
00:12:27.780
It renders a partial for each comment, followed by an area to add a new comment if the user is logged in.
00:12:38.700
To make updates with Sync, we'll change this from 'render' to 'sync partial' and specify that our resource is the comment.
00:12:52.310
This requires a little more verbosity than before, but Sync requires that structure.
00:12:59.040
Next, we'll create a new directory under app/views called 'sync'. Under 'sync', we'll add another folder called 'comments'.
00:13:12.570
We'll place a new file here called 'comments.html.erb', which will serve as our partial.
00:13:19.800
Instead of looking at our normal template, when we call 'sync partial', it will look for the one in the sync directory.
00:13:30.540
For the most part, we can copy our existing partial, but we may have to trim some parts out.
00:13:39.930
One caveat about using Sync is that we can’t pull information about the current context because this will get pushed out to everyone. So, we must treat it similarly to how we cache templates.
00:14:05.460
We can have the username and the comment body, but we can't have editing options for each user.
00:14:15.650
So, we’ll adjust the implementation accordingly.
00:14:25.410
Instead of using 'comments', we'll simply reference 'comment' for the first step.
00:14:33.300
Next, whenever a new comment comes in, we want it to appear immediately.
00:14:48.510
Instead of calling 'sync', we're going to call 'sync new' for this, which will listen for new comments.
00:15:10.290
It's important that the resource is defined as 'comment new'.
00:15:17.230
This is the approach we're using. That’s not many changes we’ve made!
00:15:28.680
We’ve added Sync to the repository and tidied up the HTML to a new location under sync. Now instead of calling 'render', we use 'sync'.
00:15:38.640
Let's see if this works. I'm refreshing this page, this page, and this page for our different browsers.
00:15:52.740
We want to see if it works. All right! Let's see if it responds. Okay, it works!
00:16:07.040
What’s great about this is that it sends updates to every connected client each time our resources change.
00:16:20.030
We didn't need to write extra JavaScript, nor did we change our presentation layer architecture.
00:16:30.160
Now, I’ve lost my ability to edit comments after using Sync, so I want to regain that.
00:16:52.060
In the syncing loop, we want to check if we can edit the comment based on the current user.
00:17:08.650
If I can edit it, I'll use the same partial I had before, but if I can't, I will use the one that is synced.
00:17:25.060
It's a slight change, but it ensures that if I have edit permissions, I won't receive notifications of changes.
00:17:38.470
So, let's refresh it and see! Now, because of that, I have my editing tools back. I can edit and delete.
00:17:51.130
Now I can go and say, 'Yes, this does work there!' and the updates will reflect across all browsers.
00:17:58.320
Okay! There is another bug I want to show you. Do you want to see it? It's pretty fun!
00:18:06.270
Here’s a blog post without comments. I'm going to go to a different post and say, ‘We have a comment.’
00:18:17.810
A bug appears! When I do that, my comment shows up on this different post.
00:18:28.980
Right now, we're looking for all comments whenever something is updated.
00:18:32.980
What we need to do is scope these comments to this page, so let's add that really quickly.
00:18:47.580
We'll open our Comment model and create a new scope.
00:18:56.180
This will be a lambda that takes a post, which will filter based on the post ID.
00:19:05.530
With that scope in place, we can go to our sync partials and apply that scope to ensure the right comments are displayed.
00:19:14.700
Let’s refresh and check! I'm logged in as Stanley, and my comment shows up here, but not on that other post.
00:19:30.700
The scoping made the change we wanted. You simply define that scope and apply it to the relevant areas.
00:19:40.990
So, that’s how easy it is to scope comments! We want to ensure it works correctly across all posts.
00:19:55.130
Sync is a pretty cool library; it holds a WebSocket connection with the server.
00:20:07.390
It puts a mechanism in Rails to communicate via that WebSocket, allowing real-time updates without changing our front-end architecture.
00:20:23.610
This way, we're able to utilize real-time functionality without making significant changes to how we organize our templates.
00:20:39.620
Let's open a Rails console and get a comment from a user to demonstrate.
00:20:55.350
I have this comment from Jason, who’s watching remotely.
00:21:05.870
What we can do is run 'sync model enable' to allow all changes within our process. Though it’s separate from the web server, it's still within Rails.
00:21:17.810
So let's pull this up and watch those changes take effect.
00:21:29.100
We can change Jason’s comment and, once saved, it will update in real-time.
00:21:43.180
Similarly, we want to sync our Post model too, enabling real-time updates for our posts.
00:21:57.680
In our Post controller, we enable Sync just like we did for Comments.
00:22:07.780
At the Post index page, we’ll render posts while allowing users to add new posts.
00:22:18.780
Again, the challenge here is that editing permissions will affect which data is shown.
00:22:31.510
To adapt, we have to remove those parts from the templates and handle them differently.
00:22:47.800
The template will call 'sync partial' for each post and manage incoming post data accordingly.
00:23:04.270
Refreshing the page should reflect our latest posts seamlessly.
00:23:21.600
However, we've noticed that some comments are not updating as quickly as we want them to between users.
00:23:38.250
To resolve this, we’ll need to ensure that comments sync with their associated posts correctly.
00:23:54.870
By employing 'sync touch post' within the comment controller, we ensure that updates are seen by all users.
00:24:08.780
Now, refreshing all browsers should show two comments each.
00:24:20.950
Let's ensure everything syncs back to Jason's comment as well.
00:24:35.270
As we edit comments and add new ones, they should persist correctly through Sync.
00:24:54.920
Finally, we want to check the post show page to ensure updates continue smoothly.
00:25:06.930
The issue we previously faced was due to not having the show page synced with the comments at the time of post updates.
00:25:20.610
Quickly, we can render a new partial for that post page.
00:25:34.350
We will call the new partial called 'post full' which includes all body content associated with the post.
00:25:45.050
Refreshing this will ensure that, as edits are made, updates will reflect across all connected users.
00:26:01.460
I’m redoing edits now and seeing it working as expected. Everything is in sync!
00:26:15.360
We're really powering through demonstrating Sync's capability to update resources without losing integrity.
00:26:31.890
Using Sync effectively allows us to manage resources in real time without needing to consume excessive additional bandwidth.
00:26:48.220
I've really enjoyed using Sync for internal applications, where monitoring updates in real time can be a game-changer.
00:27:06.200
It avoids the need for constant polling and provides a much more streamlined handling of updates.
00:27:19.180
As we continue to evolve, Rails has managed to remain a frontrunner in innovative approaches to web development.
00:27:38.650
I strongly argue that to stay relevant, Rails must adapt to provide real-time functionalities.
00:27:53.490
While there are limitations, integrating real-time capabilities with existing Rails structures can offer tremendous advantages.
00:28:06.310
With that in mind, I’ll open the floor to any questions.
00:28:17.310
Thank you for your attention!