Browse Source

Merge branch 'master' into develop

feature/remove-domains
Bryan Donovan 9 years ago
parent
commit
f2f9afb568
  1. 3
      .jshintrc
  2. 2
      README.md
  3. 22
      lib/caching.js
  4. 86
      lib/multi_caching.js
  5. 3
      lib/stores/memory.js
  6. 1
      package.json
  7. 56
      test/caching.unit.js
  8. 130
      test/multi_caching.unit.js
  9. 4
      test/run.js

3
.jshintrc

@ -20,6 +20,7 @@
"predef" : [ // Extra globals. "predef" : [ // Extra globals.
"__dirname", "__dirname",
"Promise",
"Buffer", "Buffer",
"event", "event",
"exports", "exports",
@ -58,7 +59,7 @@
"expr" : false, // Tolerate `ExpressionStatement` as Programs. "expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnProperty`. "forin" : false, // Tolerate `for in` loops without `hasOwnProperty`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : true, // Prohibit variable use before definition. "latedef" : "nofunc", // Prohibit variable use before definition.
"loopfunc" : true, // Allow functions to be defined within loops. "loopfunc" : true, // Allow functions to be defined within loops.
"maxparams" : 4, "maxparams" : 4,
"maxdepth" : 5, "maxdepth" : 5,

2
README.md

@ -33,6 +33,8 @@ See the [Express.js cache-manager example app](https://github.com/BryanDonovan/n
* [node-cache-manager-mongodb](https://github.com/v4l3r10/node-cache-manager-mongodb) * [node-cache-manager-mongodb](https://github.com/v4l3r10/node-cache-manager-mongodb)
* [node-cache-manager-fs](https://github.com/hotelde/node-cache-manager-fs)
## Overview ## Overview
First, it includes a `wrap` function that lets you wrap any function in cache. First, it includes a `wrap` function that lets you wrap any function in cache.

22
lib/caching.js

@ -42,6 +42,24 @@ var caching = function(args) {
}; };
} }
function wrapPromise(key, promise, options) {
return new Promise(function(resolve, reject) {
self.wrap(key, function(cb) {
Promise.resolve()
.then(promise)
.then(function(result) {
cb(null, result);
})
.catch(cb);
}, options, function(err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
}
/** /**
* Wraps a function in cache. I.e., the first time the function is run, * Wraps a function in cache. I.e., the first time the function is run,
* its results are stored in cache so subsequent calls retrieve from cache * its results are stored in cache so subsequent calls retrieve from cache
@ -69,6 +87,10 @@ var caching = function(args) {
options = {}; options = {};
} }
if (!cb) {
return wrapPromise(key, work, options);
}
var hasKey = callbackFiller.has(key); var hasKey = callbackFiller.has(key);
callbackFiller.add(key, {cb: cb, domain: process.domain}); callbackFiller.add(key, {cb: cb, domain: process.domain});
if (hasKey) { return; } if (hasKey) { return; }

86
lib/multi_caching.js

@ -47,12 +47,27 @@ var multiCaching = function(caches, options) {
} }
} }
function getFromHighestPriorityCachePromise(key, options) {
return new Promise(function(resolve, reject) {
getFromHighestPriorityCache(key, options, function(err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
}
function getFromHighestPriorityCache(key, options, cb) { function getFromHighestPriorityCache(key, options, cb) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
options = {}; options = {};
} }
if (!cb) {
return getFromHighestPriorityCachePromise(key, options);
}
var i = 0; var i = 0;
async.eachSeries(caches, function(cache, next) { async.eachSeries(caches, function(cache, next) {
var callback = function(err, result) { var callback = function(err, result) {
@ -72,11 +87,29 @@ var multiCaching = function(caches, options) {
}; };
cache.store.get(key, options, callback); cache.store.get(key, options, callback);
}, cb); }, function(err, result) {
return cb(err, result);
});
}
function setInMultipleCachesPromise(caches, opts) {
return new Promise(function(resolve, reject) {
setInMultipleCaches(caches, opts, function(err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
} }
function setInMultipleCaches(caches, opts, cb) { function setInMultipleCaches(caches, opts, cb) {
opts.options = opts.options || {}; opts.options = opts.options || {};
if (!cb) {
return setInMultipleCachesPromise(caches, opts);
}
async.each(caches, function(cache, next) { async.each(caches, function(cache, next) {
var _isCacheableValue = getIsCacheableValueFunction(cache); var _isCacheableValue = getIsCacheableValueFunction(cache);
@ -85,7 +118,20 @@ var multiCaching = function(caches, options) {
} else { } else {
next(); next();
} }
}, cb); }, function(err, result) {
cb(err, result);
});
}
function getAndPassUpPromise(key) {
return new Promise(function(resolve, reject) {
self.getAndPassUp(key, function(err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
} }
/** /**
@ -96,13 +142,15 @@ var multiCaching = function(caches, options) {
* @param {function} cb * @param {function} cb
*/ */
self.getAndPassUp = function(key, cb) { self.getAndPassUp = function(key, cb) {
if (!cb) {
return getAndPassUpPromise(key);
}
getFromHighestPriorityCache(key, function(err, result, index) { getFromHighestPriorityCache(key, function(err, result, index) {
if (err) { if (err) {
return cb(err); return cb(err);
} }
cb(err, result);
if (index) { if (index) {
var cachesToUpdate = caches.slice(0, index); var cachesToUpdate = caches.slice(0, index);
async.each(cachesToUpdate, function(cache, next) { async.each(cachesToUpdate, function(cache, next) {
@ -113,9 +161,29 @@ var multiCaching = function(caches, options) {
} }
}); });
} }
return cb(err, result);
}); });
}; };
function wrapPromise(key, promise, options) {
return new Promise(function(resolve, reject) {
self.wrap(key, function(cb) {
Promise.resolve()
.then(promise)
.then(function(result) {
cb(null, result);
})
.catch(cb);
}, options, function(err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
}
/** /**
* Wraps a function in one or more caches. * Wraps a function in one or more caches.
* Has same API as regular caching module. * Has same API as regular caching module.
@ -145,6 +213,10 @@ var multiCaching = function(caches, options) {
}; };
} }
if (!cb) {
return wrapPromise(key, work, options);
}
var hasKey = callbackFiller.has(key); var hasKey = callbackFiller.has(key);
callbackFiller.add(key, {cb: cb, domain: process.domain}); callbackFiller.add(key, {cb: cb, domain: process.domain});
if (hasKey) { return; } if (hasKey) { return; }
@ -165,8 +237,6 @@ var multiCaching = function(caches, options) {
.on('error', function(err) { .on('error', function(err) {
if (callbackFiller.has(key)) { if (callbackFiller.has(key)) {
callbackFiller.fill(key, err); callbackFiller.fill(key, err);
} else {
cb(err);
} }
}) })
.bind(work)(function(err, data) { .bind(work)(function(err, data) {
@ -211,7 +281,7 @@ var multiCaching = function(caches, options) {
options: options options: options
}; };
setInMultipleCaches(caches, opts, cb); return setInMultipleCaches(caches, opts, cb);
}; };
/** /**
@ -230,7 +300,7 @@ var multiCaching = function(caches, options) {
options = {}; options = {};
} }
getFromHighestPriorityCache(key, options, cb); return getFromHighestPriorityCache(key, options, cb);
}; };
/** /**

3
lib/stores/memory.js

@ -27,6 +27,8 @@ var memoryStore = function(args) {
lruCache.set(key, value, maxAge); lruCache.set(key, value, maxAge);
if (cb) { if (cb) {
process.nextTick(cb); process.nextTick(cb);
} else {
return Promise.resolve(value);
} }
}; };
@ -35,6 +37,7 @@ var memoryStore = function(args) {
cb = options; cb = options;
} }
var value = lruCache.get(key); var value = lruCache.get(key);
if (cb) { if (cb) {
process.nextTick(function() { process.nextTick(function() {
cb(null, value); cb(null, value);

1
package.json

@ -25,6 +25,7 @@
}, },
"devDependencies": { "devDependencies": {
"coveralls": "^2.3.0", "coveralls": "^2.3.0",
"es6-promise": "^3.0.2",
"istanbul": "^0.2.11", "istanbul": "^0.2.11",
"jscs": "^1.9.0", "jscs": "^1.9.0",
"jsdoc": "^3.3.0", "jsdoc": "^3.3.0",

56
test/caching.unit.js

@ -673,6 +673,62 @@ describe("caching", function() {
}); });
}); });
}); });
describe("using native promises", function() {
beforeEach(function() {
cache = caching({
store: 'memory',
max: 50,
ttl: 5 * 60
});
});
it("should be able to chain with simple promise", function(done) {
cache.wrap('key', function() {
return 'OK';
})
.then(function(res) {
assert.equal(res, 'OK');
done();
});
});
it("should be able to chain with cache function as a promise", function(done) {
cache.wrap('key', function() {
return new Promise(function(resolve) {
resolve('OK');
});
})
.then(function(res) {
assert.equal(res, 'OK');
done();
});
});
it("should be able to catch errors in cache function as a promise", function(done) {
cache.wrap('key', function() {
return new Promise(function(resolve, reject) {
reject('NOK');
});
})
.then(function() {
done(new Error('It should not call then since there is an error in the cache function!'));
})
.catch(function() {
done();
});
});
it("should be able to chain with non-cacheable value", function(done) {
cache.wrap('key', function() {
return;
})
.then(function(res) {
assert.equal(res, undefined);
done();
});
});
});
}); });
describe("instantiating with no store passed in", function() { describe("instantiating with no store passed in", function() {

130
test/multi_caching.unit.js

@ -181,6 +181,36 @@ describe("multiCaching", function() {
}); });
}); });
}); });
describe('using promises', function() {
it('gets data from first cache that has it', function(done) {
memoryCache3.set(key, value)
.then(function() {
return multiCache.get(key);
})
.then(function(result) {
assert.equal(result, value);
})
.then(done);
});
it("passes any options to underlying caches", function(done) {
var opts = {foo: 'bar'};
multiCache.set(key, value)
.then(function() {
sinon.spy(memoryCache.store, 'get');
return multiCache.get(key, opts);
})
.then(function(result) {
assert.equal(result, value);
assert.ok(memoryCache.store.get.calledWith(key, opts));
memoryCache.store.get.restore();
})
.then(done);
});
});
}); });
describe("del()", function() { describe("del()", function() {
@ -259,6 +289,17 @@ describe("multiCaching", function() {
}); });
}); });
}); });
it("gets data from first cache that has it using promises", function(done) {
memoryCache3.set(key, value)
.then(function() {
return multiCache.getAndPassUp(key);
})
.then(function(result) {
assert.equal(result, value);
done();
});
});
}); });
describe("when value is not found in any cache", function() { describe("when value is not found in any cache", function() {
@ -323,6 +364,22 @@ describe("multiCaching", function() {
}); });
}); });
it("checks to see if higher levels have item using promises", function(done) {
memoryCache3.set(key, value)
.then(function() {
return multiCache.getAndPassUp(key);
})
.then(function(result) {
assert.equal(result, value);
})
.then(function() {
process.nextTick(function() {
assert.equal(memoryCache.get(key), value);
});
})
.then(done);
});
context("when a cache store calls back with an error", function() { context("when a cache store calls back with an error", function() {
var fakeError; var fakeError;
var memoryStoreStub; var memoryStoreStub;
@ -347,6 +404,15 @@ describe("multiCaching", function() {
done(); done();
}); });
}); });
it("bubbles up errors from caches and reject promise", function(done) {
multiCache.getAndPassUp(key)
.catch(function(err) {
assert.ok(memoryStoreStub.get.called);
assert.equal(err, fakeError);
done();
});
});
}); });
}); });
}); });
@ -970,6 +1036,70 @@ describe("multiCaching", function() {
}); });
}); });
}); });
describe("using native promises", function() {
beforeEach(function() {
multiCache = multiCaching([memoryCache, memoryCache3]);
});
it("should be able to chain with simple promise", function(done) {
multiCache.wrap('key', function() {
return 'OK';
})
.then(function(res) {
assert.equal(res, 'OK');
done();
});
});
it("should be able to chain with cache function as a promise", function(done) {
multiCache.wrap('key', function() {
return new Promise(function(resolve) {
resolve('OK');
});
})
.then(function(res) {
assert.equal(res, 'OK');
done();
});
});
it("should be able to catch errors in cache function as a promise", function(done) {
multiCache.wrap('key', function() {
return new Promise(function(resolve, reject) {
reject('NOK');
});
})
.then(function() {
done(new Error('It should not call then since there is an error in the cache function!'));
})
.catch(function() {
done();
});
});
it("should be able to catch a throw in cache function as a promise", function(done) {
multiCache.wrap('key', function() {
throw 'NOK';
})
.then(function() {
done(new Error('It should not call then since there is an error in the cache function!'));
})
.catch(function() {
done();
});
});
it("should be able to chain with non-cacheable value", function(done) {
multiCache.wrap('key', function() {
return;
})
.then(function(res) {
assert.equal(res, undefined);
done();
});
});
});
}); });
context("when instantiated with a non-Array 'caches' arg", function() { context("when instantiated with a non-Array 'caches' arg", function() {

4
test/run.js

@ -6,6 +6,10 @@ var Mocha = require('mocha');
var optimist = require('optimist'); var optimist = require('optimist');
var walkDir = require('./support').walkDir; var walkDir = require('./support').walkDir;
if (typeof Promise === "undefined") {
global.Promise = require('es6-promise').Promise;
}
var argv = optimist var argv = optimist
.usage("Usage: $0 -t [types] --reporter [reporter] --timeout [timeout]")['default']( .usage("Usage: $0 -t [types] --reporter [reporter] --timeout [timeout]")['default'](
{types: 'unit,functional', reporter: 'spec', timeout: 6000}) {types: 'unit,functional', reporter: 'spec', timeout: 6000})

Loading…
Cancel
Save