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.