Comparing version 0.0.9 to 0.1.0
@@ -122,3 +122,3 @@ /*jslint plusplus: true, devel: true, nomen: true, vars: true, node: true, es5: true, indent: 4, maxerr: 50 */ | ||
* | ||
* @param {Object|Function} | ||
* @param {Object} | ||
* @param {Function} callback this will be called after executing this method. The first parameter will contain an error object when the cursor is already closed while the second parameter will contain a reference to this object upon successful execution. | ||
@@ -439,2 +439,21 @@ * @return {Cursor} an instance of this object. | ||
Cursor.prototype.wrapCallback = function (funcName, wrapper) { | ||
var that = this, | ||
func = this[funcName]; | ||
this[funcName] = function () { | ||
var args = Array.prototype.slice.call(arguments), | ||
callback = args.shift(); | ||
function handleCallback() { | ||
var args = Array.prototype.slice.call(arguments); | ||
args.push(callback); | ||
wrapper.apply(that, args); | ||
} | ||
args.unshift(handleCallback); | ||
func.apply(that, args); | ||
}; | ||
}; | ||
Cursor.prototype.reset = function () { | ||
@@ -441,0 +460,0 @@ // abstract |
@@ -8,2 +8,11 @@ /*jslint plusplus: true, devel: true, nomen: true, vars: true, node: true, indent: 4, maxerr: 50 */ | ||
function getCursorKey(cursor) { | ||
return cursor.provider._uuid + | ||
JSON.stringify(cursor.query) + | ||
cursor.skipValue + | ||
cursor.limitValue + | ||
(cursor.mapping || "") + | ||
(cursor.criteria || ""); | ||
} | ||
exports.interception = function (stores, actions) { | ||
@@ -40,6 +49,63 @@ var cache, _stores, i; | ||
function wrapCursor(cursor, done) { | ||
var key = getCursorKey(cursor); | ||
cache.get(key, function (err, entry) { | ||
if (err) { | ||
return done(err); | ||
} | ||
var current = 0, | ||
items = [], | ||
_nextObject = cursor._nextObject, | ||
_count = cursor.count; | ||
if (!entry) { | ||
entry = {}; | ||
} | ||
cursor._nextObject = function (callback) { | ||
if (entry.items) { | ||
callback(null, entry.items[current++]); | ||
} else { | ||
_nextObject.call(cursor, function (err, item) { | ||
if (!err) { | ||
if (item) { | ||
items.push(item); | ||
} else { | ||
entry.items = items; | ||
cache.set(key, entry); | ||
} | ||
} | ||
callback(err, item); | ||
}); | ||
} | ||
}; | ||
cursor.count = function (callback) { | ||
if (entry.count) { | ||
callback(null, entry.count); | ||
} else { | ||
_count.call(cursor, function (err, count) { | ||
if (!err) { | ||
entry.count = count; | ||
cache.set(key, entry); | ||
} | ||
callback(err, count); | ||
}); | ||
} | ||
}; | ||
done(null, cursor); | ||
}); | ||
} | ||
return function (action, context, item, next, out) { | ||
if (!out) { | ||
throw new Error("Cache interceptor is asynchronous and therefore it requires a callback function."); | ||
} | ||
var key; | ||
if (actions || actions.indexOf(action) === -1) { | ||
if (actions && actions.indexOf(action) === -1) { | ||
return next(item, out); | ||
@@ -52,19 +118,21 @@ } | ||
case "_update": | ||
key = this.provider._getId(item); | ||
key = this._getId(item); | ||
cache.set(key, item); | ||
break; | ||
return next(item, out); | ||
case "_delete": | ||
key = this.provider._getId(item); | ||
key = this._getId(item); | ||
cache.del(key); | ||
break; | ||
return next(item, out); | ||
case "_get": | ||
key = this.provider._getId(item); | ||
cache.wrap(key, function (cb) { next(item, cb); }, out); | ||
break; | ||
key = this._getId(item); | ||
return cache.wrap(key, function (cb) { next(item, cb); }, out); | ||
case "_select": | ||
key = JSON.stringify(item); | ||
cache.wrap(key, function (cb) { next(item, cb); }, out); | ||
break; | ||
return next(item, function (err, res) { | ||
if (err) { | ||
return out(err); | ||
} | ||
wrapCursor(res, out); | ||
}); | ||
} | ||
}; | ||
}; |
@@ -7,2 +7,3 @@ /*jslint plusplus: true, devel: true, nomen: true, vars: true, node: true, sloppy: true, es5: true, indent: 4, maxerr: 50 */ | ||
_ = require("lodash"), | ||
uuid = require("node-uuid"), | ||
util = require("util"); | ||
@@ -111,2 +112,3 @@ | ||
this._stack = []; | ||
this._uuid = uuid.v1(); | ||
} | ||
@@ -180,3 +182,2 @@ | ||
Provider.prototype.use = function (fn) { | ||
fn.provider = this; | ||
this._stack.push(fn); | ||
@@ -210,3 +211,3 @@ }; | ||
if (layer) { | ||
return layer(action, context, itm, next, out); | ||
return layer.call(that, action, context, itm, next, out); | ||
} | ||
@@ -213,0 +214,0 @@ |
@@ -10,3 +10,2 @@ /*jslint node: true, plusplus: true, devel: true, nomen: true, vars: true, es5: true, indent: 4, maxerr: 50 */ | ||
util = require("util"), | ||
uuid = require("node-uuid"), | ||
_ = require("lodash"), | ||
@@ -252,4 +251,2 @@ _conns = {}; | ||
this._uuid = uuid.v1(); | ||
if (!this.options.safeMode) { | ||
@@ -256,0 +253,0 @@ this.options.safeMode = 1; |
{ | ||
"name" : "entree", | ||
"description" : "Data provider model abstraction.", | ||
"version" : "0.0.9", | ||
"version" : "0.1.0", | ||
"main" : "index", | ||
@@ -6,0 +6,0 @@ "scripts" : { |
@@ -5,28 +5,36 @@ /*jslint node: true, plusplus: true, devel: true, nomen: true, vars: true, indent: 4, maxerr: 50 */ | ||
function Store(name) { | ||
this.name = name; | ||
this.getCalls = 0; | ||
this.setCalls = 0; | ||
this.delCalls = 0; | ||
this.store = []; | ||
} | ||
var store = { | ||
stors: [], | ||
create: function () { | ||
var self = {}; | ||
Store.prototype.get = function (key, callback) { | ||
this.getCalls++; | ||
callback(null, this.store[key]); | ||
}; | ||
self.index = this.stors.length; | ||
self.getCalls = 0; | ||
self.setCalls = 0; | ||
self.delCalls = 0; | ||
self.storage = {}; | ||
this.stors.push(self); | ||
Store.prototype.del = function (key, callback) { | ||
this.delCalls++; | ||
delete this.store[key]; | ||
if (callback) { | ||
callback(); | ||
} | ||
}; | ||
self.get = function (key, callback) { | ||
self.getCalls++; | ||
callback(null, self.storage[key]); | ||
}; | ||
Store.prototype.set = function (key, val, callback) { | ||
this.setCalls++; | ||
this.store[key] = val; | ||
if (callback) { | ||
callback(); | ||
self.del = function (key, callback) { | ||
self.delCalls++; | ||
delete self.storage[key]; | ||
if (callback) { | ||
callback(); | ||
} | ||
}; | ||
self.set = function (key, val, callback) { | ||
self.setCalls++; | ||
self.storage[key] = val; | ||
if (callback) { | ||
callback(); | ||
} | ||
}; | ||
return self; | ||
} | ||
@@ -39,4 +47,2 @@ }; | ||
cache = require("../lib/interceptors/cache"), | ||
store1 = new Store("store1"), | ||
store2 = new Store("store2"), | ||
manager; | ||
@@ -50,3 +56,3 @@ | ||
provider.use(cache.interception(store1, ["get", "delete"])); | ||
provider.use(cache.interception({ store: store }, ["get", "delete", "select"])); | ||
@@ -60,12 +66,188 @@ manager = new Manager(); | ||
"Insert Item": function (test) { | ||
test.expect(5); | ||
test.expect(10); | ||
manager.testProv.insert({_id: 1, name: "Foo", age: 20 }, function (err, item) { | ||
test.ok(!err); | ||
test.ok(item); | ||
test.equal(store1.setCalls, 0); | ||
test.equal(store1.getCalls, 0); | ||
test.equal(store1.delCalls, 0); | ||
test.equal(manager.testProv.store["1"].name, "Foo"); | ||
test.equal(store.stors[0].setCalls, 0); | ||
test.equal(store.stors[0].getCalls, 0); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(manager.testProv.insertCalls, 1); | ||
test.equal(manager.testProv.updateCalls, 0); | ||
test.equal(manager.testProv.getCalls, 0); | ||
test.equal(manager.testProv.selectCalls, 0); | ||
test.done(); | ||
}); | ||
}, | ||
"Get Not Cached Item": function (test) { | ||
test.expect(10); | ||
manager.testProv.get(1, function (err, item) { | ||
test.ok(!err); | ||
test.equal(item.name, "Foo"); | ||
test.equal(store.stors[0].setCalls, 1); | ||
test.equal(store.stors[0].getCalls, 1); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(store.stors[0].storage["1"].name, "Foo"); | ||
test.equal(manager.testProv.insertCalls, 1); | ||
test.equal(manager.testProv.updateCalls, 0); | ||
test.equal(manager.testProv.getCalls, 1); | ||
test.equal(manager.testProv.selectCalls, 0); | ||
test.done(); | ||
}); | ||
}, | ||
"Update Item Without Changeing Cached": function (test) { | ||
test.expect(10); | ||
manager.testProv.update({_id: 1, name: "Baz", age: 25 }, function (err, item) { | ||
test.ok(!err); | ||
test.equal(item.name, "Baz"); | ||
test.equal(store.stors[0].setCalls, 1); | ||
test.equal(store.stors[0].getCalls, 1); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(store.stors[0].storage["1"].name, "Foo"); | ||
test.equal(manager.testProv.insertCalls, 1); | ||
test.equal(manager.testProv.updateCalls, 1); | ||
test.equal(manager.testProv.getCalls, 1); | ||
test.equal(manager.testProv.selectCalls, 0); | ||
test.done(); | ||
}); | ||
}, | ||
"Get Cached Item": function (test) { | ||
test.expect(10); | ||
manager.testProv.get(1, function (err, item) { | ||
test.ok(!err); | ||
test.equal(item.name, "Foo"); | ||
test.equal(store.stors[0].setCalls, 1); | ||
test.equal(store.stors[0].getCalls, 2); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(manager.testProv.store["1"].name, "Baz"); | ||
test.equal(manager.testProv.insertCalls, 1); | ||
test.equal(manager.testProv.updateCalls, 1); | ||
test.equal(manager.testProv.getCalls, 1); | ||
test.equal(manager.testProv.selectCalls, 0); | ||
test.done(); | ||
}); | ||
}, | ||
"Add More Items and Select": function (test) { | ||
test.expect(17); | ||
var items = [ | ||
{_id: 2, name: "Bar", age: 25 }, | ||
{_id: 3, name: "Qux", age: 20 }, | ||
{_id: 4, name: "Door", age: 30 }, | ||
{_id: 5, name: "Red", age: 25 } | ||
]; | ||
manager.testProv.insert(items, function (err, items) { | ||
test.ok(!err); | ||
test.ok(items); | ||
test.equal(manager.testProv.store["2"].name, "Bar"); | ||
test.equal(store.stors[0].setCalls, 1); | ||
test.equal(store.stors[0].getCalls, 2); | ||
test.equal(store.stors[0].delCalls, 0); | ||
manager.testProv.select({ age: 25 }, function (err, res) { | ||
test.ok(!err); | ||
res.toArray(function (err, arr) { | ||
test.ok(!err); | ||
test.ok(arr); | ||
test.equal(arr.length, 3); | ||
test.equal(store.stors[0].setCalls, 2); | ||
test.equal(store.stors[0].getCalls, 3); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(manager.testProv.insertCalls, 2); | ||
test.equal(manager.testProv.updateCalls, 1); | ||
test.equal(manager.testProv.getCalls, 1); | ||
test.equal(manager.testProv.selectCalls, 1); | ||
test.done(); | ||
}); | ||
}); | ||
}); | ||
}, | ||
"Select from Cache": function (test) { | ||
test.expect(10); | ||
manager.testProv.select({ age: 25 }, function (err, res) { | ||
test.ok(!err); | ||
test.equal(store.stors[0].setCalls, 2); | ||
test.equal(store.stors[0].getCalls, 4); | ||
test.equal(store.stors[0].delCalls, 0); | ||
test.equal(manager.testProv.insertCalls, 2); | ||
test.equal(manager.testProv.updateCalls, 1); | ||
test.equal(manager.testProv.getCalls, 1); | ||
test.equal(manager.testProv.selectCalls, 2); | ||
res.toArray(function (err, arr) { | ||
test.ok(!err); | ||
test.equal(arr.length, 3); | ||
test.done(); | ||
}); | ||
}); | ||
}, | ||
"Throw on Select Without Callback": function (test) { | ||
test.expect(1); | ||
test.throws(function () { | ||
manager.testProv.select({age: 25}); | ||
}, "Cache interceptor is asynchronous and therefore it requires a callback function."); | ||
test.done(); | ||
}, | ||
"Tiered Caches": function (test) { | ||
test.expect(1); | ||
var provider = new Provider({ connStr: "test connection string" }, { __collName: "multiCache" }); | ||
provider.use(cache.interception([{ store: store }, { store: store }])); | ||
manager.addProvider(provider, "multiCache", function (err) { | ||
test.ok(!err); | ||
test.done(); | ||
}); | ||
}, | ||
"Get Item": function (test) { | ||
test.expect(25); | ||
manager.multiCache.insert({_id: 1, name: "Foo", age: 20 }, function (err, item) { | ||
test.ok(!err); | ||
test.ok(item); | ||
test.equal(manager.multiCache.store["1"].name, "Foo"); | ||
test.equal(store.stors[1].setCalls, 1); | ||
test.equal(store.stors[1].getCalls, 0); | ||
test.equal(store.stors[1].delCalls, 0); | ||
test.equal(store.stors[2].setCalls, 1); | ||
test.equal(store.stors[2].getCalls, 0); | ||
test.equal(store.stors[2].delCalls, 0); | ||
test.equal(manager.multiCache.insertCalls, 1); | ||
test.equal(manager.multiCache.updateCalls, 0); | ||
test.equal(manager.multiCache.getCalls, 0); | ||
test.equal(manager.multiCache.selectCalls, 0); | ||
manager.multiCache.get(1, function (err, item) { | ||
test.ok(!err); | ||
test.equal(item.name, "Foo"); | ||
test.equal(store.stors[1].setCalls, 1); | ||
test.equal(store.stors[1].getCalls, 1); | ||
test.equal(store.stors[1].delCalls, 0); | ||
test.equal(store.stors[2].setCalls, 1); | ||
test.equal(store.stors[2].getCalls, 0); | ||
test.equal(store.stors[2].delCalls, 0); | ||
test.equal(manager.multiCache.insertCalls, 1); | ||
test.equal(manager.multiCache.updateCalls, 0); | ||
test.equal(manager.multiCache.getCalls, 0); | ||
test.equal(manager.multiCache.selectCalls, 0); | ||
test.done(); | ||
}); | ||
}); | ||
}, | ||
"Fixture Tear Down": function (test) { | ||
@@ -72,0 +254,0 @@ test.expect(1); |
@@ -19,3 +19,3 @@ /*jslint plusplus: true, devel: true, nomen: true, node: true, es5: true, indent: 4, maxerr: 50 */ | ||
"./fs-common.js", | ||
// "./el-common.js", | ||
"./el-common.js", | ||
"./mo-common.js", | ||
@@ -22,0 +22,0 @@ "./manager.js", |
@@ -91,17 +91,7 @@ /*jslint plusplus: true, devel: true, nomen: true, vars: true, node: true, es5: true, indent: 4, maxerr: 50 */ | ||
function wrapCursor(cursor) { | ||
var func = cursor._nextObject; | ||
cursor._nextObject = function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
var callback = args.shift(); | ||
function handleCallback(err, item) { | ||
if (item) { | ||
item.timestamp = new Date(); | ||
} | ||
callback(err, item); | ||
} | ||
args.unshift(handleCallback); | ||
func.apply(cursor, args); | ||
}; | ||
function wrapper(err, item, callback) { | ||
if (item) { | ||
item.timestamp = new Date(); | ||
} | ||
callback(err, item); | ||
} | ||
@@ -120,3 +110,3 @@ | ||
case "_select": | ||
wrapCursor(item); | ||
item.wrapCallback("_nextObject", wrapper); | ||
break; | ||
@@ -130,5 +120,5 @@ } | ||
if (result) { | ||
wrapCursor(result); | ||
result.wrapCallback("_nextObject", wrapper); | ||
} | ||
return result; | ||
}; |
@@ -14,4 +14,10 @@ /*jslint plusplus: true, devel: true, nomen: true, vars: true, node: true, sloppy: true, indent: 4, maxerr: 50 */ | ||
Provider.call(this, options, schema); | ||
this.store = []; | ||
this.store = {}; | ||
this.sync = false; | ||
this.insertCalls = 0; | ||
this.upsertCalls = 0; | ||
this.getCalls = 0; | ||
this.updateCalls = 0; | ||
this.selectCalls = 0; | ||
this.deleteCalls = 0; | ||
} | ||
@@ -24,2 +30,4 @@ | ||
this.insertCalls++; | ||
function storeItem(item) { | ||
@@ -59,2 +67,4 @@ var id = that._getId(item); | ||
this.upsertCalls++; | ||
if (!id) { | ||
@@ -77,2 +87,4 @@ id = uuid.v1(); | ||
this.updateCalls++; | ||
if (!id) { | ||
@@ -99,2 +111,4 @@ return this.handleError("Identifier not specified.", callback); | ||
this.getCalls++; | ||
if (!id) { | ||
@@ -120,2 +134,4 @@ return this.handleError("Identifier not specified.", callback); | ||
this.deleteCalls++; | ||
if (!id) { | ||
@@ -140,2 +156,5 @@ return this.handleError("Identifier not specified.", callback); | ||
var cursor = new Cursor(this, args.query, args.options); | ||
this.selectCalls++; | ||
if (callback) { | ||
@@ -142,0 +161,0 @@ callback(null, cursor); |
118658
3580