| 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();
}
 |