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.
"__dirname",
"Promise",
"Buffer",
"event",
"exports",
@ -58,7 +59,7 @@
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnProperty`.
"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.
"maxparams" : 4,
"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-fs](https://github.com/hotelde/node-cache-manager-fs)
## Overview
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,
* its results are stored in cache so subsequent calls retrieve from cache
@ -69,6 +87,10 @@ var caching = function(args) {
options = {};
}
if (!cb) {
return wrapPromise(key, work, options);
}
var hasKey = callbackFiller.has(key);
callbackFiller.add(key, {cb: cb, domain: process.domain});
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) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (!cb) {
return getFromHighestPriorityCachePromise(key, options);
}
var i = 0;
async.eachSeries(caches, function(cache, next) {
var callback = function(err, result) {
@ -72,11 +87,29 @@ var multiCaching = function(caches, options) {
};
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) {
opts.options = opts.options || {};
if (!cb) {
return setInMultipleCachesPromise(caches, opts);
}
async.each(caches, function(cache, next) {
var _isCacheableValue = getIsCacheableValueFunction(cache);
@ -85,7 +118,20 @@ var multiCaching = function(caches, options) {
} else {
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
*/
self.getAndPassUp = function(key, cb) {
if (!cb) {
return getAndPassUpPromise(key);
}
getFromHighestPriorityCache(key, function(err, result, index) {
if (err) {
return cb(err);
}
cb(err, result);
if (index) {
var cachesToUpdate = caches.slice(0, index);
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.
* 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);
callbackFiller.add(key, {cb: cb, domain: process.domain});
if (hasKey) { return; }
@ -165,8 +237,6 @@ var multiCaching = function(caches, options) {
.on('error', function(err) {
if (callbackFiller.has(key)) {
callbackFiller.fill(key, err);
} else {
cb(err);
}
})
.bind(work)(function(err, data) {
@ -211,7 +281,7 @@ var multiCaching = function(caches, options) {
options: options
};
setInMultipleCaches(caches, opts, cb);
return setInMultipleCaches(caches, opts, cb);
};
/**
@ -230,7 +300,7 @@ var multiCaching = function(caches, 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);
if (cb) {
process.nextTick(cb);
} else {
return Promise.resolve(value);
}
};
@ -35,6 +37,7 @@ var memoryStore = function(args) {
cb = options;
}
var value = lruCache.get(key);
if (cb) {
process.nextTick(function() {
cb(null, value);

1
package.json

@ -25,6 +25,7 @@
},
"devDependencies": {
"coveralls": "^2.3.0",
"es6-promise": "^3.0.2",
"istanbul": "^0.2.11",
"jscs": "^1.9.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() {

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() {
@ -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() {
@ -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() {
var fakeError;
var memoryStoreStub;
@ -347,6 +404,15 @@ describe("multiCaching", function() {
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() {

4
test/run.js

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

Loading…
Cancel
Save