不要怂,就是干,撸起袖子干!

Commit 02737942 by Jan Aagaard Meier

Merge master

2 parents ad9561db 5677d65f
Notice: All 1.7.x changes are present in 2.0.x aswell Notice: All 1.7.x changes are present in 2.0.x aswell
#### Backwards compatability changes
- find/findAll will now always return primary keys regardless of `attributes` settings. (Motivation was to fix various issues with eager loading)
# v2.0.0 (alpha1) #
- [FEATURE] async validations. [#580](https://github.com/sequelize/sequelize/pull/580). thanks to Interlock
# v1.7.0-rc6 # v1.7.0-rc6
- [BUG] Encode binary strings as bytea in postgres, and fix a case where using a binary as key in an association would produce an error [1364](https://github.com/sequelize/sequelize/pull/1364). Thanks to @SohumB - [BUG] Encode binary strings as bytea in postgres, and fix a case where using a binary as key in an association would produce an error [1364](https://github.com/sequelize/sequelize/pull/1364). Thanks to @SohumB
...@@ -19,9 +25,6 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -19,9 +25,6 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- [BUG] fixes regression bug with 1:M self associations - [BUG] fixes regression bug with 1:M self associations
- [FEATURE] significant performance improvements for 1:1 and single primary key includes for 500+ rows [#1333](https://github.com/sequelize/sequelize/pull/1333) - [FEATURE] significant performance improvements for 1:1 and single primary key includes for 500+ rows [#1333](https://github.com/sequelize/sequelize/pull/1333)
#### Backwards compatability changes
- find/findAll will now always return primary keys regardless of `attributes` settings. (Motivation was to fix various issues with eager loading)
# v1.7.0-rc3 # v1.7.0-rc3
- [FEATURE] dropAllTables now takes an option parameter with `skip` as an option [#1280](https://github.com/sequelize/sequelize/pull/1280) - [FEATURE] dropAllTables now takes an option parameter with `skip` as an option [#1280](https://github.com/sequelize/sequelize/pull/1280)
- [FEATURE] implements .spread for eventemitters [#1277](https://github.com/sequelize/sequelize/pull/1277) - [FEATURE] implements .spread for eventemitters [#1277](https://github.com/sequelize/sequelize/pull/1277)
...@@ -41,9 +44,6 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -41,9 +44,6 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- [FEATURE] .col()/.literal()/etc now works with findAll [#1249](https://github.com/sequelize/sequelize/issues/1249) - [FEATURE] .col()/.literal()/etc now works with findAll [#1249](https://github.com/sequelize/sequelize/issues/1249)
- [BUG] now currectly handles connection timeouts as errors [#1207](https://github.com/sequelize/sequelize/issues/1207) - [BUG] now currectly handles connection timeouts as errors [#1207](https://github.com/sequelize/sequelize/issues/1207)
# v2.0.0 (alpha1) #
- [FEATURE] async validations. [#580](https://github.com/sequelize/sequelize/pull/580). thanks to Interlock
# v1.7.0-rc1 # v1.7.0-rc1
- [FEATURE] instance.createAssociationInstance functionality added [#1213](https://github.com/sequelize/sequelize/pull/1213) - [FEATURE] instance.createAssociationInstance functionality added [#1213](https://github.com/sequelize/sequelize/pull/1213)
- [BUG] fixes a few bugs with transactions in regards to associations - [BUG] fixes a few bugs with transactions in regards to associations
......
...@@ -2,6 +2,81 @@ var Validator = require("validator") ...@@ -2,6 +2,81 @@ var Validator = require("validator")
, Utils = require("./utils") , Utils = require("./utils")
, sequelizeError = require("./errors") , sequelizeError = require("./errors")
// Backwards compat for people using old validation function
// We cannot use .extend, since it coerces the first arg to string
Validator.notNull = function (val) {
return [null, undefined].indexOf(val) === -1
}
// https://github.com/chriso/validator.js/blob/1.5.0/lib/validators.js
Validator.extend('notEmpty', function(str) {
return !str.match(/^[\s\t\r\n]*$/);
})
Validator.extend('len', function(str, min, max) {
return this.isLength(str, min, max)
})
Validator.extend('isUrl', function(str) {
return this.isURL(str)
})
Validator.extend('isIPv6', function(str) {
return this.isIP(str, 6)
})
Validator.extend('isIPv4', function(str) {
return this.isIP(str, 4)
})
Validator.extend('notIn', function(str, values) {
return !this.isIn(str, values)
})
Validator.extend('regex', function(str, pattern, modifiers) {
str += '';
if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
pattern = new RegExp(pattern, modifiers);
}
return str.match(pattern);
})
Validator.extend('notRegex', function(str, pattern, modifiers) {
return !this.regex(str, pattern, modifiers);
})
Validator.extend('isDecimal', function(str) {
return str !== '' && str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
})
Validator.extend('min', function(str, val) {
var number = parseFloat(str);
return isNaN(number) || number >= val;
})
Validator.extend('max', function(str, val) {
var number = parseFloat(str);
return isNaN(number) || number <= val;
})
Validator.extend('not', function(str, pattern, modifiers) {
return this.notRegex(str, pattern, modifiers);
})
Validator.extend('contains', function(str, elem) {
return str.indexOf(elem) >= 0 && !!elem;
})
Validator.extend('notContains', function(str, elem) {
return !this.contains(str, elem);
})
Validator.extend('is', function(str, pattern, modifiers) {
return this.regex(str, pattern, modifiers);
})
var DaoValidator = module.exports = function(modelInstance, options) { var DaoValidator = module.exports = function(modelInstance, options) {
options = options || {} options = options || {}
options.skip = options.skip || [] options.skip = options.skip || []
...@@ -9,6 +84,12 @@ var DaoValidator = module.exports = function(modelInstance, options) { ...@@ -9,6 +84,12 @@ var DaoValidator = module.exports = function(modelInstance, options) {
this.modelInstance = modelInstance this.modelInstance = modelInstance
this.chainer = new Utils.QueryChainer() this.chainer = new Utils.QueryChainer()
this.options = options this.options = options
/**
* Expose validator.js to allow users to extend
* @name Validator
*/
this.Validator = Validator
} }
/** @define {string} The error key for arguments as passed by custom validators */ /** @define {string} The error key for arguments as passed by custom validators */
...@@ -171,8 +252,6 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio ...@@ -171,8 +252,6 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio
} }
}.bind(this) }.bind(this)
} else { } else {
// it is a validator module function?
// extract extra arguments for the validator // extract extra arguments for the validator
validatorArgs = details.hasOwnProperty("args") ? details.args : details validatorArgs = details.hasOwnProperty("args") ? details.args : details
...@@ -183,13 +262,10 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio ...@@ -183,13 +262,10 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio
} }
// extract the error msg // extract the error msg
errorMessage = details.hasOwnProperty("msg") ? details.msg : undefined errorMessage = details.hasOwnProperty("msg") ? details.msg : 'Validation ' + validatorType + ' failed'
// check method exists
var validator = Validator.check(value, errorMessage)
// check if Validator knows that kind of validation test // check if Validator knows that kind of validation test
if (!Utils._.isFunction(validator[validatorType])) { if (!Utils._.isFunction(Validator[validatorType])) {
throw new Error("Invalid validator function: " + validatorType) throw new Error("Invalid validator function: " + validatorType)
} }
...@@ -197,11 +273,10 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio ...@@ -197,11 +273,10 @@ var prepareValidationOfAttribute = function(value, details, validatorType, optio
validatorFunction = function(next) { validatorFunction = function(next) {
var args = Array.prototype.slice.call(arguments, 1) var args = Array.prototype.slice.call(arguments, 1)
try { if (Validator[validatorType].apply(Validator, [value].concat(args))) {
validator[validatorType].apply(validator, args)
next() next()
} catch (err) { } else {
next(err.message) next(errorMessage)
} }
} }
} }
......
...@@ -2,6 +2,7 @@ var url = require("url") ...@@ -2,6 +2,7 @@ var url = require("url")
, Path = require("path") , Path = require("path")
, Utils = require("./utils") , Utils = require("./utils")
, DAOFactory = require("./dao-factory") , DAOFactory = require("./dao-factory")
, DAOValidator = require("./dao-validator")
, DataTypes = require('./data-types') , DataTypes = require('./data-types')
, DAOFactoryManager = require("./dao-factory-manager") , DAOFactoryManager = require("./dao-factory-manager")
, QueryInterface = require("./query-interface") , QueryInterface = require("./query-interface")
...@@ -137,6 +138,8 @@ module.exports = (function() { ...@@ -137,6 +138,8 @@ module.exports = (function() {
Sequelize.QueryTypes = QueryTypes Sequelize.QueryTypes = QueryTypes
Sequelize.DAOValidator = DAOValidator
for (var dataType in DataTypes) { for (var dataType in DataTypes) {
Sequelize[dataType] = DataTypes[dataType] Sequelize[dataType] = DataTypes[dataType]
} }
...@@ -375,7 +378,7 @@ module.exports = (function() { ...@@ -375,7 +378,7 @@ module.exports = (function() {
options = Utils._.extend({}, this.options.sync, options) options = Utils._.extend({}, this.options.sync, options)
} }
options.logging = options.logging === undefined ? false : Boolean(options.logging) options.logging = options.logging === undefined ? false : options.logging
var chainer = new Utils.QueryChainer() var chainer = new Utils.QueryChainer()
......
...@@ -40,36 +40,35 @@ ...@@ -40,36 +40,35 @@
"url": "https://github.com/sequelize/sequelize/issues" "url": "https://github.com/sequelize/sequelize/issues"
}, },
"dependencies": { "dependencies": {
"lodash": "~2.2.0", "commander": "~2.1.0",
"lodash": "~2.4.0",
"underscore.string": "~2.3.0", "underscore.string": "~2.3.0",
"lingo": "~0.0.5", "lingo": "~0.0.5",
"validator": "~1.5.0", "validator": "~3.2.0",
"moment": "~2.4.0", "moment": "~2.5.0",
"commander": "~2.0.0", "dottie": "0.0.9-0",
"dottie": "0.0.8-0", "toposort-class": "~0.3.0",
"toposort-class": "~0.2.0",
"generic-pool": "2.0.4", "generic-pool": "2.0.4",
"sql": "~0.31.0", "sql": "~0.35.0",
"circular-json": "~0.1.5", "circular-json": "~0.1.5",
"bluebird": "~0.11.5", "bluebird": "~1.0.0",
"node-uuid": "~1.4.1" "node-uuid": "~1.4.1"
}, },
"devDependencies": { "devDependencies": {
"sqlite3": "~2.1.12", "sqlite3": "~2.1.12",
"mysql": "2.0.1", "mysql": "2.0.1",
"pg": "~2.8.1", "pg": "~2.8.1",
"watchr": "~2.4.3", "watchr": "~2.4.11",
"yuidocjs": "~0.3.36", "chai": "~1.9.0",
"chai": "~1.8.0", "mocha": "~1.17.0",
"mocha": "~1.13.0",
"chai-datetime": "~1.1.1", "chai-datetime": "~1.1.1",
"sinon": "~1.7.3", "sinon": "~1.8.1",
"mariasql": "0.1.20", "mariasql": "0.1.20",
"chai-spies": "~0.5.1", "chai-spies": "~0.5.1",
"lcov-result-merger": "0.0.2", "lcov-result-merger": "0.0.2",
"istanbul": "~0.1.45", "istanbul": "~0.1.45",
"coveralls": "~2.5.0", "coveralls": "~2.7.1",
"async": "~0.2.9", "async": "~0.2.10",
"coffee-script": "~1.7.1" "coffee-script": "~1.7.1"
}, },
"keywords": [ "keywords": [
......
...@@ -409,7 +409,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -409,7 +409,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it("raises an error if saving an empty string into a column allowing null or URL", function(done) { it("raises an error if saving an empty string into a column allowing null or URL", function(done) {
var StringIsNullOrUrl = this.sequelize.define('StringIsNullOrUrl', { var StringIsNullOrUrl = this.sequelize.define('StringIsNullOrUrl', {
str: { type: Sequelize.STRING, allowNull: true, validate: { isUrl: true } } str: { type: Sequelize.STRING, allowNull: true, validate: { isURL: true } }
}) })
this.sequelize.options.omitNull = false this.sequelize.options.omitNull = false
...@@ -423,7 +423,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -423,7 +423,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
StringIsNullOrUrl.create({ str: '' }).error(function(err) { StringIsNullOrUrl.create({ str: '' }).error(function(err) {
expect(err).to.exist expect(err).to.exist
expect(err.str[0]).to.match(/Invalid URL/) expect(err.str[0]).to.match(/Validation isURL failed/)
done() done()
}) })
...@@ -969,8 +969,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -969,8 +969,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(errors[0].errors.name[0]).to.equal('name cannot be null') expect(errors[0].errors.name[0]).to.equal('name cannot be null')
expect(errors[1].record.name).to.equal('bar') expect(errors[1].record.name).to.equal('bar')
expect(errors[1].record.code).to.equal('1') expect(errors[1].record.code).to.equal('1')
expect(errors[1].errors.code[0]).to.match(/String is not in range/) expect(errors[1].errors.code[0]).to.equal('Validation len failed')
done() done()
}) })
}) })
......
...@@ -853,7 +853,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -853,7 +853,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
expect(err).to.be.instanceof(Object) expect(err).to.be.instanceof(Object)
expect(err.validateTest).to.be.instanceof(Array) expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.above(-1) expect(err.validateTest[0]).to.equal('Validation isInt failed')
done() done()
}) })
}) })
...@@ -879,7 +879,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -879,7 +879,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
expect(err.validateTest).to.exist expect(err.validateTest).to.exist
expect(err.validateTest).to.be.instanceof(Array) expect(err.validateTest).to.be.instanceof(Array)
expect(err.validateTest[0]).to.exist expect(err.validateTest[0]).to.exist
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.above(-1) expect(err.validateTest[0]).to.equal('Validation isInt failed')
done() done()
}) })
}) })
......
...@@ -176,17 +176,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -176,17 +176,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
} }
} }
for (var validator in checks) { var applyFailTest = function applyFailTest(validatorDetails, i, validator) {
if (checks.hasOwnProperty(validator)) {
validator = validator.replace(/\$$/, '')
var validatorDetails = checks[validator]
if (!validatorDetails.hasOwnProperty("raw")) {
validatorDetails.fail = [ validatorDetails.fail ]
validatorDetails.pass = [ validatorDetails.pass ]
}
function applyFailTest(validatorDetails, i, validator) {
var failingValue = validatorDetails.fail[i] var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) { it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
...@@ -208,7 +198,6 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -208,7 +198,6 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
}) })
var failingUser = UserFail.build({ name : failingValue }) var failingUser = UserFail.build({ name : failingValue })
, errors = undefined
failingUser.validate().done( function(err, _errors) { failingUser.validate().done( function(err, _errors) {
expect(_errors).to.not.be.null expect(_errors).to.not.be.null
...@@ -218,8 +207,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -218,8 +207,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
}) })
}) })
} }
, applyPassTest = function applyPassTest(validatorDetails, j, validator) {
function applyPassTest(validatorDetails, j, validator) {
var succeedingValue = validatorDetails.pass[j] var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) { it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
...@@ -249,17 +237,20 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -249,17 +237,20 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
}) })
} }
////////////////////////// for (var validator in checks) {
// test the error cases // if (checks.hasOwnProperty(validator)) {
////////////////////////// validator = validator.replace(/\$$/, '')
var validatorDetails = checks[validator]
if (!validatorDetails.hasOwnProperty("raw")) {
validatorDetails.fail = [ validatorDetails.fail ]
validatorDetails.pass = [ validatorDetails.pass ]
}
for (var i = 0; i < validatorDetails.fail.length; i++) { for (var i = 0; i < validatorDetails.fail.length; i++) {
applyFailTest(validatorDetails, i, validator); applyFailTest(validatorDetails, i, validator);
} }
////////////////////////////
// test the success cases //
////////////////////////////
for (var j = 0; j < validatorDetails.pass.length; j++) { for (var j = 0; j < validatorDetails.pass.length; j++) {
applyPassTest(validatorDetails, j, validator); applyPassTest(validatorDetails, j, validator);
} }
...@@ -311,7 +302,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -311,7 +302,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
Model.create({name: 'World'}).success(function(model) { Model.create({name: 'World'}).success(function(model) {
model.updateAttributes({name: ''}).error(function(err) { model.updateAttributes({name: ''}).error(function(err) {
expect(err).to.be.instanceOf(Error) expect(err).to.be.instanceOf(Error)
expect(err.name).to.deep.equal(['String is empty']); expect(err.name).to.deep.equal(['Validation notEmpty failed']);
done() done()
}) })
}) })
...@@ -333,7 +324,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -333,7 +324,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
Model.create({name: 'World'}).success(function(model) { Model.create({name: 'World'}).success(function(model) {
Model.update({name: ''}, {id: 1}).error(function(err) { Model.update({name: ''}, {id: 1}).error(function(err) {
expect(err).to.be.instanceOf(Error) expect(err).to.be.instanceOf(Error)
expect(err.name).to.deep.equal(['String is empty']); expect(err.name).to.deep.equal(['Validation notEmpty failed']);
done() done()
}) })
}) })
...@@ -412,7 +403,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -412,7 +403,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
User.sync({ force: true }).success(function() { User.sync({ force: true }).success(function() {
User.create({id: 'helloworld'}).error(function(err) { User.create({id: 'helloworld'}).error(function(err) {
expect(err).to.be.instanceOf(Error) expect(err).to.be.instanceOf(Error)
expect(err.id).to.deep.equal(['Invalid integer']); expect(err.id).to.deep.equal(['Validation isInt failed']);
done() done()
}) })
}) })
...@@ -663,7 +654,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() { ...@@ -663,7 +654,7 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
failingBar.validate().success(function(errors) { failingBar.validate().success(function(errors) {
expect(errors).not.to.be.null expect(errors).not.to.be.null
expect(errors.field).to.have.length(1) expect(errors.field).to.have.length(1)
expect(errors.field[0]).to.equal("Unexpected value or invalid argument") expect(errors.field[0]).to.equal("Validation isIn failed")
}) })
}) })
......
...@@ -277,7 +277,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () { ...@@ -277,7 +277,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(err).to.be.an("object") expect(err).to.be.an("object")
expect(err.validateTest).to.be.an("array") expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.greaterThan(-1) expect(err.validateTest[0]).to.equal('Validation isInt failed')
done() done()
}); });
}) })
...@@ -304,7 +304,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () { ...@@ -304,7 +304,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
expect(err.validateTest).to.be.ok expect(err.validateTest).to.be.ok
expect(err.validateTest).to.be.an("array") expect(err.validateTest).to.be.an("array")
expect(err.validateTest[0]).to.be.ok expect(err.validateTest[0]).to.be.ok
expect(err.validateTest[0].indexOf('Invalid integer')).to.be.greaterThan(-1) expect(err.validateTest[0]).to.equal('Validation isInt failed')
done() done()
}) })
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!