[api doc test] Finalize API. Add more test coverage
This commit is contained in:
parent
772de110bb
commit
73bf78339f
7 changed files with 472 additions and 46 deletions
76
lib/nconf.js
76
lib/nconf.js
|
@ -9,25 +9,55 @@ require.paths.unshift(__dirname);
|
||||||
|
|
||||||
var nconf = exports;
|
var nconf = exports;
|
||||||
|
|
||||||
nconf.utils = require('nconf/utils');
|
|
||||||
nconf.stores = require('nconf/stores');
|
nconf.stores = require('nconf/stores');
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function use (type, options)
|
||||||
|
// #### @type {string} Type of the nconf store to use.
|
||||||
|
// #### @options {Object} Options for the store instance.
|
||||||
|
// Sets the active `nconf.store` to a new instance of the
|
||||||
|
// specified `type`.
|
||||||
|
//
|
||||||
nconf.use = function (type, options) {
|
nconf.use = function (type, options) {
|
||||||
nconf.store = new nconf.stores[type](options);
|
nconf.store = new nconf.stores[type](options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function get (key, callback)
|
||||||
|
// #### @key {string} Key to retrieve for this instance.
|
||||||
|
// #### @callback {function} **Optional** Continuation to respond to when complete.
|
||||||
|
// Retrieves the value for the specified key (if any).
|
||||||
|
//
|
||||||
nconf.get = function (key, callback) {
|
nconf.get = function (key, callback) {
|
||||||
return nconf.store.get(key, callback);
|
return nconf.store.get(key, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function set (key, value, callback)
|
||||||
|
// #### @key {string} Key to set in this instance
|
||||||
|
// #### @value {literal|Object} Value for the specified key
|
||||||
|
// #### @callback {function} **Optional** Continuation to respond to when complete.
|
||||||
|
// Sets the `value` for the specified `key` in this instance.
|
||||||
|
//
|
||||||
nconf.set = function (key, value, callback) {
|
nconf.set = function (key, value, callback) {
|
||||||
return nconf.store.set(key, value, callback);
|
return nconf.store.set(key, value, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function clear (key, callback)
|
||||||
|
// #### @key {string} Key to remove from this instance
|
||||||
|
// #### @callback {function} **Optional** Continuation to respond to when complete.
|
||||||
|
// Removes the value for the specified `key` from this instance.
|
||||||
|
//
|
||||||
nconf.clear = function (key, callback) {
|
nconf.clear = function (key, callback) {
|
||||||
return nconf.store.clear(key, callback);
|
return nconf.store.clear(key, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function load (callback)
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Responds with an Object representing all keys associated in this instance.
|
||||||
|
//
|
||||||
nconf.load = function (callback) {
|
nconf.load = function (callback) {
|
||||||
if (!nconf.store.load) {
|
if (!nconf.store.load) {
|
||||||
var error = new Error('nconf store ' + nconf.store.type + ' has no load() method');
|
var error = new Error('nconf store ' + nconf.store.type + ' has no load() method');
|
||||||
|
@ -41,7 +71,19 @@ nconf.load = function (callback) {
|
||||||
return nconf.store.load(callback);
|
return nconf.store.load(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
nconf.save = function (callback) {
|
//
|
||||||
|
// ### function save (value, callback)
|
||||||
|
// #### @value {Object} **Optional** Config object to set for this instance
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Removes any existing configuration settings that may exist in this
|
||||||
|
// instance and then adds all key-value pairs in `value`.
|
||||||
|
//
|
||||||
|
nconf.save = function (value, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
callback = value;
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!nconf.store.save) {
|
if (!nconf.store.save) {
|
||||||
var error = new Error('nconf store ' + nconf.store.type + ' has no save() method');
|
var error = new Error('nconf store ' + nconf.store.type + ' has no save() method');
|
||||||
if (callback) {
|
if (callback) {
|
||||||
|
@ -51,5 +93,31 @@ nconf.save = function (callback) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nconf.store.save(callback);
|
return nconf.store.save(value, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function reset (callback)
|
||||||
|
// #### @callback {function} **Optional** Continuation to respond to when complete.
|
||||||
|
// Clears all keys associated with this instance.
|
||||||
|
//
|
||||||
|
nconf.reset = function (callback) {
|
||||||
|
return nconf.store.reset(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function key (arguments)
|
||||||
|
// Returns a `:` joined string from the `arguments`.
|
||||||
|
//
|
||||||
|
nconf.key = function () {
|
||||||
|
return Array.prototype.slice.call(arguments).join(':');
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function path (key)
|
||||||
|
// #### @key {string} The ':' delimited key to split
|
||||||
|
// Returns a fully-qualified path to a nested nconf key.
|
||||||
|
//
|
||||||
|
nconf.path = function (key) {
|
||||||
|
return key.split(':');
|
||||||
|
};
|
||||||
|
|
|
@ -9,6 +9,12 @@ var fs = require('fs'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
Memory = require('./memory').Memory;
|
Memory = require('./memory').Memory;
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function File (options)
|
||||||
|
// #### @options {Object} Options for this instance
|
||||||
|
// Constructor function for the File nconf store, a simple abstraction
|
||||||
|
// around the Memory store that can persist configuration to disk.
|
||||||
|
//
|
||||||
var File = exports.File = function (options) {
|
var File = exports.File = function (options) {
|
||||||
if (!options.file) {
|
if (!options.file) {
|
||||||
throw new Error ('Missing required option `files`');
|
throw new Error ('Missing required option `files`');
|
||||||
|
@ -20,14 +26,32 @@ var File = exports.File = function (options) {
|
||||||
this.format = options.format || JSON;
|
this.format = options.format || JSON;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Inherit from the Memory store
|
||||||
util.inherits(File, Memory);
|
util.inherits(File, Memory);
|
||||||
|
|
||||||
File.prototype.save = function (callback) {
|
//
|
||||||
|
// ### function save (value, callback)
|
||||||
|
// #### @value {Object} _Ignored_ Left here for consistency
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Saves the current configuration object to disk at `this.file`
|
||||||
|
// using the format specified by `this.format`.
|
||||||
|
//
|
||||||
|
File.prototype.save = function (value, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
callback = value;
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
fs.save(this.file, this.format.stringify(this.store), function (err) {
|
fs.save(this.file, this.format.stringify(this.store), function (err) {
|
||||||
return err ? callback(err) : callback();
|
return err ? callback(err) : callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function load (callback)
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Responds with an Object representing all keys associated in this instance.
|
||||||
|
//
|
||||||
File.prototype.load = function (callback) {
|
File.prototype.load = function (callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
fs.load(this.file, function (err, data) {
|
fs.load(this.file, function (err, data) {
|
||||||
|
|
|
@ -7,15 +7,32 @@
|
||||||
|
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function Memory (options)
|
||||||
|
// #### @options {Object} Options for this instance
|
||||||
|
// Constructor function for the Memory nconf store which maintains
|
||||||
|
// a nested json structure based on key delimiters `:`.
|
||||||
|
//
|
||||||
|
// e.g. `my:nested:key` ==> `{ my: { nested: { key: } } }`
|
||||||
|
//
|
||||||
var Memory = exports.Memory = function (options) {
|
var Memory = exports.Memory = function (options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
this.store = {};
|
this.store = {};
|
||||||
|
this.mtimes = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function get (key)
|
||||||
|
// #### @key {string} Key to retrieve for this instance.
|
||||||
|
// Retrieves the value for the specified key (if any).
|
||||||
|
//
|
||||||
Memory.prototype.get = function (key) {
|
Memory.prototype.get = function (key) {
|
||||||
var target = this.store,
|
var target = this.store,
|
||||||
path = nconf.utils.path(key);
|
path = nconf.path(key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scope into the object to get the appropriate nested context
|
||||||
|
//
|
||||||
while (path.length > 0) {
|
while (path.length > 0) {
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
|
@ -29,10 +46,24 @@ Memory.prototype.get = function (key) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function set (key, value)
|
||||||
|
// #### @key {string} Key to set in this instance
|
||||||
|
// #### @value {literal|Object} Value for the specified key
|
||||||
|
// Sets the `value` for the specified `key` in this instance.
|
||||||
|
//
|
||||||
Memory.prototype.set = function (key, value) {
|
Memory.prototype.set = function (key, value) {
|
||||||
var target = this.store,
|
var target = this.store,
|
||||||
path = nconf.utils.path(key);
|
path = nconf.path(key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the `mtime` (modified time) of the key
|
||||||
|
//
|
||||||
|
this.mtimes[key] = Date.now();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scope into the object to get the appropriate nested context
|
||||||
|
//
|
||||||
while (path.length > 1) {
|
while (path.length > 1) {
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
|
@ -42,15 +73,29 @@ Memory.prototype.set = function (key, value) {
|
||||||
target = target[key];
|
target = target[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the specified value in the nested JSON structure
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
target[key] = value;
|
target[key] = value;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function clear (key)
|
||||||
|
// #### @key {string} Key to remove from this instance
|
||||||
|
// Removes the value for the specified `key` from this instance.
|
||||||
|
//
|
||||||
Memory.prototype.clear = function (key) {
|
Memory.prototype.clear = function (key) {
|
||||||
var target = this.store,
|
var target = this.store,
|
||||||
path = nconf.utils.path(key);
|
path = nconf.path(key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove the key from the set of `mtimes` (modified times)
|
||||||
|
//
|
||||||
|
delete this.mtimes[key];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scope into the object to get the appropriate nested context
|
||||||
|
//
|
||||||
while (path.length > 1) {
|
while (path.length > 1) {
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
if (!target[key]) {
|
if (!target[key]) {
|
||||||
|
@ -60,7 +105,18 @@ Memory.prototype.clear = function (key) {
|
||||||
target = target[key];
|
target = target[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete the key from the nested JSON structure
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
delete target[key];
|
delete target[key];
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function reset (callback)
|
||||||
|
// Clears all keys associated with this instance.
|
||||||
|
//
|
||||||
|
Memory.prototype.reset = function () {
|
||||||
|
this.mtimes = {};
|
||||||
|
this.store = {};
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -11,23 +11,56 @@ var async = require('async'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
Memory = require('./Memory').Memory;
|
Memory = require('./Memory').Memory;
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function Redis (options)
|
||||||
|
// #### @options {Object} Options for this instance
|
||||||
|
// Constructor function for the Redis nconf store which maintains
|
||||||
|
// a nested Redis key structure based on key delimiters `:`.
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
// my:nested:key, 'value'
|
||||||
|
// namespace:keys ==> ['my']
|
||||||
|
// namespace:nested:keys ==> ['key']
|
||||||
|
// namespace:nested:key ==> 'value'
|
||||||
|
//
|
||||||
var Redis = exports.Redis = function (options) {
|
var Redis = exports.Redis = function (options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
this.namespace = options.namespace || 'nconf';
|
this.namespace = options.namespace || 'nconf';
|
||||||
this.host = options.host || 'localhost';
|
this.host = options.host || 'localhost';
|
||||||
this.port = options.port || 6379;
|
this.port = options.port || 6379;
|
||||||
|
this.ttl = options.ttl || 60 * 60 * 1000;
|
||||||
this.cache = new Memory();
|
this.cache = new Memory();
|
||||||
this.redis = redis.createClient(options.port, options.host);
|
this.redis = redis.createClient(options.port, options.host);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function get (key, callback)
|
||||||
|
// #### @key {string} Key to retrieve for this instance.
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Retrieves the value for the specified key (if any).
|
||||||
|
//
|
||||||
Redis.prototype.get = function (key, callback) {
|
Redis.prototype.get = function (key, callback) {
|
||||||
var self = this,
|
var self = this,
|
||||||
result = {},
|
result = {},
|
||||||
fullKey = nconf.utils.key(this.namespace, key);
|
now = Date.now(),
|
||||||
|
mtime = this.cache.mtimes[key],
|
||||||
|
fullKey = nconf.key(this.namespace, key);
|
||||||
|
|
||||||
this.redis.smembers(nconf.utils.key(fullKey, 'keys'), function (err, keys) {
|
//
|
||||||
|
// If the key exists in the cache and the ttl is less than
|
||||||
|
// the value set for this instance, return from the cache.
|
||||||
|
//
|
||||||
|
if (mtime && now - mtime < this.ttl) {
|
||||||
|
return callback(null, this.cache.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the set of all children keys for the `key` supplied. If the value
|
||||||
|
// to be returned is an Object, this list will not be empty.
|
||||||
|
//
|
||||||
|
this.redis.smembers(nconf.key(fullKey, 'keys'), function (err, keys) {
|
||||||
function addValue (source, next) {
|
function addValue (source, next) {
|
||||||
self.get(nconf.utils.key(key, source), function (err, value) {
|
self.get(nconf.key(key, source), function (err, value) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
@ -38,62 +71,253 @@ Redis.prototype.get = function (key, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys && keys.length > 0) {
|
if (keys && keys.length > 0) {
|
||||||
|
//
|
||||||
|
// If the value to be retrieved is an Object, recursively attempt
|
||||||
|
// to get the value from redis. Here we use a recursive call to `this.get`
|
||||||
|
// to support nested Object keys.
|
||||||
|
//
|
||||||
async.forEach(keys, addValue, function (err) {
|
async.forEach(keys, addValue, function (err) {
|
||||||
return err ? callback(err) : callback(null, result);
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.set(key, result);
|
||||||
|
callback(null, result);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
//
|
||||||
|
// If there are no keys, then the value to be retrieved is a literal
|
||||||
|
// and we can simply return the value from redis directly.
|
||||||
|
//
|
||||||
self.redis.get(fullKey, function (err, value) {
|
self.redis.get(fullKey, function (err, value) {
|
||||||
return err ? callback(err) : callback(null, JSON.parse(value));
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = JSON.parse(value);
|
||||||
|
if (result) {
|
||||||
|
self.cache.set(key, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function set (key, value, callback)
|
||||||
|
// #### @key {string} Key to set in this instance
|
||||||
|
// #### @value {literal|Object} Value for the specified key
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Sets the `value` for the specified `key` in this instance.
|
||||||
|
//
|
||||||
Redis.prototype.set = function (key, value, callback) {
|
Redis.prototype.set = function (key, value, callback) {
|
||||||
var self = this,
|
var self = this,
|
||||||
path = nconf.utils.path(key);
|
path = nconf.path(key);
|
||||||
|
|
||||||
function addKey (partial, next) {
|
function addKey (partial, next) {
|
||||||
var index = path.indexOf(partial),
|
var index = path.indexOf(partial),
|
||||||
base = [self.namespace].concat(path.slice(0, index)),
|
base = [self.namespace].concat(path.slice(0, index)),
|
||||||
parent = nconf.utils.key(base.concat(['keys']));
|
parent = nconf.key.apply(null, base.concat(['keys']));
|
||||||
|
|
||||||
self.redis.sadd(parent, partial, next);
|
self.redis.sadd(parent, partial, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Iterate over the entire key path and add each key to the
|
||||||
|
// parent key-set if it doesn't exist already.
|
||||||
|
//
|
||||||
async.forEach(path, addKey, function (err) {
|
async.forEach(path, addKey, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullKey = nconf.utils.key(self.namespace, key);
|
var fullKey = nconf.key(self.namespace, key);
|
||||||
|
|
||||||
if (!Array.isArray(value) && typeof value === 'object') {
|
if (!Array.isArray(value) && typeof value === 'object') {
|
||||||
|
//
|
||||||
|
// If the value is an `Object` (and not an `Array`) then
|
||||||
|
// nest into the value and set the child keys appropriately.
|
||||||
|
// This is done for efficient lookup when setting Object keys.
|
||||||
|
// (i.e. If you set and Object then wish to later retrieve only a
|
||||||
|
// member of that Object, the entire Object need not be retrieved).
|
||||||
|
//
|
||||||
self._setObject(fullKey, value, callback);
|
self._setObject(fullKey, value, callback);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
//
|
||||||
|
// If the value is a simple literal (or an `Array`) then JSON
|
||||||
|
// stringify it and put it into Redis.
|
||||||
|
//
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
|
self.cache.set(key, value);
|
||||||
self.redis.set(fullKey, value, callback);
|
self.redis.set(fullKey, value, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function clear (key, callback)
|
||||||
|
// #### @key {string} Key to remove from this instance
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Removes the value for the specified `key` from this instance.
|
||||||
|
//
|
||||||
Redis.prototype.clear = function (key, callback) {
|
Redis.prototype.clear = function (key, callback) {
|
||||||
|
var self = this,
|
||||||
|
result = {},
|
||||||
|
path = [this.namespace].concat(nconf.path(key)),
|
||||||
|
last = path.pop(),
|
||||||
|
fullKey = nconf.key(this.namespace, key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Clear the key from the cache for this instance
|
||||||
|
//
|
||||||
|
this.cache.clear(key);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove the `key` from the parent set of keys.
|
||||||
|
//
|
||||||
|
this.redis.srem(nconf.key.apply(null, path.concat(['keys'])), last, function (err) {
|
||||||
|
//
|
||||||
|
// Remove the value from redis by iterating over the set of keys (if any)
|
||||||
|
// and deleting each value. If no keys, then just delete the simple literal.
|
||||||
|
//
|
||||||
|
self.redis.smembers(nconf.key(fullKey, 'keys'), function (err, keys) {
|
||||||
|
function removeValue (child, next) {
|
||||||
|
//
|
||||||
|
// Recursively call `self.clear` here to ensure we remove any
|
||||||
|
// nested Objects completely from this instance.
|
||||||
|
//
|
||||||
|
self.clear(nconf.key(key, child), next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys && keys.length > 0) {
|
||||||
|
//
|
||||||
|
// If there are child keys then iterate over them,
|
||||||
|
// removing each child along the way.
|
||||||
|
//
|
||||||
|
async.forEach(keys, removeValue, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//
|
||||||
|
// Otherwise if this is just a simple literal, then
|
||||||
|
// simply remove it from Redis directly.
|
||||||
|
//
|
||||||
|
self.redis.del(fullKey, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function save (value, callback)
|
||||||
|
// #### @value {Object} Config object to set for this instance
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Removes any existing configuration settings that may exist in this
|
||||||
|
// instance and then adds all key-value pairs in `value`.
|
||||||
|
//
|
||||||
|
Redis.prototype.save = function (value, callback) {
|
||||||
|
if (Array.isArray(value) || typeof value !== 'object') {
|
||||||
|
return callback(new Error('`value` to be saved must be an object.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
keys = Object.keys(value);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Clear all existing keys associated with this instance.
|
||||||
|
//
|
||||||
|
this.reset(function (err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Iterate over the keys in the new value, setting each of them.
|
||||||
|
//
|
||||||
|
async.forEach(keys, function (key, next) {
|
||||||
|
self.set(key, value[key], next);
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function load (callback)
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Responds with an Object representing all keys associated in this instance.
|
||||||
|
//
|
||||||
|
Redis.prototype.load = function (callback) {
|
||||||
|
var self = this,
|
||||||
|
result = {};
|
||||||
|
|
||||||
|
this.redis.smembers(nconf.key(this.namespace, 'keys'), function (err, keys) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addValue (key, next) {
|
||||||
|
self.get(key, function (err, value) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
result[key] = value;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async.forEach(keys, addValue, function (err) {
|
||||||
|
return err ? callback(err) : callback(null, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function reset (callback)
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Clears all keys associated with this instance.
|
||||||
|
//
|
||||||
|
Redis.prototype.reset = function (callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the list of of top-level keys, then clear each of them
|
||||||
|
//
|
||||||
|
this.redis.smembers(nconf.key(this.namespace, 'keys'), function (err, existing) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.forEach(existing, function (key, next) {
|
||||||
|
self.clear(key, next);
|
||||||
|
}, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### @private function _setObject (key, value, callback)
|
||||||
|
// #### @key {string} Key to set in this instance
|
||||||
|
// #### @value {Object} Value for the specified key
|
||||||
|
// #### @callback {function} Continuation to respond to when complete.
|
||||||
|
// Internal helper function for setting all keys of a nested object.
|
||||||
|
//
|
||||||
Redis.prototype._setObject = function (key, value, callback) {
|
Redis.prototype._setObject = function (key, value, callback) {
|
||||||
var self = this,
|
var self = this,
|
||||||
keys = Object.keys(value);
|
keys = Object.keys(value);
|
||||||
|
|
||||||
function addValue (child, next) {
|
function addValue (child, next) {
|
||||||
self.redis.sadd(nconf.utils.key(key, 'keys'), child, function (err) {
|
//
|
||||||
|
// Add the child key to the parent key-set, then set the value.
|
||||||
|
// Recursively call `_setObject` in the event of nested Object(s).
|
||||||
|
//
|
||||||
|
self.redis.sadd(nconf.key(key, 'keys'), child, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullKey = nconf.utils.key(key, child),
|
var fullKey = nconf.key(key, child),
|
||||||
childValue = value[child];
|
childValue = value[child];
|
||||||
|
|
||||||
if (!Array.isArray(childValue) && typeof childValue === 'object') {
|
if (!Array.isArray(childValue) && typeof childValue === 'object') {
|
||||||
|
@ -106,6 +330,9 @@ Redis.prototype._setObject = function (key, value, callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Iterate over the keys of the Object and set the appropriate values.
|
||||||
|
//
|
||||||
async.forEach(keys, addValue, function (err) {
|
async.forEach(keys, addValue, function (err) {
|
||||||
return err ? callback(err) : callback();
|
return err ? callback(err) : callback();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
/*
|
|
||||||
* utils.js: Utils for the nconf module.
|
|
||||||
*
|
|
||||||
* (C) 2011, Charlie Robbins
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var utils = exports;
|
|
||||||
|
|
||||||
utils.key = function () {
|
|
||||||
return Array.prototype.slice.call(arguments).join(':');
|
|
||||||
};
|
|
||||||
|
|
||||||
utils.path = function (key) {
|
|
||||||
return key.split(':');
|
|
||||||
};
|
|
|
@ -5,7 +5,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require.paths.unshift(require('path').join(__dirname, '..', '..', 'lib'));
|
require.paths.unshift(require('path').join(__dirname, '..', 'lib'));
|
||||||
|
|
||||||
var vows = require('vows'),
|
var vows = require('vows'),
|
||||||
assert = require('assert'),
|
assert = require('assert'),
|
|
@ -5,7 +5,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require.paths.unshift(require('path').join(__dirname, '..', '..', 'lib'));
|
require.paths.unshift(require('path').join(__dirname, '..', 'lib'));
|
||||||
|
|
||||||
var vows = require('vows'),
|
var vows = require('vows'),
|
||||||
assert = require('assert'),
|
assert = require('assert'),
|
||||||
|
@ -94,12 +94,79 @@ vows.describe('nconf/stores/redis').addBatch({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).addBatch({
|
}).addBatch({
|
||||||
/*,
|
"When using the nconf redis store": {
|
||||||
"the clear() method": {
|
topic: new nconf.stores.Redis(),
|
||||||
"should respond with the true": function (store) {
|
"the clear() method": {
|
||||||
assert.equal(store.get('foo:bar:bazz'), 'buzz');
|
topic: function (store) {
|
||||||
assert.isTrue(store.clear('foo:bar:bazz'));
|
var that = this;
|
||||||
assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined');
|
store.clear('foo', function (err) {
|
||||||
|
if (err) {
|
||||||
|
return that.callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.get('foo', that.callback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"should actually remove the value from Redis": function (err, value) {
|
||||||
|
assert.isNull(err);
|
||||||
|
assert.isNull(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
}).addBatch({
|
||||||
|
"When using the nconf redis store": {
|
||||||
|
topic: new nconf.stores.Redis(),
|
||||||
|
"the save() method": {
|
||||||
|
topic: function (store) {
|
||||||
|
var that = this;
|
||||||
|
store.save(data, function (err) {
|
||||||
|
if (err) {
|
||||||
|
return that.callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.get('obj', that.callback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"should set all values correctly": function (err, value) {
|
||||||
|
assert.isNull(err);
|
||||||
|
assert.deepEqual(value, data.obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).addBatch({
|
||||||
|
"When using the nconf redis store": {
|
||||||
|
topic: new nconf.stores.Redis(),
|
||||||
|
"the load() method": {
|
||||||
|
topic: function (store) {
|
||||||
|
store.load(this.callback);
|
||||||
|
},
|
||||||
|
"should respond with the correct object": function (err, value) {
|
||||||
|
assert.isNull(err);
|
||||||
|
assert.deepEqual(value, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).addBatch({
|
||||||
|
"When using the nconf redis store": {
|
||||||
|
topic: new nconf.stores.Redis(),
|
||||||
|
"the reset() method": {
|
||||||
|
topic: function (store) {
|
||||||
|
var that = this;
|
||||||
|
this.store = store;
|
||||||
|
|
||||||
|
store.reset(function (err) {
|
||||||
|
if (err) {
|
||||||
|
return that.callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.get('obj', that.callback);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"should remove all keys from redis": function (err, value) {
|
||||||
|
assert.isNull(err);
|
||||||
|
assert.isNull(value);
|
||||||
|
assert.length(Object.keys(this.store.cache.store), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}).export(module);
|
}).export(module);
|
Loading…
Reference in a new issue