MIKE MOORE: Hello. My name is Mike Moore, and I'm gonna talk about real-time Rails with Sync. So, hi. Hello. How's Rails Conf? My name is Mike Moore. You might know be as blowmage or blowmage 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 a hundred percent, totally prepared for this. I was not up all night. I did not make these slides twenty minutes ago. Yeah. So. Who likes live coding? Three people! All right. K. We're gonna talk about Sync. Sync is a fantastic little Rails engine written by Chris McCord. Is Chris, where's Chris? Is he here? There he is. If you don't like it - that guy right there. You can find it on the GitHub. That's at chrismccord slash sync. So yeah. Let's do this. All right. So I have a. I have an app. And I'm just gonna load it up here. And it's a very simple app. I was gonna 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 bloggish 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 git repo for Sync. So please check it out. K, so. I want to just demonstrate this very simple Rails application. We'll go look at a little bit of code, and we're going to, we're gonna add Sync to this application, and we're gonna turn it from a very classic, kind of CRUD-y 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. So we have a series of posts. And then when you click on a post, you go to the post's show action. On that post's 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 gonna need to be looking at the comments on the actual resource. We're gonna be looking at it on the post resource. And that's it. So, you know, say, Hello RailsConf. Woo. Oh. Also, disclaimer, I cannot type in public. So this is gonna 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. So like that, right. Pretty simple. OK. So let's bump this out a little bit. So here is our application. You, same thing as we saw before. Our home controller is the, the home page. That showed the Jumbotron there. And this is all very bootstrappy. I'm sorry. Here's our posts 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, it fits the need. And I can use these methods in the controller and also in the views. OK. So let's take a look at the routes. So you see we're not, we're not, 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, with their responsiveness, and just kind of like updating the UI because something changed. And so instead of basically rewriting our entire frontend, our entire presentation layer, and then also creating an API to support that Javascript presentation layer, it's my conjecture that, conj- it's my assertion here that we can use Rails the way Rails is intended to be used, but still gain a good sub, a good, large portion of, 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 gemfile and we want to add a few gems. The first one is faye. And the reason we need to add faye is just for, for development. So we're gonna use faye for our browser to talk over websocket back to the server. And then faye also needs thin, but we don't like thin very much, so we're gonna not require it by default. And then the last one is Sync. K. So we'll bundle install that. Told you I couldn't type. There you go. So now we've got, we've added sync to the application. We need to go a couple 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 everywhere. It will get pulled in by the, the, 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 gonna use a little helper from Sync, adapter Javascript url. And we'll talk about what all these mean, hopefully by the end of this. OK. Now we're good to go. So what we need to do, before we do anything else, is we need to start up faye to run our web socket connections. So we can do that pretty easily. We can just say rack up Sync. Oh. I'm sorry. One more thing. We need to just take a look at our generators. Oh, gosh. K. There is now this install generator that was added by, 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, we can run this. Now this is going to run faye 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 faye is running, but we're not actually talking to it. But we know that it, 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 gonna create a new file called, a new proc file, and then inside of it I'm gonna 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. AUDIENCE: I think you can just stand up and do it. M.M.: Are you sure? All right. Exec, so rackup, sync conf, sync ru. OK. The other thing we need to do is come back over here and add foreman. K. So, so 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, to start all of those. So now instead of going to multiple terminals to open up, 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 then I want people to see it as soon as it shows up, right. So if I say see me, my, my browser refreshes. But all of these browsers over here won't necessarily. So let's go to this guy. K. 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 we ask it to. So let's make that happen. To do that, we are going to register our ActiveRecord 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 map up our comment model. And we're gonna 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, we need to open up our controller, and we need to enable sync here as well. K. And this is, again, just to, so the controller knows to look for all of the messages from the models that something has updated and respond 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, where you've got all the content for the blog post. So the title, the user, those tags that are on the, the blog post. Some editing links, if they are there, and then also the markdown of the body. OK. Then after that we've got our comments section, which is 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 gonna do, we're gonna change this from render to sync partial, and then we're gonna say that our resource is our comment. So we've made it just a little more verbose than what it was before, but sync needs that. The next thing we're gonna do is we're gonna add a new directory under app/views called sync, and under sync we're gonna add another folder called comments. And under comments, we're gonna add a new file, and that's gonna be called comments dot html dot erb. That's gonna be our partial, K. So, when we say sync partial here, instead of looking at our normal template, it's gonna go look for the one in the sync directory. And for the most part, we're just gonna take our trusty old partial that we're using right now and copy and paste that. We can kind of just 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 this, the same strategy you have for caching templates, caching partials within your application, you're gonna apply that same strategy to the real time updates as well. And so stuff like this is probably just, just gonna have to go, right. We can have the user name, we have have what the body is. But we really can't have all those editing options. AUDIENCE: [indecipherable - 00:15:35] M.M.: Oh, did I? Thank you. Let's rename this. So comment 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 gonna call sync.new. This is going to watch for new comments. In here, the resource is going to have to be a comment dot new, is that right? We can probably go a little bit further and say posts.comment dot new. K. All right. 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 the Javascript. We've went in, went ahead, moved some of our html from the original locations to a new location under sync and instead of calling render, we're gonna call sync and sync new. AUDIENCE: [indecipherable - 00:16:47] M.M.: What's that? AUDIENCE: [indecipherable - 00:16:49] M.M.: Post, yes. Thank you. Live coding, ladies and gentlemen. Yeah. I transposed the plural. There you go. AUDIENCE: Pair programming to scale. M.M.: Pair programming to scale. All right. So let's see if this works. What I'm gonna do is I'm gonna refresh this page. I'm gonna refresh this page. I'm gonna refresh this page. Now Firefox is not logged in. Safari here is logged in here by Stanley, who's back there somewhere, and then Chrome is logged in by me. So let's see if this works. Who wants to place a bet? Does this work? Anybody know? All right. Let's see if it works. All right. There we go. Does this work? K. Yay! So what's nice about this approach is that this is gonna go out no matter how many clients you've got connected, theoretically. Every time your, your assets change, your resources change in your application, they can be notified in real time. K. And 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 gonna use the, the same infrastructure that we're using today. All right. OK. Now, because of this, I kind of lost my ability to, 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 gonna add this missing 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, 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. K. K. So, it's a small change. But what it's, 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 am likely the browser that's gonna be changing it. And, and I really want the tools to be able to edit and delete. So I'm gonna just come back here and refresh this UI, and you'll 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. It looks like we've got a little bug. And then eventually I can actually delete it as well. And it gets rid of it. K. And there's another bug with this. Do you guys want to see what it is? It's pretty fun. Here is a, well, let's go to this one. Here is a blog post that doesn't have any comments yet. I'm gonna go onto this different blog post, right, and, say we gotta bug. When I do that, my comment is showing up on this different blog post, right. Because it's, right now we're looking for all comments whenever it gets updated, we're gonna 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. We're gonna come back here to, come back here to our comment model, and we're gonna add a new scope. Now, this is gonna be different than a normal scope. Might be a redundancy, but sync needs it. So let's add it, called by_posts. And it's gonna take a lambda, and we're gonna give it a post and we're gonna say where post id is post id. K. K. So we've just added a scope for post. Then we're gonna come over here into our sync partials. And we're going to add that scope here. So let's, let's see scope dot by_posts. And let's also add that to this one. K. So now if we refresh this, come 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, K. And if I refresh this we'll get rid of that comment. So scoping is, is easy as declaring a new scope on your model, in the same syntax that we're using for normal scopes. And then whenever we render out our partials, we just have to, we have to reference that scope. M.M.: Why do we need what? M.M.: The scope in both? Cause there are two different partials. It, it would be possible to- AUDIENCE: [indecipherable - 00:22:59] M.M.: 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, if. Whoa. Nevermind. 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 web socket to a server, and then it's going to put mechanisms within Rails to talk to that, that web socket, and then when our resources change, it will render those templates and push that out to that web socket, which the clients will then pull down, K. So without re, without rearchitecting our presentation tier, we are able to take advantage of real time, and 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 a Rails console, K, and we'll say. Just 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, K. What we can say here is sync model dot 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 on 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? Say, I don't know, real time. K, 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? Applettes. AUDIENCE: Wait till afterwards. M.M.: Afterwards? All right. OK. Let's continue and add this to some of the other pages as well. So let's go to post. Let's do the same thing. We want to sync our posts as well. And our posts controller, we want to enable sync here as. Enable sync on our post controller and in our posts model. So let's go ahead and take a look at our post index page. Our index page, where we're listing all the posts, we're gonna go through and render the post. And then, if we have, if we can, we'll add, we'll 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, or whether or not we can remove it. So this logic is problematic when we're talking about a cache template that's gonna get pushed out to everybody, cause not everybody's gonna 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 gonna take it out and put it into the index template. Well. Actually, I'm just gonna take it out of, of this view altogether. So, again, in order to change our template from a static template to a real time template, we're gonna say sync and partial is a post. And then our resource is also gonna be that same post, and then, if we get a new post, it just lists him down below. We don't, 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 gonna remove some, some code here. I'm gonna remove all of this editing. Anything that's gonna talk to current user, the current request is not gonna work. So let's get rid of that. And now let's take a look at, take a look at our, our post pages. I don't know what that is. We're asking for posts, it doesn't exist outside of that block. So we're gonna say post dot new. OK. So, again, user not logged in, user logged in, and as a different user logged in. So if I come over here, I want to add new post. See if it works. And let's add that. So, Stanley added this post. It shows up immediately on, on Mike's browser. And it also shows up in the not logged in browser. Let's come over here. We'll, we'll say congrats. Now. This browser over 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, 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 controll- or, comment object, and we'll say sync touch post. K. So this is similar to normal touch. If you want, have a resource that you want to touch through an association whenever it updates, it works just the same, only it's a special one that'll work for sync. So let's save that and refresh all of these browsers. K. So now they all have two comments. OK. I come over here. Let's have Stanley add one more, add one more comment. Man, Chris. Now the comments are updating multiple times. I'm curious to why that is. It wasn't doing that an hour ago. What did I miss? OK. There's one more, there's one more review of posts here, and that is the actual post page. So we can edit this. So let's say it sort of works, but now this page isn't updating and this page isn't updating, right. So we, 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 this same information where we're pawing out all of these links determining, you know, whether or not we can edit. I'm gonna punt on that. I'm just gonna put that down below. So it's not gonna 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 gonna be outside of the, outside of the, the partial. So I'm just gonna sit. We're gonna sync a new partial and it's gonna be called, oh my gosh. I cannot type. It really is so uncomfortable. So a new partial, we're gonna call this postfull, because we have the full body embedded in it. And the resource is going to be that post object. And then that's really it. We come back here to our sync directory under posts, create a new file, and we'll save this as postfull. And then we'll come back, restart all of these browsers, and come back here and edit this and so it really does work. What? What am I missing? AUDIENCE: [indecipherable - 00:34:01] M.M.: I did refresh the browsers. Let's refresh it one more time. AUDIENCE: Did you try rebooting? M.M.: Huh. All right. Again. It worked an hour ago. So, that's 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 a Rails app like you always have with templates and having those templates update whenever resource is changed 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, to popularize certain approaches for web development. It was there at the very beginning with Ajax. It was there are the very beginning with REST. I, 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 is very difficult to do with Rails as it is today, and 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. So, I'll open it up to questions.