provider.js | |
---|---|
/*
* provider.js: Abstraction providing an interface into pluggable configuration storage.
*
* (C) 2011, Charlie Robbins
*
*/
var async = require('async'),
common = require('./common'),
stores = require('./stores'); | |
function Provider (options)@options {Object} Options for this instance.Constructor function for the Provider object responsible
for exposing the pluggable storage features of | var Provider = exports.Provider = function (options) {
var self = this;
|
Setup default options for working with | 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 = [];
|
Add the default | this.add('system', options);
if (options.type) {
this.add(options.type, options);
}
else if (options.store) {
this.add(options.store.name || options.store.type, options.store);
}
else if (options.stores) {
Object.keys(options.stores).forEach(function (store) {
self.add(store.name || store.type, store);
});
}
}; |
function use (name, options)@type {string} Type of the nconf store to use.@options {Object} Options for the store instance.Adds (or replaces) a new store with the specified provider.use('file'); 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;
function sameOptions (store) {
return Object.keys(options).every(function (key) {
return options[key] === store[key];
});
}
var store = this[name],
update = store && !sameOptions(store);
if (!store || update) {
if (update) {
this.remove(name);
}
this.add(name, options);
}
return this;
}; |
function add (name, options)@name {string} Name of the store to add to this instance@options {Object} Options for the store to createAdds a new store with the specified provider.add('memory'); 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);
if (this[name].loadSync) {
this[name].loadSync();
}
}; |
function remove (name)@name {string} Name of the store to remove from this instanceRemoves a store with the specified | 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);
}; |
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 | Provider.prototype.create = function (type, options) {
return new stores[common.capitalize(type.toLowerCase())](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). | 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 | var current = 0,
self = this,
response;
async.whilst(function () {
return typeof response === 'undefined' && current < self._stores.length;
}, function (next) {
var store = self[self._stores[current]];
current++;
if (store.get.length >= 2) {
return store.get(key, function (err, value) {
if (err) {
return next(err);
}
response = value;
next();
});
}
response = store.get(key);
next();
}, function (err) {
return err ? callback(err) : callback(null, response);
});
}; |
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 | Provider.prototype.set = function (key, value, callback) {
return this._execute('set', 2, key, value, callback);
}; |
function reset (callback)@callback {function} Optional Continuation to respond to when complete.Clears all keys associated with this instance. | Provider.prototype.reset = function (callback) {
return this._execute('reset', 0, 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 | Provider.prototype.clear = function (key, callback) {
return this._execute('clear', 1, key, callback);
}; |
function merge ([key,] value [, callback])@key {string} Key to merge the value into@value {literal|Object} Value to merge into the key@callback {function} Optional Continuation to respond to when complete.Merges the properties in
| 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);
}; |
function load (callback)@callback {function} Continuation to respond to when complete.Responds with an Object representing all keys associated in this instance. | Provider.prototype.load = function (callback) {
var self = this;
function loadStoreSync(name) {
var store = self[name];
if (!store.loadSync) {
throw new Error('nconf store ' + store.type + ' has no loadSync() method');
}
return store.loadSync();
}
function loadStore(name, next) {
var store = self[name];
if (!store.load && !store.loadSync) {
return next(new Error('nconf store ' + store.type + ' has no load() method'));
}
return store.loadSync
? next(null, store.loadSync())
: store.load(next);
}
|
If we don't have a callback and the current store is capable of loading synchronously then do so. | if (!callback) {
return common.merge(this._stores.map(loadStoreSync));
}
async.map(this._stores, loadStore, function (err, objs) {
return err ? callback(err) : callback(null, common.merge(objs));
});
}; |
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 | Provider.prototype.save = function (value, callback) {
if (!callback && typeof value === 'function') {
callback = value;
value = null;
}
var self = this;
function saveStoreSync(name) {
var store = self[name];
if (!store.saveSync) {
throw new Error('nconf store ' + store.type + ' has no saveSync() method');
}
return store.saveSync();
}
function saveStore(name, next) {
var store = self[name];
if (!store.save && !store.saveSync) {
return next(new Error('nconf store ' + store.type + ' has no save() method'));
}
return store.saveSync
? next(null, store.saveSync())
: store.save(next);
}
|
If we don't have a callback and the current store is capable of saving synchronously then do so. | if (!callback) {
return common.merge(this._stores.map(saveStoreSync));
}
async.map(this._stores, saveStore, function (err, objs) {
return err ? callback(err) : callback();
});
}; |
@private function _execute (action, syncLength, [arguments])@action {string} Action to execute on
| Provider.prototype._execute = function (action, syncLength /* [arguments] */) {
var args = Array.prototype.slice.call(arguments, 2),
callback = typeof args[args.length - 1] === 'function' && args.pop(),
self = this,
response;
function runAction (name, next) {
var store = self[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 err ? callback(err) : callback();
});
}
this._stores.forEach(function (name) {
var store = self[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 | 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 | Provider.prototype.__defineSetter__('env', function (val) { updateSystem.call(this, 'env', val) });
Provider.prototype.__defineGetter__('env', function () { return this._env }); |
Throw the | function onError(err, callback) {
if (callback) {
return callback(err);
}
throw err;
} |
Helper function for working with the
default | function updateSystem(prop, value) {
var system = this['system'];
if (system[prop] === value) {
return;
}
value = value || false;
this['_' + prop] = value;
system[prop] = value;
system.loadSync();
}
|