diff --git a/docs/nconf.html b/docs/nconf.html index 6f65d65..cb9a1f9 100644 --- a/docs/nconf.html +++ b/docs/nconf.html @@ -1,4 +1,4 @@ - nconf.js
Jump To …

nconf.js

/*
+      nconf.js           

nconf.js

/*
  * nconf.js: Top-level include for the nconf module
  *
  * (C) 2011, Charlie Robbins
@@ -9,19 +9,12 @@
     async = require('async'),
     common = require('./nconf/common'),
     Provider = require('./nconf/provider').Provider,
-    nconf = module.exports = Object.create(Provider.prototype);

Use the memory engine by default.

nconf.use('memory');

Expose the version from the package.json using pkginfo.

require('pkginfo')(module, 'version');

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

function key (arguments)

- -

Returns a : joined string from the arguments.

nconf.key = function () {
-  return Array.prototype.slice.call(arguments).join(':');
-};

Expose the various components included with nconf

nconf.loadFiles = common.loadFiles;
-nconf.formats   = require('./nconf/formats');
-nconf.stores    = require('./nconf/stores');
-nconf.Provider  = Provider;
+    nconf = module.exports = new Provider();

Expose the version from the package.json using pkginfo.

require('pkginfo')(module, 'version');

Expose the various components included with nconf

nconf.key           = common.key;
+nconf.path          = common.path;
+nconf.loadFiles     = common.loadFiles;
+nconf.loadFilesSync = common.loadFilesSync;
+nconf.formats       = require('./nconf/formats');
+nconf.stores        = require('./nconf/stores');
+nconf.Provider      = Provider;
 
 
\ No newline at end of file diff --git a/docs/nconf/common.html b/docs/nconf/common.html index 899ba70..5c359ce 100644 --- a/docs/nconf/common.html +++ b/docs/nconf/common.html @@ -1,4 +1,4 @@ - common.js

common.js

/*
+      common.js           

common.js

/*
  * utils.js: Utility functions for the nconf module.
  *
  * (C) 2011, Charlie Robbins
@@ -8,9 +8,19 @@
 var fs = require('fs'),
     async = require('async'),
     formats = require('./formats'),
-    stores = require('./stores');
+    Memory = require('./stores/Memory').Memory;
 
-var common = exports;

function loadFiles (files)

+var common = exports;

function path (key)

+ +

@key {string} The ':' delimited key to split

+ +

Returns a fully-qualified path to a nested nconf key.

common.path = function (key) {
+  return key.split(':');
+};

function key (arguments)

+ +

Returns a : joined string from the arguments.

common.key = function () {
+  return Array.prototype.slice.call(arguments).join(':');
+};

function loadFiles (files, callback)

@files {Object|Array} List of files (or settings object) to load.

@@ -21,28 +31,55 @@ return callback(null, {}); } - var options = Array.isArray(files) ? { files: files } : files, - store = new stores.Memory();

Set the default JSON format if not already + var options = Array.isArray(files) ? { files: files } : files;

Set the default JSON format if not already specified

  options.format = options.format || formats.json;
 
-  function loadFile (file, next) {
+  function parseFile (file, next) {
     fs.readFile(file, function (err, data) {
-      if (err) {
-        return next(err);
-      }
-
-      data = options.format.parse(data.toString());
-      Object.keys(data).forEach(function (key) {
-        store.merge(key, data[key]);
-      });
-
-      next();
+      return !err 
+        ? next(null, options.format.parse(data.toString()))
+        : next(err);
     });
   }
 
-  async.forEach(files, loadFile, function (err) {
-    return err ? callback(err) : callback(null, store.store);
+  async.map(files, parseFile, function (err, objs) {
+    return err ? callback(err) : callback(null, common.merge(objs));
   });
+};

function loadFilesSync (files)

+ +

@files {Object|Array} List of files (or settings object) to load.

+ +

Loads all the data in the specified files synchronously.

common.loadFilesSync = function (files) {
+  if (!files) {
+    return;
+  }

Set the default JSON format if not already +specified

  var options = Array.isArray(files) ? { files: files } : files;
+  options.format = options.format || formats.json;
+
+  return common.merge(files.map(function (file) {
+    return options.format.parse(fs.readFileSync(file, 'utf8'));
+  }));
+};

function merge (objs)

+ +

@objs {Array} Array of object literals to merge

+ +

Merges the specified objs using a temporary instance +of stores.Memory.

common.merge = function (objs) {
+  var store = new Memory();
+  
+  objs.forEach(function (obj) {
+    Object.keys(obj).forEach(function (key) {
+      store.merge(key, obj[key]);
+    });
+  });
+  
+  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);
 };
 
 
\ No newline at end of file diff --git a/docs/nconf/formats.html b/docs/nconf/formats.html index 0766925..a2ab2f5 100644 --- a/docs/nconf/formats.html +++ b/docs/nconf/formats.html @@ -1,4 +1,4 @@ - formats.js

formats.js

/*
+      formats.js           

formats.js

/*
  * formats.js: Default formats supported by nconf
  *
  * (C) 2011, Charlie Robbins
diff --git a/docs/nconf/provider.html b/docs/nconf/provider.html
index 1b50174..66c6718 100644
--- a/docs/nconf/provider.html
+++ b/docs/nconf/provider.html
@@ -1,4 +1,4 @@
-      provider.js           

provider.js

/*
+      provider.js           

provider.js

/*
  * provider.js: Abstraction providing an interface into pluggable configuration storage.
  *
  * (C) 2011, Charlie Robbins
@@ -6,7 +6,6 @@
  */
 
 var async = require('async'),
-    optimist = require('optimist'),
     common = require('./common'),
     stores = require('./stores');

function Provider (options)

@@ -14,51 +13,161 @@

Constructor function for the Provider object responsible for exposing the pluggable storage features of nconf.

var Provider = exports.Provider = function (options) {
-  options        = options           || {};
-  this.overrides = options.overrides || null
-  this.useArgv   = options.useArgv   || false;
-
-  this.store = stores.create(options.type || 'memory', options);
-};

function use (type, options)

+ var self = this; +

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    = [];
+  

Add the default system store for working with +overrides, process.env, process.argv and +a simple in-memory objects.

  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.

-

Sets the active this.store to a new instance of the -specified type.

Provider.prototype.use = function (type, options) {
-  var self = this;
-  options = options || {};
+

Adds (or replaces) a new store with the specified name +and options. If options.type is not set, then name +will be used instead:

- function sameOptions () { +

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] === self.store[key];
+      return options[key] === store[key];
     });
   }
   
-  if (!this.store || type.toLowerCase() !== this.store.type 
-    || !sameOptions()) {
-    this.store = stores.create(type, options);
+  var store = this[name],
+      update = store && !sameOptions(store);
+  
+  if (!store || update) {
+    if (update) {
+      this.remove(name);
+    }
+    
+    this.add(name, options);
   }
   
   return this;
-};

function get (key, callback)

+};

function add (name, options)

+ +

@name {string} Name of the store to add to this instance

+ +

@options {Object} Options for the store to create

+ +

Adds a new store with the specified name and options. If options.type +is not set, then name will be used instead:

+ +

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 instance

+ +

Removes a store with the specified name from this instance. Users +are allowed to pass in a type argument (e.g. memory) as name if +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);
+};

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 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 (this.overrides && Object.prototype.hasOwnProperty.call(this.overrides, key)) {
-    if (callback) {
-      callback(null, this.overrides[key]);
+

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 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,
+      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();
+      });
     }
     
-    return this.overrides[key];
-  }
-  
-  return this._execute('get', 1, key, callback);
-};

function set (key, value, callback)

+ 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

@@ -68,7 +177,21 @@ specified type.

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

function merge ([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 key from this instance.

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

@@ -101,43 +224,44 @@ specified type.

} return this._execute('merge', 2, key, value, callback); -};

function mergeFiles (files, callback)

- -

@files {Object|Array} List of files (or settings object) to load.

+};

function load (callback)

@callback {function} Continuation to respond to when complete.

-

Merges all key:value pairs in the files supplied into the -store that is managed by this provider instance.

Provider.prototype.mergeFiles = function (files, callback) {
+

Responds with an Object representing all keys associated in this instance.

Provider.prototype.load = function (callback) {
   var self = this;
   
-  common.loadFiles(files, function (err, merged) {
-    return !err 
-      ? self.merge(merged, callback)
-      : onError(err);
-  });
-};

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.

Provider.prototype.clear = function (key, callback) {
-  return this._execute('clear', 1, key, 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) {

If we don't have a callback and the current -store is capable of loading synchronously -then do so.

  if (!callback && this.store.loadSync) {
-    return this.store.loadSync();
+  function loadStoreSync(name) {
+    var store = self[name];
+    
+    if (!store.loadSync) {
+      throw new Error('nconf store ' + store.type + ' has no loadSync() method');
+    }
+    
+    return store.loadSync();
   }
   
-  return !this.store.load
-    ? onError(new Error('nconf store ' + this.store.type + ' has no load() method'), callback)
-    : this.store.load(callback);
-};

function save (value, callback)

+ 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

@@ -145,26 +269,44 @@ then do so.

Removes any existing configuration settings that may exist in this instance and then adds all key-value pairs in value.

Provider.prototype.save = function (value, callback) {
-  if (!callback) {
+  if (!callback && typeof value === 'function') {
     callback = value;
     value = null;
-    

If we still don't have a callback and the -current store is capable of saving synchronously -then do so.

    if (!callback && this.store.saveSync) {
-      return this.store.saveSync();
-    }
   }
   
-  return !this.store.save 
-    ? onError(new Error('nconf store ' + this.store.type + ' has no save() method'), callback)
-    : this.store.save(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);  
-};

@private function _execute (action, syncLength, [arguments])

+ 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 this.store.

@@ -172,41 +314,65 @@ then do so.

@arguments {Array} Arguments array to apply to the action -

Executes the specified action on this.store, ensuring a callback supplied +

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,
+      callback = typeof args[args.length - 1] === 'function' && args.pop(),
+      self = this,
       response;
-      
-  if (this.store[action].length > syncLength) {
-    return this.store[action].apply(this.store, args);
-  }
   
-  callback = typeof args[args.length - 1] === 'function' && args.pop();
-  response = this.store[action].apply(this.store, args);
+  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) {
-    callback(null, response);
+    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;
-}

getter @useArgv {boolean}

+}

@argv {boolean}

-

Gets a property indicating if we should wrap calls to .get -by checking optimist.argv.

Provider.prototype.__defineGetter__('useArgv', function () {
-  return this._useArgv;
-});

setter @useArgv {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}

-

Sets a property indicating if we should wrap calls to .get -by checking optimist.argv.

Provider.prototype.__defineSetter__('useArgv', function (val) {
-  this._useArgv  = val            || false;
-  this.overrides = this.overrides || optimist.argv;
-});

Throw the err if a callback is not supplied

function onError(err, callback) {
+

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

function onError(err, callback) {
   if (callback) {
     return callback(err);
   }
   
   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();
 }
 
 
\ No newline at end of file diff --git a/docs/nconf/stores.html b/docs/nconf/stores.html index 34eb214..d799966 100644 --- a/docs/nconf/stores.html +++ b/docs/nconf/stores.html @@ -1,4 +1,4 @@ - stores.js

stores.js

/*
+      stores.js           

stores.js

/*
  * stores.js: Top-level include for all nconf stores
  *
  * (C) 2011, Charlie Robbins
@@ -6,26 +6,14 @@
  */
  
 var fs = require('fs'),
-    stores = exports;
-
-function capitalize (str) {
-  return str && str[0].toUpperCase() + str.slice(1);
-};

Setup all stores as lazy-loaded getters.

fs.readdirSync(__dirname + '/stores').forEach(function (file) {
+    common = require('./common'),
+    stores = exports;

Setup all stores as lazy-loaded getters.

fs.readdirSync(__dirname + '/stores').forEach(function (file) {
   var store = file.replace('.js', ''),
-      name  = capitalize(store);
+      name  = common.capitalize(store);
       
   stores.__defineGetter__(name, function () {
     return require('./stores/' + store)[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.

stores.create = function (type, options) {
-  return new stores[capitalize(type.toLowerCase())](options);
-};
+});
 
 
\ No newline at end of file diff --git a/docs/nconf/stores/file.html b/docs/nconf/stores/file.html index 5c38659..7a00765 100644 --- a/docs/nconf/stores/file.html +++ b/docs/nconf/stores/file.html @@ -1,4 +1,4 @@ - file.js

file.js

/*
+      file.js           

file.js

/*
  * file.js: Simple file storage engine for nconf files
  *
  * (C) 2011, Charlie Robbins
@@ -16,9 +16,9 @@
 
 

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) {
-  if (!options.file) {
+  if (!options || !options.file) {
     throw new Error ('Missing required option `files`');
-  } 
+  }
 
   Memory.call(this, options);
 
@@ -97,8 +97,8 @@ using the format specified by this.format synchronously.

data = {}; } else {

Else, the path exists, read it from disk

    try {
-      data = fs.readFileSync(this.file, 'utf8');
-      this.store = this.format.parse(data);
+      data = this.format.parse(fs.readFileSync(this.file, 'utf8'));
+      this.store = data;
     }
     catch (ex) {
       throw new Error("Error parsing your JSON configuration file.")
diff --git a/docs/nconf/stores/memory.html b/docs/nconf/stores/memory.html
index 8994c70..8867394 100644
--- a/docs/nconf/stores/memory.html
+++ b/docs/nconf/stores/memory.html
@@ -1,11 +1,11 @@
-      memory.js           

memory.js

/*
+      memory.js           

memory.js

/*
  * memory.js: Simple memory storage engine for nconf configuration(s)
  *
  * (C) 2011, Charlie Robbins
  *
  */
 
-var nconf = require('../../nconf');

function Memory (options)

+var common = require('../common');

function Memory (options)

@options {Object} Options for this instance

@@ -13,17 +13,23 @@ a nested json structure based on key delimiters :.

e.g. my:nested:key ==> { my: { nested: { key: } } }

var Memory = exports.Memory = function (options) {
-  options     = options || {};
-  this.type   = 'memory';
-  this.store  = {};
-  this.mtimes = {};
+  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);
+  }
 };

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) {
   var target = this.store, 
-      path   = nconf.path(key);

Scope into the object to get the appropriate nested context

  while (path.length > 0) {
+      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)) {
       return;
@@ -41,8 +47,12 @@ a nested json structure based on key delimiters :.

@value {literal|Object} Value for the specified key

Sets the value for the specified key in this instance.

Memory.prototype.set = function (key, value) {
+  if (this.readOnly) {
+    return false;
+  }
+  
   var target = this.store, 
-      path   = nconf.path(key);
+      path   = common.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) {
     key = path.shift();
@@ -60,8 +70,12 @@ a nested json structure based on key delimiters :.

@key {string} Key to remove from this instance

Removes the value for the specified key from this instance.

Memory.prototype.clear = function (key) {
+  if (this.readOnly) {
+    return false;
+  }
+  
   var target = this.store, 
-      path   = nconf.path(key);
+      path   = common.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) {
     key = path.shift();
@@ -82,14 +96,18 @@ a nested json structure based on key delimiters :.

Merges the properties in value into the existing object value at key. If the existing value key is not an Object, it will be -completely overwritten.

Memory.prototype.merge = function (key, value) {

If the key is not an Object or is an Array, +completely overwritten.

Memory.prototype.merge = function (key, value) {
+  if (this.readOnly) {
+    return false;
+  }
+  

If the key is not an Object or is an Array, then simply set it. Merging is for Objects.

  if (typeof value !== 'object' || Array.isArray(value)) {
     return this.set(key, value);
   }
   
   var self    = this,
       target  = this.store, 
-      path    = nconf.path(key),
+      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) {
@@ -113,6 +131,10 @@ is an Object.

};

function reset (callback)

Clears all keys associated with this instance.

Memory.prototype.reset = function () {
+  if (this.readOnly) {
+    return false;
+  }
+  
   this.mtimes = {};
   this.store  = {};
   return true;
diff --git a/docs/nconf/stores/redis.html b/docs/nconf/stores/redis.html
deleted file mode 100644
index b86fdbb..0000000
--- a/docs/nconf/stores/redis.html
+++ /dev/null
@@ -1,308 +0,0 @@
-      redis.js           

redis.js

/*
- * redis.js: Redis storage engine for nconf configuration(s)
- *
- * (C) 2011, Charlie Robbins
- *
- */
-
-var async = require('async'),
-    redis = require('redis'),
-    nconf = require('../../nconf'),
-    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) {
-  options        = options || {};
-  this.type      = 'redis';
-  this.namespace = options.namespace || 'nconf';
-  this.host      = options.host || 'localhost';
-  this.port      = options.port || 6379;
-  this.ttl       = options.ttl  || 60 * 60 * 1000;
-  this.cache     = new Memory();
-  this.redis     = redis.createClient(options.port, options.host);
-  
-  if (options.auth) {
-    this.redis.auth(options.auth);
-  }
-  

Suppress errors from the Redis client

  this.redis.on('error', function (err) { 
-    console.dir(err);
-  });
-};

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) {
-  var self    = this,
-      result  = {},
-      now     = Date.now(),
-      mtime   = this.cache.mtimes[key],
-      fullKey = nconf.key(this.namespace, key);
-  

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };
-  

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) {
-      self.get(nconf.key(key, source), function (err, value) {
-        if (err) {
-          return next(err);
-        }
-        
-        result[source] = value;
-        next();
-      });
-    }
-    
-    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) {
-        if (err) {
-          return callback(err);
-        }
-        
-        self.cache.set(key, result);
-        callback(null, result);
-      })
-    }
-    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) {
-        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) {
-  var self = this,
-      path = nconf.path(key);
-  

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };
-  
-  this._addKeys(key, function (err) {
-    if (err) {
-      return callback(err);
-    }
-    
-    var fullKey = nconf.key(self.namespace, key);
-    
-    if (!Array.isArray(value) && value !== null && 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.cache.set(key, value);
-      self._setObject(fullKey, value, callback);
-    }
-    else {

If the value is a simple literal (or an Array) then JSON -stringify it and put it into Redis.

      self.cache.set(key, value);
-      value = JSON.stringify(value);
-      self.redis.set(fullKey, value, callback);
-    }
-  });
-};

function merge (key, value, callback)

- -

@key {string} Key to merge the value into

- -

@value {literal|Object} Value to merge into the key

- -

2callback {function} Continuation to respond to when complete.

- -

Merges the properties in value into the existing object value -at key. If the existing value key is not an Object, it will be -completely overwritten.

Redis.prototype.merge = function (key, value, callback) {

If the key is not an Object or is an Array, -then simply set it. Merging is for Objects.

  if (typeof value !== 'object' || Array.isArray(value)) {
-    return this.set(key, value, callback);
-  }
-  
-  var self    = this,
-      path    = nconf.path(key),
-      fullKey = nconf.key(this.namespace, key);
-  

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };
-  

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._addKeys(key, function (err) {
-    self.redis.smembers(nconf.key(fullKey, 'keys'), function (err, keys) {
-      function nextMerge (nested, next) {
-        var keyPath = nconf.key.apply(null, path.concat([nested]));
-        self.merge(keyPath, value[nested], next);
-      }
-      
-      if (keys && keys.length > 0) {

If there are existing keys then we must do a recursive merge -of the two Objects.

        return async.forEach(Object.keys(value), nextMerge, callback);
-      }

Otherwise, we can simply invoke set to override the current -literal or Array value with our new Object value

      self.set(key, 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) {
-  var self    = this,
-      result  = {},
-      path    = [this.namespace].concat(nconf.path(key)),
-      last    = path.pop(),
-      fullKey = nconf.key(this.namespace, key);

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };

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

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };

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 = {};
-  

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };
-
-  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();
-      });
-    }
-    
-    keys = keys || [];
-    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;
-  

Set the callback if not provided for "fire and forget"

  callback = callback || function () { };
-  

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);
-    }
-    
-    existing = existing || [];
-    async.forEach(existing, function (key, next) {
-      self.clear(key, next);
-    }, callback);
-  });
-};

@private function _addKeys (key, callback)

- -

@key {string} Key to add parent keys for

- -

@callback {function} Continuation to respond to when complete.

- -

Adds the full key path to Redis via sadd.

Redis.prototype._addKeys = function (key, callback) {
-  var self = this,
-      path = nconf.path(key);
-  
-  function addKey (partial, next) {
-    var index  = path.indexOf(partial),
-        base   = [self.namespace].concat(path.slice(0, index)),
-        parent = nconf.key.apply(null, base.concat(['keys']));
-
-    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, 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) {
-  var self = this,
-      keys = Object.keys(value);
-  
-  function addValue (child, next) {

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) {
-        return next(err);
-      }
-
-      var fullKey = nconf.key(key, child),
-          childValue = value[child];
-          
-      if (!Array.isArray(childValue) && typeof childValue === 'object') {
-        self._setObject(fullKey, childValue, next);
-      }
-      else {
-        childValue = JSON.stringify(childValue);
-        self.redis.set(fullKey, childValue, next);
-      }
-    });
-  }
-  

Iterate over the keys of the Object and set the appropriate values.

  async.forEach(keys, addValue, function (err) {
-    return err ? callback(err) : callback();    
-  });
-};
-
-
\ No newline at end of file diff --git a/docs/nconf/stores/system.html b/docs/nconf/stores/system.html new file mode 100644 index 0000000..9e89544 --- /dev/null +++ b/docs/nconf/stores/system.html @@ -0,0 +1,98 @@ + system.js

system.js

/*
+ * system.js: Simple memory-based store for process environment variables and
+ *            command-line arguments.
+ *
+ * (C) 2011, Charlie Robbins
+ *
+ */
+ 
+var util = require('util'),
+    Memory = require('./memory').Memory;
+ 

function System (options)

+ +

@options {Object} Options for this instance.

+ +

Constructor function for the System nconf store, a simple abstraction +around the Memory store that can read process environment variables +and command-line arguments.

var System = exports.System = function (options) {
+  options = options || {};
+  Memory.call(this, options);
+
+  this.type      = 'system';
+  this.overrides = options.overrides || null;
+  this.env       = options.env       || false;
+  this.argv      = options.argv      || false;
+};

Inherit from the Memory store

util.inherits(System, Memory);

function loadSync ()

+ +

Loads the data passed in from process.env into this instance.

System.prototype.loadSync = function () {
+  if (this.env) {
+    this.loadEnv();
+  }
+  
+  if (this.argv) {
+    this.loadArgv();
+  }
+  
+  if (this.overrides) {
+    this.loadOverrides();
+  }
+  
+  return this.store;
+};

function loadOverrides ()

+ +

Loads any overrides set on this instance into +the underlying managed Memory store.

System.prototype.loadOverrides = function () {
+  if (!this.overrides) {
+    return;
+  }
+  
+  var self = this,
+      keys = Object.keys(this.overrides);
+  
+  keys.forEach(function (key) {
+    self.set(key, self.overrides[key]);
+  });
+  
+  return this.store;
+};

function loadArgv ()

+ +

Loads the data passed in from the command-line arguments +into this instance.

System.prototype.loadArgv = function () {
+  var self = this, 
+      argv;
+  
+  if (typeof this.argv === 'object') {
+    argv = require('optimist').options(this.argv).argv;
+  }
+  else if (this.argv) {
+    argv = require('optimist').argv;
+  }
+  
+  if (!argv) {
+    return;
+  }
+  
+  Object.keys(argv).forEach(function (key) {
+    self.set(key, argv[key]);
+  });
+  
+  return this.store;
+};

function loadEnv ()

+ +

Loads the data passed in from process.env into this instance.

System.prototype.loadEnv = function () {
+  var self = this;
+  
+  if (!this.env) {
+    return;
+  }
+  
+  Object.keys(process.env).filter(function (key) {
+    return !self.env.length || self.env.indexOf(key) !== -1;
+  }).forEach(function (key) {
+    self.set(key, process.env[key]);
+  });
+    
+  return this.store;
+};
+
+
\ No newline at end of file