& 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.