Skip to content

Commit

Permalink
Merge pull request #138 from damnhipster/master
Browse files Browse the repository at this point in the history
Support for built in formats in schema files
  • Loading branch information
madarche committed Mar 31, 2016
2 parents 8444301 + f571669 commit 168755a
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 124 deletions.
21 changes: 16 additions & 5 deletions lib/convict.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@ function contains (options, x) {
JSON.stringify(options));
}

var BUILT_INS = [Object, Array, String, Number, Boolean];
var BUILT_INS_BY_NAME = {
'Object': Object,
'Array': Array,
'String': String,
'Number': Number,
'Boolean': Boolean
};
var BUILT_IN_NAMES = Object.keys(BUILT_INS_BY_NAME);
var BUILT_INS = BUILT_IN_NAMES.map(function (name) {
return BUILT_INS_BY_NAME[name];
});

function normalizeSchema (name, node, props, fullName, env, argv) {
// If the current schema node is not a config property (has no "default"), recursively normalize it.
Expand Down Expand Up @@ -137,15 +147,16 @@ function normalizeSchema (name, node, props, fullName, env, argv) {
var format = o.format;
var newFormat;

if (BUILT_INS.indexOf(format) >= 0) {
if (BUILT_INS.indexOf(format) >= 0 || BUILT_IN_NAMES.indexOf(format) >= 0) {
// if the format property is a built-in JavaScript constructor,
// assert that the value is of that type
var Format = typeof format === 'string' ? BUILT_INS_BY_NAME[format] : format;
newFormat = function(x) {
assert(Object.prototype.toString.call(x) ==
Object.prototype.toString.call(new format()),
'must be of type ' + format.name);
Object.prototype.toString.call(new Format()),
'must be of type ' + Format.name);
};
o.format = format.name.toLowerCase();
o.format = Format.name.toLowerCase();

} else if (typeof format === 'string') {
// store declared type
Expand Down
22 changes: 22 additions & 0 deletions test/cases/schema-built-in-formats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"someNumber": {
"format": "Number",
"default": 1
},
"someObject": {
"format": "Object",
"default": {}
},
"someArray": {
"format": "Array",
"default": []
},
"someBoolean": {
"format": "Boolean",
"default": true
},
"someString": {
"format": "String",
"default": "foo"
}
}
249 changes: 130 additions & 119 deletions test/schema-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,171 +17,182 @@ describe('convict schema file', function() {
conf = convict(path.join(__dirname, 'schema.json'));
});

it('must be valid', function() {
(function() { conf.validate(); }).must.not.throw();
it('must parse a specification with built-in formats', function() {
conf = convict(path.join(__dirname, 'cases/schema-built-in-formats.json'));
});

it('must be valid again', function() {
(function() { conf2.validate(); }).must.not.throw();
});
describe('after being parsed', function() {

beforeEach(function() {
conf = convict(path.join(__dirname, 'schema.json'));
});

it('must be valid', function() {
(function() { conf.validate(); }).must.not.throw();
});

it('must export all its properties as JSON', function() {
var res = conf.getProperties();
res.must.eql({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
it('must be valid again', function() {
(function() { conf2.validate(); }).must.not.throw();
});

it('must export all its properties as JSON', function() {
var res = conf.getProperties();
res.must.eql({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
}
}
}
}
});
});
});

it('must export all its properties as JSON (deprecated method)', function() {
var res = conf.root();
res.must.eql({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
it('must export all its properties as JSON (deprecated method)', function() {
var res = conf.root();
res.must.eql({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
}
}
}
}
});
});
});

it('must export all its properties as a string', function() {
var res = conf.toString();
res.must.eql(JSON.stringify({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
it('must export all its properties as a string', function() {
var res = conf.toString();
res.must.eql(JSON.stringify({
'foo': {
'bar': 7,
'baz': {
'bing': 'foo',
'name with spaces': {
'name_with_underscores': true
}
}
}
}
}, null, 2));
});
}, null, 2));
});

it('must export the schema as JSON', function() {
var res = conf.getSchema();
res.must.eql({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
it('must export the schema as JSON', function() {
var res = conf.getSchema();
res.must.eql({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
}
}
}
}
}
}
}
}
});
});
});

it('must export the schema as a JSON string', function() {
var res = conf.getSchemaString();
res.must.eql(JSON.stringify({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
it('must export the schema as a JSON string', function() {
var res = conf.getSchemaString();
res.must.eql(JSON.stringify({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
}
}
}
}
}
}
}
}
}, null, 2));
});
}, null, 2));
});

it('must export the schema as a JSON string (deprecated method)', function() {
var res = conf.toSchemaString();
res.must.eql(JSON.stringify({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
it('must export the schema as a JSON string (deprecated method)', function() {
var res = conf.toSchemaString();
res.must.eql(JSON.stringify({
'properties': {
'foo': {
'properties': {
'bar': {},
'baz': {
'properties': {
'bing': {},
'name with spaces': {
'properties': {
'name_with_underscores': {}
}
}
}
}
}
}
}
}
}, null, 2));
});

describe('.has()', function() {
it('must not have undefined properties', function() {
var val = conf.has('foo.bar.madeup');
val.must.be(false);
}, null, 2));
});

it('must not have properties specified with a default of undefined', function() {
var val = conf2.has('foo.none');
val.must.be(false);
});
});
describe('.has()', function() {
it('must not have undefined properties', function() {
var val = conf.has('foo.bar.madeup');
val.must.be(false);
});

describe('.get()', function() {
it('must find a nested value', function() {
var val = conf.get('foo.bar');
val.must.be(7);
it('must not have properties specified with a default of undefined', function() {
var val = conf2.has('foo.none');
val.must.be(false);
});
});

it('must handle three levels of nesting', function() {
conf.get('foo.baz.bing').must.be('foo');
});
describe('.get()', function() {
it('must find a nested value', function() {
var val = conf.get('foo.bar');
val.must.be(7);
});

it('must handle names with spaces and underscores', function() {
conf.get('foo.baz.name with spaces.name_with_underscores').must.be(true);
});
it('must handle three levels of nesting', function() {
conf.get('foo.baz.bing').must.be('foo');
});

it('must handle names with spaces and underscores', function() {
conf.get('foo.baz.name with spaces.name_with_underscores').must.be(true);
});

it('must throw if conf doesn\'t exist', function() {
(function() { conf.get('foo.no'); }).must.throw();
it('must throw if conf doesn\'t exist', function() {
(function() { conf.get('foo.no'); }).must.throw();
});
});
});

describe('.default()', function() {
// Temporarily modify a property while testing default()
before(function() { conf.set('foo.bar', 8); });
after(function() { conf.set('foo.bar', 7); });
describe('.default()', function() {
// Temporarily modify a property while testing default()
beforeEach(function() { conf.set('foo.bar', 8); });
afterEach(function() { conf.set('foo.bar', 7); });

it('must report the default value of a property', function() {
conf.get('foo.bar').must.be(8); // Modified
conf.default('foo.bar').must.be(7);
});
it('must report the default value of a property', function() {
conf.get('foo.bar').must.be(8); // Modified
conf.default('foo.bar').must.be(7);
});

it('must throw if key doesn\'t exist', function() {
(function() { conf.default('foo.no'); }).must.throw();
it('must throw if key doesn\'t exist', function() {
(function() { conf.default('foo.no'); }).must.throw();
});
});
});
});
Expand Down

0 comments on commit 168755a

Please sign in to comment.