RailsConf 2014

Real-time Rails with Sync

Real-time Rails with Sync

by Mike Moore

Summary of 'Real-time Rails with Sync'

In his session at RailsConf 2014, Mike Moore discusses the implementation of real-time features in Rails applications using the Sync library. The goal of this session is to demonstrate how developers can enhance the interactivity of their applications without completely transitioning to JavaScript frameworks.

Key Points:

  • Introduction to Sync:

    • Sync is a lightweight Rails engine designed to allow Rails partials to update in real-time on the client-side, mimicking the behavior of JavaScript MVC frameworks.
    • Developed by Chris McCord, it allows real-time client-server communication without the necessity to rewrite the front-end.
  • Sample Application Setup:

    • Mike presents a simple CRUD application highlighting posts and comments. He emphasizes that design is not a focus during the demonstration to keep the coding process straightforward.
    • Sync is integrated by adding the necessary gems (Faye, Thin, and Sync) to the Gemfile and setting up the backend to facilitate WebSocket connections.
  • Real-time Features Implementation:

    • By adding specific Sync methods for models and controllers (e.g., sync_all, sync_new), real-time commenting functionality is introduced. When a comment is added or deleted in one browser instance, it is reflected across other connected browsers.
    • Mike demonstrates live coding where he modifies the comment model and controller to enable real-time updates, illustrating how quick and efficient Sync can be for achieving live updates.
  • Synchronizing Different Resources:

    • Mike works on scoping comments within specific posts and demonstrates how to control what updates in real-time by using scopes within the model, such as by_post.
    • He also discusses the implications of enabling sync for other resources, like posts, emphasizing the need for careful management of what gets rendered to avoid inconsistencies.
  • Limitations and Considerations:

    • The session highlights some caveats, such as the difficulty in managing user permissions in templates that are sent to all clients. Careful consideration and conditional logic must be applied to ensure appropriate editing rights.
    • Mike concludes by emphasizing the importance of server-rendered HTML and the advantages it offers, discussing the risk of developers moving away from Rails for real-time features if not addressed effectively.

Conclusions and Takeaways:

  • Sync allows significant improvements in interactivity for Rails applications without a complete architectural overhaul.
  • Real-time capabilities can be integrated efficiently into existing Rails applications, preserving the strengths of server-side rendering.
  • It's crucial for developers to rethink traditional Rails approaches to maintain competitive applications in the evolving tech landscape.
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!