New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

promised-models2

Package Overview
Dependencies
Maintainers
3
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

promised-models2 - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

38

lib/attribute.js
var Inheritable = require('./inheritable');
var Inheritable = require('./inheritable'),
Vow = require('vow'),
fulfill = require('./fulfill');

@@ -52,9 +54,22 @@ /**

* @abstract
* @return {Boolean|Promise<{Boolean}>}
* @return {Promise}
*/
validate: function () {
return true;
var error = this.getValidationError();
if (!error) {
return fulfill(true);
} else {
return Vow.reject(error);
}
},
/**
* Helper method for attribute validation.
* Attribute is not valid if it will return non-falsy value
* @abstract
* @returns {*}
*/
getValidationError: function () { },
/**
* return serializable value of attribute

@@ -220,4 +235,21 @@ * @return {*}

}, {
ValidationError: (function () {
/**
* @param {String} message
*/
var ValidationError = function (message) {
this.name = 'AttributeValidationError';
this.message = message;
Error.call(this);
Error.captureStackTrace(this, this.constructor);
};
ValidationError.prototype = Object.create(Error.prototype);
ValidationError.prototype.constructor = ValidationError;
return ValidationError;
})()
});
module.exports = Attribute;

69

lib/model.js

@@ -9,2 +9,3 @@ /**

IdAttribute = require('./types/id'),
Attribute = require('./attribute'),
fulfill = require('./fulfill'),

@@ -212,3 +213,3 @@

* check if model is valid
* @return {Promise<, {Model.ValidationError}>}
* @return {Promise<Boolean, Model.ValidationError>}
*/

@@ -218,22 +219,40 @@ validate: function () {

return model.ready().then(function () {
return Vow.all(model._attributesAr.map(function (attribute) {
return Vow.allResolved(model._attributesAr.map(function (attribute) {
return attribute.validate();
}));
}).then(function (validateResults) {
var isValid = validateResults.every(function(result) {
return result === true;
}).then(function (validationPromises) {
var errors = [],
error;
validationPromises.forEach(function (validationPromise, index) {
var validationResult, error;
if (validationPromise.isFulfilled()) {
return;
}
validationResult = validationPromise.valueOf();
if (validationResult instanceof Error) {
error = validationResult;
} else {
error = new Attribute.ValidationError();
error.attribute = model._attributesAr[index];
if (typeof validationResult === 'string') {
error.message = validationResult;
} else if (typeof validationResult !== 'boolean') {
error.data = validationResult;
}
}
errors.push(error);
});
if (isValid) {
if (errors.length) {
error = new model.__self.ValidationError();
error.attributes = errors;
return Vow.reject(error);
} else {
return fulfill(true);
} else {
return Vow.reject(validateResults.reduce(function (err, validationResult, k) {
if (validationResult !== true) {
if (validationResult === false) {
validationResult = new model.__self.ValidationAttributeError(model._attributesAr[k]);
}
err.attributes.push(validationResult);
}
return err;
}, new model.__self.ValidationError()));
}

@@ -604,18 +623,2 @@ });

/**
* @param {Attribute} attribute
*/
ValidationAttributeError: (function () {
var ValidationAttributeError = function (attribute, type) {
this.name = 'ValidationAttributeError';
this.attribute = attribute;
this.type = type;
Error.call(this);
Error.captureStackTrace(this, this.constructor);
};
ValidationAttributeError.prototype = new Error();
ValidationAttributeError.prototype.constructor = ValidationAttributeError;
return ValidationAttributeError;
})(),
/**
* @class <{Error}>

@@ -631,3 +634,3 @@ * @prop {Array<{Attribute}>} attributes

};
ValidationError.prototype = new Error();
ValidationError.prototype = Object.create(Error.prototype);
ValidationError.prototype.constructor = ValidationError;

@@ -634,0 +637,0 @@ return ValidationError;

var Attribute = require('../attribute'),
Collection = require('../collection'),
Vow = require('vow'),
CollectionAttribute;

@@ -25,2 +26,29 @@

validate: function () {
return Vow.allResolved(this.value.map(function (model) {
return model.validate();
})).then(function (validationPromises) {
var modelsErrors = [],
error;
validationPromises.forEach(function (promise, index) {
var error;
if (promise.isRejected()) {
error = promise.valueOf();
error.index = index;
modelsErrors.push(error);
}
});
if (modelsErrors.length) {
error = new CollectionAttribute.ValidationError();
error.modelsErrors = modelsErrors;
return Vow.reject(error);
} else {
return true;
}
}.bind(this));
},
__constructor: function () {

@@ -126,4 +154,19 @@ this.__base.apply(this, arguments);

}, {
ValidationError: (function () {
/**
* @param {String} message
*/
var ValidationError = function (message) {
Attribute.ValidationError.call(this, message);
this.name = 'CollectionAttributeValidationError';
};
ValidationError.prototype = Object.create(Attribute.ValidationError.prototype);
ValidationError.constructor = ValidationError;
return ValidationError;
})()
});
module.exports = CollectionAttribute;
/**
* Nested model attribute
*/
var Attribute = require('../attribute'), ModelAttribute, ModelAttributeStatic;
var Attribute = require('../attribute'),
Vow = require('vow'),
ModelAttribute, ModelAttributeStatic;

@@ -47,3 +49,7 @@ /**

validate: function () {
return this.value.validate();
return this.value.validate().fail(function (modelValidationError) {
var error = new ModelAttribute.ValidationError();
error.modelError = modelValidationError;
return Vow.reject(error);
}.bind(this));
},

@@ -169,2 +175,17 @@

}, {
ValidationError: (function () {
/**
* @param {String} message
*/
var ValidationError = function (message) {
Attribute.ValidationError.call(this, message);
this.name = 'ModelAttributeValidationError';
};
ValidationError.prototype = Object.create(Attribute.ValidationError.prototype);
ValidationError.constructor = ValidationError;
return ValidationError;
})()
});

@@ -186,2 +207,3 @@

ModelAttributeStatic.inherit = ModelAttribute.inherit.bind(ModelAttribute);
ModelAttributeStatic.ValidationError = ModelAttribute.ValidationError;
module.exports = ModelAttributeStatic;
{
"description": "promise based, typed attributes, nested models and collections",
"name": "promised-models2",
"version": "0.1.0",
"version": "0.1.1",
"repository": "git@github.com:bem-node/promised-models.git",

@@ -6,0 +6,0 @@ "keywords": [

@@ -1,3 +0,3 @@

# Promised Models
Promised Models 2 [![Build Status](https://travis-ci.org/bem-node/promised-models.svg?branch=master)](https://travis-ci.org/bem-node/promised-models)
===
## Key features

@@ -12,3 +12,3 @@

$npm install --save promised-models
$npm install --save promised-models2

@@ -351,5 +351,3 @@ ## Usage

}).then(function () {
return true; //valid
}, function () {
return false; //invalid
Vow.reject('Value is Invalid!');
});

@@ -364,3 +362,6 @@ }

if (err instanceof Model.ValidationError) {
console.log('Invalid attributes:' + err.attributes.join());
console.log('Invalid attributes:');
err.attributes.forEach(function (attrErr) {
console.log('Attribute "' + attrErr.attribute.name + '" error:', attrError);
});
} else {

@@ -372,2 +373,30 @@ return err;

Fulfilled attribute validation promise means that attribute is valid, otherwise it's not. `Model.ValidationError#attributes` is array of attributes errors (`Attribute.ValidationError`).
**Note:** For `Model` and `Collection` attributes `validation` method is already defined. It validates nested entity and if it's not returns promise rejected with specific error contains nested errors.
If `validate` returns promise rejected with `String` this string will be used as message for `Attribute.ValidationError`. If with something else (besides `Boolean`) - rejected value will be available in `Attribute.ValidationError#data`.
If attribute can be validated synchronously, you can define `getValidationError` method. If it returns non-falsy value, validation promise will be rejected with returned value.
```js
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String.inherit({
getValidationError: function () {
if (this.get() !== 'validValue') {
return 'Value is Invalid!';
}
}
})
}
}),
model = new FashionModel();
model.validate().fail(function (err) {
console.log(err.attributes[0]);
});
```
#### ready `model.ready()`

@@ -374,0 +403,0 @@

@@ -5,2 +5,4 @@ /**

var Models = require('../../lib/model'),
Attribute = require('../../lib/attribute'),
Vow = require('vow'),
fulfill = require('../../lib/fulfill');

@@ -27,6 +29,20 @@

default: 'validValue',
validate: function () {
return this.value === 'validValue' ? true : new Models.ValidationAttributeError(this, 'error');
getValidationError: function () {
return this.value !== 'validValue';
}
}),
withSyncValidationMessage: Models.attributeTypes.String.inherit({
type: 'string',
default: 'validValue',
getValidationError: function () {
return this.value !== 'validValue' && 'invalid';
}
}),
withSyncValidationData: Models.attributeTypes.String.inherit({
type: 'string',
default: 'validValue',
getValidationError: function () {
return this.value !== 'validValue' && {data: 'data'};
}
}),
withAsyncValidation: Models.attributeTypes.String.inherit({

@@ -36,6 +52,7 @@ type: 'string',

validate: function () {
var attribute = this;
return fulfill().delay(0).then(function () {
return attribute.value === 'validValue' ? true : new Models.ValidationAttributeError(this, 'error');
});
if (this.value !== 'validValue') {
return Vow.reject('Invalid');
}
}.bind(this));
}

@@ -42,0 +59,0 @@ })

@@ -5,2 +5,3 @@ /**

var Model = require('../../lib/model'),
Attribute = require('../../lib/attribute'),
NestedModel = Model.inherit({

@@ -11,4 +12,4 @@ attributes: {

invalid: Model.attributeTypes.Number.inherit({
validate: function () {
return Boolean(this.value);
getValidationError: function () {
return !this.value;
}

@@ -24,2 +25,5 @@ })

}),
nestedCollection: Model.attributeTypes.Collection.inherit({
modelType: NestedModel
}),
nestedAsync: Model.attributeTypes.Model(require('./with-calculations')),

@@ -26,0 +30,0 @@ collection: Model.attributeTypes.ModelsList(require('./with-calculations')),

var expect = require('chai').expect,
Attribute = require('../lib/attribute'),
ModelAttribute = require('../lib/types/model'),
CollectionAttribute = require('../lib/types/collection'),
Vow = require('vow');

@@ -19,3 +22,3 @@

return model.validate().always(function (p) {
if (p.isFullfiled) {
if (p.isFulfilled()) {
return Vow.reject();

@@ -30,4 +33,5 @@ } else {

model.set('withAsyncValidation', 'notValid');
return model.validate();
}).always(function (p) {
if (p.isFullfiled) {
if (p.isFulfilled()) {
return Vow.reject();

@@ -46,3 +50,3 @@ } else {

var error;
if (p.isFullfiled) {
if (p.isFulfilled()) {
return Vow.reject();

@@ -56,7 +60,100 @@ } else {

expect(error).to.have.deep.property('attributes[1].name');
expect(error.attributes[0]).to.be.instanceOf(ModelClass.ValidationAttributeError);
expect(error.attributes[0].type).to.be.equal('error');
expect(error.attributes[0]).to.be.instanceOf(Attribute.ValidationError);
}
});
});
it('should set error message if getValidationError returns string', function () {
model.set({
withSyncValidationMessage: 'notValid'
});
return model.validate().always(function (p) {
var error;
if (p.isFulfilled()) {
return Vow.reject();
} else {
error = p.valueOf();
expect(error.attributes[0]).to.be.instanceOf(Attribute.ValidationError);
expect(error.attributes[0].message).to.be.equal('invalid');
}
});
});
it('should set data to error if getValidationError returns any besides string or boolean', function () {
model.set({
withSyncValidationData: 'notValid'
});
return model.validate().always(function (p) {
var error;
if (p.isFulfilled()) {
return Vow.reject();
} else {
error = p.valueOf();
expect(error.attributes[0]).to.be.instanceOf(Attribute.ValidationError);
expect(error.attributes[0].data).to.be.deep.equal({data: 'data'});
}
});
});
describe('with nested entities', function () {
var model,
ModelClass = require('./models/with-nested');
beforeEach(function () {
model = new ModelClass({
a: 'a1',
nested: {
invalid: true
}
});
});
it('should report invalid nested model attribute with specific error', function () {
model.get('nested').set('invalid', false);
return model.validate().always(function (p) {
var error;
if (p.isFulfilled()) {
return Vow.reject();
} else {
error = p.valueOf();
expect(error).to.be.instanceOf(Error);
expect(error).to.be.instanceOf(ModelClass.ValidationError);
expect(error.attributes[0]).to.be.instanceOf(ModelAttribute.ValidationError);
expect(error.attributes[0].modelError).to.be.instanceOf(ModelClass.ValidationError);
}
});
});
it('should report invalid nested collection attribute with specific error', function () {
model.get('nestedCollection').set([
{invalid: false},
{invalid: true},
{invalid: false}
]);
return model.validate().always(function (p) {
var error;
if (p.isFullfiled) {
return Vow.reject();
} else {
error = p.valueOf();
expect(error).to.be.instanceOf(Error);
expect(error).to.be.instanceOf(ModelClass.ValidationError);
expect(error.attributes[0]).to.be.instanceOf(CollectionAttribute.ValidationError);
expect(error.attributes[0].modelsErrors).to.be.an('array');
expect(error.attributes[0].modelsErrors.length).to.be.equal(2);
expect(error.attributes[0].modelsErrors[0]).to.be.instanceOf(ModelClass.ValidationError);
expect(error.attributes[0].modelsErrors[0].index).to.be.equal(0);
expect(error.attributes[0].modelsErrors[1]).to.be.instanceOf(ModelClass.ValidationError);
expect(error.attributes[0].modelsErrors[1].index).to.be.equal(2);
}
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc