UNPKG

spincycle

Version:

A reactive message router and object manager that lets clients subscribe to object property changes on the server

620 lines (578 loc) 20.1 kB
// Generated by CoffeeScript 1.9.3 (function() { var $q, Chillman, debug, opts, spinpolymer, uuid, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; $q = Q; opts = { max: 1000, maxAgeInMilliseconds: 1000 * 60 * 60 * 24 * 4 }; debug = true; uuid = UUID4; Chillman = (function() { function Chillman() {} Chillman.underwayCache = new LRUCache(); Chillman.callbackCache = new LRUCache(); Chillman.lookup = function(key, type, resolveFunc) { var callbacks, q, underway; q = $q.defer(); underway = Chillman.underwayCache.get(key + '_' + type); if (underway) { callbacks = Chillman.callbackCache.get(key + '_' + type) || []; callbacks.push(q); Chillman.callbackCache.set(key + '_' + type, callbacks); } else { Chillman.underwayCache.set(key + '_' + type, true); Chillman._doLookup(key, type, resolveFunc, q); } return q.promise; }; Chillman._doLookup = function(key, type, resolveFunc, q) { return resolveFunc(key, type).then(function(result) { var callbacks, cbcount; Chillman.underwayCache.remove(key + '_' + type); callbacks = Chillman.callbackCache.get(key + '_' + type) || []; cbcount = callbacks.length; callbacks.forEach(function(_q) { _q.resolve(result); if (--cbcount === 0) { return Chillman.callbackCache.remove(key + '_' + type); } }); return q.resolve(result); }); }; return Chillman; })(); spinpolymer = (function() { function spinpolymer(dbUrl) { this.dbUrl = dbUrl; this.flattenModel = bind(this.flattenModel, this); this.listTargets = bind(this.listTargets, this); this.getModelFor = bind(this.getModelFor, this); this.emitMessage = bind(this.emitMessage, this); this._deRegisterObjectsSubscriber = bind(this._deRegisterObjectsSubscriber, this); this.deRegisterObjectsSubscriber = bind(this.deRegisterObjectsSubscriber, this); this._registerObjectSubscriber = bind(this._registerObjectSubscriber, this); this.registerObjectSubscriber = bind(this.registerObjectSubscriber, this); this._registerPopulationSubscriber = bind(this._registerPopulationSubscriber, this); this.registerPopulationChangeSubscriber = bind(this.registerPopulationChangeSubscriber, this); this._deRegisterPopulationChangesSubscriber = bind(this._deRegisterPopulationChangesSubscriber, this); this.deRegisterPopulationChangesSubscriber = bind(this.deRegisterPopulationChangesSubscriber, this); this.registerListener = bind(this.registerListener, this); this.hasSeenThisMessage = bind(this.hasSeenThisMessage, this); this.setup = bind(this.setup, this); this._emit = bind(this._emit, this); this.emit = bind(this.emit, this); this._doGet = bind(this._doGet, this); this.open = false; this.subscribers = {}; this.objsubscribers = []; this.popsubscribers = {}; this.populationsubscribers = {}; this.objectsSubscribedTo = []; this.onsubscribers = {}; this.outstandingMessages = []; this.modelcache = []; this.seenMessages = []; this.sessionId = null; this.objects = new LRUCache(opts); this.failure = false; this.failureMessage = ''; this.savedMessagesInCaseOfRetries = new LRUCache({ max: 1000, maxAgeInMilliseconds: 5000 }); if (debug) { console.log('polymer-spincycle dbUrl = ' + this.dbUrl); } this.subscribers['OBJECT_UPDATE'] = [ (function(_this) { return function(obj) { var k, o, objsubs, prop, results, v, val; objsubs = _this.objsubscribers[obj.id] || []; results = []; for (k in objsubs) { v = objsubs[k]; if (!_this.objects.get(obj.id)) { _this.objects.set(obj.id, obj); } else { o = _this.objects.get(obj.id); for (prop in obj) { val = obj[prop]; o[prop] = val; } } results.push(v(obj)); } return results; }; })(this) ]; this.subscribers['POPULATION_UPDATE'] = [ (function(_this) { return function(update) { var k, obj, objsubs, results, v; obj = update.added || update.removed; if (obj) { objsubs = _this.populationsubscribers[obj.type] || {}; results = []; for (k in objsubs) { v = objsubs[k]; if (v.cb) { results.push(v.cb(update)); } else { results.push(void 0); } } return results; } }; })(this) ]; this.setup(); } spinpolymer.prototype.on = function(id, type, cb, onlyupdates) { if (typeof id === 'object') { xyzzy(); } if (!onlyupdates) { this.get(type, id).then(function(o) { return cb(o); }); } return this._registerObjectSubscriber({ id: id, type: type, cb: cb }); }; spinpolymer.prototype.get = function(type, id) { var d, o; if (typeof id === 'object') { xyzzy(); } d = $q.defer(); o = this.objects.get(id); if (!o) { Chillman.lookup(id, type, this._doGet).then(function(oo) { return d.resolve(oo); }); } else { d.resolve(o); } return d.promise; }; spinpolymer.prototype._doGet = function(k, t) { var dd; dd = $q.defer(); this.emitMessage({ target: '_get' + t, type: t, obj: { id: k, type: t } }).then((function(_this) { return function(_oo) { _this.objects.set(_oo.id, _oo); return dd.resolve(_oo); }; })(this)); return dd.promise; }; spinpolymer.prototype.save = function(o) { this.objects.set(o.id, o); return this.emitMessage({ target: '_update' + o.type, obj: o }).then(function(sres) { return console.log('saved obj result: ' + sres); }); }; spinpolymer.prototype.failed = function(msg) { console.log('spinclient message failed!! ' + JSON.toString(msg)); if (this.onFailure) { return this.onFailure(msg.info); } }; spinpolymer.prototype.setSessionId = function(id) { if (id) { console.log('++++++++++++++++++++++++++++++++++++++ spinclient setting session id to ' + id); return this.sessionId = id; } }; spinpolymer.prototype.dumpOutstanding = function() { console.log('-------------------------------- ' + this.outstandingMessages.length + ' outstanding messages ---------------------------------'); this.outstandingMessages.forEach(function(os) { return console.log(os.messageId + ' -> ' + os.target + ' - ' + os.d); }); return console.log('-----------------------------------------------------------------------------------------'); }; spinpolymer.prototype.emit = function(message) { return this._emit(message); }; spinpolymer.prototype._emit = function(message) { this.savedMessagesInCaseOfRetries.set(message.messageId, message); return this.socket.emit('message', JSON.stringify(message)); }; spinpolymer.prototype.setup = function() { console.log('.....connecting to "' + this.dbUrl + "'"); this.socket = io(this.dbUrl); this.socket.on('connect', (function(_this) { return function() { return _this.emit({ target: 'listcommands' }); }; })(this)); return this.socket.on('message', (function(_this) { return function(reply) { var detail, i, index, info, isNew, isPopulationUpdate, message, oldmsg, status, subs; status = reply.status; message = reply.payload; info = reply.info; isNew = !_this.hasSeenThisMessage(reply.messageId); isPopulationUpdate = reply.info === 'POPULATION_UPDATE'; if (info === 'list of available targets') { console.log('Spincycle server channel is up and awake'); return _this.open = true; } else { if (message && message.error && message.error === 'ERRCHILLMAN') { oldmsg = _this.savedMessagesInCaseOfRetries[reply.messageId]; if (oldmsg) { console.log('got ERRCHILLMAN from spinycle service, preparing to retry sending message...'); return setTimeout(function() { console.log('resending message ' + oldmsg.messageId + ' due to target endpoint not open yet'); return _this.emit(oldmsg); }, 250); } } else if (isNew) { _this.savedMessagesInCaseOfRetries.remove(reply.messageId); if (reply.messageId && reply.messageId !== 'undefined') { _this.seenMessages.push(reply.messageId); } if (_this.seenMessages.length > 10) { _this.seenMessages.shift(); } index = -1; if (reply.messageId) { i = 0; while (i < _this.outstandingMessages.length) { index = i; detail = _this.outstandingMessages[i]; if (detail && !detail.delivered && detail.messageId === reply.messageId) { if (reply.status === 'FAILURE' || reply.status === 'NOT_ALLOWED') { console.log('spinclient message FAILURE'); console.dir(reply); _this.failure = true; _this.failureMessage = reply.info; console.log('--- initial message was'); console.dir(detail); _this.failed(reply); detail.d.reject(reply); break; } else { detail.d.resolve(message); break; } detail.delivered = true; } i++; } if (index > -1) { return _this.outstandingMessages.splice(index, 1); } } else { subs = _this.subscribers[info]; if (subs) { return subs.forEach(function(listener) { return listener(message); }); } else { if (debug) { console.log('no subscribers for message ' + message); } if (debug) { return console.dir(reply); } } } } else { if (debug) { return console.log('-- skipped resent message ' + reply.messageId); } } } }; })(this)); }; spinpolymer.prototype.hasSeenThisMessage = function(messageId) { return this.seenMessages.some(function(mid) { return messageId === mid; }); }; spinpolymer.prototype.registerListener = function(detail) { var subs; subs = this.subscribers[detail.message] || []; subs.push(detail.callback); return this.subscribers[detail.message] = subs; }; spinpolymer.prototype.deRegisterPopulationChangesSubscriber = function(detail) { var count, j, k, len, localsubs, sid, type, v; sid = detail.listenerid; type = detail.type; localsubs = this.populationsubscribers[type]; if (localsubs[sid]) { console.log('deregistering local updates for model type ' + type); delete localsubs[sid]; count = 0; for (v = j = 0, len = localsubs.length; j < len; v = ++j) { k = localsubs[v]; count++; } if (count === 1) { return this._deRegisterPopulationChangesSubscriber('remotesid', type); } } }; spinpolymer.prototype._deRegisterPopulationChangesSubscriber = function(sid, type) { var subs; subs = this.popsubscribers[type] || []; if (subs && subs[sid]) { delete subs[sid]; this.popsubscribers[type] = subs; return this.emitMessage({ target: 'deRegisterForPopulationChangesFor', type: type, listenerid: sid }).then(function(reply) { return console.log('deregistering server updates for population changes for ' + type); }); } }; spinpolymer.prototype.registerPopulationChangeSubscriber = function(detail) { var d, localsubs, sid; d = $q.defer(); sid = uuid.generate(); localsubs = this.populationsubscribers[detail.type]; if (!localsubs) { localsubs = {}; this._registerPopulationSubscriber({ type: detail.type, cb: (function(_this) { return function(updatedobj) { var k, lsubs, results, v; lsubs = _this.populationsubscribers[detail.type]; results = []; for (k in lsubs) { v = lsubs[k]; if (v.cb) { results.push(v.cb(updatedobj)); } else { results.push(void 0); } } return results; }; })(this) }).then((function(_this) { return function(remotesid) { localsubs['remotesid'] = remotesid; localsubs[sid] = detail; _this.populationsubscribers[detail.type] = localsubs; return d.resolve(sid); }; })(this), (function(_this) { return function(rejection) { console.log('spinpolymer registerPopulationSubscriber rejection: ' + rejection); return console.dir(rejection); }; })(this)); } else { localsubs[sid] = detail; } return d.promise; }; spinpolymer.prototype._registerPopulationSubscriber = function(detail) { var d, subs; d = $q.defer(); subs = this.popsubscribers[detail.type] || {}; this.emitMessage({ target: 'registerForPopulationChangesFor', type: detail.type }).then((function(_this) { return function(reply) { subs[reply] = detail.cb; _this.popsubscribers[detail.type] = subs; return d.resolve(reply); }; })(this), (function(_this) { return function(reply) { return _this.failed(reply); }; })(this)); return d.promise; }; spinpolymer.prototype.registerObjectSubscriber = function(detail) { var d, localsubs, sid; d = $q.defer(); sid = uuid.generate(); localsubs = this.objectsSubscribedTo[detail.id]; if (!localsubs) { localsubs = []; this._registerObjectSubscriber({ id: detail.id, type: detail.type, cb: (function(_this) { return function(updatedobj) { var k, lsubs, results, v; lsubs = _this.objectsSubscribedTo[detail.id]; results = []; for (k in lsubs) { v = lsubs[k]; if (v.cb) { results.push(v.cb(updatedobj)); } else { results.push(void 0); } } return results; }; })(this) }).then((function(_this) { return function(remotesid) { localsubs['remotesid'] = remotesid; localsubs[sid] = detail; _this.objectsSubscribedTo[detail.id] = localsubs; return d.resolve(sid); }; })(this), (function(_this) { return function(rejection) { console.log('spinpolymer registerObjectSubscriber rejection: ' + rejection); return console.dir(rejection); }; })(this)); } else { localsubs[sid] = detail; } return d.promise; }; spinpolymer.prototype._registerObjectSubscriber = function(detail) { var d, subs; d = $q.defer(); subs = this.objsubscribers[detail.id] || []; this.emitMessage({ target: 'registerForUpdatesOn', obj: { id: detail.id, type: detail.type } }).then((function(_this) { return function(reply) { subs[reply] = detail.cb; _this.objsubscribers[detail.id] = subs; return d.resolve(reply); }; })(this), (function(_this) { return function(reply) { return _this.failed(reply); }; })(this)); return d.promise; }; spinpolymer.prototype.deRegisterObjectsSubscriber = function(sid, o) { var count, j, k, len, localsubs, v; localsubs = this.objectsSubscribedTo[o.id] || []; if (localsubs[sid]) { delete localsubs[sid]; count = 0; for (v = j = 0, len = localsubs.length; j < len; v = ++j) { k = localsubs[v]; count++; } if (count === 1) { return this._deRegisterObjectsSubscriber('remotesid', o); } } }; spinpolymer.prototype._deRegisterObjectsSubscriber = function(sid, o) { var subs; subs = this.objsubscribers[o.id] || []; if (subs && subs[sid]) { delete subs[sid]; this.objsubscribers[o.id] = subs; return this.emitMessage({ target: 'deRegisterForUpdatesOn', id: o.id, type: o.type, listenerid: sid }).then(function(reply) {}); } }; spinpolymer.prototype.emitMessage = function(detail) { var d; d = $q.defer(); detail.messageId = uuid.generate(); detail.sessionId = detail.sessionId || this.sessionId; detail.d = d; this.outstandingMessages.push(detail); this.emit(detail); return d.promise; }; spinpolymer.prototype.getModelFor = function(type) { var d; d = $q.defer(); if (this.modelcache[type]) { d.resolve(this.modelcache[type]); } else { this.emitMessage({ target: 'getModelFor', modelname: type }).then((function(_this) { return function(model) { _this.modelcache[type] = model; return d.resolve(model); }; })(this), (function(_this) { return function(rejection) { console.log('+++++++++++++++++ spinpolymer getModelFor rejection: ' + rejection); return console.dir(rejection); }; })(this)); } return d.promise; }; spinpolymer.prototype.listTargets = function() { var d; d = $q.defer(); this.emitMessage({ target: 'listcommands' }).then(function(targets) { return d.resolve(targets); }, function(rejection) { console.log('spinpolymer listTargets rejection: ' + rejection); return console.dir(rejection); }); return d.promise; }; spinpolymer.prototype.flattenModel = function(model) { var k, rv, v; rv = {}; for (k in model) { v = model[k]; if (angular.isArray(v)) { rv[k] = v.map(function(e) { return e.id; }); } else { rv[k] = v; } } return rv; }; return spinpolymer; })(); window.SpinClient = spinpolymer; }).call(this); //# sourceMappingURL=spinclient.js.map