UNPKG

nerms

Version:

NodeJS Entity Relationship Models

463 lines (321 loc) 10.2 kB
'use strict'; module.exports = new function() { var _data = { instance : this, expire : { entities : [], timer : null, interval : 1000 }, events : { types : { expire : [], destroy : [], create : [] }, fire : function(type, entity) { for(var i in _data.events.types[type]) { _data.events.types[type][i](entity); } } }, type : { event : function(type) { if(undefined === _data.events.types[type]) { throw 'Type "' + type + '" is not a known event type'; } }, entity : function(type) { if(undefined === _data.instance.entities[type]) { throw 'Entity "' + type + '" does not exists'; } } }, relations : {} }; //Entities _data.instance.entities = {}; //Models _data.instance.models = {}; //Type _data.instance.type = { add : function(type) { //Create model _data.instance.models[type.name] = new function() { var _entities = []; var _instance = this; _instance.push = function(entity) { if(entity instanceof _data.instance.entities[type.name]) { return _entities.push(entity); } throw 'Entity needs to be an instance of "' + type.name + '"'; } _instance.list = function() { return _entities; } _instance.find = {}; for(var i in type.properties) { (function(property) { _instance.find[property] = function(value, strict) { var found = []; for(var a in _entities) { if((strict && _entities[a][property].get() === value) || (!strict && _entities[a][property].get() == value)) { found.push(_entities[a]); } } if(1 === found.length) { return found[0]; } if(0 < found.length) { return found; } return null; }; })(i); } return _instance; }; //Create entity _data.instance.entities[type.name] = function Entity(data) { var _instance = this; //Properties var _properties = JSON.parse(JSON.stringify(type.properties)); //Functions for(var i in type.properties) { (function(property) { _instance[property] = {}; //Push if(true === Array.isArray(_properties[property])) { //List _instance[property].list = function() { return _properties[property]; } _instance[property].push = function(entity) { _properties[property].push(entity); return _instance; } return; } //Get _instance[property].get = function() { return _properties[property]; } //Set _instance[property].set = function(value) { _properties[property] = value; return _instance; } })(i); } _instance.constructor.prototype.toString = function() { return type.name; } if(data) { for(var a in _instance) { if(_instance[a] && _instance[a].set) { _instance[a].set(data[a]); } } } _data.instance.models[type.name].push(_instance); //Relations if(_data.relations[type.name]) { for(var a in _data.relations[type.name].now) { var relation = _data.relations[type.name].now[a]; var ent = _data.instance.models[relation.model].find[relation.on[1]](data[relation.on[0]]); if(null !== ent) { _instance[relation.property[0]].push(ent); ent[relation.property[1]].push(_instance); } else { if(undefined === _data.relations[relation.model]) { _data.relations[relation.model] = {now : [], later : []}; } var relation = JSON.parse(JSON.stringify(relation)); relation.value = [data[relation.on[1]], _instance]; _data.relations[relation.model].later.push(relation); } } var remove = []; for(var a in _data.relations[type.name].later) { var relation = _data.relations[type.name].later[a]; var ent = _data.instance.models[type.name].find[relation.on[1]](relation.value[0]); if(null !== ent) { if(_instance[relation.on[1]].get() === ent[relation.on[1]].get()) { _instance[relation.property[1]].push(relation.value[1]); relation.value[1][relation.property[0]].push(_instance); remove.push(a); } } } for(var a in remove) { _data.relations[type.name].later.splice(remove[a], 1); } } return _instance; }; return { link : function(relation) { //Model validation if(undefined === relation.model) { throw 'Link: Model property is required'; } if(undefined === _data.instance.models[relation.model]) { throw 'Link: Model "' + relation.model + '" does not exists'; } //On property validation if(undefined === relation.on) { throw 'Link: Property "on" is required'; } if(false === Array.isArray(relation.on) || relation.on.length < 2) { throw 'Link: Property "on" must be an Array with two String elements'; } if(undefined === _data.instance.models[type.name].find[relation.on[1]]) { throw 'Link: Model "'+ type.name +'" does not contain the property "'+ relation.on[1] +'" in "on" property'; } //Property property validation if(undefined === relation.property) { throw 'Link: Property "property" is required'; } if(false === Array.isArray(relation.property) || relation.property.length < 2) { throw 'Link: Property "property" must be an Array with two String elements'; } if(undefined === _data.instance.models[type.name].find[relation.property[0]]) { throw 'Link: Model "'+ type.name +'" does not contain the property "'+ relation.property[0] +'" in "propery" property'; } //Create relation if not exists if(undefined === _data.relations[type.name]) { _data.relations[type.name] = {now : [], later : []}; } //Add relation _data.relations[type.name].now.push(relation); } }; }, destroy : function(type) { //Delete entities var entities = _data.instance.models[type].list(); while(entities.length > 0) { _data.instance.destroy(entities[0]); } //Delete model object delete _data.instance.models[type]; //Delete entity object delete _data.instance.entities[type]; } } //Mapper _data.instance.map = function(objects, type) { _data.type.entity(type); //If objects are not an array (singel entity) if(false === Array.isArray(objects)) { objects = [objects]; } for(var i in objects) { var entity = new _data.instance.entities[type](); //Set entity attributes for(var a in entity) { if(entity[a] && entity[a].set) { entity[a].set(objects[i][a]); } } //Execute create callbacks _data.events.fire('create', entity); } return _data.instance; } //Expire _data.instance.expire = function(entity, seconds) { var date = new Date(); date.setSeconds(date.getSeconds() + seconds); //Push new entry _data.expire.entities.push({entity : entity, date : date}); if(null === _data.expire.timer) { _data.expire.timer = setInterval(function() { var timestamp = new Date().getTime(); for(var i in _data.expire.entities) { if(_data.expire.entities[i].date.getTime() <= timestamp) { //Execute expire callbacks _data.events.fire('expire', _data.expire.entities[i].entity); //Destroy entity _data.instance.destroy(_data.expire.entities[i].entity); } } }, _data.expire.interval) } return _data.instance; } //Destroy _data.instance.destroy = function(entity) { if(!Array.isArray(entity)) { entity = [entity]; } for(var a in entity) { for(var b in _data.instance.models) { //Delete entity from models var entities = _data.instance.models[b].list(); var index = entities.indexOf(entity[a]); if(index >= 0) { //Execute destroy callbacks _data.events.fire('destroy', entity[a]); entities.splice(index, 1); } //Delete entity from within other entities for(var c in entities) { for(var d in entities[c]) { //Check if its an array if(entities[c][d].push) { var list = entities[c][d].list(); var index = list.indexOf(entity[a]); if(index >= 0) { list.splice(index, 1); } } } } } //Delete entity from expire for(var e in _data.expire.entities) { if(_data.expire.entities[e].entity === entity[a]) { _data.expire.entities.splice(i, 1); } } } //Clear expire timer if there are no entities left if(_data.expire.entities.length === 0) { clearInterval(_data.expire.timer); } } //Event on _data.instance.on = function(type, callback) { _data.type.event(type); _data.events.types[type].push(callback); } //Event off _data.instance.off = function(type) { _data.type.event(type); _data.events.types[type] = []; } _data.instance.dump = function(entities) { var dump = []; if(entities) { if(false === Array.isArray(entities)) { entities = [entities]; } for(var a in entities) { var plain = {}; _data.type.entity(entities[a].toString()); for(var b in entities[a]) { if(entities[a][b] && entities[a][b].get && typeof(entities[a][b].get) === 'function') { plain[b] = entities[a][b].get(); } } dump.push(plain); } } if(1 === dump.length) { return dump[0]; } return dump; } };