First pass at transform functions (#279)
This commit is contained in:
parent
b9c345bf96
commit
856fdf8dff
5 changed files with 167 additions and 5 deletions
78
README.md
78
README.md
|
@ -193,6 +193,32 @@ A simple in-memory storage engine that stores a nested JSON representation of th
|
||||||
### Argv
|
### Argv
|
||||||
Responsible for loading the values parsed from `process.argv` by `yargs` into the configuration hierarchy. See the [yargs option docs](https://github.com/bcoe/yargs#optionskey-opt) for more on the option format.
|
Responsible for loading the values parsed from `process.argv` by `yargs` into the configuration hierarchy. See the [yargs option docs](https://github.com/bcoe/yargs#optionskey-opt) for more on the option format.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
##### `parseValues: {true|false}` (default: `false`)
|
||||||
|
Attempt to parse well-known values (e.g. 'false', 'true', 'null', 'undefined', '3', '5.1' and JSON values)
|
||||||
|
into their proper types. If a value cannot be parsed, it will remain a string.
|
||||||
|
|
||||||
|
##### `transform: function(obj)`
|
||||||
|
Pass each key/value pair to the specified function for transformation.
|
||||||
|
|
||||||
|
The input `obj` contains two properties passed in the following format:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
key: '<string>',
|
||||||
|
value: '<string>'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The transformation function may alter both the key and the value.
|
||||||
|
|
||||||
|
The function may return either an object in the asme format as the input or a value that evaluates to false.
|
||||||
|
If the return value is falsey, the entry will be dropped from the store, otherwise it will replace the original key/value.
|
||||||
|
|
||||||
|
*Note: If the return value doesn't adhere to the above rules, an exception will be thrown.*
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
``` js
|
``` js
|
||||||
//
|
//
|
||||||
// Can optionally also be an object literal to pass to `yargs`.
|
// Can optionally also be an object literal to pass to `yargs`.
|
||||||
|
@ -202,16 +228,52 @@ Responsible for loading the values parsed from `process.argv` by `yargs` into th
|
||||||
alias: 'example',
|
alias: 'example',
|
||||||
describe: 'Example description for usage generation',
|
describe: 'Example description for usage generation',
|
||||||
demand: true,
|
demand: true,
|
||||||
default: 'some-value'
|
default: 'some-value',
|
||||||
|
parseValues: true,
|
||||||
|
transform: function(obj) {
|
||||||
|
if (obj.key === 'foo') {
|
||||||
|
obj.value = 'baz';
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Env
|
### Env
|
||||||
Responsible for loading the values parsed from `process.env` into the configuration hierarchy.
|
Responsible for loading the values parsed from `process.env` into the configuration hierarchy.
|
||||||
Usually the env variables values are loaded into the configuration as strings.
|
By default, the env variables values are loaded into the configuration as strings.
|
||||||
To ensure well-known strings ('false', 'true', 'null', 'undefined', '3', '5.1') and JSON values
|
|
||||||
are properly parsed, the `parseValues` boolean option is available.
|
#### Options
|
||||||
|
|
||||||
|
##### `lowerCase: {true|false}` (default: `false`)
|
||||||
|
Convert all input keys to lower case. Values are not modified.
|
||||||
|
|
||||||
|
If this option is enabled, all calls to `nconf.get()` must pass in a lowercase string (e.g. `nconf.get('port')`)
|
||||||
|
|
||||||
|
##### `parseValues: {true|false}` (default: `false`)
|
||||||
|
Attempt to parse well-known values (e.g. 'false', 'true', 'null', 'undefined', '3', '5.1' and JSON values)
|
||||||
|
into their proper types. If a value cannot be parsed, it will remain a string.
|
||||||
|
|
||||||
|
##### `transform: function(obj)`
|
||||||
|
Pass each key/value pair to the specified function for transformation.
|
||||||
|
|
||||||
|
The input `obj` contains two properties passed in the following format:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
key: '<string>',
|
||||||
|
value: '<string>'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The transformation function may alter both the key and the value.
|
||||||
|
|
||||||
|
The function may return either an object in the asme format as the input or a value that evaluates to false.
|
||||||
|
If the return value is falsey, the entry will be dropped from the store, otherwise it will replace the original key/value.
|
||||||
|
|
||||||
|
*Note: If the return value doesn't adhere to the above rules, an exception will be thrown.*
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
``` js
|
``` js
|
||||||
//
|
//
|
||||||
|
@ -247,7 +309,13 @@ are properly parsed, the `parseValues` boolean option is available.
|
||||||
match: /^whatever_matches_this_will_be_whitelisted/
|
match: /^whatever_matches_this_will_be_whitelisted/
|
||||||
whitelist: ['database__host', 'only', 'load', 'these', 'values', 'if', 'whatever_doesnt_match_but_is_whitelisted_gets_loaded_too'],
|
whitelist: ['database__host', 'only', 'load', 'these', 'values', 'if', 'whatever_doesnt_match_but_is_whitelisted_gets_loaded_too'],
|
||||||
lowerCase: true,
|
lowerCase: true,
|
||||||
parseValues: true
|
parseValues: true,
|
||||||
|
transform: function(obj) {
|
||||||
|
if (obj.key === 'foo') {
|
||||||
|
obj.value = 'baz';
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
var dbHost = nconf.get('database:host');
|
var dbHost = nconf.get('database:host');
|
||||||
```
|
```
|
||||||
|
|
|
@ -141,3 +141,31 @@ common.parseValues = function (value) {
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function transform(map, fn)
|
||||||
|
// #### @map {object} Object of key/value pairs to apply `fn` to
|
||||||
|
// #### @fn {function} Transformation function that will be applied to every key/value pair
|
||||||
|
// transform a set of key/value pairs and return the transformed result
|
||||||
|
common.transform = function(map, fn) {
|
||||||
|
var pairs = Object.keys(map).map(function(key) {
|
||||||
|
var obj = { key: key, value: map[key]};
|
||||||
|
var result = fn.call(null, obj);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return null;
|
||||||
|
} else if (result.key && typeof result.value !== 'undefined') {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = new Error('Transform function passed to store returned an invalid format: ' + JSON.stringify(result));
|
||||||
|
error.name = 'RuntimeError';
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return pairs.reduce(function(accumulator, pair) {
|
||||||
|
accumulator[pair.key] = pair.value;
|
||||||
|
return accumulator;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ var Argv = exports.Argv = function (options, usage) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.usage = usage;
|
this.usage = usage;
|
||||||
this.parseValues = options.parseValues || false;
|
this.parseValues = options.parseValues || false;
|
||||||
|
this.transform = options.transform || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inherit from the Memory store
|
// Inherit from the Memory store
|
||||||
|
@ -59,6 +60,10 @@ Argv.prototype.loadArgv = function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.transform) {
|
||||||
|
argv = common.transform(argv, this.transform);
|
||||||
|
}
|
||||||
|
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
Object.keys(argv).forEach(function (key) {
|
Object.keys(argv).forEach(function (key) {
|
||||||
var val = argv[key];
|
var val = argv[key];
|
||||||
|
|
|
@ -25,6 +25,7 @@ var Env = exports.Env = function (options) {
|
||||||
this.separator = options.separator || '';
|
this.separator = options.separator || '';
|
||||||
this.lowerCase = options.lowerCase || false;
|
this.lowerCase = options.lowerCase || false;
|
||||||
this.parseValues = options.parseValues || false;
|
this.parseValues = options.parseValues || false;
|
||||||
|
this.transform = options.transform || false;
|
||||||
|
|
||||||
if (({}).toString.call(options.match) === '[object RegExp]'
|
if (({}).toString.call(options.match) === '[object RegExp]'
|
||||||
&& typeof options !== 'string') {
|
&& typeof options !== 'string') {
|
||||||
|
@ -67,6 +68,10 @@ Env.prototype.loadEnv = function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.transform) {
|
||||||
|
env = common.transform(env, this.transform);
|
||||||
|
}
|
||||||
|
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
Object.keys(env).filter(function (key) {
|
Object.keys(env).filter(function (key) {
|
||||||
if (self.match && self.whitelist.length) {
|
if (self.match && self.whitelist.length) {
|
||||||
|
|
|
@ -184,5 +184,61 @@ vows.describe('nconf/multiple-stores').addBatch({
|
||||||
teardown: function () {
|
teardown: function () {
|
||||||
nconf.remove('env');
|
nconf.remove('env');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
}).addBatch({
|
||||||
|
// Threw this in it's own batch to make sure it's run separately from the
|
||||||
|
// sync check
|
||||||
|
"When using env with transform:fn": {
|
||||||
|
topic: function () {
|
||||||
|
|
||||||
|
function testTransform(obj) {
|
||||||
|
if (obj.key === 'FOO') {
|
||||||
|
obj.key = 'FOOD';
|
||||||
|
obj.value = 'BARFOO';
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
helpers.cp(complete, completeTest, function () {
|
||||||
|
nconf.env({ transform: testTransform })
|
||||||
|
that.callback();
|
||||||
|
});
|
||||||
|
}, "env vars": {
|
||||||
|
"port key/value properly transformed": function() {
|
||||||
|
assert.equal(nconf.get('FOOD'), 'BARFOO');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
teardown: function () {
|
||||||
|
nconf.remove('env');
|
||||||
|
}
|
||||||
|
}).addBatch({
|
||||||
|
// Threw this in it's own batch to make sure it's run separately from the
|
||||||
|
// sync check
|
||||||
|
"When using env with a bad transform:fn": {
|
||||||
|
topic: function () {
|
||||||
|
function testTransform() {
|
||||||
|
return {foo: 'bar'};
|
||||||
|
}
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
helpers.cp(complete, completeTest, function () {
|
||||||
|
try {
|
||||||
|
nconf.env({ transform: testTransform });
|
||||||
|
that.callback();
|
||||||
|
} catch (err) {
|
||||||
|
that.callback(null, err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, "env vars": {
|
||||||
|
"port key/value throws transformation error": function(err) {
|
||||||
|
assert.equal(err.name, 'RuntimeError');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
teardown: function () {
|
||||||
|
nconf.remove('env');
|
||||||
}
|
}
|
||||||
}).export(module);
|
}).export(module);
|
Loading…
Reference in a new issue