diff --git a/docs/nconf.html b/docs/nconf.html index 170b10f..6f65d65 100644 --- a/docs/nconf.html +++ b/docs/nconf.html @@ -1,11 +1,14 @@ - nconf.js
Jump To …

nconf.js

/*
+      nconf.js           

nconf.js

/*
  * nconf.js: Top-level include for the nconf module
  *
  * (C) 2011, Charlie Robbins
  *
  */
 
-var Provider = require('./nconf/provider').Provider,
+var fs = require('fs'),
+    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

@@ -16,7 +19,9 @@

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.stores   = require('./nconf/stores');
-nconf.Provider = Provider;
+};

Expose the various components included with nconf

nconf.loadFiles = common.loadFiles;
+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 new file mode 100644 index 0000000..899ba70 --- /dev/null +++ b/docs/nconf/common.html @@ -0,0 +1,48 @@ + common.js

common.js

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

function loadFiles (files)

+ +

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

+ +

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

+ +

Loads all the data in the specified files.

common.loadFiles = function (files, callback) {
+  if (!files) {
+    return callback(null, {});
+  }
+
+  var options = Array.isArray(files) ? { files: files } : files,
+      store = new stores.Memory();

Set the default JSON format if not already +specified

  options.format = options.format || formats.json;
+
+  function loadFile (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();
+    });
+  }
+
+  async.forEach(files, loadFile, function (err) {
+    return err ? callback(err) : callback(null, store.store);
+  });
+};
+
+
\ No newline at end of file diff --git a/docs/nconf/formats.html b/docs/nconf/formats.html new file mode 100644 index 0000000..0766925 --- /dev/null +++ b/docs/nconf/formats.html @@ -0,0 +1,22 @@ + formats.js

formats.js

/*
+ * formats.js: Default formats supported by nconf
+ *
+ * (C) 2011, Charlie Robbins
+ *
+ */
+
+var ini = require('ini');
+
+var formats = exports;

@json

+ +

Standard JSON format which pretty prints .stringify().

formats.json = {
+  stringify: function (obj) {
+    return JSON.stringify(obj, null, 2)
+  },
+  parse: JSON.parse
+};

@ini

+ +

Standard INI format supplied from the ini module +http://en.wikipedia.org/wiki/INI_file

formats.ini = ini;
+
+
\ No newline at end of file diff --git a/docs/nconf/provider.html b/docs/nconf/provider.html index 776fb86..1b50174 100644 --- a/docs/nconf/provider.html +++ b/docs/nconf/provider.html @@ -1,17 +1,23 @@ - provider.js

provider.js

/*
+      provider.js           

provider.js

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

function Provider (options)

+var async = require('async'), + optimist = require('optimist'), + 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 nconf.

var Provider = exports.Provider = function (options) {
-  options = 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)

@@ -21,9 +27,21 @@ for exposing the pluggable storage features of nconf.

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

Provider.prototype.use = function (type, options) {
-  if (!this.store || type.toLowerCase() !== this.store.type) {
+  var self = this;
+  options = options || {};
+
+  function sameOptions () {
+    return Object.keys(options).every(function (key) {
+      return options[key] === self.store[key];
+    });
+  }
+  
+  if (!this.store || type.toLowerCase() !== this.store.type 
+    || !sameOptions()) {
     this.store = stores.create(type, options);
   }
+  
+  return this;
 };

function get (key, callback)

@key {string} Key to retrieve for this instance.

@@ -31,7 +49,15 @@ specified type.

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

Retrieves the value for the specified key (if any).

Provider.prototype.get = function (key, callback) {
-  return this.store.get(key, callback);
+  if (this.overrides && Object.prototype.hasOwnProperty.call(this.overrides, key)) {
+    if (callback) {
+      callback(null, this.overrides[key]);
+    }
+    
+    return this.overrides[key];
+  }
+  
+  return this._execute('get', 1, key, callback);
 };

function set (key, value, callback)

@key {string} Key to set in this instance

@@ -41,47 +67,77 @@ specified type.

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

Sets the value for the specified key in this instance.

Provider.prototype.set = function (key, value, callback) {
-  return this.store.set(key, value, callback);
-};

function merge (key, value)

+ return this._execute('set', 2, key, value, callback); +};

function merge ([key,] value [, callback])

@key {string} Key to merge the value into

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

-

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.

Provider.prototype.merge = function (key, value, callback) {
-  return this.store.merge(key, value, callback);
-}

function clear (key, callback)

+

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

+ +

Merges the properties in value into the existing object value at key.

+ +
    +
  1. If the existing value key is not an Object, it will be completely overwritten.
  2. +
  3. If key is not supplied, then the value will be merged into the root.
  4. +
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 mergeFiles (files, callback)

+ +

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

+ +

@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) {
+  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.store.clear(key, callback);
-};

function load (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 +

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();
   }
   
-  
-  if (!this.store.load) {
-    var error = new Error('nconf store ' + this.store.type + ' has no load() method');
-    if (callback) {
-      return callback (error);
-    }
-    
-    throw error;
-  }
-  
-  return this.store.load(callback);
-};

function save (value, callback)

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

@value {Object} Optional Config object to set for this instance

@@ -92,29 +148,65 @@ instance and then adds all key-value pairs in value.

if (!callback) { callback = value; value = null; -

If we still don't have a callback and the +

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();
     }
   }
   
-  if (!this.store.save) {
-    var error = new Error('nconf store ' + this.store.type + ' has no save() method');
-    if (callback) {
-      return callback (error);
-    }
-    
-    throw error;
-  }
-  
-  return this.store.save(value, callback);
-};

function reset (callback)

+ 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.store.reset(callback);
-};
+  return this._execute('reset', 0, callback);  
+};

@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 this.store, 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,
+      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);
+  
+  if (callback) {
+    callback(null, response);
+  }
+  
+  return response;
+}

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

+ +

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) {
+  if (callback) {
+    return callback(err);
+  }
+  
+  throw err;
+}
 
 
\ No newline at end of file diff --git a/docs/nconf/stores.html b/docs/nconf/stores.html index b14c515..34eb214 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
diff --git a/docs/nconf/stores/file.html b/docs/nconf/stores/file.html
index a4f5158..5c38659 100644
--- a/docs/nconf/stores/file.html
+++ b/docs/nconf/stores/file.html
@@ -1,12 +1,14 @@
-      file.js           

file.js

/*
+      file.js           

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

Saves the current configuration object to disk at this.file -using the format specified by this.format synchronously.

file.js

/*
  * file.js: Simple file storage engine for nconf files
  *
  * (C) 2011, Charlie Robbins
  *
  */
- 
+
 var fs = require('fs'),
+    path = require('path'),
     util = require('util'),
+    formats = require('../formats'),
     Memory = require('./memory').Memory;
  

function File (options)

@@ -22,12 +24,8 @@ around the Memory store that can persist configuration to disk.

this.type = 'file'; this.file = options.file; - this.format = options.format || { - stringify: function (obj) { - return JSON.stringify(obj, null, 2) - }, - parse: JSON.parse - }; + this.dir = options.dir || process.cwd(); + this.format = options.format || formats.json; };

Inherit from the Memory store

util.inherits(File, Memory);

function save (value, callback)

@value {Object} Ignored Left here for consistency

@@ -51,26 +49,12 @@ using the format specified by this.format.

File.prototype.saveSync = function (value, callback) {
-  if (!callback) {
-    callback = value;
-    value = null;
-  }
-  
-  var err;
+using the format specified by this.format synchronously.

File.prototype.saveSync = function (value) {
   try {
     fs.writeFileSync(this.file, this.format.stringify(this.store));
   }
   catch (ex) {
-    err = ex;
-  }
-  
-  if (callback) {
-    return callback(err);
-  }
-  
-  if (err) {
-    throw err;
+    throw(ex);
   }
 };

function load (callback)

@@ -78,40 +62,109 @@ using the format specified by this.format synchronously.

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

File.prototype.load = function (callback) {
   var self = this;
-  fs.readFile(this.file, function (err, data) {
-    if (err) {
-      return callback(err);
+
+  path.exists(self.file, function (exists) {
+    if (!exists) {

If the path we are attempting to load doesn't exist, create it

      self.save({}, function (err) {
+        self.store = {};
+        return callback(err, self.store);
+      });
+    }
+    else {

Else, the path exists, read it from disk

      fs.readFile(self.file, function (err, data) {
+        if (err) {
+          return callback(err);
+        }
+        
+        try {
+          self.store = self.format.parse(data.toString());
+        }
+        catch (ex) {
+          return callback(new Error("Error parsing your JSON configuration file."));
+        }
+        
+        callback(null, self.store);
+      });
     }
-    
-    data = self.format.parse(data.toString());
-    self.store = data;
-    callback(null, self.store);
   });
-};

function load (callback)

+};

function load (callback)

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

-

Attempts to load the data stored in this.file synchronously and responds appropriately.

File.prototype.loadSync = function (callback) {
-  var err, data;
-  
-  try {
-    data = fs.readFileSync(this.file, 'utf8');
-    this.store = this.format.parse(data);
+

Attempts to load the data stored in this.file synchronously and responds appropriately.

File.prototype.loadSync = function () {
+  var data, self = this;
+
+  if (!path.existsSync(self.file)) {

If the path we are attempting to load doesn't exist, create it

    self.saveSync({});
+    self.store = {};
+    data = {};
   }
-  catch (ex) {
-    err = ex;
+  else {

Else, the path exists, read it from disk

    try {
+      data = fs.readFileSync(this.file, 'utf8');
+      this.store = this.format.parse(data);
+    }
+    catch (ex) {
+      throw new Error("Error parsing your JSON configuration file.")
+    }
   }
-  
-  if (callback) {
-    return callback(err, data);
-  }
-  
-  if (err) {
-    err.message = "Error parsing your JSON configuration file."
-    throw err;
-  }
-  
+
   return data;
+};

function search (base)

+ +

@base {string} Base directory (or file) to begin searching for the target file.

+ +

Attempts to find this.file by iteratively searching up the +directory structure

File.prototype.search = function (base) {
+  var looking = true,
+      fullpath,
+      previous,
+      stats;
+
+  base = base || process.cwd();
+
+  if (this.file[0] === '/') {

If filename for this instance is a fully qualified path +(i.e. it starts with a '/') then check if it exists

    try {
+      stats = fs.statSync(fs.realpathSync(this.file));
+      if (stats.isFile()) {
+        fullpath = this.file;
+        looking = false;
+      }
+    }
+    catch (ex) {

Ignore errors

    }
+  }
+
+  if (looking && base) {

Attempt to stat the realpath located at base +if the directory does not exist then return false.

    try {
+      var stat = fs.statSync(fs.realpathSync(base));
+      looking = stat.isDirectory();
+    }
+    catch (ex) {
+      return false;
+    }
+  }
+  
+  while (looking) {

Iteratively look up the directory structure from base

    try {
+      stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file)));
+      looking = stats.isDirectory();
+    }
+    catch (ex) {
+      previous = base;
+      base = path.dirname(base);
+
+      if (previous === base) {

If we've reached the top of the directory structure then simply use +the default file path.

        try {
+          stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file)));
+          if (stats.isDirectory()) {
+            fullpath = undefined;
+          }
+        }
+        catch (ex) {

Ignore errors

        }
+        
+        looking = false;
+      }
+    }
+  }

Set the file for this instance to the fullpath +that we have found during the search. In the event that +the search was unsuccessful use the original value for this.file.

  this.file = fullpath || this.file;
+  
+  return fullpath;
 };
 
 
\ No newline at end of file diff --git a/docs/nconf/stores/memory.html b/docs/nconf/stores/memory.html index 9e5bd5e..8994c70 100644 --- a/docs/nconf/stores/memory.html +++ b/docs/nconf/stores/memory.html @@ -1,4 +1,4 @@ - memory.js

memory.js

/*
+      memory.js           
target = target[key]; }

memory.js

/*
  * memory.js: Simple memory storage engine for nconf configuration(s)
  *
  * (C) 2011, Charlie Robbins
@@ -101,7 +101,7 @@ then simply set it. Merging is for Objects.

Set the specified value in the nested JSON structure

  key = path.shift();
   

If the current value at the key target is not an Object, -of is an Array then simply override it because the new value +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;
     return true;
@@ -116,6 +116,6 @@ is an Object.

this.mtimes = {}; this.store = {}; return true; -} +};
\ No newline at end of file