/*
+ 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.
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
\ 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
.
+
+
+If the existing value key
is not an Object, it will be completely overwritten.
+If key
is not supplied, then the value
will be merged into the root.
+ 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
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
.
@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 . 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
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. target = target [ key ];
} Set the specified value in the nested JSON structure
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