diff --git a/lib/caching.js b/lib/caching.js index c6e40a5..6cb1841 100644 --- a/lib/caching.js +++ b/lib/caching.js @@ -1,4 +1,5 @@ /*jshint maxcomplexity:15*/ +/*jshint -W072 */ var domain = require('domain'); var caching = function(args) { @@ -36,10 +37,10 @@ var caching = function(args) { * console.log(user); * }); */ - self.wrap = function(key, work, ttl, cb) { - if (typeof ttl === 'function') { - cb = ttl; - ttl = undefined; + self.wrap = function(key, work, options, cb) { + if (typeof options === 'function') { + cb = options; + options = undefined; } if (self.queues[key]) { @@ -59,7 +60,7 @@ var caching = function(args) { }); } - self.store.get(key, function(err, result) { + self.store.get(key, options, function(err, result) { if (err && (!self.ignoreCacheErrors)) { fillCallbacks(err); } else if (result) { @@ -75,7 +76,7 @@ var caching = function(args) { fillCallbacks(err); return; } - self.store.set(key, data, ttl, function(err) { + self.store.set(key, data, options, function(err) { if (err && (!self.ignoreCacheErrors)) { fillCallbacks(err); } else { diff --git a/lib/multi_caching.js b/lib/multi_caching.js index 0bf98de..1663226 100644 --- a/lib/multi_caching.js +++ b/lib/multi_caching.js @@ -6,15 +6,24 @@ var domain = require('domain'); */ var multi_caching = function(caches) { var self = {}; - if (!Array.isArray(caches)) { throw new Error('multi_caching requires an array of caches'); } + if (!Array.isArray(caches)) { + throw new Error('multi_caching requires an array of caches'); + } self.queues = {}; - function get_from_highest_priority_cache(key, cb) { + function get_from_highest_priority_cache(key, options, cb) { + if (typeof options === 'function') { + cb = options; + options = undefined; + } + var i = 0; async.forEachSeries(caches, function(cache, async_cb) { - cache.store.get(key, function(err, result) { - if (err) { return cb(err); } + var callback = function(err, result) { + if (err) { + return cb(err); + } if (result) { // break out of async loop. return cb(err, result, i); @@ -22,13 +31,22 @@ var multi_caching = function(caches) { i += 1; async_cb(err); - }); + }; + if (typeof options === 'object') { + cache.store.get(key, options, callback); + }else { + cache.store.get(key, callback); + } }, cb); } function set_in_multiple_caches(caches, opts, cb) { async.forEach(caches, function(cache, async_cb) { - cache.store.set(opts.key, opts.value, opts.ttl, async_cb); + if (typeof opts.options !== 'object') { + cache.store.set(opts.key, opts.value, opts.ttl, async_cb); + } else { + cache.store.set(opts.key, opts.value, opts.options, async_cb); + } }, cb); } @@ -64,10 +82,10 @@ var multi_caching = function(caches) { * If a key doesn't exist in a higher-priority cache but exists in a lower-priority * cache, it gets set in all higher-priority caches. */ - self.wrap = function(key, work, ttl, cb) { - if (typeof ttl === 'function') { - cb = ttl; - ttl = undefined; + self.wrap = function(key, work, options, cb) { + if (typeof options === 'function') { + cb = options; + options = undefined; } if (self.queues[key]) { @@ -95,18 +113,23 @@ var multi_caching = function(caches) { var opts = { key: key, value: result, - ttl: ttl + options: options }; + + if (typeof options !== 'object') { + opts.ttl = options; + } + set_in_multiple_caches(caches_to_update, opts, function(err) { fillCallbacks(err, result); }); } else { domain - .create() - .on('error', function(err) { - fillCallbacks(err); - }) - .bind(work)(function(err, data) { + .create() + .on('error', function(err) { + fillCallbacks(err); + }) + .bind(work)(function(err, data) { if (err) { fillCallbacks(err); return; @@ -114,8 +137,12 @@ var multi_caching = function(caches) { var opts = { key: key, value: data, - ttl: ttl + options: options }; + + if (typeof options !== 'object') { + opts.ttl = options; + } set_in_multiple_caches(caches, opts, function(err) { if (err) { fillCallbacks(err); @@ -128,22 +155,37 @@ var multi_caching = function(caches) { }); }; - self.set = function(key, value, ttl, cb) { + self.set = function(key, value, options, cb) { var opts = { key: key, value: value, - ttl: ttl + options: options }; + if (typeof options !== 'object') { + opts.ttl = options; + } set_in_multiple_caches(caches, opts, cb); }; - self.get = function(key, cb) { - get_from_highest_priority_cache(key, cb); + self.get = function(key, options, cb) { + if (typeof options === 'function') { + cb = options; + options = false; + } + get_from_highest_priority_cache(key, options, cb); }; - self.del = function(key, cb) { + self.del = function(key, options, cb) { + if (typeof options === 'function') { + cb = options; + options = false; + } async.forEach(caches, function(cache, async_cb) { - cache.store.del(key, async_cb); + if (typeof options === 'object') { + cache.store.del(key, options, async_cb); + }else { + cache.store.del(key, async_cb); + } }, cb); }; diff --git a/lib/stores/memory.js b/lib/stores/memory.js index 89efb40..3c3dc24 100644 --- a/lib/stores/memory.js +++ b/lib/stores/memory.js @@ -12,14 +12,17 @@ var memory_store = function(args) { var lru_cache = new Lru(lru_opts); - self.set = function(key, value, ttl, cb) { + self.set = function(key, value, options, cb) { lru_cache.set(key, value); if (cb) { process.nextTick(cb); } }; - self.get = function(key, cb) { + self.get = function(key, options, cb) { + if (typeof options === 'function') { + cb = options; + } var value = lru_cache.get(key); if (cb) { process.nextTick(function() { @@ -30,7 +33,10 @@ var memory_store = function(args) { } }; - self.del = function(key, cb) { + self.del = function(key, options, cb) { + if (typeof options === 'function') { + cb = options; + } lru_cache.del(key); if (cb) { process.nextTick(cb); diff --git a/test/caching.unit.js b/test/caching.unit.js index b627474..dc2d167 100644 --- a/test/caching.unit.js +++ b/test/caching.unit.js @@ -403,7 +403,7 @@ describe("caching", function() { it("bubbles up that error", function(done) { var fake_error = new Error(support.random.string()); - sinon.stub(memory_store_stub, 'get', function(key, cb) { + sinon.stub(memory_store_stub, 'get', function(key, options, cb) { cb(fake_error); }); @@ -423,7 +423,7 @@ describe("caching", function() { var fake_error = new Error(support.random.string()); - sinon.stub(memory_store_stub, 'get', function(key, cb) { + sinon.stub(memory_store_stub, 'get', function(key, options, cb) { cb(fake_error); }); diff --git a/test/multi_caching.unit.js b/test/multi_caching.unit.js index 52b7cb1..3387ee9 100644 --- a/test/multi_caching.unit.js +++ b/test/multi_caching.unit.js @@ -172,8 +172,8 @@ describe("multi_caching", function() { }); }); }); - }); - }, 10); + }, 10); + }); }); }); }); diff --git a/test/stores/options.unit.js b/test/stores/options.unit.js new file mode 100644 index 0000000..aec6d1f --- /dev/null +++ b/test/stores/options.unit.js @@ -0,0 +1,216 @@ +var caching = require("../../index"); +var assert = require("assert"); +var support = require("../support"); +var check_err = support.check_err; +var memoryFlag = ""; +var key; +var value; +var testStore = function(args) { + args = args || {}; + var self = {}; + self.name = "options"; + self.store = {}; + + self.get = function(key, options, cb) { + var optionsMapped = false; + if (typeof options === "function") { + cb = options; + options = false; + optionsMapped = true; + } + if (options && options.value) { + return cb(null, options.value + "ValueOption"); + } else if (options && options.fn) { + options.fn("GetFunctionOption"); + return cb(null, "GetFunctionOption"); + } else if (options && options.runNormal) { + return cb(null, self.store[key]); + } else if (optionsMapped) { + return cb(); + } + return cb("Error No Options"); + }; + + self.set = function(key, value, options, cb) { + var optionsMapped = false; + if (typeof options === "function") { + cb = options; + options = false; + optionsMapped = true; + } else if (typeof options !== "object") { + options = {ttl: options, runNormal: true}; + } + if (options && options.value) { + memoryFlag = options.value + "ValueOption"; + return cb(); + } else if (options && options.fn) { + options.fn("SetFunctionOption"); + return cb(); + } else if (options && options.runNormal) { + self.store[key] = value; + return cb(null, self.store[key]); + } else if (optionsMapped) { + return cb(); + } + return cb("Error No Options"); + }; + + self.del = function(key, options, cb) { + var optionsMapped = false; + if (typeof options === "function") { + cb = options; + options = false; + optionsMapped = true; + } + if (options && options.value) { + memoryFlag = options.value + "ValueOption"; + return cb(); + } else if (options && options.fn) { + options.fn("DeleteFunctionOption"); + return cb(); + } else if (options && options.runNormal) { + delete self.store[key]; + return cb(null, ""); + } else if (optionsMapped) { + return cb(); + } + return cb("Error No Options"); + }; + + return { + create: function() { + return self; + } + }; +}; + +describe("Methods with options", function() { + before(function() { + key = support.random.string(20); + value = support.random.string(20); + }); + describe("get with options", function() { + var testInstance = caching.caching({store: testStore()}); + var testCache; + before(function() { + testCache = caching.multi_caching([testInstance]); + }); + + it("lets us pass options by value", function(done) { + var options = {value: value}; + testCache.get(key, options, function(err, response) { + assert.equal(response, value + "ValueOption"); + done(); + }); + }); + + it("lets us pass options by function", function(done) { + var options = { + fn: function(response) { + assert.equal(response, "GetFunctionOption"); + done(); + } + }; + testCache.get(key, options, function(err, response) { + assert.equal(response, "GetFunctionOption"); + }); + }); + }); + describe("set with options", function() { + var testInstance = caching.caching({store: testStore()}); + var testCache; + var ttl = 60; + before(function() { + testCache = caching.multi_caching([testInstance]); + }); + + it("lets us pass options by value", function(done) { + var options = {ttl: ttl, value: value}; + testCache.set(key, value, options, function() { + assert.equal(memoryFlag, value + "ValueOption"); + done(); + }); + }); + + it("lets us pass options by function", function(done) { + var options = { + ttl: ttl, + fn: function(response) { + assert.equal(response, "SetFunctionOption"); + done(); + } + }; + testCache.set(key, value, options, function() {}, options); + }); + }); + describe("delete with options", function() { + var testInstance = caching.caching({store: testStore()}); + var testCache; + before(function() { + testCache = caching.multi_caching([testInstance]); + }); + + it("lets us pass options by value", function(done) { + var options = {value: value}; + testCache.del(key, options, function() { + assert.equal(memoryFlag,value + "ValueOption"); + done(); + }); + }); + + it("lets us pass options by function", function(done) { + var options = { + fn: function(response) { + assert.equal(response, "DeleteFunctionOption"); + done(); + } + }; + testCache.del(key, options, function() {}, options); + }); + }); +}); +describe("Multiple stores with options", function() { + var testInstance = caching.caching({store: testStore()}); + var memInstance = caching.caching({store: "memory"}); + var testCache; + var options = {runNormal: true}; + var ttl = 1; + before(function() { + key = support.random.string(20); + value = support.random.string(20); + testCache = caching.multi_caching([testInstance, memInstance]); + }); + + it("lets us pass options which only one store uses", function() { + testCache.set(key, value, options, function(err) { + check_err(err); + testCache.get(key, options, function(err, response) { + check_err(err); + assert.equal(response, value); + testCache.del(key, options, function(err) { + check_err(err); + testCache.get(key, options, function(err, response) { + check_err(err); + assert.equal(response, undefined); + }); + }); + }); + }); + }); + it("lets us not pass options which only one store uses", function() { + testCache.set(key, value, ttl, function(err) { + check_err(err); + testCache.get(key, function(err, response) { + check_err(err); + assert.equal(response, value); + testCache.del(key, function(err) { + check_err(err); + testCache.get(key, function(err, response) { + check_err(err); + assert.equal(response, undefined); + }); + }); + }); + }); + }); +});