UNPKG

jugglingdb

Version:

Node.js ORM for every database: redis, mysql, mongodb, postgres, sqlite, ...

301 lines (265 loc) 8.45 kB
exports.initialize = function initializeSchema(schema, callback) { schema.adapter = new Memory(); schema.adapter.connect(callback); }; function Memory(m) { if (m) { this.isTransaction = true; this.cache = m.cache; this.ids = m.ids; this._models = m._models; } else { this.isTransaction = false; this.cache = {}; this.ids = {}; this._models = {}; } } Memory.prototype.connect = function(callback) { if (this.isTransaction) { this.onTransactionExec = callback; } else { process.nextTick(callback); } }; Memory.prototype.define = function defineModel(descr) { const m = descr.model.modelName; this._models[m] = descr; this.cache[this.table(m)] = {}; this.ids[m] = 0; }; Memory.prototype.create = function create(model, data, callback) { const id = data.id ? data.id : this.ids[model] += 1; data.id = id; this.cache[this.table(model)][id] = JSON.stringify(data); process.nextTick(() => { callback(null, id, 1); }); }; /** * Updates the respective record * * @param {Object} params - { where:{uid:'10'}, update:{ Name:'New name' } } * @param callback(err, obj) */ Memory.prototype.update = function(model, params, callback) { const mem = this; this.all(model, { where: params.where, limit: params.limit, skip: params.skip }, (err, records) => { let wait = records.length; records.forEach(record => { mem.updateAttributes(model, record.id, params.update, done); }); if (wait === 0) { callback(); } function done() { wait += -1; if (wait === 0) { callback(); } } }); }; Memory.prototype.updateOrCreate = function(model, data, callback) { const mem = this; this.find(model, data.id, (err, exists) => { if (exists) { mem.save(model, merge(exists, data), callback); } else { mem.create(model, data, (err, id) => { data.id = id; callback(err, data); }); } }); }; Memory.prototype.save = function save(model, data, callback) { this.cache[this.table(model)][data.id] = JSON.stringify(data); process.nextTick(() => callback(null, data)); }; Memory.prototype.exists = function exists(model, id, callback) { const table = this.table(model); process.nextTick(() => { callback(null, this.cache[table].hasOwnProperty(id)); }); }; Memory.prototype.find = function find(model, id, callback) { const table = this.table(model); process.nextTick(() => { callback(null, id in this.cache[table] && this.fromDb(model, this.cache[table][id])); }); }; Memory.prototype.destroy = function destroy(model, id, callback) { delete this.cache[this.table(model)][id]; process.nextTick(callback); }; Memory.prototype.fromDb = function(model, data) { if (!data) { return null; } data = JSON.parse(data); const props = this._models[model].properties; Object.keys(data).forEach(key => { let val = data[key]; if (typeof val === 'undefined' || val === null) { return; } if (props[key]) { switch (props[key].type.name) { case 'Date': val = new Date(val.toString().replace(/GMT.*$/, 'GMT')); break; case 'Boolean': val = Boolean(val); break; } } data[key] = val; }); return data; }; Memory.prototype.all = function all(model, filter, callback) { const table = this.table(model); let nodes = Object.keys(this.cache[table]).map(key => { return this.fromDb(model, this.cache[table][key]); }); if (filter) { // do we need some sorting? if (filter.order) { let orders = filter.order; if (typeof filter.order === 'string') { orders = [filter.order]; } orders.forEach((key, i) => { let reverse = 1; const m = key.match(/\s+(A|DE)SC$/i); if (m) { key = key.replace(/\s+(A|DE)SC/i, ''); if (m[1].toLowerCase() === 'de') { reverse = -1; } } orders[i] = { key, reverse }; }); nodes = nodes.sort(sorting.bind(orders)); } // do we need some filtration? if (filter.where) { nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes; } // limit/skip filter.skip = filter.skip || 0; filter.limit = filter.limit || nodes.length; const countBeforeLimit = nodes.length; nodes = nodes.slice(filter.skip, filter.skip + filter.limit); nodes.countBeforeLimit = countBeforeLimit; } process.nextTick(() => { if (filter && filter.include) { this._models[model].model.include(nodes, filter.include, callback); } else { callback(null, nodes); } }); function sorting(a, b) { for (let i = 0, l = this.length; i < l; i += 1) { if (a[this[i].key] > b[this[i].key]) { return 1 * this[i].reverse; } else if (a[this[i].key] < b[this[i].key]) { return -1 * this[i].reverse; } } return 0; } }; function applyFilter(filter) { if (typeof filter.where === 'function') { return filter.where; } const keys = Object.keys(filter.where); return function(obj) { let pass = true; keys.forEach(key => { if (!test(filter.where[key], obj[key])) { pass = false; } }); return pass; }; function test(example, value) { if (typeof value === 'string' && example && example.constructor.name === 'RegExp') { return value.match(example); } if (typeof example === 'undefined') { return undefined; } if (typeof value === 'undefined') { return undefined; } if (typeof example === 'object') { if (example === null) { return value === null; } if (example.inq) { if (!value) { return false; } for (let i = 0; i < example.inq.length; i += 1) { if (String(example.inq[i]) === String(value)) { return true; } } return false; } } // not strict equality return String(example !== null ? example.toString() : example) === String(value !== null ? value.toString() : value); } } Memory.prototype.destroyAll = function destroyAll(model, callback) { const table = this.table(model); Object.keys(this.cache[table]).forEach(id => { delete this.cache[table][id]; }); this.cache[table] = {}; process.nextTick(callback); }; Memory.prototype.count = function count(model, where, callback) { const cache = this.cache[this.table(model)]; let data = Object.keys(cache); if (where) { data = data.filter(id => { let ok = true; Object.keys(where).forEach(key => { if (String(JSON.parse(cache[id])[key]) !== String(where[key])) { ok = false; } }); return ok; }); } process.nextTick(() => callback(null, data.length)); }; Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) { data.id = id; const base = JSON.parse(this.cache[this.table(model)][id]); this.save(model, merge(base, data), cb); }; Memory.prototype.transaction = function() { return new Memory(this); }; Memory.prototype.exec = function(callback) { this.onTransactionExec(); setTimeout(callback, 50); }; Memory.prototype.table = function(model) { return this._models[model].model.tableName; }; function merge(base, update) { if (!base) { return update; } Object.keys(update).forEach(key => base[key] = update[key]); return base; }