Skip to content

Commit

Permalink
[refactor] Refactor to make using nconf more fluent.
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Nov 24, 2011
1 parent 2c1ef71 commit c3c315d
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 293 deletions.
157 changes: 47 additions & 110 deletions lib/nconf/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,9 @@ var Provider = exports.Provider = function (options) {
// Setup default options for working with `stores`,
// `overrides`, `process.env` and `process.argv`.
//
options = options || {};
this._overrides = options.overrides || null;
this._argv = options.argv || false;
this._env  = options.env || false;
this._reserved = Object.keys(Provider.prototype);
this._stores = [];
this.sources = [];

//
// Add the default `system` store for working with
// `overrides`, `process.env`, `process.argv` and
// a simple in-memory objects.
//
this.add('system', options);

options = options || {};
this.sources = [];

//
// Add any stores passed in through the options
// to this instance.
Expand All @@ -55,21 +43,28 @@ var Provider = exports.Provider = 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));
});
}
};

//
// Define wrapper functions for using basic stores
// in this instance
//
['argv', 'env', 'file'].forEach(function (type) {
Provider.prototype[type] = function (options) {
return this.add(type, options);
};
});

//
// Define wrapper functions for using
// overrides and defaults
//
['defaults', 'overrides'].forEach(function (type) {
Provider.prototype[type] = function (options) {
return this.add('literal', options);
}
});

//
// ### function use (name, options)
// #### @type {string} Type of the nconf store to use.
Expand All @@ -82,13 +77,6 @@ var Provider = exports.Provider = function (options) {
// provider.use('file', { type: 'file', filename: '/path/to/userconf' })
//
Provider.prototype.use = function (name, options) {
if (name === 'system') {
return;
}
else if (this._reserved.indexOf(name) !== -1) {
throw new Error('Cannot use reserved name: ' + name);
}

options = options || {};
var type = options.type || name;

Expand All @@ -98,7 +86,7 @@ Provider.prototype.use = function (name, options) {
});
}

var store = this[name],
var store = this.sources[name],
update = store && !sameOptions(store);

if (!store || update) {
Expand All @@ -123,22 +111,17 @@ Provider.prototype.use = function (name, options) {
// provider.add('userconf', { type: 'file', filename: '/path/to/userconf' })
//
Provider.prototype.add = function (name, options) {
if (this._reserved.indexOf(name) !== -1) {
throw new Error('Cannot use reserved name: ' + name);
}

options = options || {};
var type = options.type || name;

if (Object.keys(stores).indexOf(common.capitalize(type)) === -1) {
throw new Error('Cannot add store with unknown type: ' + type);
}

this[name] = this.create(type, options);
this._stores.push(name);
this.sources[name] = this.create(type, options);

if (this[name].loadSync) {
this[name].loadSync();
if (this.sources[name].loadSync) {
this.sources[name].loadSync();
}

return this;
Expand All @@ -152,15 +135,7 @@ Provider.prototype.add = function (name, options) {
// this was used in the call to `.add()`.
//
Provider.prototype.remove = function (name) {
if (this._reserved.indexOf(name) !== -1) {
throw new Error('Cannot use reserved name: ' + name);
}
else if (!this[name]) {
throw new Error('Cannot remove store that does not exist: ' + name);
}

delete this[name];
this._stores.splice(this._stores.indexOf(name), 1);
delete this.sources[name];
return this;
};

Expand Down Expand Up @@ -196,13 +171,14 @@ Provider.prototype.get = function (key, callback) {
// the entire set of stores, but up until there is a defined value.
//
var current = 0,
names = Object.keys(this.sources),
self = this,
response;

async.whilst(function () {
return typeof response === 'undefined' && current < self._stores.length;
return typeof response === 'undefined' && current < names.length;
}, function (next) {
var store = self[self._stores[current]];
var store = self.sources[names[current]];
current++;

if (store.get.length >= 2) {
Expand Down Expand Up @@ -292,7 +268,7 @@ Provider.prototype.merge = function () {
//
Provider.prototype.load = function (callback) {
var self = this,
stores = this._stores.map(function (name) { return self[name] });
stores = Object.keys(this.sources).map(function (name) { return self.sources[name] });

function loadStoreSync(store) {
if (!store.loadSync) {
Expand Down Expand Up @@ -373,10 +349,11 @@ Provider.prototype.save = function (value, callback) {
value = null;
}

var self = this;
var self = this,
names = Object.keys(this.sources);

function saveStoreSync(name) {
var store = self[name];
var store = self.sources[name];

if (!store.saveSync) {
throw new Error('nconf store ' + store.type + ' has no saveSync() method');
Expand All @@ -386,7 +363,7 @@ Provider.prototype.save = function (value, callback) {
}

function saveStore(name, next) {
var store = self[name];
var store = self.sources[name];

if (!store.save && !store.saveSync) {
return next(new Error('nconf store ' + store.type + ' has no save() method'));
Expand All @@ -403,10 +380,10 @@ Provider.prototype.save = function (value, callback) {
// then do so.
//
if (!callback) {
return common.merge(this._stores.map(saveStoreSync));
return common.merge(names.map(saveStoreSync));
}

async.map(this._stores, saveStore, function (err, objs) {
async.map(names, saveStore, function (err, objs) {
return err ? callback(err) : callback();
});
};
Expand All @@ -426,53 +403,30 @@ Provider.prototype._execute = function (action, syncLength /* [arguments] */) {
response;

function runAction (name, next) {
var store = self[name]
var store = self.sources[name];

return store[action].length > syncLength
? store[action].apply(store, args.concat(next))
: next(null, store[action].apply(store, args));
}

if (callback) {
return async.forEach(self._stores, runAction, function (err) {
return async.forEach(Object.keys(self.sources), runAction, function (err) {
return err ? callback(err) : callback();
});
}

this._stores.forEach(function (name) {
var store = self[name];
response = store[action].apply(store, args);

Object.keys(this.sources).forEach(function (name) {
if (typeof response === 'undefined') {
var store = self.sources[name];
response = store[action].apply(store, args);
}
});

return response;
}

//
// ### @argv {boolean}
// Gets or sets a property representing overrides which supercede all
// other values for this instance.
//
Provider.prototype.__defineSetter__('overrides', function (val) { updateSystem.call(this, 'overrides', val) });
Provider.prototype.__defineGetter__('overrides', function () { return this._argv });

//
// ### @argv {boolean}
// Gets or sets a property indicating if we should wrap calls to `.get`
// by checking `optimist.argv`. Can be a boolean or the pass-thru
// options for `optimist`.
//
Provider.prototype.__defineSetter__('argv', function (val) { updateSystem.call(this, 'argv', val) });
Provider.prototype.__defineGetter__('argv', function () { return this._argv });

//
// ### @env {boolean}
// Gets or sets a property indicating if we should wrap calls to `.get`
// by checking `process.env`. Can be a boolean or an Array of
// environment variables to extract.
//
Provider.prototype.__defineSetter__('env', function (val) { updateSystem.call(this, 'env', val) });
Provider.prototype.__defineGetter__('env', function () { return this._env });

//
// Throw the `err` if a callback is not supplied
//
Expand All @@ -482,21 +436,4 @@ function onError(err, callback) {
}

throw err;
}

//
// Helper function for working with the
// default `system` store for providers.
//
function updateSystem(prop, value) {
var system = this['system'];

if (system[prop] === value) {
return;
}

value = value || false;
this['_' + prop] = value;
system[prop] = value;
system.loadSync();
}
59 changes: 59 additions & 0 deletions lib/nconf/stores/argv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* argv.js: Simple memory-based store for command-line arguments.
*
* (C) 2011, Charlie Robbins
*
*/

var util = require('util'),
optimist = require('optimist'),
Memory = require('./memory').Memory;

//
// ### function Argv (options)
// #### @options {Object} Options for this instance.
// 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) {
Memory.call(this, options);

this.type = 'argv';
this.options = options || false;
};

// Inherit from the Memory store
util.inherits(Argv, Memory);

//
// ### function loadSync ()
// Loads the data passed in from `process.argv` into this instance.
//
Argv.prototype.loadSync = function () {
this.loadArgv();
return this.store;
};

//
// ### function loadArgv ()
// Loads the data passed in from the command-line arguments
// into this instance.
//
Argv.prototype.loadArgv = function () {
var self = this,
argv;

argv = typeof this.options === 'object'
? optimist(process.argv.slice(2)).options(this.options).argv
: optimist(process.argv.slice(2)).argv;

if (!argv) {
return;
}

Object.keys(argv).forEach(function (key) {
self.set(key, argv[key]);
});

return this.store;
};
51 changes: 51 additions & 0 deletions lib/nconf/stores/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* env.js: Simple memory-based store for environment variables
*
* (C) 2011, Charlie Robbins
*
*/

var util = require('util'),
Memory = require('./memory').Memory;

//
// ### function Env (options)
// #### @options {Object} Options for this instance.
// 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) {
Memory.call(this, options);

this.type = 'env';
this.options = options || [];
};

// Inherit from the Memory store
util.inherits(Env, Memory);

//
// ### function loadSync ()
// Loads the data passed in from `process.env` into this instance.
//
Env.prototype.loadSync = function () {
this.loadEnv();
return this.store;
};

//
// ### function loadEnv ()
// Loads the data passed in from `process.env` into this instance.
//
Env.prototype.loadEnv = function () {
var self = this;

Object.keys(process.env).filter(function (key) {
return !self.options.length || self.options.indexOf(key) !== -1;
}).forEach(function (key) {
self.set(key, process.env[key]);
});

return this.store;
};

Loading

0 comments on commit c3c315d

Please sign in to comment.