From 4c8e7a5b7fea0d36ac3b2cb9161debe4c93972c1 Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 31 Mar 2011 04:11:11 -0400 Subject: [PATCH] [api] More work on Redis store --- lib/nconf/stores/redis.js | 72 +++++++++++++++++++++++++++-- test/stores/redis-test.js | 97 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 test/stores/redis-test.js diff --git a/lib/nconf/stores/redis.js b/lib/nconf/stores/redis.js index af77f77..8fc2115 100644 --- a/lib/nconf/stores/redis.js +++ b/lib/nconf/stores/redis.js @@ -6,11 +6,13 @@ */ var async = require('async'), + eyes = require('eyes'), redis = require('redis'), nconf = require('nconf'), Memory = require('./Memory').Memory; var Redis = exports.Redis = function (options) { + options = options || {} this.namespace = options.namespace || 'nconf'; this.host = options.host || 'localhost'; this.port = options.port || 6379; @@ -19,7 +21,33 @@ var Redis = exports.Redis = function (options) { }; Redis.prototype.get = function (key, callback) { + var self = this, + result = {}, + fullKey = nconf.utils.key(this.namespace, key); + this.redis.smembers(nconf.utils.key(fullKey, 'keys'), function (err, keys) { + function addValue (source, next) { + self.get(nconf.utils.key(key, source), function (err, value) { + if (err) { + return next(err); + } + + result[source] = value; + next(); + }); + } + + if (keys && keys.length > 0) { + async.forEach(keys, addValue, function (err) { + return err ? callback(err) : callback(null, result); + }) + } + else { + self.redis.get(fullKey, function (err, value) { + return err ? callback(err) : callback(null, JSON.parse(value)); + }); + } + }); }; Redis.prototype.set = function (key, value, callback) { @@ -29,20 +57,56 @@ Redis.prototype.set = function (key, value, callback) { function addKey (partial, next) { var index = path.indexOf(partial), base = [self.namespace].concat(path.slice(0, index)), - parent = nconf.utils.key(base.concat('keys')); - - self.redis.rpush(parent, partial, next); + parent = nconf.utils.key(base.concat(['keys'])); + + self.redis.sadd(parent, partial, next); }; - + async.forEach(path, addKey, function (err) { if (err) { return callback(err); } + var fullKey = nconf.utils.key(self.namespace, key); + if (!Array.isArray(value) && typeof value === 'object') { + self._setObject(fullKey, value, callback); + } + else { + value = JSON.stringify(value); + self.redis.set(fullKey, value, callback); + } }); }; Redis.prototype.clear = function (key, callback) { }; + +Redis.prototype._setObject = function (key, value, callback) { + var self = this, + keys = Object.keys(value); + + function addValue (child, next) { + self.redis.sadd(nconf.utils.key(key, 'keys'), child, function (err) { + if (err) { + return next(err); + } + + var fullKey = nconf.utils.key(key, child), + childValue = value[child]; + + if (!Array.isArray(childValue) && typeof childValue === 'object') { + self._setObject(fullKey, childValue, next); + } + else { + childValue = JSON.stringify(childValue); + self.redis.set(fullKey, childValue, next); + } + }); + } + + async.forEach(keys, addValue, function (err) { + return err ? callback(err) : callback(); + }); +}; \ No newline at end of file diff --git a/test/stores/redis-test.js b/test/stores/redis-test.js new file mode 100644 index 0000000..f5af468 --- /dev/null +++ b/test/stores/redis-test.js @@ -0,0 +1,97 @@ +/* + * redis-test.js: Tests for the redis nconf storage engine. + * + * (C) 2011, Charlie Robbins + * + */ + +require.paths.unshift(require('path').join(__dirname, '..', '..', 'lib')); + +var vows = require('vows'), + assert = require('assert'), + nconf = require('nconf'); + +var data = { + literal: 'bazz', + arr: ['one', 2, true, { value: 'foo' }], + obj: { + host: 'localhost', + port: 5984, + array: ['one', 2, true, { foo: 'bar' }], + auth: { + username: 'admin', + password: 'password' + } + } +} + +vows.describe('nconf/stores/redis').addBatch({ + "When using the nconf redis store": { + topic: new nconf.stores.Redis(), + "the set() method": { + "with a literal": { + topic: function (store) { + store.set('foo:literal', 'bazz', this.callback) + }, + "should respond without an error": function (err, ok) { + assert.isNull(err); + } + }, + "with an Array": { + topic: function (store) { + store.set('foo:array', data.arr, this.callback) + }, + "should respond without an": function (err, ok) { + assert.isNull(err); + } + }, + "with an Object": { + topic: function (store) { + store.set('foo:object', data.obj, this.callback) + }, + "should respond without an error": function (err, ok) { + assert.isNull(err); + } + } + } + } +}).addBatch({ + "When using the nconf redis store": { + topic: new nconf.stores.Redis(), + "the get() method": { + "with a literal value": { + topic: function (store) { + store.get('foo:literal', this.callback); + }, + "should respond with the correct value": function (err, value) { + assert.equal(value, data.literal); + } + }, + "with an Array value": { + topic: function (store) { + store.get('foo:array', this.callback); + }, + "should respond with the correct value": function (err, value) { + assert.deepEqual(value, data.arr); + } + }, + "with an Object value": { + topic: function (store) { + store.get('foo:object', this.callback); + }, + "should respond with the correct value": function (err, value) { + assert.deepEqual(value, data.obj); + } + } + } + } +}).addBatch({ + /*, + "the clear() method": { + "should respond with the true": function (store) { + assert.equal(store.get('foo:bar:bazz'), 'buzz'); + assert.isTrue(store.clear('foo:bar:bazz')); + assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined'); + } + }*/ +}).export(module); \ No newline at end of file