class: center, middle # Rails 4 Streaming API [http://theflyingdeveloper.com/rails_4_streaming_presentation]() ??? Rails 4 introduced a new streaming API. I'm going to talk about it. Right now. --- class: middle ``` class MyController < ActionController::Base include ActionController::Live def index 100.times { response.stream.write "hello world\n" } ensure response.stream.close end end ``` ??? Add `include ActionController::Live` to any controller. Bam! Now each of your controller methods can stream responses. Call `response.stream.write` when you want to write. Make sure to call `response.stream.close` when you're done. --- class: center, middle # That was easy ??? That's basically everything there is to know about the mechanics of streaming in Rails 4 --- class: center, middle # Let's do something useful with it # [http://github.com/davefp/crier]() ??? Crier is a project I started to learn more about ruby 2.0, Rails 4, and streaming. --- # What does it do? * Exposes an API for updating the status of a set of components * When status is updated, uses Redis to notify a pub-sub group * The streaming request handler subscribes to this group, and forwards events to the client via. Server Sent Events ??? We're going to be looking at Redis' pub-sub functionality. It allows us to subscribe to an event channel and register a callback where we can actually do stuff. In our case, we're simply going to forward the event to the client that's connected to our stream using Server Sent Events. --- class: center, middle # Server Sent Events (SSEs) ??? SSEs are a standard (if little known) method of sending information one-way over an HTTP stream. They're websockets' little brother. --- # SSE Formatting ``` data: "{\"id\":28,\"component_id\":2, \"event\":\"crashed\", \"from\":\"up\",\"to\":\"down\", \"created_at\":\"2013-07-23T15:48:14.181Z\", \"message\":null}" \n\n ``` ??? This is one of Crier's SSEs Any options are up top (we're not using any here), followed by a string of data. We're passing JSON here. Messages are delimited by a pair of newline characters --- # Generating SSEs ``` def stream response.headers['Content-Type'] = 'text/event-stream' redis = Redis.new redis.subscribe('crier:transitions') do |on| on.message do |event, data| response.stream.write "data: #{JSON.dump(data)}" response.stream.write "\n\n" end end ensure response.stream.close end ``` ??? The content type needs to be `text/event-stream`. We then subscribe to the named redis pubsub group. When an event is fired, we write it out to the stream. Make sure that the stream is closed when we're done. There will be an exception thrown when the client disconnects. --- # Publishing to Redis ``` class ComponentStatusTransition < ActiveRecord::Base belongs_to :component after_create :publish private def publish redis = Redis.new redis.publish "crier:transitions", self.to_json end end ``` Just so you can see the other side of things, here's the publishing code. It's in an `after_create` hook on the transition log model. --- class: center, middle # Demo ??? (Demo starting the server, curling for the stream, and then hitting the API with updates) --- # Listening for SSEs ```javascript jQuery(document).ready(function() { setTimeout(function() { var source = new EventSource('/stream'); source.addEventListener('update', function(e) { // Where the magic happens }); }, 1); }); ``` ??? On our page, we create a new EventSource pointed at the url we're exposing our streaming endpoint on, in this case `/stream`. We then register a callback function that fires whenever a message named 'update' arrives. --- class: center, middle # More Demo ??? (Demo the page updating auto-magically when an update is sent) --- class: inverse # Important Notes * Enable concurrent connections in dev mode * Use a multi-threaded app server. [Puma](http://puma.io/) is a good choice * IE doesn't support Server Sent Events, because it's shite. Use a good browser instead ??? These little things are easy to forget, but things will fall apart very quickly if they're not set up correctly. --- .center[ # Thanks! ] .pull-left[ ## References * [Is It Live?](http://tenderlovemaking.com/2012/07/30/is-it-live.html) - Aaron Patterson's intro to Rails 4 Streaming * [Crier](http://github.com/davefp/crier) - My streaming app * This slideshow was built using [Remark](http://remarkjs.com), which kicks butt. ] .pull-right[ ## Me * [The Flying Developer Blog](http://theflyingdeveloper.com) * [@davefp](http://twitter.com/davefp) ]