1.0.0 • Published 8 years ago

app-etc-config v1.0.0

Weekly downloads
2
License
MIT
Repository
github
Last release
8 years ago

Config

NPM version Build Status Coverage Status Dependencies

Creates a configuration API.

Installation

$ npm install app-etc-config

Usage

var etc = require( 'app-etc-config' );

etc( options )

Creates a configuration API.

var config = etc();

The constructor accepts the following options:

To specify options,

var config = etc({
	'sep': '|',
	'create': false,
	'schema': require( '/path/to/schema.json' ),
	'formats': {
		'only-a': /^a+$/
	},
	'extSchemas': {
		'ref_schema1': require( '/path/to/ref_schema1.json' ),
		'ref_schema2': require( '/path/to/ref_schema2.json' )
	}
});

===

config.set( keypath, value, options )

Sets a configuration value at a specified keypath.

var bool = config.set( 'foo.bar', 'beep' );
// returns a <boolean> indicating whether a value was set

/*
	{
		'foo': {
			'bar': 'beep'
		}
	}
*/

The method accepts the following options:

  • sep: keypath separator used when setting configuration values. See utils-deep-set.
  • create: boolean indicating whether to create a keypath if it does not exist. See utils-deep-set.

Specifying method options will override the default options provided during config creation.

===

config.merge( keypath, config, options )

Merges a configuration object or another config instance.

var bool = config.merge({
	'beep': 'boop'
});
// returns a <boolean> indicating if merge was successful

/*
	{
		'foo': {
			'bar': 'beep'
		},
		'beep': 'boop'
	}
*/

If provided a keypath, the method merges a configuration object with a sub-configuration.

var config2 = etc();
config2.set( 'hello', 'world' );

var bool = config.merge( 'foo', config2 );
/*
	{
		'foo': {
			'bar': 'beep',
			'hello': 'world'
		},
		'beep': 'boop'
	}
*/

If a keypath does not correspond to an object, the method returns false and set() should be used instead.

var bool = config.merge( 'abcdefg', config2 );
// returns false

/*
	{
		'foo': {
			'bar': 'beep',
			'hello': 'world'
		},
		'beep': 'boop'
	}
*/

bool = config.set( 'abcdefg', config2 );
// returns true

The method accepts the following options:

  • sep: keypath separator used when merging nested configuration values. See utils-deep-set.

Specifying method options will override the default options provided during config creation.

===

config.get( [ keypath, options ] )

Returns a copy of the raw configuration store.

var obj = config.get();
/*
	{
		'foo': {
			'bar': 'beep',
			'hello': 'world'
		},
		'beep': 'boop'
	}
*/

If provided a keypath, the method returns a copy of the corresponding configuration value.

var val = config.get( 'foo.hello' );
// returns 'world'

If a keypath does not exist, the method returns undefined.

var val = config.get( 'non.existent.path' );
// returns undefined

The method accepts the following options:

  • sep: keypath separator used when getting configuration values. See utils-deep-get.

Specifying method options will override the default options provided during config creation.

===

config.clone( [keypath, options ] )

Clones a config instance.

var config2 = config.clone();
console.log( config2.get() );
/*
	{
		'foo': {
			'bar': 'beep',
			'hello': 'world'
		},
		'beep': 'boop'
	}
*/

console.log( config === config2 );
// returns false

If provided a keypath, the method clones a sub-configuration value as a new config instance.

var config2;

// Configuration value is an object:
config2 = config.clone( 'foo' );
/*
	{
		'bar': 'beep',
		'hello': 'world'
	}
*/

// Configuration value is not an object:
config2 = config.clone( 'beep' );
/*
	{
		'beep': 'boop'
	}
*/

If a keypath does not exist, the method returns undefined.

var config3 = config.clone( 'non.existent.path' );
// returns undefined

The method accepts the following options:

  • sep: keypath separator used when getting configuration values. See utils-deep-get.

Specifying method options will override the default options provided during config creation.

===

config.load( filename )

Convenience method which loads and merges a configuration file.

config.load( '/path/to/config/file.<ext>' );

Note: this method does not directly support loading a configuration file into a sub-configuration. To achieve this, use app-etc-load and then merge at a specified keypath.

var load = require( 'app-etc-load' );

var obj = load( '/path/to/config/file.<ext>' );
config.merge( 'foo', obj );

===

config.validate( validator )

Validates a configuration.

// Valid configuration:
var out = config.validate();
// returns true

If a configuration is invalid, the method returns an array containing validation errors.

// Invalid configuration:
out = config.validate();
// returns [{...},{...},...]

The method accepts a validator function, which can be useful for validating against multiple schemas or when a schema was not provided during initialization. The validator should accept as its first argument the configuration object to be validated. The method returns validation results without modification.

// Example JSON schema validator:
var validator = require( 'jsen' );

var schema = require( '/path/to/schema.json' );
var validate = validator( schema, {
	'greedy': true
});

var out = config.validate( validate );
console.log( out );
console.log( validate.errors );

If a schema option was not provided during initialization and a validator is not provided at runtime, the method always returns true.

var config = etc();
config.set( 'port', 80 );

var out = config.validate();
// returns true

===

etc.exts()

Returns a list of supported filename extensions.

var exts = etc.exts();
// returns ['.json','.toml',...]

For more details, see app-etc-load.

etc.parser( extname, parser )

Returns a parser for the specified extension.

var parser = etc.parser( '.json' );

Including the . when specifying an extension is optional.

var parser = etc.parser( 'json' );

To support additional file formats or to override a parser, provide a parser function for an associated extension.

var parser = require( 'my-special-fmt-parser' );

etc.parser( '<my-ext>', parser );

Once a parser is set, all config instances will parse provided files accordingly.

config.load( './file.<my-ext>' );

For more details, see app-etc-load.


Notes

  • This module uses jsen for validating an internal configuration object against a JSON schema. One compelling feature of jsen is the ability to extend a schema definition by specifying custom error messages. For example, given the following schema,

    {
    	"type": "object",
    	"definitions": {
    		"port": {
    			"description": "schema for a port",
    			"type": "integer",
    			"minimum": 1024,
    			"maximum": 65536,
    			"requiredMessage": "port is required",
    			"messages": {
    				"type": "invalid type. Must be an integer.",
    				"minimum": "invalid value. Must be an integer greater than or equal to 1024.",
    				"maximum": "invalid value. Must be an integer less than or equal to 65536."
    			}
    		}
    	},
    	"properties": {
    		"port": {
    			"$ref": "#/definitions/port"
    		}
    	},
    	"required": [
    		"port"
    	],
    	"messages": {
    		"type": "invalid data type where an object is expected"
    	}
    }

    validation will return more informative error messages based on keywords.

    var validator = require( 'jsen' );
    
    var validate = validator( schema );
    
    var bool = validate({
    	'port': 80
    })
    
    console.dir( validate.errors );
    /*
    	[
    		{
    			'path': 'port',
    			'keyword': 'minimum',
    			'message': 'invalid value. Must be an integer greater than or equal to 1024.'
    		}
    	]
    */

Examples

var etc = require( 'app-etc-config' );

// Create a new configuration API:
var config = etc();

// Load local files:
config.load( './.travis.yml' );
console.dir( config.get() );

config.load( './package.json' );
console.dir( config.get() );

// Merge in a custom object:
config.merge( 'author', {
	'beep': 'boop'
});
console.dir( config.get( 'author' ) );

// Access a shallow value:
console.log( config.get( 'license' ) );

// Access nested values:
console.log( config.get( 'author.name' ) );

// Create and set shallow values:
config.set( 'hello', false );
console.log( config.get( 'hello' ) );

// Create and set deeply nested values:
config.set( 'foo.bar.bip', 'bap', {
	'create': true
});
console.log( config.get( 'foo.bar.bip' ) );

// Clone the current configuration:
var clone = config.clone();
console.log( config === clone );
// returns false

To run the example code from the top-level application directory,

$ node ./examples/index.js

Tests

Unit

Unit tests use the Mocha test framework with Chai assertions. To run the tests, execute the following command in the top-level application directory:

$ make test

All new feature development should have corresponding unit tests to validate correct functionality.

Test Coverage

This repository uses Istanbul as its code coverage tool. To generate a test coverage report, execute the following command in the top-level application directory:

$ make test-cov

Istanbul creates a ./reports/coverage directory. To access an HTML version of the report,

$ make view-cov

License

MIT license.

Copyright

Copyright © 2015. Athan Reines.