ActionCable for Not-Another-Chat-App-Please
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Wolgamott, Jesse
RealTime updates using WebSockets are so-hot-right-now, and Rails 5 introduces ActionCable to let the server talk to the browser. Usually, this is shown as a Chat application - but very few services actually use chats. Instead, Rails Apps want to be able to update pages with new inventory information, additional products, progress bars, and the rare notification. How can we make this happen in the real world? How can we handle this for unauthenticated users? How can we deploy this?
 Welcome to the What is New in Rails 5 track. I'm extremely proud to be part of it. I'm excited for all other talks today. I think this is a really cool release. So let's dive into cable boxes. So this was the first cable box I ever saw. And look, it's got a cable. I don't know if any of you are old enough to have seen one of these things. But like this would sit and connect to your TV and you'd be like, okay, to get to 33, I find that and I've got to do the dial. But that's not the cable we're talking about. So for this presentation, I made several high quality 8-bit art, not 2-bit, not 4-bit. But diagrams throughout this presentation, you're welcome in advance. Here we see our intrepid user connecting to Rails through a cloud. Yeah. So again, you're welcome. I'm pretty good. So how can Action Cable sort of change this up? So what we're going to do is we're going to talk about the history of Action Cable and WebSockets. We're going to talk about what it is, what it's not. We'll talk about building blocks, like what actually, what code is there for you to use, and then some patterns. We'll also talk about deployment strategies, how to actually make use of this today. My name is Jesse Walgomont. You can, I'm J-WO on Twitter in the GitHub. If you've got questions, comments on this talk, I don't think we're going to have time for Q&A. So tweet at me and I'll do my best to get you an answer. So I found that the best way to look forward at changes that we're going to take place is to look back and to see where we stand in this march of time. So let's start at our F5 refresh or our command R. So in general, this is how we would update a page for a lot of times. So checking your email was frustrating. You had to click refresh. And then you had to make sure that your two meg of storage didn't get taken up by someone sending you a 300 by 300 JPEG. And then you had to call because texting wasn't really a thing yet. You had to call your friend and be like, check your email. It is full. Kids today with your gigs upon gigs upon gigs of storage just don't understand. So other than that, if you're going to get a page to update itself, maybe you could just have it like auto refresh. So you could have like a meta tag that would say, hey, refresh yourself every three to ten seconds. Dredge report sort of did this for a long time. I think still does. And since sites were being paid by the ad impressions, like this is a pretty good story. You just leave your site up and it would just refresh, refresh, refresh. I don't know why that model didn't work. But this would be the code that would make that work. So by the time that like 2002, 2003 came along, we wanted better. And so we got polling. Polling would be in that time like Gmail, right? Like Gmail came out and did it say you've got two new messages. It didn't update the page. And at the time that's like what? Okay now this is in 2002. Or 2012, excuse me. But DHH said campfire to this day still uses a three second poll. Chat was supposed to be the poster boy for WebSock and Supremes. Now that's 2012. Times have changed. I'm glad that we can move on. But this was DHH's view. He said polling remains a great way to make your app dynamic. If you don't require sub-second responses, live updates in Basecamp are polling. So like this works on a very large set of websites where you can just have it like every three seconds fetch my new stuff. So there are apps that don't need like sub-second responses. Having your JS poll for changes on an interval is like an extremely good idea in many circumstances. So this can be what that looks like. You know, you jQuery pops up. You say set an interval. Make an Ajax recall every three seconds. And sort of set it and forget it. But then we get to WebSockets, which are so hot right now. So I first saw like WebSockets be used with Push or App. They're here. And it made everything very easy. Like they took care of everything for you. So like you'd process an image in the background and then post to Pushers API when you're done. And you're front-end listen to Pusher and like updated when it was done. So this came about as far as I can tell by RFC 6455 in 2011. It says basically it's two-way communication between the client and the server. And that the remote host has to opt into the communication that it can't just, whereas HTTP calls just go and are taken, right? But the client has to request a WebSocket. The server has to say yes. So some examples. Socket.io made things pretty simple in Node-Land to push updates from the server out to the client. Patrick McKenzie, patio 11, built a pretty sweet fake stock market. I don't know if you've played with this or not, but it is pretty sweet. So it sends trades out via WebSockets. Or you can constantly ask via an API call, like which, like, do you have any new trades that have been made? But the thing is, is like you'd have to do that on a loop. Is there any trades? You're making a new connection every time. And when you're trying to do high-frequency trading, which is what sort of it's all about, you want to get at the trade as soon as it possibly can. And so connecting via WebSockets was a way that you can actually get it almost as it happens rather than making a call in an infinite loop. So the trouble is, how can a server keep track of all these connections? Like scaling up in a big server can sort of work in a language like Elixir and others like it. But what about our dear friend Ruby? If every connection, like, lives, and there's got to be an object that's like listening for connections from server to client to server, like, that has to be taken up in memory. And Ruby loves its memory. So you're probably like, I'm uncertain about 2,000 connections, let alone 2 million connections. So with Ruby and with Rails, there's got to be a story about scaling out, not just scaling up. And in spoiler alert, there is. So is the future just real time? Probably not. I doubt that, like, we're going to default to building every feature with ActionCable. But I think it can add sort of pizzazz and fun and really fast updates. Like for certain apps that require that low latency, website cuts is how I would describe treat yourself good. My philosophy. So let's take a look at what ActionCable is and what's not. So if we know where the possible awards are, but we also know where the awesome is, we can decide if it fits in our tool belt. So first, let's start with the what it's not. In general, it's not just a silver bullet that will make everything like super fantastic great awesome sauce. So let's talk about, like, what it's not. So it's very much made to work inside of Rails. I could see a future where it's lifted out, but for one way I see we're not there yet. I don't consider this a bad thing, but it does sort of want to be in a request or it wants to connect with a controller. It wants to be in Rails. That's fine. People are going to NPM install ActionCable. It's also not necessarily a step forward for Rails into more JavaScript integration. Like there is JavaScript here, but, like, it's, I saw this and I was like, I think this makes sense. It's not, it is just a feature. It's the next JBuilder. It's the next Turbolinks. It's not the next Asset Pipeline. It's not the next ActiveRecord. It's just a feature. So let's talk about what it is. So it's a solid feature. Like it works, it's fun, it adds value to your app. I don't think it needs to be any more than this, but, like, this is sort of the scope. It's a nice feature to have for Rails apps. So like Turbolinks and JavaScript responses, it's a way to, like, snap the Fire Rails. It's a way to make it faster. Like there is a certain wow factor where you've got, like, two windows up. You make a change on one. It, like, propagates to the other. Like people are, so, like, wow, that's cool. It's also easy and fun, which, like, big props on that. So why no chat apps? Like why did I specifically come here and say, we're not going to talk about chat apps? So when I came up with the idea for this talk, I was somewhat dread, like, I was dreading the talk that I might hear if I attended. I think it'd be about chatting between users, maybe like an intercom.io style integration, but there's so much more that we can do with sockets. Because chat apps are the hello world of Web sockets. They don't add to, like, they don't add anything. Like there are features where, like, if it's a chat app, you probably want a chat app. But just adding chat to an app doesn't make it awesome. Okay, and so as we show people, hey, you can use ActionCable. Here's a chat app. It's sort of like, but that's not where the real value proposition is. Because most apps don't need chat between two different users. Like Facebook, Chir, another social network, maybe? But, like, I implore you, when you go and you try this out, don't just add chat to your existing apps as a way to check out ActionCable. But some apps do need communication between the server and the client. Like, if we're building unsocial apps, there's big wins on what ActionCable can get us. Use case number one, as I see it, is collaboration. So this can be as complete as a big Trello style where we're both editing the same document or the same card or somebody adds a card and it shows up on my page. Or maybe a price changes on a website, inventory changes, stuff like that. It'd be nice if I'm viewing everything if it did update, rather than later on on another page load. Use case number two, asynchronous tasks. So if you're implementing a task that the server needed to do, say, like fetch all new data from a third party or something. Maybe it's something like create a PDF from an invoice. You'd likely throw it on an action job and pull for when that's complete. It's not easy. It's not rocket science either, but it's doable. But ActionCable can make that fun. Okay. So the first ActionCable beta. So again, more with the history. And then we'll get to code, I promise. Okay, so last year, ActionCable was announced as vaporware. It was announced with a diagram, but no code. This is the diagram. Now not too much later, July of 2015, we got our first look at the code. That's only a couple months. So like as far as vaporware existence, that's not all that long. But it was a non-rails experience. Now it was only version 0.0, but like trying it out was a little bit rough. So there are no generators, no deployment story, no standalone, no in-app version. And again, like this is version 0, and here I am complaining about it. I was able to get it to work, but it was a pain, like sort of like diving into the source code pain to try and figure stuff out. And that's not necessarily the rails way. So I'm glad it existed. And I bring this up to say that if you tried it then and didn't like it, now is a much better experience. The core is still there, but it's got the nice sugar, too. So the current beta version, 5.0, 0.0, beta 3, which syncs across rails versions was released in February 2016. And I was sort of like this. True story. So it made all the things that I had like enumerated about for this talk, like do this, don't do this. You want unauthenticated users, you have to monkey patch this, and it made them all just go away. It was great, and I was happy. So finally some code. Let's talk about building blocks. So the modules. So what makes action cable action cable? There are four distinct modules. So the first is the cable. So this is the actual connection from client to server. So it's what is connected to. So it's a, like it lives on, messages go back and forth without having to reconnect. So then you've got a channel. All right, so you've got your cable, that's the actual connection. Then you've got a channel. The channel is the thing, is sort of a room. It's what you listen for events. So a channel is on the rail side, and it has a stream name that can be like all products, or it can be scoped down to a specific user, like cart underscore, like that user's ID. Broadcasting is sending information out from rails through the channel to the client. And then you have a subscription. Subscriptions on the client side. Generally this is just JavaScript. It doesn't have to be, but an action cable, it's JavaScript. And it's the client side. You listen and you receive data from the server. Let's dive into each of these. So the cable, the actual connection. My examples in these are going to be in JavaScript, not CoffeeScript. So you can easily make these go to coffee at, like, JS.coffee, but these are going to be JavaScript. So what we do here is we require the action cable library, all the channels, and then we create, like, a big app object. If it doesn't exist. And then we slide the cable onto the app global object. So in general, like, this is what exists, app.cable on your JavaScript side. On the rail side, we tell it to process sockets in process. So this is async at slash cable. This is sort of the easy way to do it. The slash cables, configurable, but this is sort of assumed to be the convention that you'll use. That's the actual connection between the two. So let's look at channels. On the rail side, a channel is like a controller. So it groups sort of ideas together. So you might have a products controller, maybe an inbox, a stream that's, like, specific to that user of updates. Think about this as, like, on Facebook, maybe you've got a stream on the right for, like, all your updates of what people do, and then maybe you've got a stream in the middle for your news feed. So they group together. So each channel has a connection, and it inherits from application connection. So this is very much controller inherits from application controller. So here's where you would tell that this connection that you have a current user object. So identified by current user enables you to use this later on. It enables you to be able to say that your channel is only for a specific user. And on line seven here, we see what happens when the channel is connected to. So when the channel is connected, we find the user, so user.findby, ID of cookies.signed, user ID. So we'll come back to this in a little bit, but notice that it's cookies.signed, not session ID. So you don't have a session, but you do have cookies. But since you have cookies, this could be in application controller if you're using, like, a password, what you might do here. So you would say, on line nine, cookies.signed, user ID is the user.id, and then on line five, user findbyid's cookie.signed. So you would switch from a session of user ID to cookies.signed of user ID. I have a device example later for, like, the 90% of you who are wondering how to do this with device. So just hold on. Let's talk about broadcasting. So here I am using an action job to broadcast out the message to a JavaScript channel. That'll send it up to the client. So line six shows how to send it out. So action cable.server.broadcast. And then you give it a stream name and a hash. So that hash would be the data that's going to be sent up. That hash, you'll send it out as a hash. It'll be received later in JavaScript as just a JavaScript object. So this can be done anywhere, rate tasks, action jobs, controllers, model callbacks, like anywhere in Rails has the ability to send out broadcasts, which is cool. Also here I am doing the action controller.render, which is super awesome. I think we're going to look back and see this as a very cool feature. We'll go into that a little bit later. Finally, you've got a subscription. That's the JavaScript listener. So when you create a subscription, it listens for all broadcasts for that channel. So when the server broadcasts a message, it calls the received function, and that's where you get your data. So app.product is equal to app.cable.subscriptions.create. And then you've got connected if you wanted to log that, disconnected if you wanted to log that, and then received. Received is sort of magic function, like that's what you need to call it. That's when I broadcast from Rails up to JavaScript, that's what it calls. And then you can do whatever you want with that data. So here it's like data.product.id, but you can do whatever you needed to with this. Here I'm saying let's find a product with the data.product.id of that ID and replace it with a template. So in this example, Rails is sending up the updated template for that product, and it's just going to re-render right on top of it. So let's walk through a standard action-cable interaction. We'll start on the client side this time. So by requiring action-cable, the cable cables to the Rails cable. The browser then upgrades the cable connection to a WebSockets connection. In the subscription, subscribed, connection connects. And then to start listening for updates, you create a subscription with the received function. That's what gets called. So you can have many subscriptions in like many different places. So what I mean by that is like app.channel, or app.cable.channel is this global thing. So anywhere in JavaScript, you can access it. So it could be your standard jQuery style, like listen for the event, or it could be in React, like on the component in mount. It could be in Angular. It could be in Ember, on the set of controller. Like any of these things can create a subscription and start using it. So the subscription calls receive data when the subscription receives the data. And that's it. This is a Tron DeLorean. I love Giffy. So let's talk about patterns, like how we might set up for specific scenarios. First, we'll talk about data updates. So this is what it basically called collaboration. But we can keep data in sync across tabs and users. Sorry, not sorry. So here we're looking at a pretty sweet browser setup. It's got four tabs. Up in the top right is the cart button. That's a cart. And we're selling tennis balls. So those are tennis balls. So that's your add to cart button. I'm telling you, I'm pretty good. But we're looking at four tabs because the middle two are on this site. I don't know about you, but I'll go to Amazon and I'll do a search and it'll be like open a new tab, open a new tab, open a new tab, open a new tab. So I've got all of these open that I can flip through. So if we add the cart on tab three, there's now one item in our cart. But what about tab two? It's typically not updated, right? It's typically sort of it was, it'll show whatever was on that page as it loaded, but not update itself, even though tab three updated itself. It's not a huge thing, but what could we do to make this experience even slightly better? So after the update happens, like in a controller or a job, you would broadcast a message. The message can be data or it can be a Rails rendered template partial for you to update with. So this is the application controller.render. So when I heard about application controller.render, I was basically like dancing the dance of joy. So I've wanted this for so long. The use case that I have for it is like creating reports. So each month you'd want to create reports using whenever like a rake task and I'd want to be able to use Wicked PDF to take HTML and convert it to PDF. But it's not in a request cycle. Like there is no request at that point. So I had to do some like weird things, man, to like make that work. But now it's easy. You can just render an entire part, like a partial or a page. You just hand it what locals you think this partial should have. And they can be the same partial that you would use if you're like rendering your cart or rendering out like a list of products. I think it's going to be really awesome. Here's how you could send out new cart partial to a current user only. So you would broadcast to cart underscore current underscore user dot id. So that would be scoped to just that user. It would only go out on channels where that's subscribed, where you are that user. So it's not that like the cable has all of the message. It only has the message that it's actually subscribed to. Here's the channel setup that would set up the stream. So the channel would have subscribed and it would say streamed from cart underscore current user. Both these have been on the rail side. So you've got broadcast to a channel. And then JavaScript listens for that message. So app cable subscriptions create cart underscore the current user ID. He receives the data, replaces up the template. Now Taps, who's happy? We can also sort of hook into existing JavaScript libraries. So maybe like you change a graph, update some options, can action cable be used to send messages to the server? Did something happen on the client? Yes, it can. So if you want to send a message to the server and then out to other clients, here's an example for you. So the example that I've got is like an HTML5 slides presenter mode. So imagine that this is like reveal.js and we're all looking at it. So you'd be looking at sort of like a read-only state, I'd have the ability to change the slides. And so what we would do is you'd add an event listener. So you'd add an event listener to the slide change. Now that's reveal specific. So all of this is predicated on your JavaScript libraries having hooks that you can tie into. But what gets sent from client to server when you say app.slidesnotification.advancedlide and then you send it some data. So the slides notification, that's the channel name. And then you've got a channel method on advanced slide and then you send it the data. So app.slidesnotification.advancedlide. So advanced slide is the method that JavaScript will call to tell Rails that something happened. So here we say app.slidesnotification is the app cable subscriptions. So this is setting up the subscription, the previous slide was using it. And so we say advanced slide. So that's the function that we called when we said app.slidesnotification.advancedlide. This does this.perform slides data. Back on the Rails side, we have a slides channel. So the streams from slides, that slides stream is the name of it. So earlier, one, two, three. So on line six here, we say perform, perform on slides and send it the data. That slides becomes line three here, stream from slides and calls the line six slides method. So that is what comes in from JavaScript back to Rails. So there's the broadcasting out and then the receiving message back. So line six here, slides of data, we're, I'm just going to say, ifcurrentuser.admin, admin, then action cable server broadcast, like broadcast it out again. So what's interesting about this is messages go from client to server. It's not peer to peer. So like this makes sense when we think about it, but if I want to, if we were all on a server, right, and I wanted to send a broadcast message out to all of you, I would not send it directly to you, I would send it to the server and the server would send it to you. And vice versa as you reply back. So let's talk about collaboration. So Trello, we talked about cards, like that's sort of what it would be. Pivotal tracker, also the cards. But then on GitHub, they do a neat thing where it's sort of complete with yes, no, you can merge with, TrevCI comes back. And then it updates all the other users viewing the page. So this is a neat way that if I like click and merge, it's going to other people viewing that page remove the ability to merge. I think that that's the type of thing that we can very easily get huge wins out of with action cable. Finally asynchronous tasks. So let's imagine that we have a system where we use mechanized to go out and fetch the latest data. This can take quite a bit of time so let's throw it in action cable job, or an action job. So the first thing we do is we generate a channel. So each channel is going to have many streams based on like a UUID. So each task that we do would have its own stream. Then the JS posted up, give it the UUID that defines this job. You would then create a subscription on that stream with that UUID for when messages come back from Rails. Maybe you have a site search job that performs search with a UUID. So you have your stream that you broadcast, hey, I'm starting. And then snip, you do all the stuff. Then action cable server broadcast the stream that it's complete and along with the template. And yeah, let's go ahead and discuss chat. I read that there's this concept of iMessage on iPhones lock-in that people really love seeing the bubble like when people are typing. That it makes it feel sort of instant that you're connected to someone, like there's an actual person that's actually typing right now. So, oh, no, come back, come back. So Aziz Ansari talks about this in modern romance. So he asked someone out. He saw the bubble, but then nothing. Like the bubble just went away after a while. He never got a reply in the sad face for Aziz. But there's a sort of emotional thing with this. Okay, so what would you do? So you could listen for the key up event on a text area and then tell the server that you're typing in, that you're typing in the conversation between you and Taylor Swift. Then when the message is sent, you would stop the bubble and render the message text. And maybe you would handle if you haven't typed in a while. End of chat. Let's talk about deployment. So in-app mode is basically easy mode. Think of it like you'd use Sidekick, not inline jobs. So you're not doing Sidekick, it's just inline. It works, but scalability is a thing. So it can be done with only one server, no rescue Sidekick. You can just say that everything is inline, everything is async. So you would tell it, my QAdapter is async, and then my config, cable.yaml, async, async, async. Everything is done async, so that's just threaded. It connects to slash cable on the Rails server. And generally can work pretty well. But if you've got more than one server, like you've got multiple dinos or whatever, then you want to use Redis. So you want to have server, server, Redis, and Redis will keep it in sync. Otherwise, everything is just going to be in memory on each server, and that's no good. Because if I connect to one server, that's a physical connection, you connect to another dino, we have to have some way that I can actually pass messages back and forth. Redis would be that. Standalone is basically hard mode. It's slightly more involved. It's the same scale as sort of moving from async to sidekick. You connect via Redis. You've got to have an actual, like sockets.domain.io. You've got to configure where it is so it knows how to connect more with our person. So our green person connects to Rails. Again, you're welcome. When you broadcast it, it sends the message to Redis. Action cable is listening for that, and then action cable sends it back out. So quick likes and gotchas. So what about miss messages? I don't think we know yet what this story sounds like. But I think we have to think about the idea that if I'm just sending update, update, update, and you miss some messages, you go offline. What does that look like? In a physical, like it's a physical network connection to somewhere in the world. So you want to use ActionJob. Don't inline, like just broadcast it out in your controller because it would have to send like somewhere along the world. Don't wait for that. Also no session, only cookies. So here is your device version. You're welcome. You have to configure your request origins to allow or disallow people to be able to connect to you. Fun fact, don't leave a trailing slash in the host because it will not work. And finally, the config.cable.yaml default setup, this is what gets generated. The production URL for Redis is localhost. You'll need to change that. So where do you go from here? So I've got a simple example that you can look at, J-O-O slash inventory cable. There is also ActionCable examples. These are very good. There's documentation on this URL. But so ActionCable is part of the Rails standard, just like ActionController. Finally, I want to give some many thanks. So thanks to DHH for having the idea, announcing it, making it happen. This is very cool. The 42 plus contributors on the ActionCable project, you all rule also. And thanks to the IronYard for paying for me to come here, spend today with you. So we're hiring developers across the US that want to learn how to teach, make the world better, change lives, improve diversity. So if you're interested, tweet at me, come talk to me. I'm J-O-O. Thank you very much. Thank you.
 Welcome to the What is New in Rails 5 track.  I'm extremely proud to be part of it.  I'm excited for all other talks today.  I think this is a really cool release.  So let's dive into cable boxes.  So this was the first cable box I ever saw.  And look, it's got a cable.  I don't know if any of you are old enough to have seen one of these things.  But like this would sit and connect to your TV and you'd be like, okay, to get to 33,  I find that and I've got to do the dial.  But that's not the cable we're talking about.  So for this presentation, I made several high quality 8-bit art, not 2-bit, not 4-bit.  But diagrams throughout this presentation, you're welcome in advance.  Here we see our intrepid user connecting to Rails through a cloud.  Yeah.  So again, you're welcome.  I'm pretty good.  So how can Action Cable sort of change this up?  So what we're going to do is we're going to talk about the history of Action Cable and  WebSockets.  We're going to talk about what it is, what it's not.  We'll talk about building blocks, like what actually, what code is there for you to use,  and then some patterns.  We'll also talk about deployment strategies, how to actually make use of this today.  My name is Jesse Walgomont.  You can, I'm J-WO on Twitter in the GitHub.  If you've got questions, comments on this talk, I don't think we're going to have time  for Q&A.  So tweet at me and I'll do my best to get you an answer.  So I found that the best way to look forward at changes that we're going to take place  is to look back and to see where we stand in this march of time.  So let's start at our F5 refresh or our command R. So in general, this is how we would update  a page for a lot of times.  So checking your email was frustrating.  You had to click refresh.  And then you had to make sure that your two meg of storage didn't get taken up by someone  sending you a 300 by 300 JPEG.  And then you had to call because texting wasn't really a thing yet.  You had to call your friend and be like, check your email.  It is full.  Kids today with your gigs upon gigs upon gigs of storage just don't understand.  So other than that, if you're going to get a page to update itself, maybe you could just  have it like auto refresh.  So you could have like a meta tag that would say, hey, refresh yourself every three to  ten seconds.  Dredge report sort of did this for a long time.  I think still does.  And since sites were being paid by the ad impressions, like this is a pretty good story.  You just leave your site up and it would just refresh, refresh, refresh.  I don't know why that model didn't work.  But this would be the code that would make that work.  So by the time that like 2002, 2003 came along, we wanted better.  And so we got polling.  Polling would be in that time like Gmail, right?  Like Gmail came out and did it say you've got two new messages.  It didn't update the page.  And at the time that's like what?  Okay now this is in 2002.  Or 2012, excuse me.  But DHH said campfire to this day still uses a three second poll.  Chat was supposed to be the poster boy for WebSock and Supremes.  Now that's 2012.  Times have changed.  I'm glad that we can move on.  But this was DHH's view.  He said polling remains a great way to make your app dynamic.  If you don't require sub-second responses, live updates in Basecamp are polling.  So like this works on a very large set of websites where you can just have it like every three  seconds fetch my new stuff.  So there are apps that don't need like sub-second responses.  Having your JS poll for changes on an interval is like an extremely good idea in many circumstances.  So this can be what that looks like.  You know, you jQuery pops up.  You say set an interval.  Make an Ajax recall every three seconds.  And sort of set it and forget it.  But then we get to WebSockets, which are so hot right now.  So I first saw like WebSockets be used with Push or App.  They're here.  And it made everything very easy.  Like they took care of everything for you.  So like you'd process an image in the background and then post to Pushers API when you're done.  And you're front-end listen to Pusher and like updated when it was done.  So this came about as far as I can tell by RFC 6455 in 2011.  It says basically it's two-way communication between the client and the server.  And that the remote host has to opt into the communication that it can't just, whereas  HTTP calls just go and are taken, right?  But the client has to request a WebSocket.  The server has to say yes.  So some examples.  Socket.io made things pretty simple in Node-Land to push updates from the server out to the  client.  Patrick McKenzie, patio 11, built a pretty sweet fake stock market.  I don't know if you've played with this or not, but it is pretty sweet.  So it sends trades out via WebSockets.  Or you can constantly ask via an API call, like which, like, do you have any new trades  that have been made?  But the thing is, is like you'd have to do that on a loop.  Is there any trades?  You're making a new connection every time.  And when you're trying to do high-frequency trading, which is what sort of it's all about,  you want to get at the trade as soon as it possibly can.  And so connecting via WebSockets was a way that you can actually get it almost as it  happens rather than making a call in an infinite loop.  So the trouble is, how can a server keep track of all these connections?  Like scaling up in a big server can sort of work in a language like Elixir and others  like it.  But what about our dear friend Ruby?  If every connection, like, lives, and there's got to be an object that's like listening  for connections from server to client to server, like, that has to be taken up in memory.  And Ruby loves its memory.  So you're probably like, I'm uncertain about 2,000 connections, let alone 2 million connections.  So with Ruby and with Rails, there's got to be a story about scaling out, not just scaling  up.  And in spoiler alert, there is.  So is the future just real time?  Probably not.  I doubt that, like, we're going to default to building every feature with ActionCable.  But I think it can add sort of pizzazz and fun and really fast updates.  Like for certain apps that require that low latency, website cuts is how I would describe  treat yourself good.  My philosophy.  So let's take a look at what ActionCable is and what's not.  So if we know where the possible awards are, but we also know where the awesome is, we  can decide if it fits in our tool belt.  So first, let's start with the what it's not.  In general, it's not just a silver bullet that will make everything like super fantastic  great awesome sauce.  So let's talk about, like, what it's not.  So it's very much made to work inside of Rails.  I could see a future where it's lifted out, but for one way I see we're not there yet.  I don't consider this a bad thing, but it does sort of want to be in a request or it  wants to connect with a controller.  It wants to be in Rails.  That's fine.  People are going to NPM install ActionCable.  It's also not necessarily a step forward for Rails into more JavaScript integration.  Like there is JavaScript here, but, like, it's, I saw this and I was like, I think this  makes sense.  It's not, it is just a feature.  It's the next JBuilder.  It's the next Turbolinks.  It's not the next Asset Pipeline.  It's not the next ActiveRecord.  It's just a feature.  So let's talk about what it is.  So it's a solid feature.  Like it works, it's fun, it adds value to your app.  I don't think it needs to be any more than this, but, like, this is sort of the scope.  It's a nice feature to have for Rails apps.  So like Turbolinks and JavaScript responses, it's a way to, like, snap the Fire Rails.  It's a way to make it faster.  Like there is a certain wow factor where you've got, like, two windows up.  You make a change on one.  It, like, propagates to the other.  Like people are, so, like, wow, that's cool.  It's also easy and fun, which, like, big props on that.  So why no chat apps?  Like why did I specifically come here and say, we're not going to talk about chat apps?  So when I came up with the idea for this talk, I was somewhat dread, like, I was dreading  the talk that I might hear if I attended.  I think it'd be about chatting between users, maybe like an intercom.io style integration,  but there's so much more that we can do with sockets.  Because chat apps are the hello world of Web sockets.  They don't add to, like, they don't add anything.  Like there are features where, like, if it's a chat app, you probably want a chat app.  But just adding chat to an app doesn't make it awesome.  Okay, and so as we show people, hey, you can use ActionCable.  Here's a chat app.  It's sort of like, but that's not where the real value proposition is.  Because most apps don't need chat between two different users.  Like Facebook, Chir, another social network, maybe?  But, like, I implore you, when you go and you try this out, don't just add chat to your  existing apps as a way to check out ActionCable.  But some apps do need communication between the server and the client.  Like, if we're building unsocial apps, there's big wins on what ActionCable can get us.  Use case number one, as I see it, is collaboration.  So this can be as complete as a big Trello style where we're both editing the same document  or the same card or somebody adds a card and it shows up on my page.  Or maybe a price changes on a website, inventory changes, stuff like that.  It'd be nice if I'm viewing everything if it did update, rather than later on on another  page load.  Use case number two, asynchronous tasks.  So if you're implementing a task that the server needed to do, say, like fetch all new data  from a third party or something.  Maybe it's something like create a PDF from an invoice.  You'd likely throw it on an action job and pull for when that's complete.  It's not easy.  It's not rocket science either, but it's doable.  But ActionCable can make that fun.  Okay.  So the first ActionCable beta.  So again, more with the history.  And then we'll get to code, I promise.  Okay, so last year, ActionCable was announced as vaporware.  It was announced with a diagram, but no code.  This is the diagram.  Now not too much later, July of 2015, we got our first look at the code.  That's only a couple months.  So like as far as vaporware existence, that's not all that long.  But it was a non-rails experience.  Now it was only version 0.0, but like trying it out was a little bit rough.  So there are no generators, no deployment story, no standalone, no in-app version.  And again, like this is version 0, and here I am complaining about it.  I was able to get it to work, but it was a pain, like sort of like diving into the source  code pain to try and figure stuff out.  And that's not necessarily the rails way.  So I'm glad it existed.  And I bring this up to say that if you tried it then and didn't like it, now is a much  better experience.  The core is still there, but it's got the nice sugar, too.  So the current beta version, 5.0, 0.0, beta 3, which syncs across rails versions was released  in February 2016.  And I was sort of like this.  True story.  So it made all the things that I had like enumerated about for this talk, like do this,  don't do this.  You want unauthenticated users, you have to monkey patch this, and it made them all just  go away.  It was great, and I was happy.  So finally some code.  Let's talk about building blocks.  So the modules.  So what makes action cable action cable?  There are four distinct modules.  So the first is the cable.  So this is the actual connection from client to server.  So it's what is connected to.  So it's a, like it lives on, messages go back and forth without having to reconnect.  So then you've got a channel.  All right, so you've got your cable, that's the actual connection.  Then you've got a channel.  The channel is the thing, is sort of a room.  It's what you listen for events.  So a channel is on the rail side, and it has a stream name that can be like all products,  or it can be scoped down to a specific user, like cart underscore, like that user's ID.  Broadcasting is sending information out from rails through the channel to the client.  And then you have a subscription.  Subscriptions on the client side.  Generally this is just JavaScript.  It doesn't have to be, but an action cable, it's JavaScript.  And it's the client side.  You listen and you receive data from the server.  Let's dive into each of these.  So the cable, the actual connection.  My examples in these are going to be in JavaScript, not CoffeeScript.  So you can easily make these go to coffee at, like, JS.coffee, but these are going to  be JavaScript.  So what we do here is we require the action cable library, all the channels, and then  we create, like, a big app object.  If it doesn't exist.  And then we slide the cable onto the app global object.  So in general, like, this is what exists, app.cable on your JavaScript side.  On the rail side, we tell it to process sockets in process.  So this is async at slash cable.  This is sort of the easy way to do it.  The slash cables, configurable, but this is sort of assumed to be the convention that  you'll use.  That's the actual connection between the two.  So let's look at channels.  On the rail side, a channel is like a controller.  So it groups sort of ideas together.  So you might have a products controller, maybe an inbox, a stream that's, like, specific  to that user of updates.  Think about this as, like, on Facebook, maybe you've got a stream on the right for, like,  all your updates of what people do, and then maybe you've got a stream in the middle for  your news feed.  So they group together.  So each channel has a connection, and it inherits from application connection.  So this is very much controller inherits from application controller.  So here's where you would tell that this connection that you have a current user object.  So identified by current user enables you to use this later on.  It enables you to be able to say that your channel is only for a specific user.  And on line seven here, we see what happens when the channel is connected to.  So when the channel is connected, we find the user, so user.findby, ID of cookies.signed,  user ID.  So we'll come back to this in a little bit, but notice that it's cookies.signed, not session  ID.  So you don't have a session, but you do have cookies.  But since you have cookies, this could be in application controller if you're using,  like, a password, what you might do here.  So you would say, on line nine, cookies.signed, user ID is the user.id, and then on line five,  user findbyid's cookie.signed.  So you would switch from a session of user ID to cookies.signed of user ID.  I have a device example later for, like, the 90% of you who are wondering how to do this  with device.  So just hold on.  Let's talk about broadcasting.  So here I am using an action job to broadcast out the message to a JavaScript channel.  That'll send it up to the client.  So line six shows how to send it out.  So action cable.server.broadcast.  And then you give it a stream name and a hash.  So that hash would be the data that's going to be sent up.  That hash, you'll send it out as a hash.  It'll be received later in JavaScript as just a JavaScript object.  So this can be done anywhere, rate tasks, action jobs, controllers, model callbacks,  like anywhere in Rails has the ability to send out broadcasts, which is cool.  Also here I am doing the action controller.render, which is super awesome.  I think we're going to look back and see this as a very cool feature.  We'll go into that a little bit later.  Finally, you've got a subscription.  That's the JavaScript listener.  So when you create a subscription, it listens for all broadcasts for that channel.  So when the server broadcasts a message, it calls the received function, and that's where  you get your data.  So app.product is equal to app.cable.subscriptions.create.  And then you've got connected if you wanted to log that, disconnected if you wanted to  log that, and then received.  Received is sort of magic function, like that's what you need to call it.  That's when I broadcast from Rails up to JavaScript, that's what it calls.  And then you can do whatever you want with that data.  So here it's like data.product.id, but you can do whatever you needed to with this.  Here I'm saying let's find a product with the data.product.id of that ID and replace  it with a template.  So in this example, Rails is sending up the updated template for that product, and it's  just going to re-render right on top of it.  So let's walk through a standard action-cable interaction.  We'll start on the client side this time.  So by requiring action-cable, the cable cables to the Rails cable.  The browser then upgrades the cable connection to a WebSockets connection.  In the subscription, subscribed, connection connects.  And then to start listening for updates, you create a subscription with the received function.  That's what gets called.  So you can have many subscriptions in like many different places.  So what I mean by that is like app.channel, or app.cable.channel is this global thing.  So anywhere in JavaScript, you can access it.  So it could be your standard jQuery style, like listen for the event, or it could be  in React, like on the component in mount.  It could be in Angular.  It could be in Ember, on the set of controller.  Like any of these things can create a subscription and start using it.  So the subscription calls receive data when the subscription receives the data.  And that's it.  This is a Tron DeLorean.  I love Giffy.  So let's talk about patterns, like how we might set up for specific scenarios.  First, we'll talk about data updates.  So this is what it basically called collaboration.  But we can keep data in sync across tabs and users.  Sorry, not sorry.  So here we're looking at a pretty sweet browser setup.  It's got four tabs.  Up in the top right is the cart button.  That's a cart.  And we're selling tennis balls.  So those are tennis balls.  So that's your add to cart button.  I'm telling you, I'm pretty good.  But we're looking at four tabs because the middle two are on this site.  I don't know about you, but I'll go to Amazon and I'll do a search and it'll be like open  a new tab, open a new tab, open a new tab, open a new tab.  So I've got all of these open that I can flip through.  So if we add the cart on tab three, there's now one item in our cart.  But what about tab two?  It's typically not updated, right?  It's typically sort of it was, it'll show whatever was on that page as it loaded, but  not update itself, even though tab three updated itself.  It's not a huge thing, but what could we do to make this experience even slightly better?  So after the update happens, like in a controller or a job, you would broadcast a message.  The message can be data or it can be a Rails rendered template partial for you to update  with.  So this is the application controller.render.  So when I heard about application controller.render, I was basically like dancing the dance of joy.  So I've wanted this for so long.  The use case that I have for it is like creating reports.  So each month you'd want to create reports using whenever like a rake task and I'd want  to be able to use Wicked PDF to take HTML and convert it to PDF.  But it's not in a request cycle.  Like there is no request at that point.  So I had to do some like weird things, man, to like make that work.  But now it's easy.  You can just render an entire part, like a partial or a page.  You just hand it what locals you think this partial should have.  And they can be the same partial that you would use if you're like rendering your cart  or rendering out like a list of products.  I think it's going to be really awesome.  Here's how you could send out new cart partial to a current user only.  So you would broadcast to cart underscore current underscore user dot id.  So that would be scoped to just that user.  It would only go out on channels where that's subscribed, where you are that user.  So it's not that like the cable has all of the message.  It only has the message that it's actually subscribed to.  Here's the channel setup that would set up the stream.  So the channel would have subscribed and it would say streamed from cart underscore current  user.  Both these have been on the rail side.  So you've got broadcast to a channel.  And then JavaScript listens for that message.  So app cable subscriptions create cart underscore the current user ID.  He receives the data, replaces up the template.  Now Taps, who's happy?  We can also sort of hook into existing JavaScript libraries.  So maybe like you change a graph, update some options, can action cable be used to send  messages to the server?  Did something happen on the client?  Yes, it can.  So if you want to send a message to the server and then out to other clients, here's an  example for you.  So the example that I've got is like an HTML5 slides presenter mode.  So imagine that this is like reveal.js and we're all looking at it.  So you'd be looking at sort of like a read-only state, I'd have the ability to change the  slides.  And so what we would do is you'd add an event listener.  So you'd add an event listener to the slide change.  Now that's reveal specific.  So all of this is predicated on your JavaScript libraries having hooks that you can tie into.  But what gets sent from client to server when you say app.slidesnotification.advancedlide  and then you send it some data.  So the slides notification, that's the channel name.  And then you've got a channel method on advanced slide and then you send it the data.  So app.slidesnotification.advancedlide.  So advanced slide is the method that JavaScript will call to tell Rails that something happened.  So here we say app.slidesnotification is the app cable subscriptions.  So this is setting up the subscription, the previous slide was using it.  And so we say advanced slide.  So that's the function that we called when we said app.slidesnotification.advancedlide.  This does this.perform slides data.  Back on the Rails side, we have a slides channel.  So the streams from slides, that slides stream is the name of it.  So earlier, one, two, three.  So on line six here, we say perform, perform on slides and send it the data.  That slides becomes line three here, stream from slides and calls the line six slides  method.  So that is what comes in from JavaScript back to Rails.  So there's the broadcasting out and then the receiving message back.  So line six here, slides of data, we're, I'm just going to say, ifcurrentuser.admin,  admin, then action cable server broadcast, like broadcast it out again.  So what's interesting about this is messages go from client to server.  It's not peer to peer.  So like this makes sense when we think about it, but if I want to, if we were all on a  server, right, and I wanted to send a broadcast message out to all of you, I would not send  it directly to you, I would send it to the server and the server would send it to you.  And vice versa as you reply back.  So let's talk about collaboration.  So Trello, we talked about cards, like that's sort of what it would be.  Pivotal tracker, also the cards.  But then on GitHub, they do a neat thing where it's sort of complete with yes, no, you can  merge with, TrevCI comes back.  And then it updates all the other users viewing the page.  So this is a neat way that if I like click and merge, it's going to other people viewing  that page remove the ability to merge.  I think that that's the type of thing that we can very easily get huge wins out of with  action cable.  Finally asynchronous tasks.  So let's imagine that we have a system where we use mechanized to go out and fetch the  latest data.  This can take quite a bit of time so let's throw it in action cable job, or an action  job.  So the first thing we do is we generate a channel.  So each channel is going to have many streams based on like a UUID.  So each task that we do would have its own stream.  Then the JS posted up, give it the UUID that defines this job.  You would then create a subscription on that stream with that UUID for when messages come  back from Rails.  Maybe you have a site search job that performs search with a UUID.  So you have your stream that you broadcast, hey, I'm starting.  And then snip, you do all the stuff.  Then action cable server broadcast the stream that it's complete and along with the template.  And yeah, let's go ahead and discuss chat.  I read that there's this concept of iMessage on iPhones lock-in that people really love  seeing the bubble like when people are typing.  That it makes it feel sort of instant that you're connected to someone, like there's  an actual person that's actually typing right now.  So, oh, no, come back, come back.  So Aziz Ansari talks about this in modern romance.  So he asked someone out.  He saw the bubble, but then nothing.  Like the bubble just went away after a while.  He never got a reply in the sad face for Aziz.  But there's a sort of emotional thing with this.  Okay, so what would you do?  So you could listen for the key up event on a text area and then tell the server that  you're typing in, that you're typing in the conversation between you and Taylor Swift.  Then when the message is sent, you would stop the bubble and render the message text.  And maybe you would handle if you haven't typed in a while.  End of chat.  Let's talk about deployment.  So in-app mode is basically easy mode.  Think of it like you'd use Sidekick, not inline jobs.  So you're not doing Sidekick, it's just inline.  It works, but scalability is a thing.  So it can be done with only one server, no rescue Sidekick.  You can just say that everything is inline, everything is async.  So you would tell it, my QAdapter is async, and then my config, cable.yaml, async, async,  async.  Everything is done async, so that's just threaded.  It connects to slash cable on the Rails server.  And generally can work pretty well.  But if you've got more than one server, like you've got multiple dinos or whatever, then  you want to use Redis.  So you want to have server, server, Redis, and Redis will keep it in sync.  Otherwise, everything is just going to be in memory on each server, and that's no good.  Because if I connect to one server, that's a physical connection, you connect to another  dino, we have to have some way that I can actually pass messages back and forth.  Redis would be that.  Standalone is basically hard mode.  It's slightly more involved.  It's the same scale as sort of moving from async to sidekick.  You connect via Redis.  You've got to have an actual, like sockets.domain.io.  You've got to configure where it is so it knows how to connect more with our person.  So our green person connects to Rails.  Again, you're welcome.  When you broadcast it, it sends the message to Redis.  Action cable is listening for that, and then action cable sends it back out.  So quick likes and gotchas.  So what about miss messages?  I don't think we know yet what this story sounds like.  But I think we have to think about the idea that if I'm just sending update, update, update,  and you miss some messages, you go offline.  What does that look like?  In a physical, like it's a physical network connection to somewhere in the world.  So you want to use ActionJob.  Don't inline, like just broadcast it out in your controller because it would have to  send like somewhere along the world.  Don't wait for that.  Also no session, only cookies.  So here is your device version.  You're welcome.  You have to configure your request origins to allow or disallow people to be able to  connect to you.  Fun fact, don't leave a trailing slash in the host because it will not work.  And finally, the config.cable.yaml default setup, this is what gets generated.  The production URL for Redis is localhost.  You'll need to change that.  So where do you go from here?  So I've got a simple example that you can look at, J-O-O slash inventory cable.  There is also ActionCable examples.  These are very good.  There's documentation on this URL.  But so ActionCable is part of the Rails standard, just like ActionController.  Finally, I want to give some many thanks.  So thanks to DHH for having the idea, announcing it, making it happen.  This is very cool.  The 42 plus contributors on the ActionCable project, you all rule also.  And thanks to the IronYard for paying for me to come here, spend today with you.  So we're hiring developers across the US that want to learn how to teach, make the world  better, change lives, improve diversity.  So if you're interested, tweet at me, come talk to me.  I'm J-O-O.  Thank you very much.  Thank you.
ActionCable_for_Not-Another-Chat-App-Please_0_47.png ActionCable_for_Not-Another-Chat-App-Please_48_119.png ActionCable_for_Not-Another-Chat-App-Please_120_239.png ActionCable_for_Not-Another-Chat-App-Please_240_1283.png ActionCable_for_Not-Another-Chat-App-Please_1284_2255.png ActionCable_for_Not-Another-Chat-App-Please_2256_2459.png ActionCable_for_Not-Another-Chat-App-Please_2460_3107.png ActionCable_for_Not-Another-Chat-App-Please_3108_3479.png ActionCable_for_Not-Another-Chat-App-Please_3480_6095.png ActionCable_for_Not-Another-Chat-App-Please_6096_9551.png ActionCable_for_Not-Another-Chat-App-Please_9552_12035.png ActionCable_for_Not-Another-Chat-App-Please_12036_12815.png ActionCable_for_Not-Another-Chat-App-Please_12816_12875.png ActionCable_for_Not-Another-Chat-App-Please_12876_12959.png ActionCable_for_Not-Another-Chat-App-Please_12960_13355.png ActionCable_for_Not-Another-Chat-App-Please_13356_18863.png ActionCable_for_Not-Another-Chat-App-Please_18864_20747.png ActionCable_for_Not-Another-Chat-App-Please_20748_20927.png ActionCable_for_Not-Another-Chat-App-Please_20928_21683.png ActionCable_for_Not-Another-Chat-App-Please_21684_23567.png ActionCable_for_Not-Another-Chat-App-Please_23568_30707.png ActionCable_for_Not-Another-Chat-App-Please_30708_35819.png ActionCable_for_Not-Another-Chat-App-Please_35820_36203.png ActionCable_for_Not-Another-Chat-App-Please_36204_36779.png ActionCable_for_Not-Another-Chat-App-Please_36780_41255.png ActionCable_for_Not-Another-Chat-App-Please_41256_45323.png ActionCable_for_Not-Another-Chat-App-Please_45324_45719.png ActionCable_for_Not-Another-Chat-App-Please_45720_47339.png ActionCable_for_Not-Another-Chat-App-Please_47340_47435.png ActionCable_for_Not-Another-Chat-App-Please_47436_47675.png ActionCable_for_Not-Another-Chat-App-Please_47676_49175.png ActionCable_for_Not-Another-Chat-App-Please_49176_49415.png ActionCable_for_Not-Another-Chat-App-Please_49416_50039.png ActionCable_for_Not-Another-Chat-App-Please_50040_52475.png ActionCable_for_Not-Another-Chat-App-Please_52476_52895.png ActionCable_for_Not-Another-Chat-App-Please_52896_54347.png ActionCable_for_Not-Another-Chat-App-Please_54348_56147.png ActionCable_for_Not-Another-Chat-App-Please_56148_56939.png ActionCable_for_Not-Another-Chat-App-Please_56940_57683.png ActionCable_for_Not-Another-Chat-App-Please_57684_58451.png ActionCable_for_Not-Another-Chat-App-Please_58452_58655.png ActionCable_for_Not-Another-Chat-App-Please_58656_58775.png ActionCable_for_Not-Another-Chat-App-Please_58776_58925.png