 Hello, my name is Mike Moore and I'm going to talk about real-time rails with Sync. So, hi. Hello. How's RailsConf? My name is Mike Moore. You might know me as Blomage or Blomage, depending on how you want to pronounce it. It doesn't matter. I am very happy to be here. I'm leaving in about an hour to fly home, so I'm only here for this. And I'm 100% totally prepared for this. I was not up all night. I did not make these slides 20 minutes ago. Yeah. So, who likes live coding? Three people. All right. Okay, we're going to talk about Sync. Sync is a fantastic little rails engine written by Chris McCord. Is Chris here? There he is. If you don't like it, that guy right there. You can find it on the GitHub. That's at Chris McCord slash Sync. So, yeah. Let's do this. All right. So, I have an app. And I'm just going to load it up here. And it's a very simple app. I was going to have a little bit more interesting app to demonstrate this, but I ran into an issue, and maybe we'll talk about that at the end if we have time. But this is a very simple blog-ish type of application where we've got posts, we've got comments, we've got users, and some tags. Oh, and also, this is the get repo for Sync. So, please check it out. Okay. So, I want to just demonstrate this very simple rails application. So, I'm going to look at a little bit of code, and we're going to add Sync to this application. And we're going to turn it from a very classic kind of cruddy rails application into real-time. So, a little one note before we start on code quality. This app is intentionally unfactored. So, there is lots of places where you might apply some design in an application that I have not for this. But the reason for that is so that I can, we can refactor it to Sync a little bit easier without having to unfactor it along the way. So, we'll go ahead and get started. We have a series of posts. And then, when you click on a post, you go to the post show action. On that post show action, we've got a series of comments. That is no different than going to slash comments other than it just looks a little bit different. So, we're not going to be looking at comments on the actual resource. We're going to be looking at it on the post resource. And that's it. So, you know, you say hello RailsConf. Whoa. Oh. Also, disclaimer, I cannot type in public. So, this is going to be very interesting. So, I can go ahead and I can add a comment. I can delete a comment because it's owned by me. I can go into posts that I own and I can edit those posts. Stuff like that, right? Pretty simple. Okay. So, let's bump this out a little bit. So, here is our application. Same thing as we saw before, our home controller is the home page. That showed the Jumbotron there. This is all very bootstrappy. I'm sorry. Here's our post controller. Almost straight out of the box Rails resource here. Our comments controller is nested underneath our posts controller. We've got a little bit of additional calls here for access. Those are defined in helpers. This may not be how you would do this in a real application, but for demonstration purposes it fits the need. And I can use these methods in the controller and also in the views. Okay. So, let's take a look at the route. So, you see we're not cheating. So, we've got nested comments under posts. We've also got tags. Let's take a look at tags real quick. Tags are just a string attached to these various posts. And so, if you click on the Rails tag, you see there are three of the four posts that are tagged with Rails. And then there is a list. So, let's say that we have this application and we want to make it more awesome than it is today. One of the things we really want is we want to approximate what some of the apps that are using heavy JavaScript MVC frameworks are accomplishing with their responsiveness and just kind of like updating the UI because something changed. And so, instead of basically rewriting our entire front end, our entire presentation layer, and then also creating an API to support that JavaScript presentation layer, it's my assertion here that we can use Rails the way Rails is intended to be used, but still gain a good large portion of this type of functionality. So, let's go ahead and jump in. All right, so the first thing we want to do is open the gem file and we want to add a few gems. The first one is fey. And the reason we need to add fey is just for development. So, we're going to use fey for our browser to talk over a WebSocket back to the server. And then fey also needs thin, but we don't like thin very much, so we're going to not require it by default. And then the last one is sync. Okay. So, we'll bundle install that. I told you I couldn't type. There we go. So now we've added sync to the application. We need to go a couple of steps further. The first is in our application file, we need to add the JavaScript for sync. And so this will be loaded as part of our normal application JavaScript. It will get pulled in by the asset pipeline. And then also we need to go into our layout, our main application layout, and there's another JavaScript tag that we need to add. And that is going to be... We're going to use a little helper from sync. Adapter JavaScript URL. And we'll talk about what all these mean, hopefully by the end of this. Okay. Now we're going to go. So, what we need to do before we do anything else is we need to start up fey to run our WebSocket connections. So, we can do that pretty easily. We can just say rackup sync. Oh, I'm sorry. One more thing. We need to... Let's take a look at our generators. Oh, gosh. There is now this install generator that was added by sync. So, let's go ahead and run that generator. That will create a rackup file and a configuration file. So, now that we have that, we can run this. Now, this is going to run fey in the background. And then here, we can just run our application, come back over here and refresh, and nothing has changed, but it all continues to work. So, fey is running, but we're not actually talking to it, but we know that it's up. So, that's the important first step. I don't particularly like having to open up two consoles. So, one of the things I will do is I'm going to create a new file called a new proc file. And then inside of it, I'm going to have a Web entry. So, you can't see this, but this... I have to scooch down to... So, you can hear me. And it's killing my back. You sure? Yeah. All right. Exact. So, rack up. Sync... Sync, are you? The other thing we need to do is come back over here and add foreman. So, let's install that. Foreman is a gem written by Heroku. And so, if you have lots of services that you are coordinating, foreman's a good way to start all of those. So, now instead of going to multiple terminals to open this up, I can just say foreman start. And it will start both. So, that's kind of handy. All right. Let's take a look at one of these... one of these pages. What I want is... I want to be able to come over here onto this page. And I want to add a new comment. And I want people to see it as soon as it shows up. Right? So, if I say see me, my browser refreshes, but all of these browsers over here won't necessarily. Let's go to this guy. So, if I, for example, delete this one, it still shows up in these other browsers. And I would really like it if it would disappear as soon as... as we ask it to. So, let's make that happen. To do that, we're going to register our active record models to be synced in browsers. And sync is going to take care of all of the communication from our Rails application all the way down to the browsers for us. So, to do that, there's a couple of things we want to change. First is, we're going to open up our comment model, and we're going to add this little declaration called sync all to it. This is going to insert the sync DSL into the model. So, now whenever the model changes, it will try to notify the browsers that something has changed. We also need to open up our controller, and we need to enable sync here as well. Okay? And this is, again, just to... So, the controller knows to look for all the messages from the models that something has updated and responded appropriately. Let's take a look at the post show action. Like I said, this is a mess of HTML. This is not necessarily how I would do it, but this is how it is. We've got kind of two main areas of the page. The first is, at the top, we've got all of the content for the blog post. So, the title, the user, those tags that are on the blog post, some editing links if they are there, and then also the markdown of the body. Okay? Then after that, we've got our comment section, which is just going to iterate through the comments. And because we're showing... And then we're rendering a partial for the comment. And then after that, there's also another area to add a new comment if you can. So, if you're logged in, you should be able to comment. And if you're not logged in, you shouldn't. And that's what that add comment helper there is guarding for us. So, we want to make... We just want to make this sync. It's pretty easy. What we're going to do, we're going to change this from render to sync partial, and then we're going to say that our resource is our comment. So, we made it just a little more verbose than what it was before, but sync needs that. The next thing we're going to do is we're going to add a new directory under app views called sync. And under sync, we're going to add another folder called comments. And under comments, we're going to add a new file. And that's going to be called comments.html.erb. That's going to be our partial. So, when we say sync partial here, instead of looking at our normal template, it's going to go look for the one in the sync directory. And for the most part, we can just take our trusty old partial that we're using right now, and copy and paste that. We can kind of trim some of this out. One of the caveats of using sync is that we can't really do stuff like this. We can't ask about the current context in which it's running, because this will get pushed out to everybody. So, the same strategy you have for caching templates, caching tools within your application, you're going to apply that same strategy to the real-time updates as well. So, stuff like this is probably just going to have to go. Right? We can have the username, we can have what the body is, but we really can't have all of those editing options. Oh, did I? Thank you. Name those. So, comments instead of comments. So, that's our first step. The next change we want to make is whenever a new comment comes, we want that to show up underneath. And so, instead of calling sync, we're going to call sync new. This is going to watch for new comments. And here, the resource is going to have to be a comment.new, is that right? We can probably go a little bit further and say posts, comment.new. Okay? So, that's not a lot of changes. What we've done is we've added sync to the repository to the application. We've registered all of the JavaScript. We've went ahead, moved some of our HTML from the original locations to a new location under sync. And instead of calling render, we're going to call sync and sync new. What's that? Post, yes, thank you. Live coding, ladies and gentlemen. Yeah, I transposed the parole. There we go. Pair programming at scale. All right, so let's see if this works. What I'm going to do is I'm going to refresh this page, I'm going to refresh this page, I'm going to refresh this page. Now, Firefox is not logged in. Safari here is logged in by Stanley, who's back there somewhere. And then Chrome is logged in by me. Okay? So, let's see if this works. Who wants to place the bet? Does this work? Anybody know? All right, let's see if it works. All right, there we go. Does this work? Okay. Yay! So, what's nice about this approach is that this is going to go out no matter how many clients you've got connected directly. Every time your assets change, your resources change in your application, they can be notified in real time. Okay? We didn't have to write a whole bunch of JavaScript. We didn't have to change how we were architecting our presentation layer. We're going to use the same infrastructure that we're using today. All right? Okay, now, because of this, I kind of lost my ability to edit. And I would like to have that back. So, what I want to do is in this loop where we're saying, you know, we're going to add this new sync partial, what I really want to do is I want to say if you can edit the comment, right? And that's current user. What I really want to say is that if I can edit it, I want to have the same partial I had before, right? But, if I can't, then I want to use the one that is going to be synced. So, it's a small change. But what it's saying is is that if I have permissions to edit it, I don't want it to be syncing. I don't want to be notified if it changes because I'm likely the browser is going to be changing it. And I really want the tools to be able to edit and delete. So, I'm going to come back here and refresh this UI. And you notice now, because of that, I have my tools back, my edit and delete. So, I can come back over here and say yes. It does work. See that updates there. Looks like we've got a little bug. And then eventually I can actually delete it as well. And it gets rid of it. Okay. There's another bug with this. Do you guys want to see what it is? It's pretty fun. Here is let's go to this one. Here's a blog post that doesn't have any comments yet. I'm going to go onto this different blog post, right? And say we've got a bug. When I do that, my comment is showing up on this different blog post, right? Because right now we're looking for all comments whenever it gets updated. We're going to add it to this page. So, what we need to do is we need to scope these comments to this page. So, let's add that really quick. I'm going to come back here to our comment model. And we're going to add a new scope. Now, this is going to be different than a normal scope. It might be a redundancy, but sync needs it. So, let's add it called buyPost. And it's going to take a lambda. And we're going to give it a post. And we're going to say where postID is postID. Okay. So, we've just added a scope for post. Then we're going to come over here into our sync partials. And we're going to add that scope here. So, let's say scope.buyPost. Let's also add it to this one. Okay. So now, if we refresh this over here, and now I'm logged in as Stanley, I say oh hi. It shows up here, which is what we expect, but on this other one it does not. Okay. And if I refresh this, we'll get rid of that comment. Okay. So, scoping is easy as declaring a new scope on your model. In the same syntax we're using for normal scopes. And then whenever we render out our partials, we have to reference that scope. What do we need? The scope in both? Because there are two different partials. It would be possible to... No, it affects both. The scope affects both. The reason why there are two partials here is that whenever a new comment comes into existence, we want that to be listed. So, we can move that around theoretically. I believe. Let's not touch that. Live coding. All right. So, sync is a pretty cool little library. It does quite a lot for us. What it's going to do is it's going to hold a connection via WebSocket to a server. And then it's going to put mechanisms to talk to that WebSocket. And then when our resources change it will render those templates and push that out to that WebSocket which the clients will then pull down. So, without re-architecting our presentation tier, we are able to take advantage of real time. And we're able to do it without a major change to how we're organizing or architecting our templates, our files. We can go just a little bit further as well. So, let's open up a Rails console. Okay. And we'll say, let's get just a comment out of here. All right. So, I got this comment right here which is Jason saying that he's watching but he's kind of on a delay because he's not actually in the room. Okay. So, here is syncmodel.enable and this will allow all changes that happen within our process that's not running on the Web server. Still a Rails process but we're not actually running on the Web server. It's a separate process, maybe a background job. But now all of our changes that we make can also be reflected in real time as well. So, let's pull this guy over and watch that happen. So, we can change this to what should Jason say. So, I don't know. Real time. Okay. So, let's pull him out. You see that his text is right here. I'm watching kind of. When I call save, it will change and it updates right here in real time. And duplicate. Why? Chris, why? Applets. Afterwards, all right. Okay. Let's continue and add this to some of the other pages as well. So, let's go to posts. Let's do the same thing. We want to sync our posts as well and our post controller. We want to enable sync here. Enable sync on our post controller and in our post model. So, let's go ahead and take a look at our post index page. When we're listing all the posts, we're going to go through and render the post and then if we have, if we can, we'll add well, have a button to add a new post. If we look at that post partial, you notice here, again, we've got some logic in here determining whether or not we can edit it and whether or not we can remove it. So, this logic is problematic when we're talking about a cache template that's going to get pushed out to everybody because not everybody's going to have the same amount of permissions. So, what we'll have to do is we'll have to just take that out and I'm going to take it out and put it into the index template. Well, actually I'm just going to take it out of this view all together. So, again, in order to change our template from a static template to a real-time template, we're going to say sync and partial is a post and then our resource is also going to be that same post and then if we get a new post, let's just list him down below. We probably don't need to scope on this one. The big problem we have is all of this edit information. So, let's go over here to posts and get rid of that guy. So, I'm just going to remove some code here. I'm going to remove all of this editing. Anything that's going to talk to current user or the current request is not going to work. So, let's get rid of that. And now let's take a look at our post pages. We're asking for posts that doesn't exist outside of that block, so we're going to say post.new. So, again, user not logged in, user logged in and a different user logged in. So, if I come over here, I want to add a new post. Sync works. Let's add that. So, Stanley added this post. It shows up immediately on Mike's browser and also shows up in the not logged in browser. Let's come over here. We'll say congrats. Now, this browser over on the right, the comment is not updating, right? Let's go back over here. So, here, we've got one comment. Stanley says, I know. And that one comment here is still not updated. So, we've got changes happening on a sub-resource, but it's not changing our template. Thankfully, there's a really nice DSL to make that happen. So, we'll go to our comment control or comment object and we'll say sync touch post. Okay. So, this is similar to normal touch. If you want to have a resource that you want to touch through an association, whenever it updates, it works the same, only it's a special one that it'll work for sync. So, let's save that and refresh all of these browsers. Okay. So, now, they all have two comments. I come over here. Let's have Stanley add one more. Add one more comment. Chris. Now, the comments are updating multiple times. I'm curious why that is. What did I miss? One hour ago. What did I miss? Okay. There's one more there's one more view of posts here and that is the actual post page. So, we can edit this. So, we can say it sort of works, but now this page isn't updating and this page isn't updating. Right. So, I've updated this post, but the other browsers aren't updating on the show page. The reason for that is because we're not syncing the template on that page. So, really quick, we'll see we'll show you just how easy it is to do that. We'll come over here to show. We have the same information where we're pulling out all of these links determining whether or not we can edit it. I'm going to punt on that. I'm just going to put that down below. So, it's not going to be part of any template. So, if you hit this page and you can edit it we're going to show you those links but it's going to be outside of the outside of the parcel. So, I'm just going to say we're going to sync a new parcel and it's going to be called I cannot type. It really is so uncomfortable. So, a new parcel, we're going to call this postFull because we have the full body embedded in it and the resource is going to be that post object and that's really it. We come back here to our sync directory and our posts create a new file we'll save this as postFull and then we'll come back restart all of these browsers come back here and edit this and say it really does work what am I missing? I did refresh the browsers. Let's refresh it one more time. Huh, alright. Again, it worked an hour ago. So, that sync I'm out of time but we've used this on some internal apps dealing with monitoring and instead of pulling continually asking for new information just organizing your Rails app like you always have with templates and having those templates update whenever resources change has been just a really, really great fantastic thing. There are caveats. It's a little touchy as you can see but it's definitely been getting better over time. I would just like to leave you with this little pitch that Rails has benefited over the years from being first to popularize certain approaches for web development. It was there at the very beginning with Ajax. It was there at the very beginning with REST. I strongly feel that if we are not able to do real-time information within Rails that people will move on from Rails and I totally think that you don't need to. I know that there's a lot of functionality that's difficult to do with Rails as it is today so people are looking outside of Rails for the presentation but there are so many advantages to rendering your HTML on the server that we just need to think a little bit differently and a little bit better about it. I'll open it up to questions.