Add new strict mode "validate"

When a model is configured with `strict: 'validate'`,
any dynamic properties not included in the schema trigger
a validation error.
This commit is contained in:
Miroslav Bajtoš 2015-04-09 16:57:38 +02:00
parent 2bdcce0d96
commit 99d4c6aa8d
3 changed files with 42 additions and 3 deletions

View File

@ -120,14 +120,26 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
enumerable: false,
configurable: true,
value: false
}
},
});
if (strict === 'validate') {
Object.defineProperty(this, '__unknownProperties', {
writable: true,
enumerable: false,
configrable: true,
value: []
});
}
} else {
this.__cachedRelations = {};
this.__data = {};
this.__dataSource = options.dataSource;
this.__strict = strict;
this.__persisted = false;
if (strict === 'validate') {
this.__unknownProperties = [];
}
}
if (options.persisted !== undefined) {
@ -207,6 +219,8 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
}
} else if (strict === 'throw') {
throw new Error('Unknown property: ' + p);
} else if (strict === 'validate') {
this.__unknownProperties.push(p);
}
}
}
@ -616,7 +630,7 @@ ModelBaseClass.observe = function(operation, listener) {
*/
ModelBaseClass.removeObserver = function(operation, listener) {
if (!this._observers[operation]) return;
var index = this._observers[operation].indexOf(listener);
if (index != -1) this._observers[operation].splice(index, 1);
};

View File

@ -411,8 +411,11 @@ Validatable.prototype.isValid = function (callback, data) {
var valid = true, inst = this, wait = 0, async = false;
var validations = this.constructor.validations;
var reportDiscardedProperties = this.__strict === 'validate' &&
this.__unknownProperties && this.__unknownProperties.length;
// exit with success when no errors
if (typeof validations !== 'object') {
if (typeof validations !== 'object' && !reportDiscardedProperties) {
cleanErrors(this);
if (callback) {
this.trigger('validate', function (validationsDone) {
@ -453,6 +456,16 @@ Validatable.prototype.isValid = function (callback, data) {
});
});
if (reportDiscardedProperties) {
for (var ix in inst.__unknownProperties) {
var key = inst.__unknownProperties[ix];
var code = 'unknown-property';
var msg = defaultMessages[code];
inst.errors.add(key, msg, code);
valid = false;
}
}
if (!async) {
validationsDone.call(inst, function () {
if (valid) cleanErrors(inst);
@ -568,6 +581,7 @@ function skipValidation(inst, conf, kind) {
var defaultMessages = {
presence: 'can\'t be blank',
absence: 'can\'t be set',
'unknown-property': 'is not defined in the model',
length: {
min: 'too short',
max: 'too long',

View File

@ -456,6 +456,17 @@ describe('DataSource define model', function () {
done(null, User);
});
describe('strict mode "validate"', function() {
it('should report validation error for unknown properties', function() {
var ds = new DataSource('memory');
var User = ds.define('User', { name: String }, { strict: 'validate' });
var user = new User({ name: 'Joe', age: 20 });
user.isValid().should.be.false;
var codes = user.errors && user.errors.codes || {};
codes.should.have.property('age').eql(['unknown-property']);
});
});
it('should be able to define open models', function (done) {
var ds = new DataSource('memory');