Browse Source

Merge branch 'release/0.18.0'

feature/specify-what-to-cache 0.18.0
Bryan Donovan 10 years ago
parent
commit
770af766f7
  1. 1
      .jscs.json
  2. 77
      History.md
  3. 76
      README.md
  4. 72
      examples/example.js
  5. 102
      examples/redis_example/example.js
  6. 64
      examples/redis_example/redis_store.js
  7. 3
      index.js
  8. 47
      lib/caching.js
  9. 31
      lib/callback_filler.js
  10. 118
      lib/multi_caching.js
  11. 18
      lib/stores/memory.js
  12. 2
      package.json
  13. 248
      test/caching.unit.js
  14. 458
      test/multi_caching.unit.js
  15. 26
      test/run.js
  16. 6
      test/stores/memory.unit.js
  17. 22
      test/stores/options.unit.js
  18. 34
      test/support.js

1
.jscs.json

@ -12,6 +12,7 @@
"requireSpaceAfterBinaryOperators": ["?", "+", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="], "requireSpaceAfterBinaryOperators": ["?", "+", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowSpaceAfterBinaryOperators": ["!"], "disallowSpaceAfterBinaryOperators": ["!"],
"disallowSpaceBeforeBinaryOperators": [","], "disallowSpaceBeforeBinaryOperators": [","],
"requireCamelCaseOrUpperCaseIdentifiers": true,
"disallowMultipleVarDecl": true, "disallowMultipleVarDecl": true,
"disallowEmptyBlocks": true, "disallowEmptyBlocks": true,

77
History.md

@ -1,94 +1,103 @@
- 0.18.0 2015-02-12
- Minor changes and refactorings including:
- converting to camelcase
- hiding queues inside CallbackFiller
- general example updates
- updated redis example to use latest redis npm
- not trying to pass ttl into cache.set() in getAndPassUp() (this didn't
work anyway)
- 0.17.0 2015-02-05 - 0.17.0 2015-02-05
Add Additional Options Parameter (#20) - @seanzx85 - Add Additional Options Parameter (#20) - @seanzx85
Fixing bug with nested calls to wrap() (#21) - Fixing bug with nested calls to wrap() (#21)
- 0.16.0 2015-01-07 - 0.16.0 2015-01-07
Get and pass up feature to update higher caches. (#19) - raadad - Get and pass up feature to update higher caches. (#19) - raadad
Minor style tweaks/jscs update. - Minor style tweaks/jscs update.
- 0.15.0 2014-12-18 - 0.15.0 2014-12-18
Moved cache queue before the store get function (up to 2x performance boost). (#18) - aletorrado - Moved cache queue before the store get function (up to 2x performance boost). (#18) - aletorrado
Added domain support to make sure the wrap callback function is always called - aletorrado - Added domain support to make sure the wrap callback function is always called - aletorrado
- 0.14.0 2014-10-15 - 0.14.0 2014-10-15
Set ttl in wrap #14 - nguyenchr - Set ttl in wrap #14 - nguyenchr
Added JSCS for style checking - Added JSCS for style checking
- 0.13.0 2014-10-14 - 0.13.0 2014-10-14
Applied work function locking for multi_caching (#13). -aletorrado - Applied work function locking for multi_caching (#13). -aletorrado
- 0.12.0 2014-10-09 - 0.12.0 2014-10-09
Checking for existence of del() method before binding to it. Fixes #11. - Checking for existence of del() method before binding to it. Fixes #11.
- 0.11.0 2014-09-18 - 0.11.0 2014-09-18
Prevent stalemate by executing callbacks on error. Fixes #10 - elliotttf - Prevent stalemate by executing callbacks on error. Fixes #10 - elliotttf
- 0.10.1 2014-09-10 - 0.10.1 2014-09-10
Fixed tag/version mismatch - Fixed tag/version mismatch
- 0.10.0 2014-09-10 - 0.10.0 2014-09-10
Fixing Use call instead of apply for cached results, issue #9 (thanks elliotttf) - Fixing Use call instead of apply for cached results, issue #9 (thanks elliotttf)
- 0.9.0 2014-08-19 - 0.9.0 2014-08-19
Fixing issue #8 - parallel requests to a wrapped function were calling the - Fixing issue #8 - parallel requests to a wrapped function were calling the
function multiple times. (Thanks alex-whitney). function multiple times. (Thanks alex-whitney).
- 0.8.0 2014-07-07 - 0.8.0 2014-07-07
Adding setex() (Thanks evanlucas) - Adding setex() (Thanks evanlucas)
- 0.7.1 2014-06-15 - 0.7.1 2014-06-15
Adding link to Express.js cache-manager example app - Adding link to Express.js cache-manager example app
- 0.7.0 2014-06-15 - 0.7.0 2014-06-15
Bumping package versions, mostly devDependencies - Bumping package versions, mostly devDependencies
- 0.6.0 2014-06-15 - 0.6.0 2014-06-15
Adding caching.keys() function (issue #6) - Adding caching.keys() function (issue #6)
Updating examples/redis_example/example.js with cache.keys() usage - Updating examples/redis_example/example.js with cache.keys() usage
Allow calling memory store get() without callback - Allow calling memory store get() without callback
- 0.5.0 2014-05-02 - 0.5.0 2014-05-02
Adding reset() function to caching.js. Closes #5. - Adding reset() function to caching.js. Closes #5.
- 0.4.0 2014-05-02 - 0.4.0 2014-05-02
New arg to ignore cache errors. if set cache errors will be ignored - New arg to ignore cache errors. if set cache errors will be ignored
and the cache_manager will go to the backing store. (Thanks londonjamo). and the cache_manager will go to the backing store. (Thanks londonjamo).
- 0.3.0 2013-12-08 - 0.3.0 2013-12-08
Bound the get, set and del functions to their original “this” context when assigning a store. - Bound the get, set and del functions to their original “this” context when assigning a store.
(Thanks to Boyan Rabchev) (Thanks to Boyan Rabchev)
- 0.2.0 2013-10-31 - 0.2.0 2013-10-31
Better examples, version bump. - Better examples, version bump.
- 0.1.3 2013-10-31 - 0.1.3 2013-10-31
Fixing unreleased connection in redis example. - Fixing unreleased connection in redis example.
- 0.1.2 2013-10-13 - 0.1.2 2013-10-13
Wrapping synchronous memory cache callbacks in process.nextTick() for the purists. - Wrapping synchronous memory cache callbacks in process.nextTick() for the purists.
- 0.1.1 2013-10-13 - 0.1.1 2013-10-13
Travis and Coveralls integration testing. - Travis and Coveralls integration testing.
- 0.1.0 2013-10-13 - 0.1.0 2013-10-13
Removing built-in Redis store to emphasize that you should plug in your own - Removing built-in Redis store to emphasize that you should plug in your own
cache store. cache store.
- 0.0.5 2013-10-13 - 0.0.5 2013-10-13
Removing hiredis requirement. - Removing hiredis requirement.
- 0.0.4 2013-08-01 - 0.0.4 2013-08-01
Better error checking in multi_cache.wrap(); - Better error checking in multi_cache.wrap();
- 0.0.3 2013-07-10 - 0.0.3 2013-07-10
Better error checking in cache.wrap(); - Better error checking in cache.wrap();
- 0.0.2 2013-04-08 - 0.0.2 2013-04-08
Added ability to pass in a store module that isn't already instantiated. - Added ability to pass in a store module that isn't already instantiated. E.g.,
E.g.,
```javascript ```javascript
var store = require('/path/to/my_memory_store'); var store = require('/path/to/my_memory_store');
cache = caching({store: store}); cache = caching({store: store});
``` ```
- 0.0.1 2013-04-08 - 0.0.1 2013-04-08
Initial release. - Initial release.

76
README.md

@ -35,17 +35,17 @@ First, it includes a `wrap` function that lets you wrap any function in cache.
This is probably the feature you're looking for. As an example, where you might have to do this: This is probably the feature you're looking for. As an example, where you might have to do this:
```javascript ```javascript
function get_cached_user(id, cb) { function getCachedUser(id, cb) {
memory_cache.get(id, function (err, result) { memoryCache.get(id, function (err, result) {
if (err) { return cb(err); } if (err) { return cb(err); }
if (result) { if (result) {
return cb(null, result); return cb(null, result);
} }
get_user(id, function (err, result) { getUser(id, function (err, result) {
if (err) { return cb(err); } if (err) { return cb(err); }
memory_cache.set(id, result); memoryCache.set(id, result);
cb(null, result); cb(null, result);
}); });
}); });
@ -54,9 +54,9 @@ function get_cached_user(id, cb) {
... you can instead use the `wrap` function: ... you can instead use the `wrap` function:
```javascript ```javascript
function get_cached_user(id, cb) { function getCachedUser(id, cb) {
memory_cache.wrap(id, function (cache_callback) { memoryCache.wrap(id, function (cacheCallback) {
get_user(id, cache_callback); getUser(id, cacheCallback);
}, ttl, cb); }, ttl, cb);
} }
``` ```
@ -86,40 +86,40 @@ Redis cache store with connection pooling.
### Single Store ### Single Store
```javascript ```javascript
var cache_manager = require('cache-manager'); var cacheManager = require('cache-manager');
var memory_cache = cache_manager.caching({store: 'memory', max: 100, ttl: 10/*seconds*/}); var memoryCache = cacheManager.caching({store: 'memory', max: 100, ttl: 10/*seconds*/});
var ttl = 5; var ttl = 5;
// Note: callback is optional in set() and del(). // Note: callback is optional in set() and del().
memory_cache.set('foo', 'bar', ttl, function(err) { memoryCache.set('foo', 'bar', ttl, function(err) {
if (err) { throw err; } if (err) { throw err; }
memory_cache.get('foo', function(err, result) { memoryCache.get('foo', function(err, result) {
console.log(result); console.log(result);
// >> 'bar' // >> 'bar'
memory_cache.del('foo', function(err) {}); memoryCache.del('foo', function(err) {});
}); });
}); });
function get_user(id, cb) { function getUser(id, cb) {
setTimeout(function () { setTimeout(function () {
console.log("Returning user from slow database."); console.log("Returning user from slow database.");
cb(null, {id: id, name: 'Bob'}); cb(null, {id: id, name: 'Bob'});
}, 100); }, 100);
} }
var user_id = 123; var userId = 123;
var key = 'user_' + user_id; var key = 'user_' + userId;
// Note: ttl is optional in wrap() // Note: ttl is optional in wrap()
memory_cache.wrap(key, function (cb) { memoryCache.wrap(key, function (cb) {
get_user(user_id, cb); getUser(userId, cb);
}, ttl, function (err, user) { }, ttl, function (err, user) {
console.log(user); console.log(user);
// Second time fetches user from memory_cache // Second time fetches user from memoryCache
memory_cache.wrap(key, function (cb) { memoryCache.wrap(key, function (cb) {
get_user(user_id, cb); getUser(userId, cb);
}, function (err, user) { }, function (err, user) {
console.log(user); console.log(user);
}); });
@ -143,10 +143,10 @@ function respond(res, err, data) {
} }
app.get('/foo/bar', function(req, res) { app.get('/foo/bar', function(req, res) {
var cache_key = 'foo-bar:' + JSON.stringify(req.query); var cacheKey = 'foo-bar:' + JSON.stringify(req.query);
var ttl = 10; var ttl = 10;
memory_cache.wrap(cache_key, function(cache_cb) { memoryCache.wrap(cacheKey, function(cacheCallback) {
DB.find(req.query, cache_cb); DB.find(req.query, cacheCallback);
}, ttl, function(err, result) { }, ttl, function(err, result) {
respond(res, err, result); respond(res, err, result);
}); });
@ -162,45 +162,45 @@ in an instance of it, or pass in the path to the module.
E.g., E.g.,
```javascript ```javascript
var my_store = require('your-homemade-store'); var myStore = require('your-homemade-store');
var cache = cache_manager.caching({store: my_store}); var cache = cacheManager.caching({store: myStore});
// or // or
var cache = cache_manager.caching({store: '/path/to/your/store'}); var cache = cacheManager.caching({store: '/path/to/your/store'});
``` ```
### Multi-Store ### Multi-Store
```javascript ```javascript
var multi_cache = cache_manager.multi_caching([memory_cache, some_other_cache]); var multiCache = cacheManager.multiCaching([memoryCache, someOtherCache]);
user_id2 = 456; userId2 = 456;
key2 = 'user_' + user_id; key2 = 'user_' + userId;
ttl = 5; ttl = 5;
// Sets in all caches. // Sets in all caches.
multi_cache.set('foo2', 'bar2', ttl, function(err) { multiCache.set('foo2', 'bar2', ttl, function(err) {
if (err) { throw err; } if (err) { throw err; }
// Fetches from highest priority cache that has the key. // Fetches from highest priority cache that has the key.
multi_cache.get('foo2', function(err, result) { multiCache.get('foo2', function(err, result) {
console.log(result); console.log(result);
// >> 'bar2' // >> 'bar2'
// Delete from all caches // Delete from all caches
multi_cache.del('foo2'); multiCache.del('foo2');
}); });
}); });
// Note: ttl is optional in wrap() // Note: ttl is optional in wrap()
multi_cache.wrap(key2, function (cb) { multiCache.wrap(key2, function (cb) {
get_user(user_id2, cb); getUser(userId2, cb);
}, ttl, function (err, user) { }, ttl, function (err, user) {
console.log(user); console.log(user);
// Second time fetches user from memory_cache, since it's highest priority. // Second time fetches user from memoryCache, since it's highest priority.
// If the data expires in the memory cache, the next fetch would pull it from // If the data expires in the memory cache, the next fetch would pull it from
// the 'some_other_cache', and set the data in memory again. // the 'someOtherCache', and set the data in memory again.
multi_cache.wrap(key2, function (cb) { multiCache.wrap(key2, function (cb) {
get_user(user_id2, cb); getUser(userId2, cb);
}, function (err, user) { }, function (err, user) {
console.log(user); console.log(user);
}); });

72
examples/example.js

@ -1,20 +1,20 @@
/*jshint unused:false*/ /*jshint unused:false*/
// Note: ttls are in seconds // Note: ttls are in seconds
var cache_manager = require('../'); var cacheManager = require('../');
var memory_cache = cache_manager.caching({store: 'memory', max: 100, ttl: 10}); var memoryCache = cacheManager.caching({store: 'memory', max: 100, ttl: 10});
var memory_cache2 = cache_manager.caching({store: 'memory', max: 100, ttl: 100}); var memoryCache2 = cacheManager.caching({store: 'memory', max: 100, ttl: 100});
var ttl; //Can't use a different ttl per set() call with memory cache var ttl; //Can't use a different ttl per set() call with memory cache
// //
// Basic usage // Basic usage
// //
memory_cache.set('foo', 'bar', ttl, function(err) { memoryCache.set('foo', 'bar', ttl, function(err) {
if (err) { throw err; } if (err) { throw err; }
memory_cache.get('foo', function(err, result) { memoryCache.get('foo', function(err, result) {
console.log(result); console.log(result);
// >> 'bar' // >> 'bar'
memory_cache.del('foo', function(err) { memoryCache.del('foo', function(err) {
if (err) { if (err) {
console.log(err); console.log(err);
} }
@ -22,49 +22,49 @@ memory_cache.set('foo', 'bar', ttl, function(err) {
}); });
}); });
function get_user(id, cb) { function getUser(id, cb) {
setTimeout(function() { setTimeout(function() {
console.log("Fetching user from slow database."); console.log("Fetching user from slow database.");
cb(null, {id: id, name: 'Bob'}); cb(null, {id: id, name: 'Bob'});
}, 100); }, 100);
} }
var user_id = 123; var userId = 123;
var key = 'user_' + user_id; var key = 'user_' + userId;
// //
// wrap() example // wrap() example
// //
// Instead of manually managing the cache like this: // Instead of manually managing the cache like this:
function get_cached_user_manually(id, cb) { function getCachedUserManually(id, cb) {
memory_cache.get(id, function(err, result) { memoryCache.get(id, function(err, result) {
if (err) { return cb(err); } if (err) { return cb(err); }
if (result) { if (result) {
return cb(null, result); return cb(null, result);
} }
get_user(id, function(err, result) { getUser(id, function(err, result) {
if (err) { return cb(err); } if (err) { return cb(err); }
memory_cache.set(id, result); memoryCache.set(id, result);
cb(null, result); cb(null, result);
}); });
}); });
} }
// ... you can instead use the `wrap` function: // ... you can instead use the `wrap` function:
function get_cached_user(id, cb) { function getCachedUser(id, cb) {
memory_cache.wrap(id, function(cache_callback) { memoryCache.wrap(id, function(cacheCallback) {
get_user(id, cache_callback); getUser(id, cacheCallback);
}, cb); }, cb);
} }
get_cached_user(user_id, function(err, user) { getCachedUser(userId, function(err, user) {
// First time fetches the user from the (fake) database: // First time fetches the user from the (fake) database:
console.log(user); console.log(user);
get_cached_user(user_id, function(err, user) { getCachedUser(userId, function(err, user) {
// Second time fetches from cache. // Second time fetches from cache.
console.log(user); console.log(user);
}); });
@ -76,14 +76,14 @@ get_cached_user(user_id, function(err, user) {
// { id: 123, name: 'Bob' } // { id: 123, name: 'Bob' }
// Same as above, but written differently: // Same as above, but written differently:
memory_cache.wrap(key, function(cb) { memoryCache.wrap(key, function(cb) {
get_user(user_id, cb); getUser(userId, cb);
}, function(err, user) { }, function(err, user) {
console.log(user); console.log(user);
// Second time fetches user from memory_cache // Second time fetches user from memoryCache
memory_cache.wrap(key, function(cb) { memoryCache.wrap(key, function(cb) {
get_user(user_id, cb); getUser(userId, cb);
}, function(err, user) { }, function(err, user) {
console.log(user); console.log(user);
}); });
@ -92,36 +92,36 @@ memory_cache.wrap(key, function(cb) {
// //
// multi-cache example // multi-cache example
// //
var multi_cache = cache_manager.multi_caching([memory_cache, memory_cache2]); var multiCache = cacheManager.multiCaching([memoryCache, memoryCache2]);
var user_id2 = 456; var userId2 = 456;
var key2 = 'user_' + user_id; var key2 = 'user_' + userId;
var ttl2; //Can't use a different ttl per set() call with memory cache var ttl2; //Can't use a different ttl per set() call with memory cache
multi_cache.wrap(key2, function(cb) { multiCache.wrap(key2, function(cb) {
get_user(user_id2, cb); getUser(userId2, cb);
}, function(err, user) { }, function(err, user) {
console.log(user); console.log(user);
// Second time fetches user from memory_cache, since it's highest priority. // Second time fetches user from memoryCache, since it's highest priority.
// If the data expires in the memory cache, the next fetch would pull it from // If the data expires in memoryCache, the next fetch would pull it from
// the Redis cache, and set the data in memory again. // the memoryCache2, and set the data in memoryCache.
multi_cache.wrap(key2, function(cb) { multiCache.wrap(key2, function(cb) {
get_user(user_id2, cb); getUser(userId2, cb);
}, function(err, user) { }, function(err, user) {
console.log(user); console.log(user);
}); });
// Sets in all caches. // Sets in all caches.
multi_cache.set('foo2', 'bar2', ttl2, function(err) { multiCache.set('foo2', 'bar2', ttl2, function(err) {
if (err) { throw err; } if (err) { throw err; }
// Fetches from highest priority cache that has the key. // Fetches from highest priority cache that has the key.
multi_cache.get('foo2', function(err, result) { multiCache.get('foo2', function(err, result) {
console.log(result); console.log(result);
// >> 'bar2' // >> 'bar2'
// Delete from all caches // Delete from all caches
multi_cache.del('foo2', function(err) { multiCache.del('foo2', function(err) {
if (err) { if (err) {
console.log(err); console.log(err);
} }

102
examples/redis_example/example.js

@ -1,92 +1,110 @@
// Setup: // Setup:
// npm install redis@0.6.7 sol-redis-pool@0.1.0 // npm install redis@0.12.1 sol-redis-pool@0.2.0
// node examples/redis_example/example.js // node examples/redis_example/example.js
var util = require('util'); var util = require('util');
var cache_manager = require('../../'); var assert = require('assert');
var redis_store = require('./redis_store'); var cacheManager = require('../../');
var redisStore = require('./redis_store');
// Note: ttl is in seconds // Note: ttl is in seconds
var redis_cache = cache_manager.caching({store: redis_store, db: 0, ttl: 100}); var redisCache = cacheManager.caching({store: redisStore, db: 0, ttl: 100});
var ttl = 60; var ttl = 60;
console.log("set/get/del example:"); console.log("set/get/del example:");
redis_cache.set('foo', 'bar', {ttl: ttl}, function(err) { redisCache.set('foo', 'bar', {ttl: ttl}, function(err) {
if (err) { throw err; } if (err) { throw err; }
redis_cache.get('foo', function(err, result) { redisCache.get('foo', function(err, result) {
if (err) { throw err; } if (err) { throw err; }
console.log("result fetched from cache: " + result); console.log("result fetched from cache: " + result);
// >> 'bar' // >> 'bar'
redis_cache.del('foo', function(err) {
redisCache.ttl('foo', function(err, result) {
if (err) { throw err; }
assert.ok(result > 59 && result < 61);
redisCache.del('foo', function(err) {
if (err) { throw err; } if (err) { throw err; }
}); });
}); });
});
}); });
// TTL defaults to what we passed into the caching function (100) // TTL defaults to what we passed into the caching function (100)
redis_cache.set('foo-no-ttl', 'bar-no-ttl', function(err) { redisCache.set('foo-no-ttl', 'bar-no-ttl', function(err) {
if (err) { throw err; } if (err) { throw err; }
redis_cache.get('foo-no-ttl', function(err, result) { redisCache.get('foo-no-ttl', function(err, result) {
if (err) { throw err; } if (err) { throw err; }
console.log("result fetched from cache: " + result); console.log("result fetched from cache: " + result);
// >> 'bar' // >> 'bar'
redis_cache.del('foo-no-ttl', function(err) {
redisCache.ttl('foo-no-ttl', function(err, result) {
if (err) { throw err; } if (err) { throw err; }
assert.ok(result > 99 && result < 101);
redisCache.del('foo-no-ttl', function(err) {
if (err) { throw err; }
});
}); });
}); });
}); });
// Calls Redis 'set' instead of 'setex' // Calls Redis 'set' instead of 'setex'
redis_cache.set('foo-zero-ttl', 'bar-zero-ttl', {ttl: 0}, function(err) { redisCache.set('foo-zero-ttl', 'bar-zero-ttl', {ttl: 0}, function(err) {
if (err) { throw err; } if (err) { throw err; }
redis_cache.get('foo-zero-ttl', function(err, result) { redisCache.get('foo-zero-ttl', function(err, result) {
if (err) { throw err; } if (err) { throw err; }
console.log("result fetched from cache: " + result); console.log("result fetched from cache: " + result);
// >> 'bar' // >> 'bar'
redis_cache.del('foo-zero-ttl', function(err) {
redisCache.ttl('foo-zero-ttl', function(err, result) {
if (err) { throw err; }
assert.ok(result < 0);
redisCache.del('foo-zero-ttl', function(err) {
if (err) { throw err; } if (err) { throw err; }
}); });
}); });
});
}); });
var user_id = 123; var userId = 123;
function create_key(id) { function createKey(id) {
return 'user_' + id; return 'user_' + id;
} }
function get_user(id, cb) { function getUser(id, cb) {
setTimeout(function() { setTimeout(function() {
console.log("\n\nReturning user from slow database."); console.log("\n\nReturning user from slow database.");
cb(null, {id: id, name: 'Bob'}); cb(null, {id: id, name: 'Bob'});
}, 100); }, 100);
} }
function get_user_from_cache(id, cb) { function getUserFromCache(id, cb) {
var key = create_key(id); var key = createKey(id);
redis_cache.wrap(key, function(cache_cb) { redisCache.wrap(key, function(cacheCb) {
get_user(user_id, cache_cb); getUser(userId, cacheCb);
}, {ttl: ttl}, cb); }, {ttl: ttl}, cb);
} }
get_user_from_cache(user_id, function(err, user) { getUserFromCache(userId, function(err, user) {
console.log(user); console.log(user);
// Second time fetches user from redis_cache // Second time fetches user from redisCache
get_user_from_cache(user_id, function(err, user) { getUserFromCache(userId, function(err, user) {
console.log("user from second cache request:"); console.log("user from second cache request:");
console.log(user); console.log(user);
redis_cache.keys(function(err, keys) { redisCache.keys(function(err, keys) {
console.log("keys: " + util.inspect(keys)); console.log("keys: " + util.inspect(keys));
var key = create_key(user_id); var key = createKey(userId);
redis_cache.del(key, function(err) { redisCache.del(key, function(err) {
if (err) { throw err; } if (err) { throw err; }
process.exit();
}); });
}); });
}); });
@ -97,3 +115,33 @@ get_user_from_cache(user_id, function(err, user) {
// user from second cache request: // user from second cache request:
// { id: 123, name: 'Bob' } // { id: 123, name: 'Bob' }
// keys: [ 'user_123' ] // keys: [ 'user_123' ]
var redisCache2 = cacheManager.caching({store: redisStore, db: 1, ttl: 100});
var multiCache = cacheManager.multiCaching([redisCache, redisCache2]);
var userId2 = 456;
var key2 = 'user_' + userId;
var ttl2 = 50;
multiCache.wrap(key2, function(cb) {
getUser(userId2, cb);
}, {ttl: ttl2}, function(err, user) {
console.log("user: ", user);
// Second time fetches user from redisCache, since it's highest priority.
// If the data expires in the redisCache, the next fetch would pull it from
// redisCache2, and set the data in redisCache again.
multiCache.wrap(key2, function(cb) {
getUser(userId2, cb);
}, function(err, user) {
console.log("user, second fetch:", user);
});
multiCache.getAndPassUp(key2, function(err, result) {
console.log("\ngetAndPassUp result: ", result);
multiCache.del(key2, function(err) {
if (err) { throw err; }
process.exit();
});
});
});

64
examples/redis_example/redis_store.js

@ -5,19 +5,18 @@
var RedisPool = require('sol-redis-pool'); var RedisPool = require('sol-redis-pool');
function redis_store(args) { function redisStore(args) {
args = args || {}; args = args || {};
var self = {}; var self = {};
var ttlDefault = args.ttl; var ttlDefault = args.ttl;
self.name = 'redis'; self.name = 'redis';
self.client = require('redis').createClient(args.port, args.host, args);
var redis_options = { var redisOptions = {
redis_host: args.host || '127.0.0.1', host: args.host || '127.0.0.1',
redis_port: args.port || 6379 port: args.port || 6379
}; };
var pool = new RedisPool(redis_options); var pool = new RedisPool(redisOptions, {});
function connect(cb) { function connect(cb) {
pool.acquire(function(err, conn) { pool.acquire(function(err, conn) {
@ -34,6 +33,22 @@ function redis_store(args) {
}); });
} }
function handleResponse(conn, cb, opts) {
opts = opts || {};
return function(err, result) {
pool.release(conn);
if (err) { return cb(err); }
if (opts.parse) {
result = JSON.parse(result);
}
cb(null, result);
};
}
self.get = function(key, options, cb) { self.get = function(key, options, cb) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
@ -41,12 +56,7 @@ function redis_store(args) {
connect(function(err, conn) { connect(function(err, conn) {
if (err) { return cb(err); } if (err) { return cb(err); }
conn.get(key, handleResponse(conn, cb, {parse: true}));
conn.get(key, function(err, result) {
pool.release(conn);
if (err) { return cb(err); }
cb(null, JSON.parse(result));
});
}); });
}; };
@ -61,17 +71,12 @@ function redis_store(args) {
connect(function(err, conn) { connect(function(err, conn) {
if (err) { return cb(err); } if (err) { return cb(err); }
var val = JSON.stringify(value);
if (ttl) { if (ttl) {
conn.setex(key, ttl, JSON.stringify(value), function(err, result) { conn.setex(key, ttl, val, handleResponse(conn, cb));
pool.release(conn);
cb(err, result);
});
} else { } else {
conn.set(key, JSON.stringify(value), function(err, result) { conn.set(key, val, handleResponse(conn, cb));
pool.release(conn);
cb(err, result);
});
} }
}); });
}; };
@ -79,11 +84,14 @@ function redis_store(args) {
self.del = function(key, cb) { self.del = function(key, cb) {
connect(function(err, conn) { connect(function(err, conn) {
if (err) { return cb(err); } if (err) { return cb(err); }
conn.del(key, handleResponse(conn, cb));
conn.del(key, function(err, result) {
pool.release(conn);
cb(err, result);
}); });
};
self.ttl = function(key, cb) {
connect(function(err, conn) {
if (err) { return cb(err); }
conn.ttl(key, handleResponse(conn, cb));
}); });
}; };
@ -95,11 +103,7 @@ function redis_store(args) {
connect(function(err, conn) { connect(function(err, conn) {
if (err) { return cb(err); } if (err) { return cb(err); }
conn.keys(pattern, handleResponse(conn, cb));
conn.keys(pattern, function(err, result) {
pool.release(conn);
cb(err, result);
});
}); });
}; };
@ -108,6 +112,6 @@ function redis_store(args) {
module.exports = { module.exports = {
create: function(args) { create: function(args) {
return redis_store(args); return redisStore(args);
} }
}; };

3
index.js

@ -1,6 +1,7 @@
var cache = { var cache = {
caching: require('./lib/caching'), caching: require('./lib/caching'),
multi_caching: require('./lib/multi_caching') multi_caching: require('./lib/multi_caching'), //backward compat
multiCaching: require('./lib/multi_caching')
}; };
module.exports = cache; module.exports = cache;

47
lib/caching.js

@ -1,6 +1,6 @@
/*jshint maxcomplexity:15*/ /*jshint maxcomplexity:15*/
/*jshint -W072 */
var domain = require('domain'); var domain = require('domain');
var CallbackFiller = require('./callback_filler');
var caching = function(args) { var caching = function(args) {
args = args || {}; args = args || {};
@ -14,14 +14,14 @@ var caching = function(args) {
} else if (typeof args.store === 'string' && args.store.match(/\//)) { } else if (typeof args.store === 'string' && args.store.match(/\//)) {
self.store = require(args.store).create(args); self.store = require(args.store).create(args);
} else { } else {
var store_name = args.store || 'memory'; var storeName = args.store || 'memory';
self.store = require('./stores/' + store_name).create(args); self.store = require('./stores/' + storeName).create(args);
} }
// do we handle a cache error the same as a cache miss? // do we handle a cache error the same as a cache miss?
self.ignoreCacheErrors = args.ignoreCacheErrors || false; self.ignoreCacheErrors = args.ignoreCacheErrors || false;
self.queues = {}; var callbackFiller = new CallbackFiller();
/** /**
* 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,
@ -30,9 +30,9 @@ var caching = function(args) {
* *
* @example * @example
* *
* var key = 'user_' + user_id; * var key = 'user_' + userId;
* cache.wrap(key, function(cb) { * cache.wrap(key, function(cb) {
* User.get(user_id, cb); * User.get(userId, cb);
* }, function(err, user) { * }, function(err, user) {
* console.log(user); * console.log(user);
* }); * });
@ -43,44 +43,31 @@ var caching = function(args) {
options = undefined; options = undefined;
} }
if (self.queues[key]) { var hasKey = callbackFiller.has(key);
self.queues[key].push({cb: cb, domain: process.domain}); callbackFiller.add(key, {cb: cb, domain: process.domain});
return; if (hasKey) { return; }
}
self.queues[key] = [{cb: cb, domain: process.domain}];
function fillCallbacks(err, data) {
var waiting = self.queues[key];
delete self.queues[key];
waiting.forEach(function(task) {
var taskDomain = task.domain || domain.create();
taskDomain.bind(task.cb)(err, data);
});
}
self.store.get(key, options, function(err, result) { self.store.get(key, options, function(err, result) {
if (err && (!self.ignoreCacheErrors)) { if (err && (!self.ignoreCacheErrors)) {
fillCallbacks(err); callbackFiller.fill(key, err);
} else if (result) { } else if (result) {
fillCallbacks(null, result); callbackFiller.fill(key, null, result);
} else { } else {
domain domain
.create() .create()
.on('error', function(err) { .on('error', function(err) {
fillCallbacks(err); callbackFiller.fill(key, err);
}) })
.bind(work)(function(err, data) { .bind(work)(function(err, data) {
if (err) { if (err) {
fillCallbacks(err); callbackFiller.fill(key, err);
return; return;
} }
self.store.set(key, data, options, function(err) { self.store.set(key, data, options, function(err) {
if (err && (!self.ignoreCacheErrors)) { if (err && (!self.ignoreCacheErrors)) {
fillCallbacks(err); callbackFiller.fill(key, err);
} else { } else {
fillCallbacks(null, data); callbackFiller.fill(key, null, data);
} }
}); });
}); });
@ -108,6 +95,10 @@ var caching = function(args) {
self.keys = self.store.keys.bind(self.store); self.keys = self.store.keys.bind(self.store);
} }
if (typeof self.store.ttl === 'function') {
self.ttl = self.store.ttl.bind(self.store);
}
return self; return self;
}; };

31
lib/callback_filler.js

@ -0,0 +1,31 @@
var domain = require('domain');
function CallbackFiller() {
this.queues = {};
}
CallbackFiller.prototype.fill = function(key, err, data) {
var self = this;
var waiting = self.queues[key];
delete self.queues[key];
waiting.forEach(function(task) {
var taskDomain = task.domain || domain.create();
taskDomain.bind(task.cb)(err, data);
});
};
CallbackFiller.prototype.has = function(key) {
return this.queues[key];
};
CallbackFiller.prototype.add = function(key, funcObj) {
if (this.queues[key]) {
this.queues[key].push(funcObj);
} else {
this.queues[key] = [funcObj];
}
};
module.exports = CallbackFiller;

118
lib/multi_caching.js

@ -1,28 +1,29 @@
var async = require('async'); var async = require('async');
var domain = require('domain'); var domain = require('domain');
var CallbackFiller = require('./callback_filler');
/** /**
* Module that lets you specify a hierarchy of caches. * Module that lets you specify a hierarchy of caches.
*/ */
var multi_caching = function(caches) { var multiCaching = function(caches) {
var self = {}; var self = {};
if (!Array.isArray(caches)) { if (!Array.isArray(caches)) {
throw new Error('multi_caching requires an array of caches'); throw new Error('multiCaching requires an array of caches');
} }
self.queues = {}; var callbackFiller = new CallbackFiller();
function get_from_highest_priority_cache(key, options, cb) { function getFromHighestPriorityCache(key, options, cb) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
options = undefined; options = undefined;
} }
var i = 0; var i = 0;
async.forEachSeries(caches, function(cache, async_cb) { async.eachSeries(caches, function(cache, next) {
var callback = function(err, result) { var callback = function(err, result) {
if (err) { if (err) {
return cb(err); return next(err);
} }
if (result) { if (result) {
// break out of async loop. // break out of async loop.
@ -30,8 +31,9 @@ var multi_caching = function(caches) {
} }
i += 1; i += 1;
async_cb(err); next();
}; };
if (typeof options === 'object') { if (typeof options === 'object') {
cache.store.get(key, options, callback); cache.store.get(key, options, callback);
} else { } else {
@ -40,12 +42,12 @@ var multi_caching = function(caches) {
}, cb); }, cb);
} }
function set_in_multiple_caches(caches, opts, cb) { function setInMultipleCaches(caches, opts, cb) {
async.forEach(caches, function(cache, async_cb) { async.each(caches, function(cache, next) {
if (typeof opts.options !== 'object') { if (typeof opts.options === 'object') {
cache.store.set(opts.key, opts.value, opts.ttl, async_cb); cache.store.set(opts.key, opts.value, opts.options, next);
} else { } else {
cache.store.set(opts.key, opts.value, opts.options, async_cb); cache.store.set(opts.key, opts.value, opts.ttl, next);
} }
}, cb); }, cb);
} }
@ -55,8 +57,8 @@ var multi_caching = function(caches) {
* *
* When a key is found in a lower cache, all higher levels are updated * When a key is found in a lower cache, all higher levels are updated
*/ */
self.get_and_pass_up = function(key, cb) { self.getAndPassUp = function(key, cb) {
get_from_highest_priority_cache(key, function(err, result, index) { getFromHighestPriorityCache(key, function(err, result, index) {
if (err) { if (err) {
return cb(err); return cb(err);
} }
@ -65,13 +67,21 @@ var multi_caching = function(caches) {
if (result !== undefined && index) { if (result !== undefined && index) {
var cachesToUpdate = caches.slice(0, index); var cachesToUpdate = caches.slice(0, index);
async.forEach(cachesToUpdate, function(cache, async_cb) { async.each(cachesToUpdate, function(cache, next) {
cache.set(key, result, result.ttl, async_cb); // We rely on the cache module's default TTL
cache.set(key, result, next);
}); });
} }
}); });
}; };
/**
* This is for backward-compatibility
*/
//jscs:disable requireCamelCaseOrUpperCaseIdentifiers
self.get_and_pass_up = self.getAndPassUp;
//jscs:enable requireCamelCaseOrUpperCaseIdentifiers
/** /**
* 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.
@ -88,28 +98,7 @@ var multi_caching = function(caches) {
options = undefined; options = undefined;
} }
if (self.queues[key]) { function getOptsForSet(result) {
self.queues[key].push({cb: cb, domain: process.domain});
return;
}
self.queues[key] = [{cb: cb, domain: process.domain}];
function fillCallbacks(err, data) {
var waiting = self.queues[key];
delete self.queues[key];
waiting.forEach(function(task) {
var taskDomain = task.domain || domain.create();
taskDomain.bind(task.cb)(err, data);
});
}
get_from_highest_priority_cache(key, function(err, result, index) {
if (err) {
return fillCallbacks(err);
} else if (result) {
var caches_to_update = caches.slice(0, index);
var opts = { var opts = {
key: key, key: key,
value: result, value: result,
@ -120,35 +109,38 @@ var multi_caching = function(caches) {
opts.ttl = options; opts.ttl = options;
} }
set_in_multiple_caches(caches_to_update, opts, function(err) { return opts;
fillCallbacks(err, result); }
var hasKey = callbackFiller.has(key);
callbackFiller.add(key, {cb: cb, domain: process.domain});
if (hasKey) { return; }
getFromHighestPriorityCache(key, function(err, result, index) {
if (err) {
return callbackFiller.fill(key, err);
} else if (result) {
var cachesToUpdate = caches.slice(0, index);
var opts = getOptsForSet(result);
setInMultipleCaches(cachesToUpdate, opts, function(err) {
callbackFiller.fill(key, err, result);
}); });
} else { } else {
domain domain
.create() .create()
.on('error', function(err) { .on('error', function(err) {
fillCallbacks(err); callbackFiller.fill(key, err);
}) })
.bind(work)(function(err, data) { .bind(work)(function(err, data) {
if (err) { if (err) {
fillCallbacks(err); return callbackFiller.fill(key, err);
return;
} }
var opts = {
key: key,
value: data,
options: options
};
if (typeof options !== 'object') { var opts = getOptsForSet(data);
opts.ttl = options;
} setInMultipleCaches(caches, opts, function(err) {
set_in_multiple_caches(caches, opts, function(err) { callbackFiller.fill(key, err, data);
if (err) {
fillCallbacks(err);
} else {
fillCallbacks(null, data);
}
}); });
}); });
} }
@ -164,7 +156,7 @@ var multi_caching = function(caches) {
if (typeof options !== 'object') { if (typeof options !== 'object') {
opts.ttl = options; opts.ttl = options;
} }
set_in_multiple_caches(caches, opts, cb); setInMultipleCaches(caches, opts, cb);
}; };
self.get = function(key, options, cb) { self.get = function(key, options, cb) {
@ -172,7 +164,7 @@ var multi_caching = function(caches) {
cb = options; cb = options;
options = false; options = false;
} }
get_from_highest_priority_cache(key, options, cb); getFromHighestPriorityCache(key, options, cb);
}; };
self.del = function(key, options, cb) { self.del = function(key, options, cb) {
@ -180,11 +172,11 @@ var multi_caching = function(caches) {
cb = options; cb = options;
options = false; options = false;
} }
async.forEach(caches, function(cache, async_cb) { async.each(caches, function(cache, next) {
if (typeof options === 'object') { if (typeof options === 'object') {
cache.store.del(key, options, async_cb); cache.store.del(key, options, next);
} else { } else {
cache.store.del(key, async_cb); cache.store.del(key, next);
} }
}, cb); }, cb);
}; };
@ -192,4 +184,4 @@ var multi_caching = function(caches) {
return self; return self;
}; };
module.exports = multi_caching; module.exports = multiCaching;

18
lib/stores/memory.js

@ -1,19 +1,19 @@
var Lru = require("lru-cache"); var Lru = require("lru-cache");
var memory_store = function(args) { var memoryStore = function(args) {
args = args || {}; args = args || {};
var self = {}; var self = {};
self.name = 'memory'; self.name = 'memory';
var ttl = args.ttl; var ttl = args.ttl;
var lru_opts = { var lruOpts = {
max: args.max || 500, max: args.max || 500,
maxAge: ttl ? ttl * 1000 : null maxAge: ttl ? ttl * 1000 : null
}; };
var lru_cache = new Lru(lru_opts); var lruCache = new Lru(lruOpts);
self.set = function(key, value, options, cb) { self.set = function(key, value, options, cb) {
lru_cache.set(key, value); lruCache.set(key, value);
if (cb) { if (cb) {
process.nextTick(cb); process.nextTick(cb);
} }
@ -23,7 +23,7 @@ var memory_store = function(args) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
} }
var value = lru_cache.get(key); var value = lruCache.get(key);
if (cb) { if (cb) {
process.nextTick(function() { process.nextTick(function() {
cb(null, value); cb(null, value);
@ -37,21 +37,21 @@ var memory_store = function(args) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
} }
lru_cache.del(key); lruCache.del(key);
if (cb) { if (cb) {
process.nextTick(cb); process.nextTick(cb);
} }
}; };
self.reset = function(cb) { self.reset = function(cb) {
lru_cache.reset(); lruCache.reset();
if (cb) { if (cb) {
process.nextTick(cb); process.nextTick(cb);
} }
}; };
self.keys = function(cb) { self.keys = function(cb) {
var keys = lru_cache.keys(); var keys = lruCache.keys();
if (cb) { if (cb) {
process.nextTick(function() { process.nextTick(function() {
cb(null, keys); cb(null, keys);
@ -66,7 +66,7 @@ var memory_store = function(args) {
var methods = { var methods = {
create: function(args) { create: function(args) {
return memory_store(args); return memoryStore(args);
} }
}; };

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "cache-manager", "name": "cache-manager",
"version": "0.17.0", "version": "0.18.0",
"description": "Cache module for Node.js", "description": "Cache module for Node.js",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

248
test/caching.unit.js

@ -4,12 +4,12 @@ var assert = require('assert');
var async = require('async'); var async = require('async');
var sinon = require('sinon'); var sinon = require('sinon');
var support = require('./support'); var support = require('./support');
var check_err = support.check_err; var checkErr = support.checkErr;
var caching = require('../index').caching; var caching = require('../index').caching;
var memory_store = require('../lib/stores/memory'); var memoryStore = require('../lib/stores/memory');
var methods = { var methods = {
get_widget: function(name, cb) { getWidget: function(name, cb) {
cb(null, {name: name}); cb(null, {name: name});
} }
}; };
@ -32,7 +32,7 @@ describe("caching", function() {
it("lets us set and get data in cache", function(done) { it("lets us set and get data in cache", function(done) {
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.equal(result, value); assert.equal(result, value);
done(); done();
@ -69,7 +69,7 @@ describe("caching", function() {
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(); value = support.random.string();
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
done(); done();
}); });
}); });
@ -79,7 +79,7 @@ describe("caching", function() {
assert.equal(result, value); assert.equal(result, value);
cache.del(key, function(err) { cache.del(key, function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.ok(!result); assert.ok(!result);
@ -116,7 +116,7 @@ describe("caching", function() {
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(); value = support.random.string();
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
key2 = support.random.string(20); key2 = support.random.string(20);
value2 = support.random.string(); value2 = support.random.string();
@ -127,7 +127,7 @@ describe("caching", function() {
it("clears the cache", function(done) { it("clears the cache", function(done) {
cache.reset(function(err) { cache.reset(function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.ok(!result); assert.ok(!result);
@ -155,10 +155,10 @@ describe("caching", function() {
}); });
context("when store has no del() method", function() { context("when store has no del() method", function() {
var fake_store; var fakeStore;
beforeEach(function() { beforeEach(function() {
fake_store = { fakeStore = {
get: function() {}, get: function() {},
set: function() {}, set: function() {},
}; };
@ -166,52 +166,74 @@ describe("caching", function() {
it("it doesn't throw an error", function() { it("it doesn't throw an error", function() {
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
caching({store: fake_store}); caching({store: fakeStore});
}); });
}); });
}); });
}); });
describe("setex()", function() { describe("setex()", function() {
var fake_store; var fakeStore;
beforeEach(function() { beforeEach(function() {
fake_store = { fakeStore = {
get: function() {}, get: function() {},
set: function() {}, set: function() {},
del: function() {}, del: function() {},
setex: function() {} setex: function() {}
}; };
sinon.stub(fake_store, 'setex'); sinon.stub(fakeStore, 'setex');
cache = caching({store: fake_store}); cache = caching({store: fakeStore});
}); });
it("passes the params to the underlying store's setex() method", function() { it("passes the params to the underlying store's setex() method", function() {
cache.setex('foo', 'bar', 'blah'); cache.setex('foo', 'bar', 'blah');
assert.ok(fake_store.setex.calledWith('foo', 'bar', 'blah')); assert.ok(fakeStore.setex.calledWith('foo', 'bar', 'blah'));
});
});
describe("ttl()", function() {
var fakeStore;
beforeEach(function() {
fakeStore = {
get: function() {},
set: function() {},
del: function() {},
ttl: function() {}
};
sinon.stub(fakeStore, 'ttl');
cache = caching({store: fakeStore});
});
it("passes the params to the underlying store's ttl() method", function() {
cache.ttl('foo');
assert.ok(fakeStore.ttl.calledWith('foo'));
}); });
}); });
describe("keys()", function() { describe("keys()", function() {
var key_count; var keyCount;
var saved_keys = []; var savedKeys = [];
beforeEach(function(done) { beforeEach(function(done) {
key_count = 10; keyCount = 10;
var processed = 0; var processed = 0;
cache = caching({store: 'memory'}); cache = caching({store: 'memory'});
function is_done() { function isDone() {
return processed === key_count; return processed === keyCount;
} }
async.until(is_done, function(cb) { async.until(isDone, function(cb) {
processed += 1; processed += 1;
key = support.random.string(20); key = support.random.string(20);
saved_keys.push(key); savedKeys.push(key);
value = support.random.string(); value = support.random.string();
cache.set(key, value, ttl, cb); cache.set(key, value, ttl, cb);
}, done); }, done);
@ -219,27 +241,27 @@ describe("caching", function() {
it("calls back with all keys in cache", function(done) { it("calls back with all keys in cache", function(done) {
cache.keys(function(err, keys) { cache.keys(function(err, keys) {
check_err(err); checkErr(err);
assert.deepEqual(keys.sort, saved_keys.sort); assert.deepEqual(keys.sort, savedKeys.sort);
done(); done();
}); });
}); });
it("lets us get the keys without a callback (memory store only)", function() { it("lets us get the keys without a callback (memory store only)", function() {
var keys = cache.keys(); var keys = cache.keys();
assert.deepEqual(keys.sort, saved_keys.sort); assert.deepEqual(keys.sort, savedKeys.sort);
}); });
}); });
describe("wrap()", function() { describe("wrap()", function() {
describe("using memory (lru-cache) store", function() { describe("using memory (lru-cache) store", function() {
var memory_store_stub; var memoryStoreStub;
beforeEach(function() { beforeEach(function() {
ttl = 0.1; ttl = 0.1;
memory_store_stub = memory_store.create({ttl: ttl}); memoryStoreStub = memoryStore.create({ttl: ttl});
sinon.stub(memory_store, 'create').returns(memory_store_stub); sinon.stub(memoryStore, 'create').returns(memoryStoreStub);
cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: false}); cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: false});
key = support.random.string(20); key = support.random.string(20);
@ -247,58 +269,58 @@ describe("caching", function() {
}); });
afterEach(function() { afterEach(function() {
memory_store.create.restore(); memoryStore.create.restore();
}); });
context("calls back with the result of the wrapped function", function() { context("calls back with the result of the wrapped function", function() {
beforeEach(function() { beforeEach(function() {
sinon.spy(memory_store_stub, 'set'); sinon.spy(memoryStoreStub, 'set');
}); });
afterEach(function() { afterEach(function() {
memory_store_stub.set.restore(); memoryStoreStub.set.restore();
}); });
it("when a ttl is passed in", function(done) { it("when a ttl is passed in", function(done) {
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err, widget) { }, ttl, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, ttl); sinon.assert.calledWith(memoryStoreStub.set, key, {name: name}, ttl);
done(); done();
}); });
}); });
it("when a ttl is not passed in", function(done) { it("when a ttl is not passed in", function(done) {
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, undefined); sinon.assert.calledWith(memoryStoreStub.set, key, {name: name}, undefined);
done(); done();
}); });
}); });
}); });
context("when result is already cached", function() { context("when result is already cached", function() {
function get_cached_widget(name, cb) { function getCachedWidget(name, cb) {
cache.wrap(key, function(cache_cb) { cache.wrap(key, function(cacheCb) {
methods.get_widget(name, cache_cb); methods.getWidget(name, cacheCb);
}, ttl, cb); }, ttl, cb);
} }
beforeEach(function(done) { beforeEach(function(done) {
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.ok(widget); assert.ok(widget);
memory_store_stub.get(key, function(err, result) { memoryStoreStub.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(result); assert.ok(result);
sinon.spy(memory_store_stub, 'get'); sinon.spy(memoryStoreStub, 'get');
done(); done();
}); });
@ -306,44 +328,44 @@ describe("caching", function() {
}); });
afterEach(function() { afterEach(function() {
memory_store_stub.get.restore(); memoryStoreStub.get.restore();
}); });
it("retrieves data from cache", function(done) { it("retrieves data from cache", function(done) {
var func_called = false; var funcCalled = false;
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, function(err, result) { methods.getWidget(name, function(err, result) {
func_called = true; funcCalled = true;
cb(err, result); cb(err, result);
}); });
}, ttl, function(err, widget) { }, ttl, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
assert.ok(memory_store_stub.get.calledWith(key)); assert.ok(memoryStoreStub.get.calledWith(key));
assert.ok(!func_called); assert.ok(!funcCalled);
done(); done();
}); });
}); });
}); });
it("lets us make nested calls", function(done) { it("lets us make nested calls", function(done) {
function get_cached_widget(name, cb) { function getCachedWidget(name, cb) {
cache.wrap(key, function(cache_cb) { cache.wrap(key, function(cacheCb) {
methods.get_widget(name, cache_cb); methods.getWidget(name, cacheCb);
}, cb); }, cb);
} }
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
done(); done();
}); });
@ -353,26 +375,26 @@ describe("caching", function() {
it("expires cached result after ttl seconds", function(done) { it("expires cached result after ttl seconds", function(done) {
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err, widget) { }, ttl, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_store_stub.get(key, function(err, result) { memoryStoreStub.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(result); assert.ok(result);
var func_called = false; var funcCalled = false;
setTimeout(function() { setTimeout(function() {
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, function(err, result) { methods.getWidget(name, function(err, result) {
func_called = true; funcCalled = true;
cb(err, result); cb(err, result);
}); });
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.ok(func_called); assert.ok(funcCalled);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
done(); done();
}); });
@ -382,17 +404,17 @@ describe("caching", function() {
}); });
context("when an error is thrown in the work function", function() { context("when an error is thrown in the work function", function() {
var fake_error; var fakeError;
beforeEach(function() { beforeEach(function() {
fake_error = new Error(support.random.string()); fakeError = new Error(support.random.string());
}); });
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
cache.wrap(key, function() { cache.wrap(key, function() {
throw fake_error; throw fakeError;
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
done(); done();
}); });
}); });
@ -401,17 +423,17 @@ describe("caching", function() {
context("when store.get() calls back with an error", function() { context("when store.get() calls back with an error", function() {
context("and ignoreCacheErrors is not set (default is false)", function() { context("and ignoreCacheErrors is not set (default is false)", function() {
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'get', function(key, options, cb) { sinon.stub(memoryStoreStub, 'get', function(key, options, cb) {
cb(fake_error); cb(fakeError);
}); });
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
memory_store_stub.get.restore(); memoryStoreStub.get.restore();
done(); done();
}); });
}); });
@ -421,17 +443,17 @@ describe("caching", function() {
it("does not bubble up that error", function(done) { it("does not bubble up that error", function(done) {
cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true}); cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'get', function(key, options, cb) { sinon.stub(memoryStoreStub, 'get', function(key, options, cb) {
cb(fake_error); cb(fakeError);
}); });
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, null); assert.equal(err, null);
memory_store_stub.get.restore(); memoryStoreStub.get.restore();
done(); done();
}); });
}); });
@ -441,17 +463,17 @@ describe("caching", function() {
context("when store.set() calls back with an error", function() { context("when store.set() calls back with an error", function() {
context("and ignoreCacheErrors is not set", function() { context("and ignoreCacheErrors is not set", function() {
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) { sinon.stub(memoryStoreStub, 'set', function(key, val, ttl, cb) {
cb(fake_error); cb(fakeError);
}); });
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
memory_store_stub.set.restore(); memoryStoreStub.set.restore();
done(); done();
}); });
}); });
@ -460,17 +482,17 @@ describe("caching", function() {
context("and ignoreCacheErrors is set to true", function() { context("and ignoreCacheErrors is set to true", function() {
it("does not bubbles up that error", function(done) { it("does not bubbles up that error", function(done) {
cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true}); cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) { sinon.stub(memoryStoreStub, 'set', function(key, val, ttl, cb) {
cb(fake_error); cb(fakeError);
}); });
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, null); assert.equal(err, null);
memory_store_stub.set.restore(); memoryStoreStub.set.restore();
done(); done();
}); });
}); });
@ -479,16 +501,16 @@ describe("caching", function() {
context("when wrapped function calls back with an error", function() { context("when wrapped function calls back with an error", function() {
it("calls back with that error", function(done) { it("calls back with that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(methods, 'get_widget', function(name, cb) { sinon.stub(methods, 'getWidget', function(name, cb) {
cb(fake_error, {name: name}); cb(fakeError, {name: name});
}); });
cache.wrap(key, function(cb) { cache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err, widget) { }, ttl, function(err, widget) {
methods.get_widget.restore(); methods.getWidget.restore();
assert.equal(err, fake_error); assert.equal(err, fakeError);
assert.ok(!widget); assert.ok(!widget);
done(); done();
}); });
@ -520,15 +542,15 @@ describe("caching", function() {
values.push(i); values.push(i);
} }
async.each(values, function(val, async_cb) { async.each(values, function(val, next) {
cache.wrap('key', function(cb) { cache.wrap('key', function(cb) {
construct(val, cb); construct(val, cb);
}, ttl, function(err, result) { }, ttl, function(err, result) {
assert.equal(result, 'value'); assert.equal(result, 'value');
async_cb(err); next(err);
}); });
}, function(err) { }, function(err) {
check_err(err); checkErr(err);
assert.equal(construct.callCount, 1); assert.equal(construct.callCount, 1);
done(); done();
}); });
@ -545,10 +567,10 @@ describe("caching", function() {
describe("instantiating with custom store", function() { describe("instantiating with custom store", function() {
it("allows us to pass in our own store object", function(done) { it("allows us to pass in our own store object", function(done) {
var store = memory_store.create({ttl: ttl}); var store = memoryStore.create({ttl: ttl});
cache = caching({store: store}); cache = caching({store: store});
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.equal(result, value); assert.equal(result, value);
done(); done();
@ -557,10 +579,10 @@ describe("caching", function() {
}); });
it("allows us to pass in a path to our own store", function(done) { it("allows us to pass in a path to our own store", function(done) {
var store_path = '../lib/stores/memory'; var storePath = '../lib/stores/memory';
cache = caching({store: store_path}); cache = caching({store: storePath});
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.equal(result, value); assert.equal(result, value);
done(); done();
@ -569,10 +591,10 @@ describe("caching", function() {
}); });
it("allows us to pass in a module (uninstantiated)", function(done) { it("allows us to pass in a module (uninstantiated)", function(done) {
var store = memory_store; var store = memoryStore;
cache = caching({store: store}); cache = caching({store: store});
cache.set(key, value, ttl, function(err) { cache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
cache.get(key, function(err, result) { cache.get(key, function(err, result) {
assert.equal(result, value); assert.equal(result, value);
done(); done();

458
test/multi_caching.unit.js

@ -2,33 +2,33 @@ var assert = require('assert');
var async = require('async'); var async = require('async');
var sinon = require('sinon'); var sinon = require('sinon');
var support = require('./support'); var support = require('./support');
var check_err = support.check_err; var checkErr = support.checkErr;
var caching = require('../index').caching; var caching = require('../index').caching;
var multi_caching = require('../index').multi_caching; var multiCaching = require('../index').multiCaching;
var memory_store = require('../lib/stores/memory'); var memoryStore = require('../lib/stores/memory');
var methods = { var methods = {
get_widget: function(name, cb) { getWidget: function(name, cb) {
cb(null, {name: name}); cb(null, {name: name});
} }
}; };
describe("multi_caching", function() { describe("multiCaching", function() {
var memory_cache; var memoryCache;
var memory_cache2; var memoryCache2;
var memory_cache3; var memoryCache3;
var multi_cache; var multiCache;
var key; var key;
var memory_ttl; var memoryTtl;
var name; var name;
var ttl = 5; var ttl = 5;
beforeEach(function() { beforeEach(function() {
memory_ttl = 0.1; memoryTtl = 0.1;
memory_cache = caching({store: 'memory', ttl: memory_ttl}); memoryCache = caching({store: 'memory', ttl: memoryTtl});
memory_cache2 = caching({store: 'memory', ttl: memory_ttl}); memoryCache2 = caching({store: 'memory', ttl: memoryTtl});
memory_cache3 = caching({store: 'memory', ttl: memory_ttl}); memoryCache3 = caching({store: 'memory', ttl: memoryTtl});
key = support.random.string(20); key = support.random.string(20);
name = support.random.string(); name = support.random.string();
@ -38,24 +38,26 @@ describe("multi_caching", function() {
var value; var value;
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache, memory_cache2, memory_cache3]); multiCache = multiCaching([memoryCache, memoryCache2, memoryCache3]);
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(); value = support.random.string();
}); });
describe("set()", function() { describe("set()", function() {
it("lets us set data in all caches", function(done) { it("lets us set data in all caches", function(done) {
multi_cache.set(key, value, ttl, function(err) { multiCache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
memory_cache.get(key, function(err, result) {
memoryCache.get(key, function(err, result) {
checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
done(); done();
}); });
@ -65,19 +67,22 @@ describe("multi_caching", function() {
}); });
it("lets us set data without a callback", function(done) { it("lets us set data without a callback", function(done) {
multi_cache.set(key, value, ttl); multiCache.set(key, value, ttl);
setTimeout(function() { setTimeout(function() {
multi_cache.get(key, function(err, result) { multiCache.get(key, function(err, result) {
checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache.get(key, function(err, result) {
memoryCache.get(key, function(err, result) {
checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
done(); done();
}); });
@ -88,19 +93,22 @@ describe("multi_caching", function() {
}); });
it("lets us set data without a ttl or callback", function(done) { it("lets us set data without a ttl or callback", function(done) {
multi_cache.set(key, value); multiCache.set(key, value);
setTimeout(function() { setTimeout(function() {
multi_cache.get(key, function(err, result) { multiCache.get(key, function(err, result) {
checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache.get(key, function(err, result) {
memoryCache.get(key, function(err, result) {
checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
done(); done();
}); });
@ -113,11 +121,11 @@ describe("multi_caching", function() {
describe("get()", function() { describe("get()", function() {
it("gets data from first cache that has it", function(done) { it("gets data from first cache that has it", function(done) {
memory_cache3.set(key, value, ttl, function(err) { memoryCache3.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.get(key, function(err, result) { multiCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
done(); done();
}); });
@ -127,21 +135,21 @@ describe("multi_caching", function() {
describe("del()", function() { describe("del()", function() {
it("lets us delete data in all caches", function(done) { it("lets us delete data in all caches", function(done) {
multi_cache.set(key, value, ttl, function(err) { multiCache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.del(key, function(err) { multiCache.del(key, function(err) {
check_err(err); checkErr(err);
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
assert.ok(!result); assert.ok(!result);
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(!result); assert.ok(!result);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(!result); assert.ok(!result);
done(); done();
}); });
@ -152,21 +160,22 @@ describe("multi_caching", function() {
}); });
it("lets us delete data without a callback", function(done) { it("lets us delete data without a callback", function(done) {
multi_cache.set(key, value, ttl, function(err) { multiCache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.del(key); multiCache.del(key);
setTimeout(function() { setTimeout(function() {
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
checkErr(err);
assert.ok(!result); assert.ok(!result);
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(!result); assert.ok(!result);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.ok(!result); assert.ok(!result);
done(); done();
}); });
@ -178,23 +187,23 @@ describe("multi_caching", function() {
}); });
}); });
describe("get_and_pass_up()", function() { describe("getAndPassUp()", function() {
var value; var value;
var key; var key;
describe("using a single cache store", function() { describe("using a single cache store", function() {
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache3]); multiCache = multiCaching([memoryCache3]);
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(); value = support.random.string();
}); });
it("gets data from first cache that has it", function(done) { it("gets data from first cache that has it", function(done) {
memory_cache3.set(key, value, ttl, function(err) { memoryCache3.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.get_and_pass_up(key, function(err, result) { multiCache.getAndPassUp(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
done(); done();
}); });
@ -207,21 +216,21 @@ describe("multi_caching", function() {
beforeEach(function(done) { beforeEach(function(done) {
key = support.random.string(10); key = support.random.string(10);
sinon.spy(memory_cache, 'set'); sinon.spy(memoryCache, 'set');
sinon.spy(memory_cache2, 'set'); sinon.spy(memoryCache2, 'set');
sinon.spy(memory_cache3, 'set'); sinon.spy(memoryCache3, 'set');
multi_cache.get_and_pass_up(key, function(err, result) { multiCache.getAndPassUp(key, function(err, result) {
check_err(err); checkErr(err);
response = result; response = result;
done(); done();
}); });
}); });
afterEach(function() { afterEach(function() {
memory_cache.set.restore(); memoryCache.set.restore();
memory_cache2.set.restore(); memoryCache2.set.restore();
memory_cache3.set.restore(); memoryCache3.set.restore();
}); });
it("calls back with undefined", function() { it("calls back with undefined", function() {
@ -230,9 +239,9 @@ describe("multi_caching", function() {
it("does not set anything in caches", function(done) { it("does not set anything in caches", function(done) {
process.nextTick(function() { process.nextTick(function() {
assert.ok(memory_cache.set.notCalled); assert.ok(memoryCache.set.notCalled);
assert.ok(memory_cache2.set.notCalled); assert.ok(memoryCache2.set.notCalled);
assert.ok(memory_cache3.set.notCalled); assert.ok(memoryCache3.set.notCalled);
done(); done();
}); });
}); });
@ -240,65 +249,102 @@ describe("multi_caching", function() {
describe("using multi cache store", function() { describe("using multi cache store", function() {
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache, memory_cache2, memory_cache3]); multiCache = multiCaching([memoryCache, memoryCache2, memoryCache3]);
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(); value = support.random.string();
}); });
it("checks to see if higher levels have item", function(done) { it("checks to see if higher levels have item", function(done) {
memory_cache3.set(key, value, ttl, function(err) { memoryCache3.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.get_and_pass_up(key, function(err, result) { multiCache.getAndPassUp(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, value); assert.equal(result, value);
process.nextTick(function() { process.nextTick(function() {
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
assert.equal(result, value); assert.equal(result, value);
check_err(err); checkErr(err);
done(); done();
}); });
}); });
}); });
}); });
}); });
context("when a cache store calls back with an error", function() {
var fakeError;
var memoryStoreStub;
beforeEach(function() {
memoryStoreStub = memoryStore.create({ttl: ttl});
sinon.stub(memoryStore, 'create').returns(memoryStoreStub);
memoryCache = caching({store: 'memory', ttl: ttl});
multiCache = multiCaching([memoryCache]);
fakeError = new Error(support.random.string());
sinon.stub(memoryStoreStub, 'get').yields(fakeError);
});
afterEach(function() {
memoryStore.create.restore();
});
it("bubbles up errors from caches", function(done) {
multiCache.getAndPassUp(key, function(err) {
assert.ok(memoryStoreStub.get.called);
assert.equal(err, fakeError);
done();
});
});
});
}); });
}); });
describe("wrap()", function() { describe("wrap()", function() {
describe("using a single cache store", function() { describe("using a single cache store", function() {
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache3]); multiCache = multiCaching([memoryCache3]);
}); });
context("calls back with the result of a function", function() { context("calls back with the result of a function", function() {
beforeEach(function() { beforeEach(function() {
sinon.spy(memory_cache3.store, 'set'); sinon.spy(memoryCache3.store, 'set');
}); });
afterEach(function() { afterEach(function() {
memory_cache3.store.set.restore(); memoryCache3.store.set.restore();
}); });
it('when a ttl is passed in', function(done) { it('when a ttl number is passed in', function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, ttl, function(err, widget) { }, ttl, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name});
sinon.assert.calledWith(memoryCache3.store.set, key, {name: name}, ttl);
done();
});
});
it('when a ttl option is passed in', function(done) {
multiCache.wrap(key, function(cb) {
methods.getWidget(name, cb);
}, {ttl: ttl}, function(err, widget) {
checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
sinon.assert.calledWith(memory_cache3.store.set, key, {name: name}, ttl); sinon.assert.calledWith(memoryCache3.store.set, key, {name: name}, {ttl: ttl});
done(); done();
}); });
}); });
it('when a ttl is not passed in', function(done) { it('when a ttl is not passed in', function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
sinon.assert.calledWith(memory_cache3.store.set, key, {name: name}); sinon.assert.calledWith(memoryCache3.store.set, key, {name: name});
done(); done();
}); });
}); });
@ -306,16 +352,16 @@ describe("multi_caching", function() {
context("when wrapped function calls back with an error", function() { context("when wrapped function calls back with an error", function() {
it("calls back with that error", function(done) { it("calls back with that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(methods, 'get_widget', function(name, cb) { sinon.stub(methods, 'getWidget', function(name, cb) {
cb(fake_error, {name: name}); cb(fakeError, {name: name});
}); });
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
methods.get_widget.restore(); methods.getWidget.restore();
assert.equal(err, fake_error); assert.equal(err, fakeError);
assert.ok(!widget); assert.ok(!widget);
done(); done();
}); });
@ -325,32 +371,32 @@ describe("multi_caching", function() {
describe("using two cache stores", function() { describe("using two cache stores", function() {
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache, memory_cache3]); multiCache = multiCaching([memoryCache, memoryCache3]);
}); });
it("calls back with the result of a function", function(done) { it("calls back with the result of a function", function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
done(); done();
}); });
}); });
it("sets value in all caches", function(done) { it("sets value in all caches", function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
done(); done();
}); });
@ -360,17 +406,17 @@ describe("multi_caching", function() {
context("when value exists in first store but not second", function() { context("when value exists in first store but not second", function() {
it("returns value from first store, does not set it in second", function(done) { it("returns value from first store, does not set it in second", function(done) {
memory_cache.set(key, {name: name}, ttl, function(err) { memoryCache.set(key, {name: name}, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, null); assert.equal(result, null);
done(); done();
}); });
@ -381,17 +427,17 @@ describe("multi_caching", function() {
context("when value exists in second store but not first", function() { context("when value exists in second store but not first", function() {
it("returns value from second store, sets it in first store", function(done) { it("returns value from second store, sets it in first store", function(done) {
memory_cache3.set(key, {name: name}, ttl, function(err) { memoryCache3.set(key, {name: name}, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
done(); done();
}); });
@ -403,36 +449,36 @@ describe("multi_caching", function() {
describe("using three cache stores", function() { describe("using three cache stores", function() {
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache, memory_cache3, memory_cache2]); multiCache = multiCaching([memoryCache, memoryCache3, memoryCache2]);
}); });
it("calls back with the result of a function", function(done) { it("calls back with the result of a function", function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
done(); done();
}); });
}); });
it("sets value in all caches", function(done) { it("sets value in all caches", function(done) {
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
done(); done();
}); });
@ -443,21 +489,21 @@ describe("multi_caching", function() {
context("when value exists in first store only", function() { context("when value exists in first store only", function() {
it("returns value from first store, does not set it in second or third", function(done) { it("returns value from first store, does not set it in second or third", function(done) {
memory_cache.set(key, {name: name}, ttl, function(err) { memoryCache.set(key, {name: name}, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, null); assert.equal(result, null);
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, null); assert.equal(result, null);
done(); done();
}); });
@ -469,21 +515,21 @@ describe("multi_caching", function() {
context("when value exists in second store only", function() { context("when value exists in second store only", function() {
it("returns value from second store, sets it in first store, does not set third store", function(done) { it("returns value from second store, sets it in first store, does not set third store", function(done) {
memory_cache3.set(key, {name: name}, ttl, function(err) { memoryCache3.set(key, {name: name}, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
memory_cache2.get(key, function(err, result) { memoryCache2.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.equal(result, null); assert.equal(result, null);
done(); done();
}); });
@ -495,21 +541,21 @@ describe("multi_caching", function() {
context("when value exists in third store only", function() { context("when value exists in third store only", function() {
it("returns value from third store, sets it in first and second stores", function(done) { it("returns value from third store, sets it in first and second stores", function(done) {
memory_cache2.set(key, {name: name}, ttl, function(err) { memoryCache2.set(key, {name: name}, ttl, function(err) {
check_err(err); checkErr(err);
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err, widget) { }, function(err, widget) {
check_err(err); checkErr(err);
assert.deepEqual(widget, {name: name}); assert.deepEqual(widget, {name: name});
memory_cache3.get(key, function(err, result) { memoryCache3.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
memory_cache.get(key, function(err, result) { memoryCache.get(key, function(err, result) {
check_err(err); checkErr(err);
assert.deepEqual(result, {name: name}); assert.deepEqual(result, {name: name});
done(); done();
@ -521,22 +567,22 @@ describe("multi_caching", function() {
}); });
it("lets us make nested calls", function(done) { it("lets us make nested calls", function(done) {
function get_cached_widget(name, cb) { function getCachedWidget(name, cb) {
multi_cache.wrap(key, function(cache_cb) { multiCache.wrap(key, function(cacheCb) {
methods.get_widget(name, cache_cb); methods.getWidget(name, cacheCb);
}, cb); }, cb);
} }
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
get_cached_widget(name, function(err, widget) { getCachedWidget(name, function(err, widget) {
check_err(err); checkErr(err);
assert.equal(widget.name, name); assert.equal(widget.name, name);
done(); done();
}); });
@ -546,33 +592,33 @@ describe("multi_caching", function() {
}); });
context("error handling", function() { context("error handling", function() {
var memory_store_stub; var memoryStoreStub;
var ttl; var ttl;
beforeEach(function() { beforeEach(function() {
ttl = 0.1; ttl = 0.1;
memory_store_stub = memory_store.create({ttl: ttl}); memoryStoreStub = memoryStore.create({ttl: ttl});
sinon.stub(memory_store, 'create').returns(memory_store_stub); sinon.stub(memoryStore, 'create').returns(memoryStoreStub);
memory_cache = caching({store: 'memory', ttl: ttl}); memoryCache = caching({store: 'memory', ttl: ttl});
multi_cache = multi_caching([memory_cache]); multiCache = multiCaching([memoryCache]);
}); });
afterEach(function() { afterEach(function() {
memory_store.create.restore(); memoryStore.create.restore();
}); });
context("when an error is thrown in the work function", function() { context("when an error is thrown in the work function", function() {
var fake_error; var fakeError;
beforeEach(function() { beforeEach(function() {
fake_error = new Error(support.random.string()); fakeError = new Error(support.random.string());
}); });
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
multi_cache.wrap(key, function() { multiCache.wrap(key, function() {
throw fake_error; throw fakeError;
}, ttl, function(err) { }, ttl, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
done(); done();
}); });
}); });
@ -580,17 +626,17 @@ describe("multi_caching", function() {
context("when store.get() calls back with an error", function() { context("when store.get() calls back with an error", function() {
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'get', function(key, cb) { sinon.stub(memoryStoreStub, 'get', function(key, cb) {
cb(fake_error); cb(fakeError);
}); });
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err) { }, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
memory_store_stub.get.restore(); memoryStoreStub.get.restore();
done(); done();
}); });
}); });
@ -598,17 +644,17 @@ describe("multi_caching", function() {
context("when store.set() calls back with an error", function() { context("when store.set() calls back with an error", function() {
it("bubbles up that error", function(done) { it("bubbles up that error", function(done) {
var fake_error = new Error(support.random.string()); var fakeError = new Error(support.random.string());
sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) { sinon.stub(memoryStoreStub, 'set', function(key, val, ttl, cb) {
cb(fake_error); cb(fakeError);
}); });
multi_cache.wrap(key, function(cb) { multiCache.wrap(key, function(cb) {
methods.get_widget(name, cb); methods.getWidget(name, cb);
}, function(err) { }, function(err) {
assert.equal(err, fake_error); assert.equal(err, fakeError);
memory_store_stub.set.restore(); memoryStoreStub.set.restore();
done(); done();
}); });
}); });
@ -619,7 +665,7 @@ describe("multi_caching", function() {
var construct; var construct;
beforeEach(function() { beforeEach(function() {
multi_cache = multi_caching([memory_cache, memory_cache3]); multiCache = multiCaching([memoryCache, memoryCache3]);
construct = sinon.spy(function(val, cb) { construct = sinon.spy(function(val, cb) {
var timeout = support.random.number(100); var timeout = support.random.number(100);
@ -635,15 +681,15 @@ describe("multi_caching", function() {
values.push(i); values.push(i);
} }
async.each(values, function(val, async_cb) { async.each(values, function(val, next) {
multi_cache.wrap('key', function(cb) { multiCache.wrap('key', function(cb) {
construct(val, cb); construct(val, cb);
}, function(err, result) { }, function(err, result) {
assert.equal(result, 'value'); assert.equal(result, 'value');
async_cb(err); next(err);
}); });
}, function(err) { }, function(err) {
check_err(err); checkErr(err);
assert.equal(construct.callCount, 1); assert.equal(construct.callCount, 1);
done(); done();
}); });
@ -654,8 +700,8 @@ describe("multi_caching", function() {
context("when instantiated with a non-Array 'caches' arg", function() { context("when instantiated with a non-Array 'caches' arg", function() {
it("throws an error", function() { it("throws an error", function() {
assert.throws(function() { assert.throws(function() {
multi_caching({foo: 'bar'}); multiCaching({foo: 'bar'});
}, /multi_caching requires an array/); }, /multiCaching requires an array/);
}); });
}); });
}); });

26
test/run.js

@ -4,7 +4,7 @@ require('../index');
var Mocha = require('mocha'); var Mocha = require('mocha');
var optimist = require('optimist'); var optimist = require('optimist');
var walk_dir = require('./support').walk_dir; var walkDir = require('./support').walkDir;
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'](
@ -21,29 +21,29 @@ var argv = optimist
var mocha = new Mocha({timeout: argv.timeout, reporter: argv.reporter, ui: 'bdd'}); var mocha = new Mocha({timeout: argv.timeout, reporter: argv.reporter, ui: 'bdd'});
var valid_test_types = ['unit', 'functional', 'acceptance', 'integration']; var validTestTypes = ['unit', 'functional', 'acceptance', 'integration'];
var requested_types = argv.types.split(','); var requestedTypes = argv.types.split(',');
var types_to_use = []; var typesToUse = [];
valid_test_types.forEach(function(valid_test_type) { validTestTypes.forEach(function(validTestType) {
if (requested_types.indexOf(valid_test_type) !== -1) { if (requestedTypes.indexOf(validTestType) !== -1) {
types_to_use.push(valid_test_type); typesToUse.push(validTestType);
} }
}); });
if (argv.help || types_to_use.length === 0) { if (argv.help || typesToUse.length === 0) {
console.log('\n' + optimist.help()); console.log('\n' + optimist.help());
process.exit(); process.exit();
} }
var is_valid_file = function(file) { var isValidFile = function(file) {
if (file.match(/buster/)) { if (file.match(/buster/)) {
return false; return false;
} }
for (var i = 0; i < types_to_use.length; i++) { for (var i = 0; i < typesToUse.length; i++) {
var test_type = types_to_use[i]; var testType = typesToUse[i];
var ext = test_type + ".js"; var ext = testType + ".js";
if (file.indexOf(ext) !== -1) { if (file.indexOf(ext) !== -1) {
return true; return true;
@ -54,7 +54,7 @@ var is_valid_file = function(file) {
}; };
function run(cb) { function run(cb) {
walk_dir('test', is_valid_file, function(err, files) { walkDir('test', isValidFile, function(err, files) {
if (err) { return cb(err); } if (err) { return cb(err); }
files.forEach(function(file) { files.forEach(function(file) {

6
test/stores/memory.unit.js

@ -1,11 +1,11 @@
var support = require('../support'); var support = require('../support');
var memory_store = require('../../lib/stores/memory'); var memoryStore = require('../../lib/stores/memory');
describe("memory store", function() { describe("memory store", function() {
describe("instantiating", function() { describe("instantiating", function() {
it("lets us pass in no args", function(done) { it("lets us pass in no args", function(done) {
var memory_cache = memory_store.create(); var memoryCache = memoryStore.create();
support.test_set_get_del(memory_cache, done); support.testSetGetDel(memoryCache, done);
}); });
}); });
}); });

22
test/stores/options.unit.js

@ -1,7 +1,7 @@
var caching = require("../../index"); var caching = require("../../index");
var assert = require("assert"); var assert = require("assert");
var support = require("../support"); var support = require("../support");
var check_err = support.check_err; var checkErr = support.checkErr;
var memoryFlag = ""; var memoryFlag = "";
var key; var key;
var value; var value;
@ -91,7 +91,7 @@ describe("Methods with options", function() {
before(function() { before(function() {
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(20); value = support.random.string(20);
testCache = caching.multi_caching([testInstance]); testCache = caching.multiCaching([testInstance]);
}); });
describe("get with options", function() { describe("get with options", function() {
@ -170,19 +170,19 @@ describe("Multiple stores with options", function() {
before(function() { before(function() {
key = support.random.string(20); key = support.random.string(20);
value = support.random.string(20); value = support.random.string(20);
testCache = caching.multi_caching([testInstance, memInstance]); testCache = caching.multiCaching([testInstance, memInstance]);
}); });
it("lets us pass options which only one store uses", function() { it("lets us pass options which only one store uses", function() {
testCache.set(key, value, options, function(err) { testCache.set(key, value, options, function(err) {
check_err(err); checkErr(err);
testCache.get(key, options, function(err, response) { testCache.get(key, options, function(err, response) {
check_err(err); checkErr(err);
assert.equal(response, value); assert.equal(response, value);
testCache.del(key, options, function(err) { testCache.del(key, options, function(err) {
check_err(err); checkErr(err);
testCache.get(key, options, function(err, response) { testCache.get(key, options, function(err, response) {
check_err(err); checkErr(err);
assert.equal(response, undefined); assert.equal(response, undefined);
}); });
}); });
@ -192,14 +192,14 @@ describe("Multiple stores with options", function() {
it("lets us not pass options which only one store uses", function() { it("lets us not pass options which only one store uses", function() {
testCache.set(key, value, ttl, function(err) { testCache.set(key, value, ttl, function(err) {
check_err(err); checkErr(err);
testCache.get(key, function(err, response) { testCache.get(key, function(err, response) {
check_err(err); checkErr(err);
assert.equal(response, value); assert.equal(response, value);
testCache.del(key, function(err) { testCache.del(key, function(err) {
check_err(err); checkErr(err);
testCache.get(key, function(err, response) { testCache.get(key, function(err, response) {
check_err(err); checkErr(err);
assert.equal(response, undefined); assert.equal(response, undefined);
}); });
}); });

34
test/support.js

@ -4,15 +4,15 @@ var assert = require('assert');
var support = { var support = {
random: { random: {
string: function(str_len) { string: function(strLen) {
str_len = str_len || 8; strLen = strLen || 8;
var chars = "abcdefghiklmnopqrstuvwxyz"; var chars = "abcdefghiklmnopqrstuvwxyz";
var random_str = ''; var randomStr = '';
for (var i = 0; i < str_len; i++) { for (var i = 0; i < strLen; i++) {
var rnum = Math.floor(Math.random() * chars.length); var rnum = Math.floor(Math.random() * chars.length);
random_str += chars.substring(rnum, rnum + 1); randomStr += chars.substring(rnum, rnum + 1);
} }
return random_str; return randomStr;
}, },
number: function(max) { number: function(max) {
@ -21,7 +21,7 @@ var support = {
} }
}, },
check_err: function(err) { checkErr: function(err) {
if (err) { if (err) {
var msg; var msg;
@ -38,21 +38,21 @@ var support = {
} }
}, },
assert_between: function(actual, lower, upper) { assertBetween: function(actual, lower, upper) {
assert.ok(actual >= lower, "Expected " + actual + " to be >= " + lower); assert.ok(actual >= lower, "Expected " + actual + " to be >= " + lower);
assert.ok(actual <= upper, "Expected " + actual + " to be <= " + upper); assert.ok(actual <= upper, "Expected " + actual + " to be <= " + upper);
}, },
assert_within: function(actual, expected, delta) { assertWithin: function(actual, expected, delta) {
var lower = expected - delta; var lower = expected - delta;
var upper = expected + delta; var upper = expected + delta;
this.assert_between(actual, lower, upper); this.assertBetween(actual, lower, upper);
}, },
walk_dir: function(dir, validation_function, cb) { walkDir: function(dir, validationFunction, cb) {
if (arguments.length === 2) { if (arguments.length === 2) {
cb = validation_function; cb = validationFunction;
validation_function = null; validationFunction = null;
} }
var results = []; var results = [];
@ -67,13 +67,13 @@ var support = {
file = dir + '/' + file; file = dir + '/' + file;
fs.stat(file, function(err, stat) { fs.stat(file, function(err, stat) {
if (stat && stat.isDirectory()) { if (stat && stat.isDirectory()) {
support.walk_dir(file, validation_function, function(err, res) { support.walkDir(file, validationFunction, function(err, res) {
results = results.concat(res); results = results.concat(res);
if (!--pending) { cb(null, results); } if (!--pending) { cb(null, results); }
}); });
} else { } else {
if (typeof validation_function === 'function') { if (typeof validationFunction === 'function') {
if (validation_function(file)) { if (validationFunction(file)) {
results.push(file); results.push(file);
} }
} else { } else {
@ -87,7 +87,7 @@ var support = {
}); });
}, },
test_set_get_del: function(cache, cb) { testSetGetDel: function(cache, cb) {
var key = 'TEST' + support.random.string(); var key = 'TEST' + support.random.string();
var val = support.random.string(); var val = support.random.string();
var ttl; var ttl;

Loading…
Cancel
Save