Skip to content

Instantly share code, notes, and snippets.

@cha0s
Forked from brentjanderson/adapter.js
Created December 24, 2012 15:41
Show Gist options
  • Save cha0s/4369700 to your computer and use it in GitHub Desktop.
Save cha0s/4369700 to your computer and use it in GitHub Desktop.
// Requires ember.js and ember-data.js, socket.io
// Initializer for Models
window.Models = {};
var SOCKET = '/'; // Served off the root of our app
var ID = 'uuid';
var FIND = 'find';
var FIND_MANY = 'findMany';
var FIND_QUERY = 'findQuery';
var FIND_ALL = 'findAll';
var CREATE = 'create';
var CREATES = 'creates';
var UPDATE = 'update';
var UPDATES = 'updates';
var DELETE = 'delete';
var DELETES = 'deletes';
/**
* Implementation of WebSocket for DS.Store
* Shamelessly derived from https://github.com/emberjs/data/blob/master/README.md#adapter-api and http://stackoverflow.com/a/10557122/704939
*/
DS.SocketAdapter = DS.Adapter.extend({
socket: undefined,
requests: undefined,
generateUuid: function() {
var S4 = function (){
return Math.floor(
Math.random() * 0x10000 // 65536
).toString(16);
};
return (
S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4()
);
},
send: function(action, type, data, result) {
var dataSerialized = [];
if( Object.prototype.toString.call( data ) !== '[object Array]' ) {
data = [data];
}
data.forEach(function(el) {
var obj = el.serialize();
obj.id = el.id;
dataSerialized.pushObject(obj);
});
var request = {
"uuid": this.generateUuid(), // This UUID is strictly for tracking requests within EmberJS. It does not serve to identify any objects except this particular adapter transaction, and is disposed of when the server replies.
"action": action,
"type": type.modelType,
"data": dataSerialized
};
this.socket.emit('ember-data', request);
// Modify the request to store original hash of data
request.rawData = data;
request.rawType = type;
// So we have access to the original request upon a response from the server
this.get('requests')[request.uuid] = request;
return request;
},
find: function (store, type, id) {
this.send(FIND, type, id);
},
findMany: function (store, type, ids, query) {
this.send(FIND_MANY, type, ids);
},
findQuery: function (store, type, query, modelArray) {
this.send(FIND_QUERY, type, query, modelArray).modelArray = modelArray;
},
findAll: function (store, type) {
this.send(FIND_ALL, type);
},
createRecord: function(store, type, model) {
this.send(CREATE, type, model);
},
createRecords: function(store, type, array) {
this.send(CREATES, type, array.list);
},
updateRecord: function(store, type, model) {
this.send(UPDATE, type, model);
},
updateRecords: function(store, type, array) {
this.send(UPDATES, type, array.list);
},
deleteRecord: function(store, type, model) {
this.send(DELETE, type, model);
},
deleteRecords: function(store, type, array) {
this.send(DELETES, type, array.list);
},
init: function () {
this._super();
var context = this;
this.set('requests', {});
var ws = io.connect(location.protocol + '//' + location.host);
// For all standard socket.io client events, see https://github.com/LearnBoost/socket.io-client
ws.on('ember-data', function(event) {
var response = event;
var request = context.get('requests')[response.uuid];
var type = request.rawType;
var array = request.rawData;
var data = response.data;
var store = Models.store;
switch (request.action) {
case FIND:
App.store.load(type, response.data[0]);
break;
case FIND_MANY:
App.store.loadMany(type, response.data);
break;
case FIND_QUERY:
request.modelArray.load(response.data);
break;
case FIND_ALL:
App.store.loadMany(type, response.data);
break;
case CREATE:
store.didSaveRecord(model, data);
break;
case CREATES:
store.didSaveRecords(array, data);
break;
case UPDATE:
store.didSaveRecord(model, data);
break;
case UPDATES:
store.didSaveRecords(array);
break;
case DELETE:
store.didSaveRecord(model);
break;
case DELETES:
store.didSaveRecords(array);
break;
default:
throw('Unknown Request: ' + request.action);
}
// Cleanup
context.get('requests')[response.uuid] = undefined;
});
ws.on('disconnect',function () {
});
this.set('socket', ws);
}
});
// Create ember-data datastore and define our adapter
Models.store = DS.Store.create({
revision: 10,
adapter: DS.SocketAdapter.create({})
});
/**
* Module dependencies.
*/
var express = require('express')
, http = require('http')
, path = require('path');
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', function(req, res) {
res.render('index');
});
var server = app.listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
/** Socket.IO server implementation **/
var io = require('socket.io').listen(server);
var modelActions = function(data) {
var socketActions = {
FIND: 'find',
FIND_MANY: 'findMany',
FIND_QUERY: 'findQuery',
FIND_ALL: 'findAll',
CREATE: 'create',
CREATES: 'creates',
UPDATE: 'update',
UPDATES: 'updates',
DELETE: 'delete',
DELETES: 'deletes'
};
var results = [];
for (var i = 0; i < data.data.length; i++) {
var obj = data.data[i];
switch(data.action) {
case socketActions.CREATE:
case socketActions.CREATES:
results.push(function(callback) {
models[data.type].create(obj, function(err, newModel) {
if (err) {
callback(err,null);
} else {
callback(null,newModel);
}
});
});
break;
case socketActions.UPDATE:
case socketActions.UPDATES:
results.push(function(callback) {
models[data.type].update({_id: obj.id}, { $set: obj}, callback);
});
break;
case socketActions.DELETE:
case socketActions.DELETES:
results.push(function(callback) {
models[data.type].remove({ _id: obj.id}, function(err) {
callback(err, null);
});
});
break;
case socketActions.FIND:
case socketActions.FIND_MANY:
case socketActions.FIND_QUERY:
case socketActions.FIND_ALL:
default:
throw "Unknown action " + data.action;
break;
}
}
return results;
}
var async = require('async');
io.sockets.on('connection', function(socket) {
socket.on('ember-data', function(data) {
var actions = modelActions(data);
async.parallel(actions, function(err, results) {
console.log('Parallel processing complete');
if (err) {
console.warn(err);
}
for (var i = 0; i < data.data.length; i++) {
if (results[i]) {
data.data[i].id = results[i]._id;
}
}
var response = {
uuid: data.uuid,
action: data.action,
type: data.type,
data: data.data
};
socket.emit('ember-data', response);
});
});
});
{
"name": "ember-socket.io-adapter",
"version": "0.0.1",
"private": false,
"scripts": {
"start": "node app"
},
"dependencies": {
"socket.io": "*",
"express": "3.x",
"mongoose": "3.5.x",
"async": "0.1.22"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment