Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[wip] refactoring to simplify code base

  • Loading branch information...
commit 4cc1357ff7a12b6fc55558dc85cbcc4eb4942d6b 1 parent da39d3c
@bmeck bmeck authored
View
28 lib/nconf.js
@@ -6,34 +6,32 @@
*/
var fs = require('fs'),
- async = require('async'),
common = require('./nconf/common'),
Provider = require('./nconf/provider').Provider,
nconf = module.exports = new Provider();
//
-// Expose the version from the package.json using `pkginfo`.
-//
-require('pkginfo')(module, 'version');
-
-//
// Setup all stores as lazy-loaded getters.
//
+nconf.engines = {};
fs.readdirSync(__dirname + '/nconf/stores').forEach(function (file) {
- var store = file.replace('.js', ''),
- name = common.capitalize(store);
-
- nconf.__defineGetter__(name, function () {
- return require('./nconf/stores/' + store)[name];
+ Object.defineProperty(nconf.engines, file.replace('.js', ''), {
+ get: function () {
+ return require('./nconf/stores/' + file);
+ },
+ enumerable: true
+ });
+ Object.defineProperty(nconf, file.replace('.js', '').replace(/./, function (c) {return c.toUpperCase()}), {
+ get: function () {
+ return require('./nconf/stores/' + file);
+ },
+ enumerable: true
});
});
//
// Expose the various components included with nconf
//
-nconf.key = common.key;
-nconf.path = common.path;
-nconf.loadFiles = common.loadFiles;
-nconf.loadFilesSync = common.loadFilesSync;
+nconf.common = common;
nconf.formats = require('./nconf/formats');
nconf.Provider = Provider;
View
53 lib/nconf/common.js
@@ -7,8 +7,7 @@
var fs = require('fs'),
async = require('async'),
- formats = require('./formats'),
- Memory = require('./stores/memory').Memory;
+ Memory = require('./stores/memory');
var common = exports;
@@ -20,9 +19,39 @@ var common = exports;
// '' should still be respected as a path.
//
common.path = function (key) {
- return key == null ? [] : key.split(':');
+ return key == null ? [] : ('' + key).split(':');
};
+common.scope = function (path, target) {
+ //
+ // Scope into the object to get the appropriate nested context
+ //
+ for (var i = 0; i < path.length; i++) {
+ key = path[i];
+ if (typeof target === 'object' && key in target) {
+ target = target[key];
+ continue;
+ }
+ return void 0;
+ }
+ return target;
+}
+
+common.ensure = function (path, target) {
+ //
+ // Scope into the object to get the appropriate nested context
+ //
+ for (var i = 0; i < path.length - 1; i++) {
+ key = path[i];
+ if (target && !(key in target)) {
+ target[key] = {};
+ }
+ target = target[key];
+ }
+
+ return target;
+}
+
//
// ### function key (arguments)
// Returns a `:` joined string from the `arguments`.
@@ -39,7 +68,8 @@ common.key = function () {
//
common.loadFiles = function (files, callback) {
if (!files) {
- return callback(null, {});
+ callback(null, {});
+ return;
}
var options = Array.isArray(files) ? { files: files } : files;
@@ -48,7 +78,7 @@ common.loadFiles = function (files, callback) {
// Set the default JSON format if not already
// specified
//
- options.format = options.format || formats.json;
+ options.format = options.format || require('../nconf').formats.json;
function parseFile (file, next) {
fs.readFile(file, function (err, data) {
@@ -70,7 +100,7 @@ common.loadFiles = function (files, callback) {
//
common.loadFilesSync = function (files) {
if (!files) {
- return;
+ return void 0;
}
//
@@ -78,7 +108,7 @@ common.loadFilesSync = function (files) {
// specified
//
var options = Array.isArray(files) ? { files: files } : files;
- options.format = options.format || formats.json;
+ options.format = options.format || require('../nconf').formats.json;
return common.merge(files.map(function (file) {
return options.format.parse(fs.readFileSync(file, 'utf8'));
@@ -102,12 +132,3 @@ common.merge = function (objs) {
return store.store;
};
-
-//
-// ### function capitalize (str)
-// #### @str {string} String to capitalize
-// Capitalizes the specified `str`.
-//
-common.capitalize = function (str) {
- return str && str[0].toUpperCase() + str.slice(1);
-};
View
6 lib/nconf/formats.js
@@ -7,13 +7,11 @@
var ini = require('ini');
-var formats = exports;
-
//
// ### @json
// Standard JSON format which pretty prints `.stringify()`.
//
-formats.json = {
+exports.json = {
stringify: function (obj, replacer, spacing) {
return JSON.stringify(obj, replacer || null, spacing || 2)
},
@@ -25,4 +23,4 @@ formats.json = {
// Standard INI format supplied from the `ini` module
// http://en.wikipedia.org/wiki/INI_file
//
-formats.ini = ini;
+exports.ini = ini;
View
352 lib/nconf/provider.js
@@ -7,6 +7,7 @@
var async = require('async'),
common = require('./common');
+
//
// ### function Provider (options)
@@ -29,8 +30,10 @@ var Provider = exports.Provider = function (options) {
// Define wrapper functions for using basic stores
// in this instance
//
-['argv', 'env'].forEach(function (type) {
+['argv', 'env', 'http'].forEach(function (type) {
Provider.prototype[type] = function (options) {
+ options = options || {};
+ options.type = type;
return this.add(type, options);
};
});
@@ -48,14 +51,12 @@ var Provider = exports.Provider = function (options) {
//
Provider.prototype.file = function (key, options) {
if (arguments.length == 1) {
- options = typeof key === 'string' ? { file: key } : key;
+ options = key;
key = 'file';
}
- else {
- options = typeof options === 'string'
- ? { file: options }
- : options;
- }
+ options = typeof options === 'string'
+ ? { file: options }
+ : options;
options.type = 'file';
return this.add(key, options);
@@ -68,9 +69,7 @@ Provider.prototype.file = function (key, options) {
['defaults', 'overrides'].forEach(function (type) {
Provider.prototype[type] = function (options) {
options = options || {};
- if (!options.type) {
- options.type = 'literal';
- }
+ options.type = options.type || 'literal';
return this.add(type, options);
};
@@ -85,29 +84,20 @@ Provider.prototype.file = function (key, options) {
// will be used instead:
//
// provider.use('file');
-// provider.use('file', { type: 'file', filename: '/path/to/userconf' })
+// provider.use('file', { type: 'file', file: '/path/to/userconf' })
//
Provider.prototype.use = function (name, options) {
options = options || {};
var type = options.type || name;
- function sameOptions (store) {
- return Object.keys(options).every(function (key) {
- return options[key] === store[key];
- });
- }
-
var store = this.stores[name],
update = store && !sameOptions(store);
- if (!store || update) {
- if (update) {
- this.remove(name);
- }
-
- this.add(name, options);
+ if (store) {
+ this.remove(name);
}
+ this.add(name, options);
return this;
};
@@ -121,18 +111,30 @@ Provider.prototype.use = function (name, options) {
// provider.add('memory');
// provider.add('userconf', { type: 'file', filename: '/path/to/userconf' })
//
-Provider.prototype.add = function (name, options) {
+var nconf = null;
+Provider.prototype.add = function (name, options, cb) {
options = options || {};
- var type = options.type || name;
-
- if (!require('../nconf')[common.capitalize(type)]) {
- throw new Error('Cannot add store with unknown type: ' + type);
+ var store;
+ nconf = nconf || require('../nconf');
+ if (options instanceof nconf.engines.memory) {
+ store = this.stores[name] = options;
}
-
- this.stores[name] = this.create(type, options);
-
- if (this.stores[name].loadSync) {
- this.stores[name].loadSync();
+ else {
+ var type = options.type || name;
+ if (!nconf.engines[type]) {
+ throw new Error('Cannot add store with unknown type: ' + type);
+ }
+
+ store = this.stores[name] = new nconf.engines[type](options);
+ }
+ if (cb && store.load) {
+ store.load(cb);
+ }
+ else if (store.loadSync) {
+ store.loadSync();
+ }
+ else {
+ throw new Error('Unable to load store with name: ' + name)
}
return this;
@@ -151,17 +153,6 @@ Provider.prototype.remove = function (name) {
};
//
-// ### function create (type, options)
-// #### @type {string} Type of the nconf store to use.
-// #### @options {Object} Options for the store instance.
-// Creates a store of the specified `type` using the
-// specified `options`.
-//
-Provider.prototype.create = function (type, options) {
- return new (require('../nconf')[common.capitalize(type.toLowerCase())])(options);
-};
-
-//
// ### function init (options)
// #### @options {Object} Options to initialize this instance with.
// Initializes this instance with additional `stores` or `sources` in the
@@ -186,19 +177,6 @@ Provider.prototype.init = function (options) {
self.add(store.name || name || store.type, store);
});
}
-
- //
- // Add any read-only sources to this instance
- //
- if (options.source) {
- this.sources.push(this.create(options.source.type || options.source.name, options.source));
- }
- else if (options.sources) {
- Object.keys(options.sources).forEach(function (name) {
- var source = options.sources[name];
- self.sources.push(self.create(source.type || source.name || name, source));
- });
- }
};
//
@@ -212,60 +190,31 @@ Provider.prototype.get = function (key, callback) {
// If there is no callback we can short-circuit into the default
// logic for traversing stores.
//
- if (!callback) {
- return this._execute('get', 1, key, callback);
- }
-
- //
- // Otherwise the asynchronous, hierarchical `get` is
- // slightly more complicated because we do not need to traverse
- // the entire set of stores, but up until there is a defined value.
- //
- var current = 0,
- names = Object.keys(this.stores),
- self = this,
- response,
- mergeObjs = [];
-
- async.whilst(function () {
- return typeof response === 'undefined' && current < names.length;
- }, function (next) {
- var store = self.stores[names[current]];
- current++;
-
- if (store.get.length >= 2) {
- return store.get(key, function (err, value) {
- if (err) {
- return next(err);
- }
-
- response = value;
-
- // Merge objects if necessary
- if (typeof response === 'object' && !Array.isArray(response)) {
- mergeObjs.push(response);
- response = undefined;
- }
-
- next();
- });
- }
-
- response = store.get(key);
-
- // Merge objects if necessary
- if (typeof response === 'object' && !Array.isArray(response)) {
- mergeObjs.push(response);
- response = undefined;
+ var self = this;
+ var results = null;
+ var mergedValue = void 0;
+ async.forEach(Object.keys(this.stores).reverse(), function (storeName, next) {
+ var store = self.stores[storeName];
+ var value = store.get(key);
+ if (value !== void 0) {
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
+ ;(results || (results = [])).push(value);
+ }
+ else {
+ results = null;
+ mergedValue = value;
+ }
}
-
next();
}, function (err) {
- if (!err && mergeObjs.length) {
- response = common.merge(mergeObjs.reverse());
+ if (err) {
+ onError(err, callback);
+ }
+ if (results !== null) {
+ mergedValue = common.merge(results.reverse());
}
- return err ? callback(err) : callback(null, response);
});
+ return mergedValue;
};
//
@@ -276,7 +225,12 @@ Provider.prototype.get = function (key, callback) {
// Sets the `value` for the specified `key` in this instance.
//
Provider.prototype.set = function (key, value, callback) {
- return this._execute('set', 2, key, value, callback);
+ var self = this;
+ async.forEach(Object.keys(this.stores), function (storeName, next) {
+ var store = self.stores[storeName];
+ store.set(key, value);
+ next();
+ }, callback);
};
//
@@ -285,7 +239,12 @@ Provider.prototype.set = function (key, value, callback) {
// Clears all keys associated with this instance.
//
Provider.prototype.reset = function (callback) {
- return this._execute('reset', 0, callback);
+ var self = this;
+ async.forEach(Object.keys(this.stores), function (storeName, next) {
+ var store = self.stores[storeName];
+ store.reset();
+ next();
+ }, callback);
};
//
@@ -295,7 +254,12 @@ Provider.prototype.reset = function (callback) {
// Removes the value for the specified `key` from this instance.
//
Provider.prototype.clear = function (key, callback) {
- return this._execute('clear', 1, key, callback);
+ var self = this;
+ async.forEach(Object.keys(this.stores), function (storeName, next) {
+ var store = self.stores[storeName];
+ store.clear();
+ next();
+ }, callback);
};
//
@@ -308,26 +272,13 @@ Provider.prototype.clear = function (key, callback) {
// 1. If the existing value `key` is not an Object, it will be completely overwritten.
// 2. If `key` is not supplied, then the `value` will be merged into the root.
//
-Provider.prototype.merge = function () {
- var self = this,
- args = Array.prototype.slice.call(arguments),
- callback = typeof args[args.length - 1] === 'function' && args.pop(),
- value = args.pop(),
- key = args.pop();
-
- function mergeProperty (prop, next) {
- return self._execute('merge', 2, prop, value[prop], next);
- }
-
- if (!key) {
- if (Array.isArray(value) || typeof value !== 'object') {
- return onError(new Error('Cannot merge non-Object into top-level.'), callback);
- }
-
- return async.forEach(Object.keys(value), mergeProperty, callback || function () { })
- }
-
- return this._execute('merge', 2, key, value, callback);
+Provider.prototype.merge = function (key, value, callback) {
+ var self = this;
+ async.forEach(Object.keys(this.stores), function (storeName, next) {
+ var store = self.stores[storeName];
+ store.merge(key, value);
+ next();
+ }, callback);
};
//
@@ -366,7 +317,8 @@ Provider.prototype.load = function (callback) {
function loadBatch (targets, done) {
if (!done) {
- return common.merge(targets.map(loadStoreSync));
+ common.merge(targets.map(loadStoreSync));
+ return;
}
async.map(targets, loadStore, function (err, objs) {
@@ -398,7 +350,8 @@ Provider.prototype.load = function (callback) {
//
if (!callback) {
mergeSources(loadBatch(sourceHierarchy));
- return loadBatch(getStores());
+ loadBatch(getStores());
+ return;
}
loadBatch(sourceHierarchy, function (err, data) {
@@ -426,140 +379,27 @@ Provider.prototype.load = function (callback) {
// ignored. Returns an object consisting of all of the data which was
// actually saved.
//
-Provider.prototype.save = function (value, callback) {
- if (!callback && typeof value === 'function') {
- callback = value;
- value = null;
- }
-
- var self = this,
- names = Object.keys(this.stores);
-
- function saveStoreSync(memo, name) {
- var store = self.stores[name];
-
- //
- // If the `store` doesn't have a `saveSync` method,
- // just ignore it and continue.
- //
- if (store.saveSync) {
- var ret = store.saveSync();
- if (typeof ret == 'object' && ret !== null) {
- memo.push(ret);
- }
- }
- return memo;
- }
-
- function saveStore(memo, name, next) {
- var store = self.stores[name];
-
- //
- // If the `store` doesn't have a `save` or saveSync`
- // method(s), just ignore it and continue.
- //
-
- if (store.save) {
- return store.save(function (err, data) {
- if (err) {
- return next(err);
- }
-
- if (typeof data == 'object' && data !== null) {
- memo.push(data);
- }
-
- next(null, memo);
- });
- }
- else if (store.saveSync) {
- memo.push(store.saveSync());
- }
-
- next(null, memo);
- }
-
- //
- // If we don't have a callback and the current
- // store is capable of saving synchronously
- // then do so.
- //
- if (!callback) {
- return common.merge(names.reduce(saveStoreSync, []));
- }
-
- async.reduce(names, [], saveStore, function (err, objs) {
- return err ? callback(err) : callback(null, common.merge(objs));
+Provider.prototype.save = function (callback) {
+ var self = this;
+ var wasAsync = false;
+ var result;
+ async.map(Object.keys(this.stores), function (storeName, next) {
+ var store = self.stores[storeName];
+ store.save ? (wasAsync = true, store.save(next)) : next(null, store.saveSync ? store.saveSync() : void 0);
+ }, function (err, stores) {
+ if (err) onError(err, callback);
+ result = common.merge(stores);
+ callback && callback(null, result);
});
+ if (!wasAsync) return result;
};
//
-// ### @private function _execute (action, syncLength, [arguments])
-// #### @action {string} Action to execute on `this.store`.
-// #### @syncLength {number} Function length of the sync version.
-// #### @arguments {Array} Arguments array to apply to the action
-// Executes the specified `action` on all stores for this instance, ensuring a callback supplied
-// to a synchronous store function is still invoked.
-//
-Provider.prototype._execute = function (action, syncLength /* [arguments] */) {
- var args = Array.prototype.slice.call(arguments, 2),
- callback = typeof args[args.length - 1] === 'function' && args.pop(),
- destructive = ['set', 'clear', 'merge', 'reset'].indexOf(action) !== -1,
- self = this,
- response,
- mergeObjs = [];
-
- function runAction (name, next) {
- var store = self.stores[name];
-
- if (destructive && store.readOnly) {
- return next();
- }
-
- return store[action].length > syncLength
- ? store[action].apply(store, args.concat(next))
- : next(null, store[action].apply(store, args));
- }
-
- if (callback) {
- return async.forEach(Object.keys(this.stores), runAction, function (err) {
- return err ? callback(err) : callback();
- });
- }
-
-
- Object.keys(this.stores).forEach(function (name) {
- if (typeof response === 'undefined') {
- var store = self.stores[name];
-
- if (destructive && store.readOnly) {
- return;
- }
-
- response = store[action].apply(store, args);
-
- // Merge objects if necessary
- if (response && action === 'get' && typeof response === 'object' && !Array.isArray(response)) {
- mergeObjs.push(response);
- response = undefined;
- }
- }
- });
-
- if (mergeObjs.length) {
- response = common.merge(mergeObjs.reverse());
- }
-
- return response;
-}
-
-//
// Throw the `err` if a callback is not supplied
//
function onError(err, callback) {
if (callback) {
return callback(err);
}
-
throw err;
}
View
10 lib/nconf/stores/argv.js
@@ -6,7 +6,7 @@
*/
var util = require('util'),
- Memory = require('./memory').Memory;
+ Memory = require('./memory');
//
// ### function Argv (options)
@@ -14,7 +14,7 @@ var util = require('util'),
// Constructor function for the Argv nconf store, a simple abstraction
// around the Memory store that can read command-line arguments.
//
-var Argv = exports.Argv = function (options) {
+var Argv = module.exports = function (options) {
Memory.call(this, options);
this.type = 'argv';
@@ -43,9 +43,11 @@ Argv.prototype.loadArgv = function () {
var self = this,
argv;
+ var optimist = require('optimist')(process.argv.slice(2));
+
argv = typeof this.options === 'object'
- ? require('optimist')(process.argv.slice(2)).options(this.options).argv
- : require('optimist')(process.argv.slice(2)).argv;
+ ? optimist.options(this.options).argv
+ : optimist.argv;
if (!argv) {
return;
View
4 lib/nconf/stores/env.js
@@ -7,7 +7,7 @@
var util = require('util'),
common = require('../common'),
- Memory = require('./memory').Memory;
+ Memory = require('./memory');
//
// ### function Env (options)
@@ -15,7 +15,7 @@ var util = require('util'),
// Constructor function for the Env nconf store, a simple abstraction
// around the Memory store that can read process environment variables.
//
-var Env = exports.Env = function (options) {
+var Env = module.exports = function (options) {
Memory.call(this, options);
options = options || {};
View
13 lib/nconf/stores/file.js
@@ -9,7 +9,7 @@ var fs = require('fs'),
path = require('path'),
util = require('util'),
formats = require('../formats'),
- Memory = require('./memory').Memory,
+ Memory = require('./memory'),
exists = fs.exists || path.exists,
existsSync = fs.existsSync || path.existsSync;
@@ -19,7 +19,7 @@ var fs = require('fs'),
// 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 = module.exports = function (options) {
if (!options || !options.file) {
throw new Error ('Missing required option `file`');
}
@@ -52,9 +52,16 @@ File.prototype.save = function (value, callback) {
callback = value;
value = null;
}
+ console.error(value, callback, 777)
+ if (!callback) {
+ this.saveSync();
+ return;
+ }
+ var self = this;
fs.writeFile(this.file, this.format.stringify(this.store, null, this.json_spacing), function (err) {
- return err ? callback(err) : callback();
+ console.error('written', err, self.store)
+ return err ? callback(err, null) : callback(null, self.store);
});
};
View
124 lib/nconf/stores/http.js
@@ -0,0 +1,124 @@
+/*
+ * http.js: Simple http storage engine for nconf files
+ *
+ * (C) 2011, Nodejitsu Inc.
+ *
+ */
+
+var http = require('http'),
+ url = require('url');
+ util = require('util'),
+ formats = require('../formats'),
+ Memory = require('./memory');
+
+//
+// ### function Http (options)
+// #### @options {Object} Options for this instance
+// Constructor function for the Http nconf store, a simple abstraction
+// around the Memory store that can persist configuration to disk.
+//
+var Http = module.exports = function (options) {
+ if (!options || !options.url) {
+ throw new Error ('Missing required option `url`');
+ }
+
+ Memory.call(this, options);
+
+ this.type = 'http';
+ this.url = options.url;
+ this.poll = options.poll || false;
+ this.format = options.format || formats.json;
+ this.interval = options.interval || 60 * 1000;
+
+ if (this.poll) {
+ setInterval(this.load.bind(this, function () {}), this.interval);
+ }
+};
+
+// Inherit from the Memory store
+util.inherits(Http, Memory);
+
+//
+// ### 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`.
+//
+Http.prototype.save = function (value, callback) {
+ if (!callback) {
+ callback = value;
+ value = null;
+ }
+
+ var self = this;
+ var options = url.parse(self.url);
+ options.method = 'put';
+ var req = http.request(options);
+ var done = false;
+ req.on('error', function (e) {
+ if (done) return;
+ done = true;
+ callback(e, null);
+ });
+ req.on('response', function (res) {
+ if (done) return;
+ done = true;
+ if (res.statusCode > 299 || res.statusCode < 200) {
+ callback(new Error('Status code ' + res.statusCode), null);
+ }
+ else {
+ callback(null, self.store);
+ }
+ });
+ req.write(JSON.stringify(self.store));
+ req.end();
+};
+
+//
+// ### function load (callback)
+// #### @callback {function} Continuation to respond to when complete.
+// Responds with an Object representing all keys associated in this instance.
+//
+Http.prototype.load = function (callback) {
+ var self = this;
+ var options = url.parse(self.url);
+ var req = http.request(options);
+ var done = false;
+ req.on('error', function (e) {
+ if (done) return;
+ done = true;
+ callback(e, null);
+ });
+ req.on('response', function (res) {
+ if (res.statusCode > 299 || res.statusCode < 200) {
+ done = true;
+ callback(new Error('Status code ' + res.statusCode), null);
+ return;
+ }
+ else {
+ var store = '';
+ res.on('error', function (e) {
+ if (done) return;
+ done = true;
+ callback(e, null);
+ });
+ res.on('data', function (data) {
+ store += data;
+ });
+ res.on('end', function () {
+ if (done) return;
+ done = true;
+ try {
+ self.store = self.format.parse(store);
+ }
+ catch (e) {
+ callback(e, null);
+ return;
+ }
+ callback(null, self.store);
+ });
+ }
+ });
+ req.end();
+};
View
4 lib/nconf/stores/literal.js
@@ -6,9 +6,9 @@
*/
var util = require('util'),
- Memory = require('./memory').Memory
+ Memory = require('./memory')
-var Literal = exports.Literal = function Literal (options) {
+var Literal = module.exports = function Literal (options) {
Memory.call(this, options);
options = options || {}
View
156 lib/nconf/stores/memory.js
@@ -6,6 +6,7 @@
*/
var common = require('../common');
+var merge = require('merge-recursive');
//
// ### function Memory (options)
@@ -15,17 +16,12 @@ var common = require('../common');
//
// e.g. `my:nested:key` ==> `{ my: { nested: { key: } } }`
//
-var Memory = exports.Memory = function (options) {
+var Memory = module.exports = function (options) {
options = options || {};
this.type = 'memory';
this.store = {};
- this.mtimes = {};
this.readOnly = false;
this.loadFrom = options.loadFrom || null;
-
- if (this.loadFrom) {
- this.store = common.loadFilesSync(this.loadFrom);
- }
};
//
@@ -34,22 +30,8 @@ var Memory = exports.Memory = function (options) {
// Retrieves the value for the specified key (if any).
//
Memory.prototype.get = function (key) {
- var target = this.store,
- path = common.path(key);
-
- //
- // Scope into the object to get the appropriate nested context
- //
- while (path.length > 0) {
- key = path.shift();
- if (target && key in target) {
- target = target[key];
- continue;
- }
- return undefined;
- }
-
- return target;
+ var value = common.scope(common.path(key), this.store);
+ return value;
};
//
@@ -63,13 +45,10 @@ Memory.prototype.set = function (key, value) {
return false;
}
- var target = this.store,
- path = common.path(key);
-
- if (path.length === 0) {
- //
- // Root must be an object
- //
+ //
+ // Root must be an object
+ //
+ if (key == null) {
if (!value || typeof value !== 'object') {
return false;
}
@@ -79,27 +58,10 @@ Memory.prototype.set = function (key, value) {
return true;
}
}
-
- //
- // 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) {
- key = path.shift();
- if (!target[key] || typeof target[key] !== 'object') {
- target[key] = {};
- }
-
- target = target[key];
- }
-
- // Set the specified value in the nested JSON structure
- key = path.shift();
- target[key] = value;
+ var path = common.path(key);
+ var attribute = path.pop();
+ var target = common.ensure(path, this.store);
+ target[attribute] = value;
return true;
};
@@ -113,31 +75,15 @@ Memory.prototype.clear = function (key) {
return false;
}
- var target = this.store,
- value = target,
- path = common.path(key);
-
- //
- // Remove the key from the set of `mtimes` (modified times)
- //
- delete this.mtimes[key];
+ var path = common.path(key),
+ attribute = path.pop(),
+ target = common.scope(path, this.store);
- //
- // Scope into the object to get the appropriate nested context
- //
- for (var i = 0; i < path.length - 1; i++) {
- key = path[i];
- value = target[key];
- if (typeof value !== 'function' && typeof value !== 'object') {
- return false;
- }
- target = value;
+ if (target && typeof target === 'object') {
+ delete target[attribute];
+ return true;
}
-
- // Delete the key from the nested JSON structure
- key = path[i];
- delete target[key];
- return true;
+ return false;
};
//
@@ -161,44 +107,23 @@ Memory.prototype.merge = function (key, value) {
return this.set(key, value);
}
- var self = this,
- target = this.store,
- path = common.path(key),
- fullKey = 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) {
- key = path.shift();
- if (!target[key]) {
- target[key] = {};
- }
-
- target = target[key];
- }
-
- // Set the specified value in the nested JSON structure
- key = path.shift();
-
+ var self = this,
+ path = common.path(key),
+ attribute = path.pop(),
+ target = common.ensure(path, this.store);
+
//
// If the current value at the key target is not an `Object`,
// or is an `Array` then simply override it because the new value
// is an Object.
//
- if (typeof target[key] !== 'object' || Array.isArray(target[key])) {
- target[key] = value;
+ if (target[attribute] == null || typeof target[attribute] !== 'object' || Array.isArray(target[attribute])) {
+ target[attribute] = value;
return true;
}
-
- return Object.keys(value).every(function (nested) {
- return self.merge(common.key(fullKey, nested), value[nested]);
- });
+
+ merge.recursive(target[attribute], value);
+ return target;
};
//
@@ -209,12 +134,31 @@ Memory.prototype.reset = function () {
if (this.readOnly) {
return false;
}
-
- this.mtimes = {};
this.store = {};
return true;
};
+Memory.prototype.save = function (callback) {
+ var store = this.store;
+ if (this.saveSync) {
+ store = this.saveSync();
+ }
+ if (callback) callback(null, store);
+ return store;
+}
+
+Memory.prototype.saveSync = function (callback) {
+ return this.store;
+}
+
+Memory.prototype.load = function (callback) {
+ var store = this.store;
+ if (this.loadSync) {
+ store = this.loadSync();
+ }
+ if (callback) callback(null, store);
+}
+
//
// ### function loadSync
// Returns the store managed by this instance
View
4 package.json
@@ -19,7 +19,8 @@
"async": "0.1.x",
"ini": "1.x.x",
"optimist": "0.3.x",
- "pkginfo": "0.2.x"
+ "pkginfo": "0.2.x",
+ "merge-recursive": "0.0.3"
},
"devDependencies": {
"vows": "0.6.x"
@@ -32,4 +33,3 @@
"node": ">= 0.4.0"
}
}
-
View
4 test/common-test.js
@@ -19,13 +19,13 @@ vows.describe('nconf/common').addBatch({
"Using nconf.common module": {
"the loadFiles() method": {
topic: function () {
- nconf.loadFiles(files, this.callback);
+ nconf.common.loadFiles(files, this.callback);
},
"should merge the files correctly": helpers.assertMerged
},
"the loadFilesSync() method": {
"should merge the files correctly": function () {
- helpers.assertMerged(null, nconf.loadFilesSync(files));
+ helpers.assertMerged(null, nconf.common.loadFilesSync(files));
}
}
}
View
21 test/complete-test.js
@@ -61,7 +61,7 @@ vows.describe('nconf/multiple-stores').addBatch({
"and saving *synchronously*": {
topic: function () {
nconf.set('weebls', 'stuff');
- return nconf.save();
+ return nconf.saveSync();
},
"correct return value": function (topic) {
Object.keys(topic).forEach(function (key) {
@@ -93,6 +93,7 @@ vows.describe('nconf/multiple-stores').addBatch({
"and saving *asynchronously*": {
topic: function () {
nconf.set('weebls', 'crap');
+ console.error('SAVING')
nconf.save(this.callback);
},
"correct return value": function (err, data) {
@@ -103,24 +104,26 @@ vows.describe('nconf/multiple-stores').addBatch({
},
"the file": {
topic: function () {
- fs.readFile(completeTest, 'utf8', this.callback);
+ return fs.readFileSync(completeTest, 'utf8');
},
"saved correctly": function (err, data) {
assert.isNull(err);
+ console.error(data+'')
data = JSON.parse(data);
Object.keys(data).forEach(function (key) {
assert.deepEqual(nconf.get(key), data[key]);
});
assert.equal(nconf.get('weebls'), 'crap');
+ },
+ teardown: function () {
+ console.error(completeTest)
+ fs.unlinkSync(completeTest);
+ nconf.remove('file');
+ nconf.remove('memory');
+ nconf.remove('argv');
+ nconf.remove('env');
}
}
- },
- teardown: function () {
- fs.unlinkSync(completeTest);
- nconf.remove('file');
- nconf.remove('memory');
- nconf.remove('argv');
- nconf.remove('env');
}
}
}).export(module);
View
2  test/helpers.js
@@ -16,7 +16,7 @@ exports.assertMerged = function (err, merged) {
merged = merged instanceof nconf.Provider
? merged.store.store
: merged;
-
+
assert.isNull(err);
assert.isObject(merged);
assert.isTrue(merged.apples);
View
1  test/hierarchy-test.js
@@ -26,6 +26,7 @@ vows.describe('nconf/hierarchy').addBatch({
return nconf;
},
"should have the appropriate keys present": function () {
+ console.error(nconf.stores, nconf.get('title'))
assert.equal(nconf.get('title'), 'My specific title');
assert.equal(nconf.get('color'), 'green');
assert.equal(nconf.get('movie'), 'Kill Bill');
Please sign in to comment.
Something went wrong with that request. Please try again.