KEMBAR78
Node.js and Ruby | PDF
& Ruby


Saturday, April 17, 2010
@mbleigh

Saturday, April 17, 2010
Saturday, April 17, 2010
present.ly

Saturday, April 17, 2010
What’s Node?


Saturday, April 17, 2010
Evented I/O
                           for Javascript

Saturday, April 17, 2010
Taking JS Beyond
                     the Browser

Saturday, April 17, 2010
Runs on
                           Google’s V8

Saturday, April 17, 2010
var sys = require('sys'),
                     http = require('http');
            http.createServer(function (req, res) {
                  setTimeout(function () {
                           res.writeHead(200, {'Content-Type': 'text/plain'});
                           res.end('Hello Worldn');
                  }, 2000);
            }).listen(8000);
            sys.puts('Server running at http://127.0.0.1:8000/');




Saturday, April 17, 2010
Supported By Node

                      • HTTP, TCP
                      • File I/O
                      • Redis, Mongo, SQL (DBSlayer)

Saturday, April 17, 2010
Express: Node’s Sinatra


                      • RESTful DSL for Node webapps
                      • Cookies, sessions, caching, etc.
                      • expressjs.com

Saturday, April 17, 2010
require.paths.unshift('path/to/express/lib')
            require('express')


            get('/', function(){
                   this.redirect('/hello/world')
            })


            get('/hello/world', function(){
                   return 'Hello World'
            })


            run()



Saturday, April 17, 2010
Why Node?


Saturday, April 17, 2010
Generally Speedy


Saturday, April 17, 2010
> summary(node1$ttime)
                Min. 1st Qu.    Median        Mean   3rd Qu.     Max.
              0.0000   0.0000   1.0000      0.7437    1.0000 106.0000

            > summary(thin1$ttime)
               Min. 1st Qu. Median        Mean 3rd Qu.     Max.
              0.000   1.000   1.000      1.122   1.000   74.000

            > summary(narwhal1$ttime)
               Min. 1st Qu. Median     Mean 3rd Qu.        Max.
              15.00   22.00   23.00   23.74   24.00       88.00

            > summary(v8cgi1$ttime)
               Min. 1st Qu. Median        Mean 3rd Qu.     Max.
              12.00   13.00   13.00      14.49   18.00    39.00




                           four.livejournal.com/1019177.html



Saturday, April 17, 2010
Great at
                           Concurrency

Saturday, April 17, 2010
Asynchronous
                            Everything

Saturday, April 17, 2010
bit.ly/nodejs-fstream
            function upload_file(req, res) {
              req.setBodyEncoding('binary');

              var stream = new multipart.Stream(req);
              stream.addListener('part', function(part) {
                part.addListener('body', function(chunk) {
                  var progress = (stream.bytesReceived / stream.bytesTotal *
            100).toFixed(2);
                  var mb = (stream.bytesTotal / 1024 / 1024).toFixed(1);

                           sys.print("Uploading "+mb+"mb ("+progress+"%)015");

                  // chunk could be appended to a file if the uploaded file needs to
            be saved
                });
              });
              stream.addListener('complete', function() {
                res.sendHeader(200, {'Content-Type': 'text/plain'});
                res.sendBody('Thanks for playing!');
                res.finish();
                sys.puts("n=> Done");
              });
            }


Saturday, April 17, 2010
Javascript is
                           great for this.

Saturday, April 17, 2010
Browser and server,
                      together at last


Saturday, April 17, 2010
Why Ruby?


Saturday, April 17, 2010
Are you at the
                            right conf?

Saturday, April 17, 2010
When to Node


Saturday, April 17, 2010
Real-Time
                           Applications

Saturday, April 17, 2010
WebSockets

                      • Persistent server connections
                      • Part of HTML5
                      • True real-time for web apps
                      • Deprecate to Flash Sockets
Saturday, April 17, 2010
Why Real-Time?

                      • Better on your server
                      • Better for your user
                      • You need more buzzwords

Saturday, April 17, 2010
var socket = new WebSocket("ws://www.websocket.org");

            socket.onopen = function(evt) { alert("Open."); };
            socket.onmessage = function(evt) { alert(evt.data); };
            socket.onclose = function(evt) { alert("Closed."); };

            socket.send("Hello Web Socket!");
            socket.close();




Saturday, April 17, 2010
Why JS for WebSockets?

                      • You already write your client
                           interaction code in Javascript
                      • Just an extension of that
                      • Same interface throughout
Saturday, April 17, 2010
Hazards of a
                           Young Tool

Saturday, April 17, 2010
node.websocket.js

                      • Framework-like approach
                      • Includes fun examples
                      • Node version headaches
                      • github.com/guille/node.websocket.js/

Saturday, April 17, 2010
Socket.IO

                      • Multi-transport socket for Node
                      • Comes with client JS library
                      • For production usage
                      • github.com/rosepad/socket.io-node

Saturday, April 17, 2010
node.ws.js
                      • Minimal Node WebSocket server
                      • Talk to your browser clients
                      • Javascript all the way down
                      • Mostly for experimentation
                      • github.com/ncr/node.ws.js
Saturday, April 17, 2010
How can we
                           actually use it?

Saturday, April 17, 2010
Redis is the Bridge

                      • Super-fast, super-awesome
                      • PUBSUB = WIN!
                      • Swiss-army knife for your app.

Saturday, April 17, 2010
Example

                      • Twitter-esque status streams
                      • Want to update web interface in
                           real time
                      • Rails, Node, Redis, and Chrome
Saturday, April 17, 2010
The Ruby Code


Saturday, April 17, 2010
class User < ActiveRecord::Base
              # Include default devise modules. Others available are:
              # :token_authenticatable, :lockable and :timeoutable
              devise :database_authenticatable, :registerable, :rememberable, :validatable

                # Setup accessible (or protected) attributes for your model
                attr_accessible :email, :password, :password_confirmation

                def follow(other_user)
                  Red.sadd "user:#{other_user.id}:followers", self.id
                  Red.sadd "user:#{self.id}:follows", other_user.id
                end

                def follower_ids
                  Red.smembers "user:#{self.id}:followers"
                end

                def follow_ids
                  Red.smembers "user:#{self.id}:follows"
                end

              def update(text)
                (follower_ids + [self.id]).each do |uid|
                  Red.lpush "user:#{uid}:timeline", text
                  Red.lpush "user:#{uid}:updates", text
                  Red.publish "user:#{uid}:timeline", text
                end
              end
            end




Saturday, April 17, 2010
def update(text)
                     (follower_ids + [self.id]).each do |uid|
                       Red.lpush "user:#{uid}:timeline", text
                       Red.lpush "user:#{uid}:updates", text
                       Red.publish "user:#{uid}:timeline", text
                     end
                   end




Saturday, April 17, 2010
The Node Code


Saturday, April 17, 2010
var sys = require("sys"),
                ws = require("./ws"),
                redis = require("./redis-client");

            var pubsub = redis.createClient();

            pubsub.stream.addListener('connect', function() {
              pubsub.subscribeTo("user:*:timeline", function(channel, data) {
                var uid = channel.toString().split(':')[1];

                if (clients[uid]) {
                  sys.debug("Writing " + data + " to " + uid)
                  clients[uid].write(data);
                } else {
                  sys.debug("User " + clients[uid] + " is not connected.");
                }
              });
            });

            ws.createServer(function (websocket) {
              var user_id = null;
              var websocket = websocket;

              websocket.addListener("connect", function (resource) {
                user_id = resource.match(/timeline/([0-9]+)$/i)[1]
                clients[user_id] = websocket;
              }).addListener("close", function() {
                sys.debug("User " + user_id + " disconnected.")
              });
            }).listen(8080);




Saturday, April 17, 2010
ws.createServer(function (websocket) {
                  var user_id = null;
                  var websocket = websocket;


                  websocket.addListener("connect", function (resource) {
                       user_id = resource.match(/timeline/([0-9]+)$/i)[1]
                       clients[user_id] = websocket;
                  }).addListener("close", function() {
                       sys.debug("User " + user_id + " disconnected.")
                  });
            }).listen(8080);




Saturday, April 17, 2010
pubsub.stream.addListener('connect', function() {
                 pubsub.subscribeTo("user:*:timeline", function(channel, data) {
                      var uid = channel.toString().split(':')[1];


                      if (clients[uid]) {
                           sys.debug("Writing " + data + " to " + uid)
                           clients[uid].write(data);
                      } else {
                           sys.debug("User " + clients[uid] + " is not connected.");
                      }
                 });
            });




Saturday, April 17, 2010
The Browser Code


Saturday, April 17, 2010
if ("WebSocket" in window) {
              var ws = new WebSocket("ws://127.0.0.1:8080/timeline/" +
            current_user);

                 ws.onmessage = function(evt) {
                   $('ul.timeline').prepend("<li>" + evt.data + "</li>");
                 }
            }




Saturday, April 17, 2010
How else can we
                       use Node?

Saturday, April 17, 2010
Asynchronous
                           Applications

Saturday, April 17, 2010
Push APIs

                      • Want to notify API subscribers in
                           real-time
                      • Utilize Webhooks
                      • Example: GitHub’s Service Hooks
Saturday, April 17, 2010
Saturday, April 17, 2010
File Transcoding


Saturday, April 17, 2010
Saturday, April 17, 2010
Online Gaming


Saturday, April 17, 2010
Ephemeral
                           Peer-to-Peer

Saturday, April 17, 2010
Wrapping up...


Saturday, April 17, 2010
Tip of the Iceberg

                      • Node’s libraries and use cases
                           are expanding rapidly
                      • Async means thinking differently
                      • Still at the early stages
Saturday, April 17, 2010
howtonode.org


Saturday, April 17, 2010
Node for Ruby?


Saturday, April 17, 2010
EventMachine


Saturday, April 17, 2010
Cramp


Saturday, April 17, 2010
Use what feels right.



Saturday, April 17, 2010
I don’t know
                           the half of it.

Saturday, April 17, 2010
Questions?


Saturday, April 17, 2010

Node.js and Ruby

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
    Evented I/O for Javascript Saturday, April 17, 2010
  • 7.
    Taking JS Beyond the Browser Saturday, April 17, 2010
  • 8.
    Runs on Google’s V8 Saturday, April 17, 2010
  • 9.
    var sys =require('sys'), http = require('http'); http.createServer(function (req, res) { setTimeout(function () { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Worldn'); }, 2000); }).listen(8000); sys.puts('Server running at http://127.0.0.1:8000/'); Saturday, April 17, 2010
  • 10.
    Supported By Node • HTTP, TCP • File I/O • Redis, Mongo, SQL (DBSlayer) Saturday, April 17, 2010
  • 11.
    Express: Node’s Sinatra • RESTful DSL for Node webapps • Cookies, sessions, caching, etc. • expressjs.com Saturday, April 17, 2010
  • 12.
    require.paths.unshift('path/to/express/lib') require('express') get('/', function(){ this.redirect('/hello/world') }) get('/hello/world', function(){ return 'Hello World' }) run() Saturday, April 17, 2010
  • 13.
  • 14.
  • 15.
    > summary(node1$ttime) Min. 1st Qu. Median Mean 3rd Qu. Max. 0.0000 0.0000 1.0000 0.7437 1.0000 106.0000 > summary(thin1$ttime) Min. 1st Qu. Median Mean 3rd Qu. Max. 0.000 1.000 1.000 1.122 1.000 74.000 > summary(narwhal1$ttime) Min. 1st Qu. Median Mean 3rd Qu. Max. 15.00 22.00 23.00 23.74 24.00 88.00 > summary(v8cgi1$ttime) Min. 1st Qu. Median Mean 3rd Qu. Max. 12.00 13.00 13.00 14.49 18.00 39.00 four.livejournal.com/1019177.html Saturday, April 17, 2010
  • 16.
    Great at Concurrency Saturday, April 17, 2010
  • 17.
    Asynchronous Everything Saturday, April 17, 2010
  • 18.
    bit.ly/nodejs-fstream function upload_file(req, res) { req.setBodyEncoding('binary'); var stream = new multipart.Stream(req); stream.addListener('part', function(part) { part.addListener('body', function(chunk) { var progress = (stream.bytesReceived / stream.bytesTotal * 100).toFixed(2); var mb = (stream.bytesTotal / 1024 / 1024).toFixed(1); sys.print("Uploading "+mb+"mb ("+progress+"%)015"); // chunk could be appended to a file if the uploaded file needs to be saved }); }); stream.addListener('complete', function() { res.sendHeader(200, {'Content-Type': 'text/plain'}); res.sendBody('Thanks for playing!'); res.finish(); sys.puts("n=> Done"); }); } Saturday, April 17, 2010
  • 19.
    Javascript is great for this. Saturday, April 17, 2010
  • 20.
    Browser and server, together at last Saturday, April 17, 2010
  • 21.
  • 22.
    Are you atthe right conf? Saturday, April 17, 2010
  • 23.
    When to Node Saturday,April 17, 2010
  • 24.
    Real-Time Applications Saturday, April 17, 2010
  • 25.
    WebSockets • Persistent server connections • Part of HTML5 • True real-time for web apps • Deprecate to Flash Sockets Saturday, April 17, 2010
  • 26.
    Why Real-Time? • Better on your server • Better for your user • You need more buzzwords Saturday, April 17, 2010
  • 27.
    var socket =new WebSocket("ws://www.websocket.org"); socket.onopen = function(evt) { alert("Open."); }; socket.onmessage = function(evt) { alert(evt.data); }; socket.onclose = function(evt) { alert("Closed."); }; socket.send("Hello Web Socket!"); socket.close(); Saturday, April 17, 2010
  • 28.
    Why JS forWebSockets? • You already write your client interaction code in Javascript • Just an extension of that • Same interface throughout Saturday, April 17, 2010
  • 29.
    Hazards of a Young Tool Saturday, April 17, 2010
  • 30.
    node.websocket.js • Framework-like approach • Includes fun examples • Node version headaches • github.com/guille/node.websocket.js/ Saturday, April 17, 2010
  • 31.
    Socket.IO • Multi-transport socket for Node • Comes with client JS library • For production usage • github.com/rosepad/socket.io-node Saturday, April 17, 2010
  • 32.
    node.ws.js • Minimal Node WebSocket server • Talk to your browser clients • Javascript all the way down • Mostly for experimentation • github.com/ncr/node.ws.js Saturday, April 17, 2010
  • 33.
    How can we actually use it? Saturday, April 17, 2010
  • 34.
    Redis is theBridge • Super-fast, super-awesome • PUBSUB = WIN! • Swiss-army knife for your app. Saturday, April 17, 2010
  • 35.
    Example • Twitter-esque status streams • Want to update web interface in real time • Rails, Node, Redis, and Chrome Saturday, April 17, 2010
  • 36.
  • 37.
    class User <ActiveRecord::Base # Include default devise modules. Others available are: # :token_authenticatable, :lockable and :timeoutable devise :database_authenticatable, :registerable, :rememberable, :validatable # Setup accessible (or protected) attributes for your model attr_accessible :email, :password, :password_confirmation def follow(other_user) Red.sadd "user:#{other_user.id}:followers", self.id Red.sadd "user:#{self.id}:follows", other_user.id end def follower_ids Red.smembers "user:#{self.id}:followers" end def follow_ids Red.smembers "user:#{self.id}:follows" end def update(text) (follower_ids + [self.id]).each do |uid| Red.lpush "user:#{uid}:timeline", text Red.lpush "user:#{uid}:updates", text Red.publish "user:#{uid}:timeline", text end end end Saturday, April 17, 2010
  • 38.
    def update(text) (follower_ids + [self.id]).each do |uid| Red.lpush "user:#{uid}:timeline", text Red.lpush "user:#{uid}:updates", text Red.publish "user:#{uid}:timeline", text end end Saturday, April 17, 2010
  • 39.
  • 40.
    var sys =require("sys"), ws = require("./ws"), redis = require("./redis-client"); var pubsub = redis.createClient(); pubsub.stream.addListener('connect', function() { pubsub.subscribeTo("user:*:timeline", function(channel, data) { var uid = channel.toString().split(':')[1]; if (clients[uid]) { sys.debug("Writing " + data + " to " + uid) clients[uid].write(data); } else { sys.debug("User " + clients[uid] + " is not connected."); } }); }); ws.createServer(function (websocket) { var user_id = null; var websocket = websocket; websocket.addListener("connect", function (resource) { user_id = resource.match(/timeline/([0-9]+)$/i)[1] clients[user_id] = websocket; }).addListener("close", function() { sys.debug("User " + user_id + " disconnected.") }); }).listen(8080); Saturday, April 17, 2010
  • 41.
    ws.createServer(function (websocket) { var user_id = null; var websocket = websocket; websocket.addListener("connect", function (resource) { user_id = resource.match(/timeline/([0-9]+)$/i)[1] clients[user_id] = websocket; }).addListener("close", function() { sys.debug("User " + user_id + " disconnected.") }); }).listen(8080); Saturday, April 17, 2010
  • 42.
    pubsub.stream.addListener('connect', function() { pubsub.subscribeTo("user:*:timeline", function(channel, data) { var uid = channel.toString().split(':')[1]; if (clients[uid]) { sys.debug("Writing " + data + " to " + uid) clients[uid].write(data); } else { sys.debug("User " + clients[uid] + " is not connected."); } }); }); Saturday, April 17, 2010
  • 43.
  • 44.
    if ("WebSocket" inwindow) { var ws = new WebSocket("ws://127.0.0.1:8080/timeline/" + current_user); ws.onmessage = function(evt) { $('ul.timeline').prepend("<li>" + evt.data + "</li>"); } } Saturday, April 17, 2010
  • 45.
    How else canwe use Node? Saturday, April 17, 2010
  • 46.
    Asynchronous Applications Saturday, April 17, 2010
  • 47.
    Push APIs • Want to notify API subscribers in real-time • Utilize Webhooks • Example: GitHub’s Service Hooks Saturday, April 17, 2010
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
    Ephemeral Peer-to-Peer Saturday, April 17, 2010
  • 53.
  • 54.
    Tip of theIceberg • Node’s libraries and use cases are expanding rapidly • Async means thinking differently • Still at the early stages Saturday, April 17, 2010
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
    Use what feelsright. Saturday, April 17, 2010
  • 60.
    I don’t know the half of it. Saturday, April 17, 2010
  • 61.