sailsCasts

Learning about sails.js one screencast at a time.

Building a Sails Application: Ep21 - Integrating socket.io and Sails With Custom Controller Actions Using Real Time Model Events.

| Comments

Transcript

Before we get started, we need to update the current version of sails to 0.9.7. To do this, we’ll open our package.json file. In the sails dependency we’ll change the version from whatever version you currently have to ~0.9.7. Go into your terminal and in the root of our project directory type npm install and you’ll be ready to go.

So I’m really excited to get started with this part of activityOverlord. I’ll be taking us through, step-by-step, how sails makes use of web sockets to deliver real-time events in your application.

First, let’s take a look at what we’ll have accomplished by the end of this episode. So, I’m going to log-in…and we’re at our user administration page, and of course I show logged in by this check mark. In order to show this functionality I’m going to actually utilize two different browsers. One just a regular browser and another that’s in incognito mode. So now I’m going to log-in here as Nikola Tesla and I don’t know if you saw this on this side but Nikola Tesla’s status changed without me having to refresh the browser. So now focus your attention on this side of the screen and I’ll sign-out and you can see that old Nick’s status has been updated to offline. What about if we add a user, so now I’m going to sign up a new user and we’ll use our friend Aimee Mann and Aimee appears on our list as well as her login status. And will go ahead and sign her out. And then finally let’s go back in as Nikola Tesla and we’ll remove Amy from the list. Which in turn is removed from the other listening browser. But how does sails do that and how can we accomplish it. And that’s what we’re going to go into during this episode.

First let’s look at what we’ve learned so far…

In Episode 18, I went over how to use plain jane node and socket.io. We learned that websockets are actually an extension of http. And since an http server can’t send data unless a client has requested it, web sockets allow for a server to send data unsolicited… You know what let me stop for a second. No one is dumb enough or brave enough to use these crazy Keynote transitions that I’m using. I found them so funny that I’ve decided to use them throughout this presentation. So I hope you take them in the spirit they are given and that they don’t distract you too much from the content. Anyway, back to programming.

So the http server can’t send data unless the client has requested it and websockets allow for a server to send data unsolicited once an initial connection is made. On the server side we built an http server with node, upgraded the http server to also act as a socket server and then on the client side we built up some html combined with javascript to connect to the server. Once the connection was made, any other client that connected would be eligible to send messages to the server which in turn emitted or broadcast those messages to the clients that were connected. We found out that part of a socket is just a unique id that identifies the socket or in our case the browser tab that is accessing the page.

Lastly, I showed how socket.io provides the concept of rooms that allow you to group sockets so that instead of messages going to all connected sockets, one can send messages only to those sockets that are associated with a room.

In Episode 20, I went over how sails can be used to emit messages based upon changes to a model using the rest blueprints. That episode may have confused some folks, but after the next several episodes I encourage you to go back to episode 20 and it should make more sense.

So how does sails add value to websockets and socket.io?

Similar to the way it automates many of the tasks associated with building an MVC framework, sails automates many of the tasks associated with enabling real time events within the MVC pattern.

I learn better by examples, so let’s look at one of the many ways sails automates enabling real-time functionality. I’ve named this functionality Real Time Model Events. And let me be clear here, Real Time Model Events is my term, it’s not something to do with javascript standards or anything the sail’s team uses, I just needed a way to identify some functionality and Real Time Model Events worked for me. I’d be curious, after you see this episode, whether the term was helpful or confusing to you.

Okay but what the heck are Real Time Model Events? Just like we create event handlers for DOM events using jquery, I’m going to show you a pattern to set-up event handlers for changes to models that get emitted to any socket that’s subscribed to the event.

Currently we have a User model in activityOverlord. One of the attributes of that model is a boolean called online. We want to know whenever the value of online changes so that we can provide a real-time update to the user administration page whenever a user logs in or out of the application. So where do we put our code?

If you’ve experimented with creating a project in sails, you’ve already been running a socket server and connecting to it when you hit the initial sails welcome page. Let’s check it out.

So I’m going to create a new sails project called socketExample. I’m going to start the sails’s server, and open up a page next to the terminal. Notice the handshake log message in the terminal. That’s our tab making a socket connection to the server. But where is that taking place? Let’s take a look at the code in the socketExample project. I want you to notice in the assets/linker/js directory we have a file added when we created this project called app.js. This app.js file is there to get you up and running with websockets. The structure of this file is purely optional but I’m going to use it as the base for the functionality in activityOverlord.

So here’s where we attempt to connect with the socket server. And then we listen for when this socket actually connects, and when it does, the socket starts listening for a message event from the server.

But wait, we didn’t create the socket server itself. That’s because sails does this for you automatically. You can have raw access to the socket.io server and client methods, but what I want to show you here is that a bunch of the work is done for you automatically if you choose to use it.

So, how do I register an event for our User model, online attribute, to listen for changes? We do that by using the following pattern.

The first step happens on the server-side within a controller. This involves subscribing the socket to the model “class” room and/or subscribing the socket to the model instance rooms. Remember, when I say socket, in this example, I’m talking about the page or browser tab that already connected to the socket server via var socket = io.connect(); back in our app.js file.

Now, You might be asking what’s the difference between subscribing to the model ‘class’ room versus the model ‘instance’ room? Subscribing to the model “class” room will enable the socket to listen for the creation of new model instances via a method called publishCreate(). Whereas subscribing to the instance class room enables the socket to listen for changes to existing models via the publishUpdate and/or publishDestroy methods. By the way, when you subscribe a socket to a model class room, that socket automatically gets introduced to the models instance room and will receive any changes via publishUpdate or publishDestroy.

So in activityOverlord we have a User model and we want to listen for the creation of new user’s as well as changes to existing users…so let’s leave all these cheesey transitions behind and go implement that in the code.

Here, we’re are in the user controller, and I’m going to add an action called subscribe.

Sails gives us access to the requesting socket via req.socket. So, first I’m going to subscribe to the User’s “class” room by entering User.subscribe and passing in req.socket as an argument. We’ve now subscribed to the User model class room.

Next, I’ll subscribe to the User model’s instance room by entering User.subscribe, again passing in the requesting socket via req.socket, but wait I need to pass a second argument, that being the existing instance user rooms or models. No problem, I’ll just wrap these two subscribe methods in a find query method. Now I can pass the returned users into the subscribe method as a second argument and we’ve now subscribed to the User model instance rooms.

But what about publishCreate, publishUpdate, and publishDestroy, where do we put those methods?

I’m going to come back to those methods in a minute, let’s move on in our pattern and address where we call our subscribe action. For that we’re going to move from the server back to our client. As you recall in app.js we’ve been notified by the socket server that a connection has been made and we’re now waiting on a message event. This is where we’re going to use a new method called socket.get() and we’re going to use it to “hit” our subscribe action.

You might be asking, I thought get was an http method, what does that have to do with sockets? Sails allows you to make requests like you would using http. You can not only make a get request over sockets, you can also use post, put, and destroy. I’m going to do a separate screencasts on using these methods but I wanted to mention that they were available.

So we’re back in app.js and here’s the code…that’s it, we’re now subscribed to both the class room and instance rooms of the User.model when each page is loaded.

So let’s review. We’ve connected to the socket server, we’ve subscribed to the User model class and instance rooms by calling the subscribe action of the UserController via socket.get and then within the subscribe action, subscribing to the user model by calling User.subscribe, and finally listening for message events from the socket server. We can now return to our controllers and start sending messages to our sockets based upon additions and changes to our model.

We started out this screencast with a goal of letting users know when other users login via the User Administration page. So let’s get to the session controller and do just that.

So here we are in the create action of the session controller. When a user authenticates, we’re going to let other users know by using the publishUpdate method. We’re going to pass the user id as the first argument, and then an object which will contain the changes that we want to send to other sockets. In this case whether loggedIn is true or false and the userid. Let’s try it out.

Okay, we have two browsers here, one in regular modeand one in incognito mode with the chrome console open in both tabs. I’m going to log into activityOverlord, which should trigger the publishUpdate and log it to the console. But the message event is not showing up in the console, what’s up? Well first let’s look at the terminal. Woah there’s a bunch going on here.

First, don’t be confused by the fact that the version of sails is v0.9.4 and that I’m getting these errors about installing sails locally. While creating these screencasts, I was using the development branch of sails in order to get the latest web socket functionality. You should be on 0.9.5 which encompassed the development branch when this screencast was made.

There are however some interesting errors here. Let’s take a look at this warning that we are trying to render a view (session/new) over sockets. If we look back at our subscribe action, we don’t have anything responding to our action. Which is an important reminder that we should at least send back a 200 if the action is successful. I’ll do that now. But why are we getting a warning that we’re trying to render a view? Ahh, let’s take a look at our policies. The current policy that will affect our subscribe action is admin. And when you take a look at that policy, it requires that we be authenticated before gaining access to the action. And look what happens if we’re not authenticated. We’re redirected to session/new which actually does render a view.

This is simple enough to fix. We’ll just add subscribe to our flash policy. Let’s see what happens.

I’ll reload the sails server, login in, and there’s our message in the console. And if we look at it, the message contains the user.id and that loggedin is true…perfect. Now you may ask why didn’t the browser that I logged in on also have a console message? It actually did, however, after I logged in successfully, the browser was redirected to the User Administration page which was refreshed instantly making it difficult to see the message before the browser refreshed.

What about when the user logs out? We’ll add a similar publishUpdate method within the delete action of the SessionController. Instead of setting loggedIn as true will set it to false. So after restarting the sails server, we’ll log out which produces a new message that has the loggedIn attribute set to false.

This is kind of the half-way point of this screencast. In the next episode I’ll go over one way of handling the DOM manipulation on the client based upon the events we’re receiving from our socket. See ya at the next screencast and thanks a lot for watching.

Comments