spincycle
Version:
A reactive message router and object manager that lets clients subscribe to object property changes on the server
187 lines (165 loc) • 6.59 kB
text/coffeescript
defer = require('node-promise').defer
uuid = require('node-uuid')
error = require('./Error').error
debug = process.env["DEBUG"]
class OStore
@objects: []
@types: []
@listeners: []
@objectsByType: []
@blackList: ['id', 'createdAt', 'createdBy', 'updatedAt', 'admin']
@updateQueue = []
@listObjectsByType: (type) =>
rv = []
if debug then console.log 'OStore::listObjectsByType called for type '+type
v = @objectsByType[type]
for kk,vv of v
if vv
rv.push vv
if debug then console.log 'adding '+vv.name
return rv
@listTypes: () =>
rv = []
console.log 'listTypes called'
console.dir @types
for k,v of @types
rv.push v
return rv
@storeObject: (obj, sendUpdates = true) =>
#if debug then console.log 'OStore.storeObject called for object '+obj.id
if obj
OStore.objects[obj.id] = obj
OStore.types[obj.type] = obj.type
objs = OStore.objectsByType[obj.type] or {}
objs[obj.id] = obj
OStore.objectsByType[obj.type] = objs
if debug then console.log 'storeObject storing '+obj.id+' with type '+obj.type
@sendUpdatesFor(obj, sendUpdates)
@getObject: (id, type) =>
q = defer()
hash = OStore.objectsByType[type] or {}
obj = hash[id]
if obj
#console.log 'getObject getting '+obj.id+' with type '+obj.type
else
#console.log '-- * -- getObject could not find object type '+type+' with id '+id
q.resolve(obj)
return q
@removeObject: (obj) =>
if obj and obj.id
delete OStore.objects[obj.id]
objs = @objectsByType[obj.type] or []
if objs[obj.id] then delete objs[obj.id]
OStore.objectsByType[obj.type] = obj
@updateObj = (record, force) ->
if debug then console.log '+ oStore.updateObj called for obj '+record.id+' force = '+force
#console.log 'updateObj '+record
#console.dir record
obj = OStore.objects[record.id]
if obj
whitelist = obj.getRecord() #FFS
delete whitelist.id
delete whitelist.type
diff = {}
changed = false;
record.modifiedAt = Date.now()
for p of whitelist
#if debug then console.log 'checking whitelist property '+p
for pp of record
#if debug then console.log ' comparing to incoming property '+pp
if pp is p
#if debug then console.log ' match!'
if pp not in OStore.blackList
if obj[pp] != record[pp] or (record[pp] and obj[pp].length and obj[pp].length != record[pp].length) or force == true
clean = @makeClean(record[pp])
if clean
diff[pp] = clean
changed = true
obj[pp] = clean
if debug then console.log '** updating property "'+pp+'" on '+obj.type+' id '+record.id+' to '+clean
#if debug then console.dir pp
#if debug then console.log '--------------------------------------------------------------------------'
#if debug then console.dir clean
OStore.objects[record.id] = obj
if OStore.anyoneIsListening(obj.id) or force
#console.log 'updateObj calling sendUpdates for '+record.id
if not changed then changed = force
OStore.sendUpdatesFor(obj, changed, force)
else
console.log 'OStore: tried to update an object which we did not have in cache!'
@makeClean: (property) ->
rv = ""
if property and property isnt null and property isnt "undefined" and property isnt "null"
if property.filter
rv = property.filter (item)-> item and item isnt null and item isnt "undefined" and item isnt "null"
else
rv = property
rv
@sendUpdatesFor: (obj, changed, force) =>
#if debug then console.log 'OStore.sendUpdatesFor called for obj '+obj.id+' type '+obj.type+' changed = '+changed+', force = '+force+', anyone is listening == '+OStore.anyoneIsListening(obj.id)
if (changed or force) and OStore.anyoneIsListening(obj.id)
#console.dir obj
#console.log 'adding obj to updateQueue..'
OStore.updateQueue.push obj
@sendAtInterval()
@sendAllUpdatesFor: (obj, changed) =>
sendobj = {id: obj.id, type:obj.type, list:[], toClient: () -> {id: obj.id, type:obj.type, list:sendobj.list}}
count = obj.list.length
obj.list.forEach (id) =>
OStore.getObject(id, obj.type).then (o) =>
#console.log 'sendAllUpdatesFor adding list object '+id
#if debug then console.dir o
sendobj.list.push o.toClient()
if --count == 0
if changed
if OStore.anyoneIsListening(sendobj.id) then OStore.updateQueue.push sendobj
@anyoneIsListening:(id)=>
rv = false
larr = OStore.listeners[id]
if larr
for l of larr
rv = true
rv
@addListenerFor:(id, type, cb) =>
#console.log 'OStore::addListenerFor called with type:'+type+' id '+id
list = OStore.listeners[id] or []
listenerId = uuid.v4()
#console.log 'listener id = '+listenerId
list[listenerId] = cb
OStore.listeners[id] = list
#if debug then console.log 'listeners list is now'
#if debug then console.dir OStore.listeners[id]
#@getObject(id, type).then((result) ->
# cb(result)
#, error)
#console.dir OStore.listeners
return listenerId
@removeListenerFor: (id, listenerId) =>
list = OStore.listeners[id] or []
delete list[listenerId]
tmp = []
for i, cb of list
tmp[i] = cb if cb
OStore.listeners[id] = tmp
if debug then console.log 'removing listener for object '+id
if debug then console.log 'listeners list is now'
if debug then console.dir OStore.listeners[id]
@sendAtInterval: () =>
#console.log '-------------------------------------------------------sendAtInterval called '
#console.dir OStore.updateQueue
if OStore.updateQueue.length > 0
if debug then console.log 'OStore.sendAtInterval queue length = '+OStore.updateQueue.length
l = OStore.updateQueue.length
count = 0
while count < l
count++
obj = OStore.updateQueue.shift()
#console.dir OStore.listeners
listeners = OStore.listeners[obj.id] or []
for lid of listeners
#console.log 'sending to listener '+lid+' -> '+listeners[lid]
listeners[lid](obj)
if debug then console.log 'queue length after send = '+OStore.updateQueue.length
setTimeout(@sendAtInterval,50)
@sendAtInterval()
module.exports = OStore