resourceful
Version:
an isomorphic Resource engine for JavaScript
241 lines (208 loc) • 6.13 kB
JavaScript
var resourceful = require('../../resourceful'),
Cache = resourceful.Cache;
exports.stores = {};
exports.caches = {};
var Memory = exports.Memory = function (options) {
var counter = 0;
options = options || {};
this.uri = options.uri;
this.increment = function () {
return ++counter;
};
if (typeof(this.uri) === 'string') {
// Application-wide store
if (!exports.stores[this.uri]) {
this.store = exports.stores[this.uri] = {};
this.cache = exports.caches[this.uri] = new Cache();
} else {
// Use store that was created before
this.store = exports.stores[this.uri];
this.cache = exports.caches[this.uri];
}
}
else {
// Connection-wise store
this.store = {};
this.cache = new Cache();
}
};
Memory.prototype.protocol = 'memory';
Memory.prototype.load = function (data) {
if (data instanceof Array) {
var tmp = {};
data.forEach(function (e) {
tmp[e.id] = JSON.stringify(e);
});
data = tmp;
}
this.store = data;
// Update cache
if (this.uri) {
exports.stores[this.uri] = JSON.parse(JSON.stringify(this.store));
}
return this;
};
Memory.prototype.request = function (fn) {
var self = this;
process.nextTick(function () {
fn.call(self);
});
};
Memory.prototype.save = function (key, val, callback) {
var args = Array.prototype.slice.call(arguments);
var callback = args.pop(), val = args.pop();
if (!args.length || !key) {
key = this.increment();
val.id = key;
}
// Forces key to be a string
key += '';
val.id += '';
this.request(function () {
this.store[key] = JSON.stringify(val);
callback(null, val);
});
};
Memory.prototype.put = function () {
this.save.apply(this, arguments);
};
Memory.prototype.update = function (key, obj, callback) {
var jsObj = JSON.parse(this.store[key] || "{}");
this.put(key, resourceful.mixin({}, jsObj, obj), callback);
};
Memory.prototype.get = function (key, callback) {
this.request(function () {
key = key.toString();
return key in this.store ?
callback(null, JSON.parse(this.store[key] || "null"))
:
callback({ status: 404 });
});
};
Memory.prototype.destroy = function (key, callback) {
this.request(function () {
delete this.store[key];
return callback(null, { status: 204 });
});
};
Memory.prototype.find = function (conditions, callback) {
var self = this,
searchers = {};
Object.keys(conditions).forEach(function (k) {
searchers[k] = self._reduceFactory(conditions[k]);
});
this.search(function (obj) {
return Object.keys(searchers).every(function (k) {
return searchers[k](obj[k]);
});
}, callback);
};
Memory.prototype._reduceFactory = function(param) {
if (param instanceof Array) {
return function(value) {
return param.indexOf(value) >= 0;
};
} else if (param instanceof Object) {
if (param.key) {
return function(value) {
return param.key === value;
};
} else if (param.keys) {
return function(value) {
return param.keys.indexOf(value) >= 0;
};
} else if (param.keys().length === 0) {
return function(value) {
return true;
};
} else {
throw new Error('Criteria object without key or keys fields');
};
} else {
return function(value) {
return param === value;
}
};
}
Memory.prototype.search = function (filter, callback) {
this.request(function () {
var result = [],
store = this.store;
Object.keys(this.store).forEach(function (k) {
var obj = JSON.parse(store[k]);
if (filter(obj)) {
obj.id = obj.id.split('/').slice(1).join('/');
result.push(obj);
}
});
callback(null, result);
});
};
Memory.prototype.filter = function (name, data) {
var resource = data.resource,
filter = data.filter,
path = [resource._resource, name].join('/'),
map;
if (typeof(filter) === 'object') {
if (filter.map) {
// Mimic CouchDB's view API.
map = new Function([
'return (function (_doc, emit) {',
' (' + filter.map.toString() + ')(_doc)',
'})'
].join('\n'));
resource[name] = function (param, callback) {
var results = [],
reduce;
if (typeof param === 'function') {
callback = param;
param = {};
};
reduce = resource.connection._reduceFactory(param);
function emit(id, doc) {
if(reduce(id)) {
results.push(doc);
};
};
resource.connection.request(function () {
Object.keys(resource.connection.store).forEach(function (key) {
map()(JSON.parse(resource.connection.store[key]), emit);
});
resource.view(path, { err: null, res: results }, callback);
});
};
} else {
// Simple object criteria (can use .find)
resource[name] = function (callback) {
var conditions = resourceful.mixin({}, {resource: resource._resource}, filter)
resource.connection.find(conditions, function(err, res) {
resource.view(path, { err: err, res: res }, callback);
});
};
};
} else if (typeof(filter) === 'function') {
// Function which generates object criteria (can use .find)
resource[name] = function (param, callback) {
if (typeof param === 'function') {
callback = param;
param = {};
};
conditions = filter(param);
conditions = resourceful.mixin({}, {resource: resource._resource}, conditions)
resource.connection.find(conditions, function(err, res) {
resource.view(path, { err: err, res: res }, callback);
});
};
} else {
throw new TypeError('Filter must be an object or function');
};
};
Memory.prototype.view = function (path, opts, callback) {
if(opts.err) {
callback(opts.err);
};
callback(null, opts.res);
};
Memory.prototype.sync = function (factory, callback) {
process.nextTick(function () { callback(); });
};