Comparing version 0.1.1 to 0.2.0
var extend = require('deap/shallow'), | ||
slice = Array.prototype.slice; | ||
timestr = require('timestr'), | ||
slice = Array.prototype.slice, | ||
noop = function() {}; | ||
@@ -10,2 +12,7 @@ module.exports = function(stores) { | ||
function parseTTL(value) { | ||
if(typeof value != 'string') return value; | ||
return timestr(value).toSeconds(); | ||
} | ||
function Cache(config) { | ||
@@ -52,7 +59,7 @@ config = extend({}, DEFAULTS, config||{}); | ||
load(key, function(err, loadedValue) { | ||
load(key, function(err, loadedValue, tags) { | ||
if(err) return done(err); | ||
done(null, loadedValue); | ||
self.set(key, loadedValue, ttl); | ||
self.set(key, loadedValue, tags, ttl, noop); | ||
}); | ||
@@ -64,10 +71,24 @@ }); | ||
set: function(key, value, ttl, done) { | ||
if(typeof ttl == 'function') { | ||
done = ttl; | ||
ttl = undefined; | ||
set: function(/* key, value, tags, ttl, done */) { | ||
var args = slice.call(arguments), | ||
done = args.pop(), | ||
key = args.shift(), | ||
value = args.shift(), | ||
tags, ttl; | ||
if(args.length == 2) { | ||
tags = args.shift(); | ||
ttl = args.shift(); | ||
} else if(args.length == 1) { | ||
tags = args.shift(); | ||
if(typeof tags != 'object') { | ||
ttl = tags; | ||
tags = undefined; | ||
} | ||
} | ||
this.store.set(key, value, ttl, done); | ||
this.store.set(key, value, convertTags(tags), parseTTL(ttl), done); | ||
return this; | ||
@@ -98,8 +119,7 @@ }, | ||
loadMissingKeys(keys, values, load, function(err, allthethings, loaded) { | ||
loadMissingKeys(keys, values, load, function(err, allthethings, loaded, tagMap) { | ||
if(err) return done(err); | ||
done(null, allthethings); | ||
self.mset(loaded, ttl); | ||
self.mset(loaded, tagMap, ttl, noop); | ||
}); | ||
@@ -111,9 +131,28 @@ }); | ||
mset: function(keysValues, ttl, done) { | ||
if(typeof ttl == 'function') { | ||
done = ttl; | ||
ttl = undefined; | ||
mset: function(/* keysValues, tags, ttl, done */) { | ||
var args = slice.call(arguments), | ||
done = args.pop(), | ||
keysValues = args.shift(), | ||
tags, ttl; | ||
if(args.length == 2) { | ||
tags = args.shift(); | ||
ttl = args.shift(); | ||
} else if(args.length == 1) { | ||
tags = args.shift(); | ||
if(typeof tags != 'object') { | ||
ttl = tags; | ||
tags = undefined; | ||
} | ||
} | ||
this.store.mset(keysValues, ttl, done); | ||
this.store.mset( | ||
keysValues, | ||
tags && Object.keys(tags).reduce(function(map, key) { | ||
map[key] = convertTags(tags[key]); | ||
return map; | ||
}, {}), | ||
parseTTL(ttl), | ||
done | ||
); | ||
@@ -129,5 +168,12 @@ return this; | ||
this.store.expire(key, ttl, done); | ||
this.store.expire(key, parseTTL(ttl), done); | ||
return this; | ||
}, | ||
getKeys: function(tags, done) { | ||
if(!this.store.getKeys) throw new Error('Cache store does not support tagging'); | ||
this.store.getKeys(convertTags(tags), done); | ||
return this; | ||
} | ||
@@ -137,2 +183,24 @@ | ||
function convertTags(tags) { | ||
if(!tags) return undefined; | ||
if(tags instanceof Array) return tags; | ||
if(typeof tags == 'string') return [tags]; | ||
var rTags = []; | ||
for(var key in tags) { | ||
rTags.push([key, tags[key]].join(':')); | ||
} | ||
return Array.prototype.concat.apply( | ||
[], | ||
Object.keys(tags).map(function(name) { | ||
var values = (tags[name] instanceof Array) ? tags[name] : [tags[name]]; | ||
return values.map(function(value) { | ||
return [name, value].join(':'); | ||
}); | ||
}) | ||
); | ||
} | ||
function loadMissingKeys(keys, values, load, done) { | ||
@@ -153,10 +221,13 @@ var results = {}, | ||
// call the load() callback provided to load missing keys | ||
load(missingKeys, function(err, values) { | ||
load(missingKeys, function(err, values, tags) { | ||
if(!err && !(values instanceof Array)) err = new Error('Invalid values passed into load function: "values" must be an array'); | ||
if(err) return done(err); | ||
var loaded = {}; | ||
var loaded = {}, | ||
tagMap = {}; | ||
(values||[]).forEach(function(value, idx) { | ||
loaded[missingKeys[idx]] = value; | ||
if(tags) tagMap[missingKeys[idx]] = tags[idx]; | ||
}); | ||
@@ -170,3 +241,4 @@ extend(results, loaded); | ||
}), | ||
loaded | ||
loaded, | ||
tagMap | ||
); | ||
@@ -173,0 +245,0 @@ }); |
@@ -5,2 +5,3 @@ | ||
this.data = {}; | ||
this.tags = {}; | ||
}; | ||
@@ -15,4 +16,6 @@ Memory.prototype = { | ||
set: function(key, value, ttl, done) { | ||
set: function(key, value, tags, ttl, done) { | ||
this.data[key] = packageData(value, ttl); | ||
if(tags) setTags.call(this, key, tags); | ||
if(done) done(null, true); | ||
@@ -34,10 +37,16 @@ | ||
mset: function(hash, ttl, done) { | ||
mset: function(hash, tagMap, ttl, done) { | ||
var self = this; | ||
var self = this, | ||
keys = Object.keys(hash||{}); | ||
Object.keys(hash||{}).forEach(function(key) { | ||
keys.forEach(function(key) { | ||
self.data[key] = packageData(hash[key], ttl); | ||
}); | ||
if(tagMap) { | ||
Object.keys(tagMap).forEach(function(key) { | ||
setTags.call(self, key, tagMap[key]); | ||
}); | ||
} | ||
if(done) done(null, true); | ||
@@ -62,2 +71,16 @@ | ||
return this; | ||
}, | ||
getKeys: function(tags, done) { | ||
var self = this; | ||
done(null, Object.keys( | ||
Array.prototype.concat.apply( | ||
[], | ||
tags.map(function(tag) { return self.tags[tag] || []; }) | ||
).reduce(function(map, key) { //to make it unique | ||
map[key] = true; | ||
return map; | ||
}, {}) | ||
)); | ||
return this; | ||
} | ||
@@ -67,2 +90,12 @@ | ||
function setTags(key, tags, done) { | ||
var self = this; | ||
tags.forEach(function(tag) { | ||
if(!self.tags[tag]) self.tags[tag] = []; | ||
self.tags[tag].push(key); | ||
}); | ||
} | ||
function getValue(cache, key) { | ||
@@ -69,0 +102,0 @@ var value = cache.data[key]; |
{ | ||
"name": "cashbox", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "javascript cache library with configurable storage", | ||
@@ -20,6 +20,18 @@ "main": "index.js", | ||
"author": "Brad Harris <bmharris@gmail.com> (http://selfcontained.us)", | ||
"contributors": [ | ||
{ | ||
"name": "Brad Harris", | ||
"email": "bmharris@gmail.com", | ||
"url": "http://selfcontained.us" | ||
}, | ||
{ | ||
"name": "Nathan Broslawsky", | ||
"url": "http://github.com/nbroslawsky" | ||
} | ||
], | ||
"license": "BSD", | ||
"readmeFilename": "README.md", | ||
"dependencies": { | ||
"deap": "~0.1.2" | ||
"deap": "~0.1.2", | ||
"timestr": "~0.1.0" | ||
}, | ||
@@ -26,0 +38,0 @@ "devDependencies": { |
@@ -116,2 +116,25 @@ var assert = require('chai').assert, | ||
it('should have tagging capabilities on the load object for get()', function(done) { | ||
var cache = new Cache(); | ||
cache.get({ | ||
key: key, | ||
ttl: 1, | ||
load: function(key, cb) { | ||
cb(null, value, { test : 1 }); | ||
}, | ||
done: function(err, v) { | ||
assert.isNull(err); | ||
assert.equal(v, value); | ||
setTimeout(function() { | ||
cache.getKeys({ test : 1 }, function(err, keys) { | ||
assert.equal(keys[0], key); | ||
done(); | ||
}); | ||
}, 0); | ||
} | ||
}); | ||
}); | ||
describe('without a ttl', function() { | ||
@@ -284,2 +307,80 @@ | ||
it('should accept an object with tagging capabilities (as an object)', function(done) { | ||
var cache = new Cache(); | ||
cache.mget({ | ||
keys: [key1, key2], | ||
ttl: 1, | ||
load: function(keys, cb) { | ||
cb( | ||
null, | ||
[value1, value2], | ||
[{ test : 1 }, { test : 2 }] | ||
); | ||
}, | ||
done: function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.equal(results[0], value1); | ||
assert.equal(results[1], value2); | ||
setTimeout(function() { | ||
cache.getKeys({ test : 2 }, function(err, keys) { | ||
assert.equal(keys[0], key2); | ||
done(); | ||
}); | ||
}, 0); | ||
} | ||
}); | ||
}); | ||
it('should accept an object with tagging capabilities (as an array)', function(done) { | ||
var cache = new Cache(); | ||
cache.mget({ | ||
keys: [key1, key2], | ||
ttl: 1, | ||
load: function(keys, cb) { | ||
cb( | ||
null, | ||
[value1, value2], | ||
['test_1', 'test_2'] | ||
); | ||
}, | ||
done: function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.equal(results[0], value1); | ||
assert.equal(results[1], value2); | ||
setTimeout(function() { | ||
cache.getKeys('test_2', function(err, keys) { | ||
assert.equal(keys[0], key2); | ||
done(); | ||
}); | ||
}, 0); | ||
} | ||
}); | ||
}); | ||
it('should accept an object with a string ttl', function(done) { | ||
var cache = new Cache(); | ||
cache.mget({ | ||
keys: [key1, key2], | ||
ttl: '1 second', | ||
load: function(keys, cb) { | ||
cb(null, [value1, value2]); | ||
}, | ||
done: function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.equal(results[0], value1); | ||
assert.equal(results[1], value2); | ||
done(); | ||
} | ||
}); | ||
}); | ||
it('should handle a load function for missing values', function(done) { | ||
@@ -327,2 +428,23 @@ var called = false, | ||
it('should handle a load function and a string ttl for missing values', function(done) { | ||
var called = false, | ||
cache = new Cache(); | ||
function loadIt(keys, cb) { | ||
called = true; | ||
cb(null, [value2, value1]); | ||
} | ||
cache.mget([key2, key1], loadIt, '1s', function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.equal(results[0], value2); | ||
assert.equal(results[1], value1); | ||
assert.isTrue(called); | ||
done(); | ||
}); | ||
}); | ||
it('should handle a load function and a ttl for missing values and not return them after expiration', function(done) { | ||
@@ -359,2 +481,33 @@ var called = false, | ||
it('should handle a load function and a string ttl for missing values and not return them after expiration', function(done) { | ||
var called = false, | ||
cache = new Cache(); | ||
function loadIt(keys, cb) { | ||
called = true; | ||
cb(null, [value2, value1]); | ||
} | ||
cache.mget([key2, key1], loadIt, '1 sec', function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.equal(results[0], value2); | ||
assert.equal(results[1], value1); | ||
assert.isTrue(called); | ||
setTimeout(function() { | ||
cache.mget([key1, key2], function(err, results) { | ||
assert.isNull(err); | ||
assert.lengthOf(results, 2); | ||
assert.isUndefined(results[0]); | ||
assert.isUndefined(results[1]); | ||
done(); | ||
}); | ||
}, 1025); | ||
}); | ||
}); | ||
}); | ||
@@ -361,0 +514,0 @@ |
23906
796
2
+ Addedtimestr@~0.1.0
+ Addedtimestr@0.1.0(transitive)