Skip to content

Instantly share code, notes, and snippets.

@tresbailey
Created July 20, 2012 17:16
Show Gist options
  • Save tresbailey/3151970 to your computer and use it in GitHub Desktop.
Save tresbailey/3151970 to your computer and use it in GitHub Desktop.
Chat App using nodejs for server, socket.io for passing messages to/from the browser, coffeescript for some server-side files (for now), node-redis for persisting chats, express(node) for http wrapping, and jade for templating (for now), and backbone for
var NodeChatController = {
init: function() {
this.socket = io.connect('http://localhost');
var mysocket = this.socket;
this.model = new models.NodeChatModel();
this.view = new NodeChatView({model: this.model, socket: this.socket, el: $('#content')});
var view = this.view;
this.socket.on('message', function(msg) {
console.log('msg rcved'+msg);
view.msgReceived($.parseJSON(msg))
});
this.view.render();
return this;
}
};
$(document).ready(function () {
window.app = NodeChatController.init();
});
!!! 5
html(lang="en")
head
title nodechat
script(type="text/javascript", src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js")
script(type="text/javascript", src="lib/underscore.js")
script(type="text/javascript", src="lib/backbone.js")
script(type="text/javascript", src="/socket.io/socket.io.js")
script(type="text/javascript", src="models.js")
script(type="text/javascript", src="views/views.js")
script(type="text/javascript", src="controllers/controller.js")
body
#heading
h1 nodechat
#content
p
| connected clients
span#client_count 0
p
| Fun Chat Messages
ul#chat_list
form(method="post", action="#", onsubmit="return false;")#messageForm
p
label Name:
input(type="text", name="user_name")
p
label Message:
input(type="text", name="message")
input(type="submit", value="send")
#footer
var ChatView = Backbone.View.extend({
tagName: 'li',
initialize: function(options) {
_.bindAll(this, 'render');
this.model.bind('all', this.render);
},
render: function() {
$(this.el).html( this.model.get("name") + ": " + this.model.get("text"));
return this;
}
});
var ClientCountView = Backbone.View.extend({
initialize: function(options) {
_.bindAll(this, 'render');
this.model.bind('all', this.render);
},
render: function() {
$(this.el).html(" -> " + this.model.get('clients'));
return this;
}
});
var NodeChatView = Backbone.View.extend({
initialize: function(options) {
this.model.chats.bind('add', this.addChat);
this.socket = options.socket;
this.clientCountView = new ClientCountView({ model: new models.ClientCountModel(),
el: $('#client_count')
});
},
events: {
"submit #messageForm": "sendMessage"
},
addChat: function(chat) {
var view = new ChatView({ model: chat });
$('#chat_list').append(view.render().el);
},
msgReceived: function(message) {
switch(message.event) {
case 'initial':
this.model.mport(message.data);
break;
case 'chat':
var newChatEntry = new models.ChatEntry();
newChatEntry.mport(message.data);
this.model.chats.add(newChatEntry);
break;
case 'update':
this.clientCountView.model.mport(message.data);
break;
}
},
sendMessage: function(e) {
var inputField = $('input[name=message]');
var nameField = $('input[name=user_name]');
console.log(inputField);
var chatEntry = new models.ChatEntry({ name: nameField.val(),
text: inputField.val()
});
this.socket.send(JSON.stringify(chatEntry.xport()));
inputField.val('');
}
});
app = require('express').createServer()
jade = require 'jade'
_ = require('underscore')._
Backbone = require 'backbone'
redis = require 'redis'
rc = redis.createClient()
models = require './models'
$ = require('jquery')
app.set 'view engine', 'jade'
app.set 'view options', {layout: false}
app.listen 4000
app.get '/*.(js|css)', (req, res) ->
res.sendfile "./#{ req.url}"
app.get '/', (req, res) ->
res.render 'index'
activeClients = 0
clientCountModel = new models.ClientCountModel {clients: activeClients}
nodeChatModel = new models.NodeChatModel()
rc.lrange 'chatentries', -10, -1, (err, data) ->
if data
_.each data, (jsonChat) ->
chat = new models.ChatEntry()
chat.mport jsonChat
nodeChatModel.chats.add chat
console.log "Revived #{ JSON.stringify nodeChatModel.chats } chats"
else
console.log "No data returned for key"
io = require('socket.io').listen app
io.sockets.on 'connection', (client) ->
console.log "client count mode: #{ JSON.stringify clientCountModel }"
clientCountModel.incrClients()
console.log "client count mode: #{ JSON.stringify clientCountModel }"
client.on 'disconnect', () -> clientDisconnect(client)
client.on 'message', (msg) -> chatMessage(client, io, msg)
console.log "all clients are #{ JSON.stringify nodeChatModel.xport() }"
client.send JSON.stringify({
event: 'initial',
data: nodeChatModel.xport()
})
io.sockets.send JSON.stringify({
event: 'update',
data: clientCountModel.xport()
})
chatMessage = (client, socket, msg) ->
chat = new models.ChatEntry()
console.log "importing chat #{JSON.parse msg }"
chat.mport JSON.parse(msg)
console.log "imported chat #{ chat.get('name') }"
rc.incr 'next.chatentry.id', (err, newId) ->
chat.set {id: newId}
nodeChatModel.chats.add chat
console.log "(#{ client.sessionId}) #{ chat.get('id') } #{ chat.get('name') }: #{ chat.get('text') }"
rc.lpush 'chatentries', chat.xport(), redis.print
rc.bgsave()
console.log "socket increment with #{ JSON.stringify chat }"
emission = { event: 'chat', data: chat.xport() }
console.log "emitting #{ JSON.stringify emission }"
client.broadcast.send JSON.stringify(emission)
clientDisconnect = (client) ->
activeClients -= 1
io.sockets.send JSON.stringify({
event: 'update',
data: clientCountModel.xport()
})
// Generated by CoffeeScript 1.3.3
(function() {
(function() {
_this = this;
var server = false, models;
if (typeof exports !== "undefined" && exports !== null) {
_ = require('underscore')._;
Backbone = require('backbone');
$ = require('jquery');
models = exports;
server = true;
} else {
models = this.models = {};
}
models.ChatEntry = Backbone.Model.extend({});
models.ClientCountModel = Backbone.Model.extend({
defaults: {
clients: 5
},
updateClients: function(clients) {
return this.set({
clients: clients
});
},
incrClients: function() {
clients = this.get('clients');
return this.set({
clients: ++clients
});
}
});
models.NodeChatModel = Backbone.Model.extend({
defaults: {
clientId: 0
},
initialize: function() {
return this.chats = new models.ChatCollection();
}
});
models.ChatCollection = Backbone.Collection.extend({
model: models.ChatEntry
});
Backbone.Model.prototype.xport = function(opt) {
var process, result, settings;
result = {};
settings = _({
recurse: true
}).extend(opt || {});
process = function(targetObj, source) {
targetObj.id = source.id || null;
targetObj.cid = source.cid || null;
targetObj.attrs = source;
return _.each(source, function(value, key) {
if (settings.recurse) {
if (key !== 'collection' && source[key] instanceof Backbone.Collection) {
targetObj.collections = targetObj.collections || {};
targetObj.collections[key] = {};
targetObj.collections[key].models = [] || null;
targetObj.collections[key].id = source[key].id || null;
return _.each(source[key].models, function(mod, index) {
return process(targetObj.collections[key].models[index] = {}, mod);
});
} else if (source[key] instanceof Backbone.Model) {
targetObj.models = targetObj.models || {};
return process(targetObj.models[key] = {}, value);
}
}
});
};
process(result, this);
return result;
};
Backbone.Model.prototype.mport = function(data, silent) {
var process;
process = function(targetObj, data) {
targetObj.id = data.id || null;
console.log('mport.process' + JSON.stringify(data.attrs));
targetObj.set(data.attrs, {
silent: silent
});
if (data.collections) {
_.each(data.collections, function(collection, name) {
targetObj[name].id = collection.id;
return _.each(collection.models, function(modelData, index) {
var newObj;
newObj = targetObj[name].add({}, {silent: silent});
return process(newObj, modelData);
});
});
}
if ($.type(data.models) !== "undefined") {
return _.each(data.models, function(modelData, name) {
return process(targetObj[name], modelData);
});
}
};
console.log('mport' + data);
if ($.type(data) == "string") {
process(this, JSON.parse(data));
} else {
process(this, data);
}
return this;
};
})();
}).call(this);
@tresbailey
Copy link
Author

This is taken from http://fzysqr.com/2011/02/28/nodechat-js-using-node-js-backbone-js-socket-io-and-redis-to-make-a-real-time-chat-app/ and brings it more up to date (plus makes it work). Some prerequisites to running it, though. You'll need to install coffeescript, which will also install nodejs. After that, you can use node's package manager (npm) to install some of the other js modules. For instance, do: npm install express to install the [email protected] module (which is broken for the latest release). You can use npm for getting local copies of jquery, backbone, etc. Anything you want to use on the client side that you install from npm should be copied from its location in the node_modules folder into a separate lib folder (see the imports on index.jade). Also, the jade and views.js files should be put into a views folder, as well as moving controllers.js into its own folder. No real reason for this, except thats just where i've got the includes looking for them in the jade template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment