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

Commit 4c276236 by Daniel Durante

Merge branch 'master' into milestones/2.0.0

Conflicts:
	lib/dao-factory.js
2 parents aabcb7f9 84a6e923
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
- [BUG] Custom primary key (not keys, just singular) should no longer be a problem for models when using any of the data retrievals with just a number or through associations [#771](https://github.com/sequelize/sequelize/pull/771). thanks to sdephold & durango - [BUG] Custom primary key (not keys, just singular) should no longer be a problem for models when using any of the data retrievals with just a number or through associations [#771](https://github.com/sequelize/sequelize/pull/771). thanks to sdephold & durango
- [BUG] Default schemas should now be utilized when describing tables [#812](https://github.com/sequelize/sequelize/pull/812). thanks to durango - [BUG] Default schemas should now be utilized when describing tables [#812](https://github.com/sequelize/sequelize/pull/812). thanks to durango
- [BUG] Fixed eager loading for many-to-many associations. [#834](https://github.com/sequelize/sequelize/pull/834). thanks to lemon-tree - [BUG] Fixed eager loading for many-to-many associations. [#834](https://github.com/sequelize/sequelize/pull/834). thanks to lemon-tree
- [BUG] allowNull: true enums can now be null [#857](https://github.com/sequelize/sequelize/pull/857). thanks to durango
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango - [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango - [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude - [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
...@@ -60,6 +61,7 @@ ...@@ -60,6 +61,7 @@
- [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard - [FEATURE] Added `isDirty` to model instances. [#798](https://github.com/sequelize/sequelize/pull/798). Thanks to mstorgaard
- [FEATURE] Added possibility to use env variable for the database connection. [#784](https://github.com/sequelize/sequelize/pull/784). Thanks to sykopomp. - [FEATURE] Added possibility to use env variable for the database connection. [#784](https://github.com/sequelize/sequelize/pull/784). Thanks to sykopomp.
- [FEATURE] Blob support. janmeier - [FEATURE] Blob support. janmeier
- [FEATURE] We can now define our own custom timestamp columns [#856](https://github.com/sequelize/sequelize/pull/856). thanks to durango
- [FEATURE] Scopes. [#748](https://github.com/sequelize/sequelize/pull/748). durango - [FEATURE] Scopes. [#748](https://github.com/sequelize/sequelize/pull/748). durango
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier - [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier - [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
......
...@@ -9,6 +9,10 @@ module.exports = (function() { ...@@ -9,6 +9,10 @@ module.exports = (function() {
var DAOFactory = function(name, attributes, options) { var DAOFactory = function(name, attributes, options) {
this.options = Utils._.extend({ this.options = Utils._.extend({
timestamps: true, timestamps: true,
createdAt: 'createdAt',
updatedAt: 'updatedAt',
deletedAt: 'deletedAt',
touchedAt: 'touchedAt',
instanceMethods: {}, instanceMethods: {},
classMethods: {}, classMethods: {},
validate: {}, validate: {},
...@@ -618,7 +622,7 @@ module.exports = (function() { ...@@ -618,7 +622,7 @@ module.exports = (function() {
, hasValue = (typeof values[attrName] !== 'undefined') , hasValue = (typeof values[attrName] !== 'undefined')
, valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) , valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
if (isEnum && hasValue && valueOutOfScope) { if (isEnum && hasValue && valueOutOfScope && !(definition.allowNull === true && values[attrName] === null)) {
throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', '))
} }
} }
...@@ -626,7 +630,7 @@ module.exports = (function() { ...@@ -626,7 +630,7 @@ module.exports = (function() {
}) })
self.QueryInterface.bulkInsert(self.tableName, records).proxy(emitter) self.QueryInterface.bulkInsert(self.tableName, records).proxy(emitter)
} }
}).run() }).run()
} }
...@@ -641,7 +645,7 @@ module.exports = (function() { ...@@ -641,7 +645,7 @@ module.exports = (function() {
*/ */
DAOFactory.prototype.destroy = function(where, options) { DAOFactory.prototype.destroy = function(where, options) {
if (this.options.timestamps && this.options.paranoid) { if (this.options.timestamps && this.options.paranoid) {
var attr = this.options.underscored ? 'deleted_at' : 'deletedAt' var attr = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
var attrValueHash = {} var attrValueHash = {}
attrValueHash[attr] = Utils.now() attrValueHash[attr] = Utils.now()
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where) return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where)
...@@ -662,7 +666,7 @@ module.exports = (function() { ...@@ -662,7 +666,7 @@ module.exports = (function() {
options.validate = options.validate || true options.validate = options.validate || true
if(this.options.timestamps) { if(this.options.timestamps) {
var attr = this.options.underscored ? 'updated_at' : 'updatedAt' var attr = Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)
attrValueHash[attr] = Utils.now() attrValueHash[attr] = Utils.now()
} }
...@@ -729,11 +733,11 @@ module.exports = (function() { ...@@ -729,11 +733,11 @@ module.exports = (function() {
} }
if (this.options.timestamps) { if (this.options.timestamps) {
defaultAttributes[Utils._.underscoredIf('createdAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} defaultAttributes[Utils._.underscoredIf(this.options.createdAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
defaultAttributes[Utils._.underscoredIf('updatedAt', this.options.underscored)] = {type: DataTypes.DATE, allowNull: false} defaultAttributes[Utils._.underscoredIf(this.options.updatedAt, this.options.underscored)] = {type: DataTypes.DATE, allowNull: false}
if (this.options.paranoid) if (this.options.paranoid)
defaultAttributes[Utils._.underscoredIf('deletedAt', this.options.underscored)] = {type: DataTypes.DATE} defaultAttributes[Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)] = {type: DataTypes.DATE}
} }
Utils._.each(defaultAttributes, function(value, attr) { Utils._.each(defaultAttributes, function(value, attr) {
......
...@@ -28,7 +28,7 @@ module.exports = (function() { ...@@ -28,7 +28,7 @@ module.exports = (function() {
Object.defineProperty(DAO.prototype, 'isDeleted', { Object.defineProperty(DAO.prototype, 'isDeleted', {
get: function() { get: function() {
var result = this.__options.timestamps && this.__options.paranoid var result = this.__options.timestamps && this.__options.paranoid
result = result && this.dataValues[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null result = result && this.dataValues[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)] !== null
return result return result
} }
...@@ -99,8 +99,8 @@ module.exports = (function() { ...@@ -99,8 +99,8 @@ module.exports = (function() {
DAO.prototype.save = function(fields, options) { DAO.prototype.save = function(fields, options) {
var self = this var self = this
, values = fields ? {} : this.dataValues , values = fields ? {} : this.dataValues
, updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt' , updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt' , createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
if (fields) { if (fields) {
if (self.__options.timestamps) { if (self.__options.timestamps) {
...@@ -144,7 +144,7 @@ module.exports = (function() { ...@@ -144,7 +144,7 @@ module.exports = (function() {
valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
} }
if (isEnum && hasValue && valueOutOfScope) { if (isEnum && hasValue && valueOutOfScope && !(definition.allowNull === true && values[attrName] === null)) {
throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', '))
} }
...@@ -241,17 +241,17 @@ module.exports = (function() { ...@@ -241,17 +241,17 @@ module.exports = (function() {
readOnlyAttributes.push('id') readOnlyAttributes.push('id')
if (this.isNewRecord !== true) { if (this.isNewRecord !== true) {
readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'created_at' : 'createdAt') readOnlyAttributes.push(Utils._.underscoredIf(this.daoFactory.options.createdAt, this.daoFactory.options.underscored))
} }
// readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt') // readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt')
readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'deleted_at' : 'deletedAt') readOnlyAttributes.push(this.daoFactory.options.deletedAt, this.daoFactory.options.underscored)
var isDirty = this.isDirty var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) { Utils._.each(updates, function(value, attr) {
var updateAllowed = ( var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) && (readOnlyAttributes.indexOf(attr) === -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) && (readOnlyAttributes.indexOf(Utils._.underscored(attr)) === -1) &&
(self.attributes.indexOf(attr) > -1) (self.attributes.indexOf(attr) > -1)
) )
...@@ -267,7 +267,7 @@ module.exports = (function() { ...@@ -267,7 +267,7 @@ module.exports = (function() {
// since we're updating the record, we should be updating the updatedAt column.. // since we're updating the record, we should be updating the updatedAt column..
if (this.daoFactory.options.timestamps === true) { if (this.daoFactory.options.timestamps === true) {
isDirty = true isDirty = true
self[this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt'] = new Date() self[Utils._.underscoredIf(this.daoFactory.options.updatedAt, this.daoFactory.options.underscored)] = new Date()
} }
this.isDirty = isDirty this.isDirty = isDirty
...@@ -275,7 +275,7 @@ module.exports = (function() { ...@@ -275,7 +275,7 @@ module.exports = (function() {
DAO.prototype.destroy = function() { DAO.prototype.destroy = function() {
if (this.__options.timestamps && this.__options.paranoid) { if (this.__options.timestamps && this.__options.paranoid) {
var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt' var attr = Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)
this.dataValues[attr] = new Date() this.dataValues[attr] = new Date()
return this.save() return this.save()
} else { } else {
...@@ -368,9 +368,9 @@ module.exports = (function() { ...@@ -368,9 +368,9 @@ module.exports = (function() {
this.__defineSetter__(attribute, has.set || function(v) { this.__defineSetter__(attribute, has.set || function(v) {
if (Utils.hasChanged(this.dataValues[attribute], v)) { if (Utils.hasChanged(this.dataValues[attribute], v)) {
//Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated //Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated
var updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt' var updatedAtAttr = Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt' , createdAtAttr = Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)
, touchedAtAttr = this.__options.underscored ? 'touched_at' : 'touchedAt' , touchedAtAttr = Utils._.underscoredIf(this.__options.touchedAt, this.__options.underscored)
if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) { if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) {
this.isDirty = true this.isDirty = true
...@@ -416,11 +416,11 @@ module.exports = (function() { ...@@ -416,11 +416,11 @@ module.exports = (function() {
} }
if (this.__options.timestamps) { if (this.__options.timestamps) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now(this.sequelize.options.dialect) defaults[Utils._.underscoredIf(this.__options.createdAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect)
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now(this.sequelize.options.dialect) defaults[Utils._.underscoredIf(this.__options.updatedAt, this.__options.underscored)] = Utils.now(this.sequelize.options.dialect)
if (this.__options.paranoid) { if (this.__options.paranoid) {
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null defaults[Utils._.underscoredIf(this.__options.deletedAt, this.__options.underscored)] = null
} }
} }
} }
......
...@@ -107,6 +107,53 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -107,6 +107,53 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}).to.throw(Error, 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field') }).to.throw(Error, 'A model validator function must not have the same name as a field. Model: Foo, field/validation name: field')
done() done()
}) })
it('should allow me to override updatedAt, createdAt, and deletedAt fields', function(done) {
var UserTable = this.sequelize.define('UserCol', {
aNumber: Sequelize.INTEGER
}, {
timestamps: true,
updatedAt: 'updatedOn',
createdAt: 'dateCreated',
deletedAt: 'deletedAtThisTime',
paranoid: true
})
UserTable.sync({force: true}).success(function() {
UserTable.create({aNumber: 4}).success(function(user) {
expect(user.updatedOn).to.exist
expect(user.dateCreated).to.exist
user.destroy().success(function(user) {
expect(user.deletedAtThisTime).to.exist
done()
})
})
})
})
it('should allow me to override updatedAt, createdAt, and deletedAt fields with underscored being true', function(done) {
var UserTable = this.sequelize.define('UserCol', {
aNumber: Sequelize.INTEGER
}, {
timestamps: true,
updatedAt: 'updatedOn',
createdAt: 'dateCreated',
deletedAt: 'deletedAtThisTime',
paranoid: true,
underscored: true
})
UserTable.sync({force: true}).success(function() {
UserTable.create({aNumber: 4}).success(function(user) {
expect(user.updated_on).to.exist
expect(user.date_created).to.exist
user.destroy().success(function(user) {
expect(user.deleted_at_this_time).to.exist
done()
})
})
})
})
}) })
describe('build', function() { describe('build', function() {
...@@ -593,6 +640,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -593,6 +640,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
it('allows null values', function(done) {
var Enum = this.sequelize.define('Enum', {
state: {
type: Sequelize.ENUM,
values: ['happy', 'sad'],
allowNull: true
}
})
Enum.sync({ force: true }).success(function() {
Enum.create({state: null}).success(function(_enum) {
expect(_enum.state).to.be.null
done()
})
})
})
}) })
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!