Support parsing simple values from env/argv strings (#273)
* simple parse, indexzero/nconf#72 * documentation for tryParse option * Combine JSON parsing and simple parsing
This commit is contained in:
parent
b8402d4eab
commit
532ac9cc57
8 changed files with 81 additions and 30 deletions
|
@ -191,6 +191,9 @@ Responsible for loading the values parsed from `process.argv` by `yargs` into th
|
||||||
|
|
||||||
### 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.
|
||||||
|
To ensure well-known strings ('false', 'true', 'null', 'undefined', '3', '5.1') and JSON values
|
||||||
|
are properly parsed, the `parseValues` boolean option is available.
|
||||||
|
|
||||||
``` js
|
``` js
|
||||||
//
|
//
|
||||||
|
@ -225,7 +228,8 @@ Responsible for loading the values parsed from `process.env` into the configurat
|
||||||
separator: '__',
|
separator: '__',
|
||||||
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
|
||||||
});
|
});
|
||||||
var dbHost = nconf.get('database:host');
|
var dbHost = nconf.get('database:host');
|
||||||
```
|
```
|
||||||
|
|
|
@ -121,3 +121,23 @@ common.merge = function (objs) {
|
||||||
common.capitalize = function (str) {
|
common.capitalize = function (str) {
|
||||||
return str && str[0].toUpperCase() + str.slice(1);
|
return str && str[0].toUpperCase() + str.slice(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### function parseValues (any)
|
||||||
|
// #### @any {string} String to parse as native data-type or return as is
|
||||||
|
// try to parse `any` as a native data-type
|
||||||
|
//
|
||||||
|
common.parseValues = function (value) {
|
||||||
|
var val = value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
val = JSON.parse(value);
|
||||||
|
} catch (ignore) {
|
||||||
|
// Check for any other well-known strings that should be "parsed"
|
||||||
|
if (value === 'undefined'){
|
||||||
|
val = void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
|
common = require('../common'),
|
||||||
Memory = require('./memory').Memory;
|
Memory = require('./memory').Memory;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -17,10 +18,12 @@ var util = require('util'),
|
||||||
var Argv = exports.Argv = function (options, usage) {
|
var Argv = exports.Argv = function (options, usage) {
|
||||||
Memory.call(this, options);
|
Memory.call(this, options);
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
this.type = 'argv';
|
this.type = 'argv';
|
||||||
this.readOnly = true;
|
this.readOnly = true;
|
||||||
this.options = options || false;
|
this.options = options;
|
||||||
this.usage = usage;
|
this.usage = usage;
|
||||||
|
this.parseValues = options.parseValues || false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inherit from the Memory store
|
// Inherit from the Memory store
|
||||||
|
@ -58,8 +61,14 @@ Argv.prototype.loadArgv = function () {
|
||||||
|
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
Object.keys(argv).forEach(function (key) {
|
Object.keys(argv).forEach(function (key) {
|
||||||
if (typeof argv[key] !== 'undefined') {
|
var val = argv[key];
|
||||||
self.set(key, argv[key]);
|
|
||||||
|
if (typeof val !== 'undefined') {
|
||||||
|
if (self.parseValues) {
|
||||||
|
val = common.parseValues(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set(key, val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -83,20 +83,7 @@ Env.prototype.loadEnv = function () {
|
||||||
var val = env[key];
|
var val = env[key];
|
||||||
|
|
||||||
if (self.parseJson) {
|
if (self.parseJson) {
|
||||||
try {
|
val = common.parseValues(val);
|
||||||
var ret = JSON.parse(val);
|
|
||||||
|
|
||||||
// apply JSON parsing only if its non-primitive types: JSON Object or Array
|
|
||||||
// avoid breaking backward-compatibility
|
|
||||||
if (typeof ret !== 'number' &&
|
|
||||||
typeof ret !== 'string' &&
|
|
||||||
typeof ret !== 'boolean') {
|
|
||||||
val = ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (ignore) {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.separator) {
|
if (self.separator) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ var Memory = exports.Memory = function (options) {
|
||||||
this.readOnly = false;
|
this.readOnly = false;
|
||||||
this.loadFrom = options.loadFrom || null;
|
this.loadFrom = options.loadFrom || null;
|
||||||
this.logicalSeparator = options.logicalSeparator || ':';
|
this.logicalSeparator = options.logicalSeparator || ':';
|
||||||
|
this.parseValues = options.parseValues || false;
|
||||||
|
|
||||||
if (this.loadFrom) {
|
if (this.loadFrom) {
|
||||||
this.store = common.loadFilesSync(this.loadFrom);
|
this.store = common.loadFilesSync(this.loadFrom);
|
||||||
|
@ -100,6 +101,9 @@ Memory.prototype.set = function (key, value) {
|
||||||
|
|
||||||
// Set the specified value in the nested JSON structure
|
// Set the specified value in the nested JSON structure
|
||||||
key = path.shift();
|
key = path.shift();
|
||||||
|
if (this.parseValues) {
|
||||||
|
value = common.parseValues.call(common, value);
|
||||||
|
}
|
||||||
target[key] = value;
|
target[key] = value;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -161,7 +161,7 @@ vows.describe('nconf/multiple-stores').addBatch({
|
||||||
}).addBatch({
|
}).addBatch({
|
||||||
// Threw this in it's own batch to make sure it's run separately from the
|
// Threw this in it's own batch to make sure it's run separately from the
|
||||||
// sync check
|
// sync check
|
||||||
"When using env with parseJson:true": {
|
"When using env with parseValues:true": {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
var that = this;
|
var that = this;
|
||||||
helpers.cp(complete, completeTest, function () {
|
helpers.cp(complete, completeTest, function () {
|
||||||
|
@ -175,15 +175,7 @@ vows.describe('nconf/multiple-stores').addBatch({
|
||||||
var val = process.env[key];
|
var val = process.env[key];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var ret = JSON.parse(val);
|
val = JSON.parse(val);
|
||||||
|
|
||||||
// apply JSON parsing only if its non-primitive types: JSON Object or Array
|
|
||||||
// avoid breaking backward-compatibility
|
|
||||||
if (typeof ret !== 'number' &&
|
|
||||||
typeof ret !== 'string' &&
|
|
||||||
typeof ret !== 'boolean') {
|
|
||||||
val = ret;
|
|
||||||
}
|
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
|
|
||||||
assert.deepEqual(nconf.get(key), val);
|
assert.deepEqual(nconf.get(key), val);
|
||||||
|
|
|
@ -47,7 +47,42 @@ vows.describe('nconf/provider').addBatch({
|
||||||
"when 'env' is true": helpers.assertSystemConf({
|
"when 'env' is true": helpers.assertSystemConf({
|
||||||
script: path.join(fixturesDir, 'scripts', 'provider-env.js'),
|
script: path.join(fixturesDir, 'scripts', 'provider-env.js'),
|
||||||
env: { SOMETHING: 'foobar' }
|
env: { SOMETHING: 'foobar' }
|
||||||
})
|
}),
|
||||||
|
"when 'env' is true and 'parseValues' option is true": {
|
||||||
|
topic: function() {
|
||||||
|
var env = {
|
||||||
|
SOMETHING: 'foobar',
|
||||||
|
SOMEBOOL: 'true',
|
||||||
|
SOMENULL: 'null',
|
||||||
|
SOMEUNDEF: 'undefined',
|
||||||
|
SOMEINT: '3600',
|
||||||
|
SOMEFLOAT: '0.5',
|
||||||
|
SOMEBAD: '5.1a'
|
||||||
|
};
|
||||||
|
var oenv = {};
|
||||||
|
Object.keys(env).forEach(function (key) {
|
||||||
|
if (process.env[key]) oenv[key] = process.env[key];
|
||||||
|
process.env[key] = env[key];
|
||||||
|
});
|
||||||
|
var provider = new nconf.Provider().use('env', {parseValues: true});
|
||||||
|
Object.keys(env).forEach(function (key) {
|
||||||
|
delete process.env[key];
|
||||||
|
if (oenv[key]) process.env[key] = oenv[key];
|
||||||
|
});
|
||||||
|
return provider;
|
||||||
|
},
|
||||||
|
"should respond with parsed values": function (provider) {
|
||||||
|
|
||||||
|
assert.equal(provider.get('SOMETHING'), 'foobar');
|
||||||
|
assert.strictEqual(provider.get('SOMEBOOL'), true);
|
||||||
|
assert.notEqual(provider.get('SOMEBOOL'), 'true');
|
||||||
|
assert.strictEqual(provider.get('SOMENULL'), null);
|
||||||
|
assert.strictEqual(provider.get('SOMEUNDEF'), undefined);
|
||||||
|
assert.strictEqual(provider.get('SOMEINT'), 3600);
|
||||||
|
assert.strictEqual(provider.get('SOMEFLOAT'), .5);
|
||||||
|
assert.strictEqual(provider.get('SOMEBAD'), '5.1a');
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"the default nconf provider": {
|
"the default nconf provider": {
|
||||||
"when 'argv' is set to true": helpers.assertSystemConf({
|
"when 'argv' is set to true": helpers.assertSystemConf({
|
||||||
|
|
|
@ -16,7 +16,7 @@ vows.describe('nconf/stores/argv').addBatch({
|
||||||
"should have the correct methods defined": function (argv) {
|
"should have the correct methods defined": function (argv) {
|
||||||
assert.isFunction(argv.loadSync);
|
assert.isFunction(argv.loadSync);
|
||||||
assert.isFunction(argv.loadArgv);
|
assert.isFunction(argv.loadArgv);
|
||||||
assert.isFalse(argv.options);
|
assert.deepEqual(argv.options, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).export(module);
|
}).export(module);
|
Loading…
Reference in a new issue