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

Commit 4481b805 by Sascha Depold

Merge branch 'master' into milestones/2.0.0

Conflicts:
	changelog.md
	lib/dao-validator.js
2 parents 238d03e8 65518552
...@@ -185,11 +185,12 @@ if (program.migrate) { ...@@ -185,11 +185,12 @@ if (program.migrate) {
if (key === "use_env_variable") { if (key === "use_env_variable") {
if (process.env[value]) { if (process.env[value]) {
var db_info = process.env[value].match( var db_info = process.env[value].match(/([^:]+):\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
/([^:]+):\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
config.database = db_info[6]; config.database = db_info[6];
config.username = db_info[2]; config.username = db_info[2];
config.password = db_info[3]; config.password = db_info[3];
options = _.extend(options, { options = _.extend(options, {
host: db_info[4], host: db_info[4],
port: db_info[5], port: db_info[5],
...@@ -207,15 +208,17 @@ if (program.migrate) { ...@@ -207,15 +208,17 @@ if (program.migrate) {
, migrator = sequelize.getMigrator(migratorOptions) , migrator = sequelize.getMigrator(migratorOptions)
if (program.undo) { if (program.undo) {
sequelize.migrator.findOrCreateSequelizeMetaDAO().success(function(Meta) { migrator.findOrCreateSequelizeMetaDAO().success(function(Meta) {
Meta.find({ order: 'id DESC' }).success(function(meta) { Meta.find({ order: 'id DESC' }).success(function(meta) {
if (meta) { if (meta) {
migrator = sequelize.getMigrator(_.extend(migratorOptions, meta), true) migrator = sequelize.getMigrator(_.extend(migratorOptions, meta.values), true)
} migrator.migrate({ method: 'down' }).success(function() {
process.exit(0)
migrator.migrate({ method: 'down' }).success(function() { })
} else {
console.log("There are no pending migrations.")
process.exit(0) process.exit(0)
}) }
}) })
}) })
} else { } else {
......
...@@ -3,6 +3,15 @@ Notice: All 1.7.x changed are present in 2.0.x aswell ...@@ -3,6 +3,15 @@ Notice: All 1.7.x changed are present in 2.0.x aswell
# v2.0.0 (alpha1) # # v2.0.0 (alpha1) #
- [FEATURE] async validations. [#580](https://github.com/sequelize/sequelize/pull/580). thanks to Interlock - [FEATURE] async validations. [#580](https://github.com/sequelize/sequelize/pull/580). thanks to Interlock
# v1.7.0-rc1
- instance.createAssociationInstance functionality added [#1213](https://github.com/sequelize/sequelize/pull/1213)
- fixes a few bugs with transactions in regards to associations
- add error handling for transaction creation
#### Backwards compatability changes
- Hooks are no longer passing value hashes. Instead, they are now passing instances of the model.
- Hook callbacks no longer take two arguments (previously: `err, newValues`). They only take the error argument since values can be changed directly on the model instance.
# v1.7.0-beta8 # v1.7.0-beta8
- max()/min() now supports dates [#1200](https://github.com/sequelize/sequelize/pull/1200) - max()/min() now supports dates [#1200](https://github.com/sequelize/sequelize/pull/1200)
- findAndCountAll now supports the include option - findAndCountAll now supports the include option
...@@ -16,7 +25,7 @@ Notice: All 1.7.x changed are present in 2.0.x aswell ...@@ -16,7 +25,7 @@ Notice: All 1.7.x changed are present in 2.0.x aswell
- Eager loading / prefetching now supports inner joins and extending the ON statement [#1199](https://github.com/sequelize/sequelize/pull/1199) - Eager loading / prefetching now supports inner joins and extending the ON statement [#1199](https://github.com/sequelize/sequelize/pull/1199)
- Eager loading / prefetching now returns the attributes of through models aswell [#1198](https://github.com/sequelize/sequelize/pull/1198) - Eager loading / prefetching now returns the attributes of through models aswell [#1198](https://github.com/sequelize/sequelize/pull/1198)
- New set/get/changed/previous feature [#1182](https://github.com/sequelize/sequelize/pull/1182) - New set/get/changed/previous feature [#1182](https://github.com/sequelize/sequelize/pull/1182)
- Various bug fixes - Various bug fixes
#### Backwards compatibility changes #### Backwards compatibility changes
None None
......
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types') , DataTypes = require('./../data-types')
, Helpers = require('./helpers') , Helpers = require('./helpers')
, Transaction = require('../transaction')
module.exports = (function() { module.exports = (function() {
var BelongsTo = function(source, target, options) { var BelongsTo = function(source, target, options) {
...@@ -28,7 +29,8 @@ module.exports = (function() { ...@@ -28,7 +29,8 @@ module.exports = (function() {
this.accessors = { this.accessors = {
get: Utils._.camelize('get_' + this.as), get: Utils._.camelize('get_' + this.as),
set: Utils._.camelize('set_' + this.as) set: Utils._.camelize('set_' + this.as),
create: Utils._.camelize('create_' + this.as)
} }
} }
...@@ -58,7 +60,6 @@ module.exports = (function() { ...@@ -58,7 +60,6 @@ module.exports = (function() {
obj[this.accessors.get] = function(params) { obj[this.accessors.get] = function(params) {
var id = this[self.identifier] var id = this[self.identifier]
, where = {} , where = {}
, options = Utils._.pick(params || {}, 'transaction')
where[primaryKey] = id where[primaryKey] = id
...@@ -72,7 +73,7 @@ module.exports = (function() { ...@@ -72,7 +73,7 @@ module.exports = (function() {
params = id params = id
} }
return self.target.find(params, options) return self.target.find(params)
} }
return this return this
...@@ -99,5 +100,30 @@ module.exports = (function() { ...@@ -99,5 +100,30 @@ module.exports = (function() {
return this return this
} }
BelongsTo.prototype.injectCreator = function(obj) {
var self = this
obj[this.accessors.create] = function(values, fieldsOrOptions) {
var instance = this
, options = {}
if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
options.transaction = fieldsOrOptions.transaction
}
return new Utils.CustomEventEmitter(function(emitter) {
self.target
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
instance[self.accessors.set](newAssociatedObject, options)
.proxy(emitter)
})
}).run()
}
return this
}
return BelongsTo return BelongsTo
})() })()
...@@ -21,7 +21,7 @@ module.exports = (function() { ...@@ -21,7 +21,7 @@ module.exports = (function() {
, options = _options || {} , options = _options || {}
, queryOptions = {} , queryOptions = {}
, targetAssociation = self.association.targetAssociation , targetAssociation = self.association.targetAssociation
//fully qualify //fully qualify
var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys) var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys)
, instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id' , instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id'
...@@ -187,11 +187,17 @@ module.exports = (function() { ...@@ -187,11 +187,17 @@ module.exports = (function() {
HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) { HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) {
var attributes = {} var attributes = {}
, targetAssociation = this.association.targetAssociation , targetAssociation = this.association.targetAssociation
, foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier; , foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier
, options = {}
var sourceKeys = Object.keys(this.association.source.primaryKeys); var sourceKeys = Object.keys(this.association.source.primaryKeys);
var targetKeys = Object.keys(this.association.target.primaryKeys); var targetKeys = Object.keys(this.association.target.primaryKeys);
if ((additionalAttributes || {}).transaction instanceof Transaction) {
options.transaction = additionalAttributes.transaction
delete additionalAttributes.transaction
}
attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id) attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id)
attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id) attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id)
...@@ -207,7 +213,7 @@ module.exports = (function() { ...@@ -207,7 +213,7 @@ module.exports = (function() {
} else { } else {
attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.name], additionalAttributes) attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.name], additionalAttributes)
this.association.through.create(attributes) this.association.through.create(attributes, options)
.success(function() { emitterProxy.emit('success', newAssociation) }) .success(function() { emitterProxy.emit('success', newAssociation) })
.error(function(err) { emitterProxy.emit('error', err) }) .error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) }) .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
......
...@@ -108,13 +108,19 @@ module.exports = (function() { ...@@ -108,13 +108,19 @@ module.exports = (function() {
.on('sql', function(sql) { emitter.emit('sql', sql) }) .on('sql', function(sql) { emitter.emit('sql', sql) })
} }
HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) { HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes) {
var primaryKeys = Object.keys(this.instance.daoFactory.primaryKeys) var primaryKeys = Object.keys(this.instance.daoFactory.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id' , primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, options = {}
if ((additionalAttributes || {}).transaction instanceof Transaction) {
options.transaction = additionalAttributes.transaction
delete additionalAttributes.transaction
}
newAssociation[this.__factory.identifier] = this.instance[primaryKey] newAssociation[this.__factory.identifier] = this.instance[primaryKey]
newAssociation.save() newAssociation.save(options)
.success(function() { emitterProxy.emit('success', newAssociation) }) .success(function() { emitterProxy.emit('success', newAssociation) })
.error(function(err) { emitterProxy.emit('error', err) }) .error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) }) .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
......
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types') , DataTypes = require('./../data-types')
, Helpers = require('./helpers') , Helpers = require('./helpers')
, _ = require('lodash') , _ = require('lodash')
, Transaction = require('../transaction')
var HasManySingleLinked = require("./has-many-single-linked") var HasManySingleLinked = require("./has-many-single-linked")
, HasManyMultiLinked = require("./has-many-double-linked") , HasManyDoubleLinked = require("./has-many-double-linked")
module.exports = (function() { module.exports = (function() {
var HasMany = function(source, target, options) { var HasMany = function(source, target, options) {
...@@ -50,7 +51,7 @@ module.exports = (function() { ...@@ -50,7 +51,7 @@ module.exports = (function() {
this.associationAccessor = this.options.as this.associationAccessor = this.options.as
if (!this.associationAccessor && (typeof this.through === "string" || Object(this.through) === this.through)) { if (!this.associationAccessor && (typeof this.through === "string" || Object(this.through) === this.through)) {
this.associationAccessor = this.through.tableName || this.through this.associationAccessor = this.through.tableName || this.through
} }
else if (!this.associationAccessor) { else if (!this.associationAccessor) {
this.associationAccessor = this.combinedTableName this.associationAccessor = this.combinedTableName
} }
...@@ -82,7 +83,7 @@ module.exports = (function() { ...@@ -82,7 +83,7 @@ module.exports = (function() {
if (paired) { if (paired) {
self.doubleLinked = true self.doubleLinked = true
association.doubleLinked = true association.doubleLinked = true
self.targetAssociation = association self.targetAssociation = association
association.targetAssociation = self association.targetAssociation = self
} }
...@@ -115,6 +116,7 @@ module.exports = (function() { ...@@ -115,6 +116,7 @@ module.exports = (function() {
get: Utils._.camelize('get_' + as), get: Utils._.camelize('get_' + as),
set: Utils._.camelize('set_' + as), set: Utils._.camelize('set_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as, this.target.options.language)), add: Utils._.camelize(Utils.singularize('add_' + as, this.target.options.language)),
create: Utils._.camelize(Utils.singularize('create_' + as, this.target.options.language)),
remove: Utils._.camelize(Utils.singularize('remove_' + as, this.target.options.language)), remove: Utils._.camelize(Utils.singularize('remove_' + as, this.target.options.language)),
hasSingle: Utils._.camelize(Utils.singularize('has_' + as, this.target.options.language)), hasSingle: Utils._.camelize(Utils.singularize('has_' + as, this.target.options.language)),
hasAll: Utils._.camelize('has_' + as) hasAll: Utils._.camelize('has_' + as)
...@@ -188,7 +190,7 @@ module.exports = (function() { ...@@ -188,7 +190,7 @@ module.exports = (function() {
var self = this var self = this
obj[this.accessors.get] = function(options) { obj[this.accessors.get] = function(options) {
var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
return new Class(self, this).injectGetter(options) return new Class(self, this).injectGetter(options)
} }
...@@ -246,7 +248,7 @@ module.exports = (function() { ...@@ -246,7 +248,7 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]() instance[self.accessors.get]()
.success(function(oldAssociatedObjects) { .success(function(oldAssociatedObjects) {
var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes) new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
}) })
.proxy(emitter, {events: ['error', 'sql']}) .proxy(emitter, {events: ['error', 'sql']})
...@@ -266,7 +268,7 @@ module.exports = (function() { ...@@ -266,7 +268,7 @@ module.exports = (function() {
.proxy(emitter, {events: ['error', 'sql']}) .proxy(emitter, {events: ['error', 'sql']})
.success(function(currentAssociatedObjects) { .success(function(currentAssociatedObjects) {
if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) { if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) {
var Class = Object(self.through) === self.through ? HasManyMultiLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length) new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length)
} else { } else {
emitter.emit('success', newAssociatedObject); emitter.emit('success', newAssociatedObject);
...@@ -323,6 +325,33 @@ module.exports = (function() { ...@@ -323,6 +325,33 @@ module.exports = (function() {
return this return this
} }
HasMany.prototype.injectCreator = function(obj) {
var self = this
obj[this.accessors.create] = function(values, fieldsOrOptions) {
var instance = this
, options = {}
if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
options.transaction = fieldsOrOptions.transaction
delete fieldsOrOptions.transaction
}
return new Utils.CustomEventEmitter(function(emitter) {
// Create the related model instance
self.target
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
instance[self.accessors.add](newAssociatedObject, options)
.proxy(emitter)
})
}).run()
}
return this
};
/** /**
* The method checks if it is ok to delete the previously defined foreign key. * The method checks if it is ok to delete the previously defined foreign key.
* This is done because we need to keep the foreign key if another association * This is done because we need to keep the foreign key if another association
......
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types') , DataTypes = require('./../data-types')
, Helpers = require("./helpers") , Helpers = require("./helpers")
, Transaction = require("../transaction")
module.exports = (function() { module.exports = (function() {
var HasOne = function(srcDAO, targetDAO, options) { var HasOne = function(srcDAO, targetDAO, options) {
...@@ -27,7 +28,8 @@ module.exports = (function() { ...@@ -27,7 +28,8 @@ module.exports = (function() {
this.accessors = { this.accessors = {
get: Utils._.camelize('get_' + this.options.as), get: Utils._.camelize('get_' + this.options.as),
set: Utils._.camelize('set_' + this.options.as) set: Utils._.camelize('set_' + this.options.as),
create: Utils._.camelize('create_' + this.options.as)
} }
} }
...@@ -73,7 +75,12 @@ module.exports = (function() { ...@@ -73,7 +75,12 @@ module.exports = (function() {
params.where = smart params.where = smart
} }
return self.target.find(params) var options = {}
if (params.transaction) {
options.transaction = params.transaction;
delete params.transaction;
}
return self.target.find(params, options)
} }
return this return this
...@@ -128,5 +135,31 @@ module.exports = (function() { ...@@ -128,5 +135,31 @@ module.exports = (function() {
return this return this
} }
HasOne.prototype.injectCreator = function(obj) {
var self = this
obj[this.accessors.create] = function(values, fieldsOrOptions) {
var instance = this
, options = {}
if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
options.transaction = fieldsOrOptions.transaction
}
return new Utils.CustomEventEmitter(function(emitter) {
self.target
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
console.log(self.accessors.set, !!newAssociatedObject, !!options.transaction);
instance[self.accessors.set](newAssociatedObject, options)
.proxy(emitter)
})
}).run()
}
return this
};
return HasOne return HasOne
})() })()
...@@ -18,6 +18,7 @@ Mixin.hasOne = function(associatedDAOFactory, options) { ...@@ -18,6 +18,7 @@ Mixin.hasOne = function(associatedDAOFactory, options) {
association.injectGetter(this.DAO.prototype); association.injectGetter(this.DAO.prototype);
association.injectSetter(this.DAO.prototype); association.injectSetter(this.DAO.prototype);
association.injectCreator(this.DAO.prototype);
return this return this
} }
...@@ -34,6 +35,7 @@ Mixin.belongsTo = function(associatedDAOFactory, options) { ...@@ -34,6 +35,7 @@ Mixin.belongsTo = function(associatedDAOFactory, options) {
association.injectGetter(this.DAO.prototype) association.injectGetter(this.DAO.prototype)
association.injectSetter(this.DAO.prototype) association.injectSetter(this.DAO.prototype)
association.injectCreator(this.DAO.prototype)
return this return this
} }
...@@ -50,6 +52,7 @@ Mixin.hasMany = function(associatedDAOFactory, options) { ...@@ -50,6 +52,7 @@ Mixin.hasMany = function(associatedDAOFactory, options) {
association.injectGetter(this.DAO.prototype) association.injectGetter(this.DAO.prototype)
association.injectSetter(this.DAO.prototype) association.injectSetter(this.DAO.prototype)
association.injectCreator(this.DAO.prototype)
return this return this
} }
......
...@@ -53,6 +53,7 @@ module.exports = (function() { ...@@ -53,6 +53,7 @@ module.exports = (function() {
} }
attributes = replaceReferencesWithTableNames(attributes) attributes = replaceReferencesWithTableNames(attributes)
this.options.hooks = this.replaceHookAliases(this.options.hooks)
this.rawAttributes = attributes this.rawAttributes = attributes
this.daoFactoryManager = null // defined in init function this.daoFactoryManager = null // defined in init function
...@@ -536,7 +537,7 @@ module.exports = (function() { ...@@ -536,7 +537,7 @@ module.exports = (function() {
plain: true, plain: true,
type: 'SELECT', type: 'SELECT',
hasJoin: hasJoin hasJoin: hasJoin
}, queryOptions)) }, queryOptions, { transaction: (options || {}).transaction }))
} }
DAOFactory.prototype.aggregate = function(field, aggregateFunction, options) { DAOFactory.prototype.aggregate = function(field, aggregateFunction, options) {
...@@ -1200,17 +1201,27 @@ module.exports = (function() { ...@@ -1200,17 +1201,27 @@ module.exports = (function() {
options.where = options.where || {} options.where = options.where || {}
var deletedAtCol = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored) var deletedAtCol = Utils._.underscoredIf(this.options.deletedAt, this.options.underscored)
, quoteIdentifiedDeletedAtCol = this.QueryInterface.quoteIdentifier(deletedAtCol)
// Don't overwrite our explicit deletedAt search value if we provide one // Don't overwrite our explicit deletedAt search value if we provide one
if (!!options.where[deletedAtCol]) { if (!!options.where[deletedAtCol]) {
return options return options
} }
if(this.tableName) {
quoteIdentifiedDeletedAtCol = this.QueryInterface.quoteIdentifier(this.tableName) + '.' + quoteIdentifiedDeletedAtCol
}
if (typeof options.where === "string") { if (typeof options.where === "string") {
options.where += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL ' options.where += ' AND ' + quoteIdentifiedDeletedAtCol + ' IS NULL '
} }
else if (Array.isArray(options.where)) { else if (Array.isArray(options.where)) {
options.where[0] += ' AND ' + this.QueryInterface.quoteIdentifier(deletedAtCol) + ' IS NULL '
// Don't overwrite our explicit deletedAt search value if we provide one
if(options.where[0].indexOf(deletedAtCol) !== -1) {
return options
}
options.where[0] += ' AND ' + quoteIdentifiedDeletedAtCol + ' IS NULL '
} else { } else {
options.where[deletedAtCol] = null options.where[deletedAtCol] = null
} }
......
...@@ -40,25 +40,22 @@ DaoValidator.prototype.hookValidate = function() { ...@@ -40,25 +40,22 @@ DaoValidator.prototype.hookValidate = function() {
, errors = {} , errors = {}
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
self.model.daoFactory.runHooks('beforeValidate', self.model.dataValues, function(err, newValues) { self.model.daoFactory.runHooks('beforeValidate', self.model, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
self.model.dataValues = newValues || self.model.dataValues
self.validate().success(function (error) { self.validate().success(function (error) {
if (!!error) { if (!!error) {
return emitter.emit('error', error) return emitter.emit('error', error)
} }
self.model.daoFactory.runHooks('afterValidate', self.model.dataValues, function(err, newValues) { self.model.daoFactory.runHooks('afterValidate', self.model, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
self.model.dataValues = newValues || self.model.dataValues emitter.emit('success', self.model)
emitter.emit('success', self.model)
}) })
}) })
}) })
......
...@@ -12,7 +12,7 @@ module.exports = (function() { ...@@ -12,7 +12,7 @@ module.exports = (function() {
this.__options = this.__factory.options this.__options = this.__factory.options
this.options = options this.options = options
this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys this.hasPrimaryKeys = this.__factory.options.hasPrimaryKeys
// What is selected values even used for? // What is selected values even used for?
this.selectedValues = options.include ? _.omit(values, options.includeNames) : values this.selectedValues = options.include ? _.omit(values, options.includeNames) : values
this.__eagerlyLoadedAssociations = [] this.__eagerlyLoadedAssociations = []
this.isNewRecord = options.isNewRecord this.isNewRecord = options.isNewRecord
...@@ -157,7 +157,7 @@ module.exports = (function() { ...@@ -157,7 +157,7 @@ module.exports = (function() {
// If not raw, and there's a customer setter // If not raw, and there's a customer setter
if (!options.raw && this._customSetters[key]) { if (!options.raw && this._customSetters[key]) {
this._customSetters[key].call(this, value, key) this._customSetters[key].call(this, value, key)
} else { } else {
// Check if we have included models, and if this key matches the include model names/aliases // Check if we have included models, and if this key matches the include model names/aliases
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) { if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1) {
...@@ -375,35 +375,40 @@ module.exports = (function() { ...@@ -375,35 +375,40 @@ module.exports = (function() {
hook = 'Update' hook = 'Update'
} }
self.__factory.runHooks('before' + hook, values, function(err, newValues) { // Add the values to the DAO
self.dataValues = _.extend(self.dataValues, values)
// Run the beforeCreate / beforeUpdate hook
self.__factory.runHooks('before' + hook, self, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
args[2] = values = newValues || values // dataValues might have changed inside the hook, rebuild
// Newest values need to be on the dao since the dao is returned from the query interface // the values hash
self.dataValues = _.extend(self.dataValues, values) values = {}
options.fields.forEach(function(field) {
if (self.dataValues[field] !== undefined) {
values[field] = self.dataValues[field]
}
})
args[2] = values
self.QueryInterface[query].apply(self.QueryInterface, args) self.QueryInterface[query].apply(self.QueryInterface, args)
.proxy(emitter, {events: ['sql', 'error']}) .proxy(emitter, {events: ['sql', 'error']})
.success(function(result) { .success(function(result) {
// Transfer database generated values (defaults, autoincrement, etc) // Transfer database generated values (defaults, autoincrement, etc)
values = _.extend(values, result.values) values = _.extend(values, result.dataValues)
// Ensure new values are on DAO, and reset previousDataValues // Ensure new values are on DAO, and reset previousDataValues
result.dataValues = _.extend(result.dataValues, values) result.dataValues = _.extend(result.dataValues, values)
result._previousDataValues = _.clone(result.dataValues) result._previousDataValues = _.clone(result.dataValues)
self.__factory.runHooks('after' + hook, values, function(err, newValues) { self.__factory.runHooks('after' + hook, result, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
if (newValues) {
// Repeat value assignment incase afterHook changed anything
result.dataValues = _.extend(result.dataValues, newValues)
result._previousDataValues = _.clone(result.dataValues)
}
emitter.emit('success', result) emitter.emit('success', result)
}) })
}) })
...@@ -483,7 +488,7 @@ module.exports = (function() { ...@@ -483,7 +488,7 @@ module.exports = (function() {
, query = null , query = null
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
self.daoFactory.runHooks(self.daoFactory.options.hooks.beforeDestroy, self.dataValues, function(err) { self.daoFactory.runHooks(self.daoFactory.options.hooks.beforeDestroy, self, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
...@@ -504,7 +509,7 @@ module.exports = (function() { ...@@ -504,7 +509,7 @@ module.exports = (function() {
emitter.emit('error', err) emitter.emit('error', err)
}) })
.success(function(results) { .success(function(results) {
self.daoFactory.runHooks(self.daoFactory.options.hooks.afterDestroy, self.dataValues, function(err) { self.daoFactory.runHooks(self.daoFactory.options.hooks.afterDestroy, self, function(err) {
if (!!err) { if (!!err) {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
......
var Utils = require("./utils")
var Hooks = module.exports = function(){} var Hooks = module.exports = function(){}
var hookAliases = {
beforeDelete: "beforeDestroy",
afterDelete: "afterDestroy"
}
Hooks.replaceHookAliases = function(hooks) {
var realHookName
Utils._.each(hooks, function(hooksArray, name) {
// Does an alias for this hook name exist?
if(realHookName = hookAliases[name]) {
// Add the hooks to the actual hook
hooks[realHookName] = (hooks[realHookName] || []).concat(hooksArray)
// Delete the alias
delete hooks[name]
}
})
return hooks
}
Hooks.runHooks = function() { Hooks.runHooks = function() {
var self = this var self = this
...@@ -43,16 +65,9 @@ Hooks.runHooks = function() { ...@@ -43,16 +65,9 @@ Hooks.runHooks = function() {
run(hooks[tick]) run(hooks[tick])
} }
Hooks.hook = function(hookType, name, fn) { // Alias for `.addHook`
// For aliases, we may want to incorporate some sort of way to mitigate this Hooks.hook = function() {
if (hookType === "beforeDelete") { Hooks.addHook.apply(this, arguments)
hookType = 'beforeDestroy'
}
else if (hookType === "afterDelete") {
hookType = 'afterDestroy'
}
Hooks.addHook.call(this, hookType, name, fn)
} }
Hooks.addHook = function(hookType, name, fn) { Hooks.addHook = function(hookType, name, fn) {
...@@ -65,6 +80,9 @@ Hooks.addHook = function(hookType, name, fn) { ...@@ -65,6 +80,9 @@ Hooks.addHook = function(hookType, name, fn) {
fn.apply(this, Array.prototype.slice.call(arguments, 0, arguments.length-1).concat(arguments[arguments.length-1])) fn.apply(this, Array.prototype.slice.call(arguments, 0, arguments.length-1).concat(arguments[arguments.length-1]))
} }
// Aliases
hookType = hookAliases[hookType] || hookType
// Just in case if we override the default DAOFactory.options // Just in case if we override the default DAOFactory.options
this.options.hooks[hookType] = this.options.hooks[hookType] || [] this.options.hooks[hookType] = this.options.hooks[hookType] || []
this.options.hooks[hookType][this.options.hooks[hookType].length] = !!name ? {name: name, fn: method} : method this.options.hooks[hookType][this.options.hooks[hookType].length] = !!name ? {name: name, fn: method} : method
...@@ -95,11 +113,11 @@ Hooks.afterDestroy = function(name, fn) { ...@@ -95,11 +113,11 @@ Hooks.afterDestroy = function(name, fn) {
} }
Hooks.beforeDelete = function(name, fn) { Hooks.beforeDelete = function(name, fn) {
Hooks.addHook.call(this, 'beforeDestroy', name, fn) Hooks.addHook.call(this, 'beforeDelete', name, fn)
} }
Hooks.afterDelete = function(name, fn) { Hooks.afterDelete = function(name, fn) {
Hooks.addHook.call(this, 'afterDestroy', name, fn) Hooks.addHook.call(this, 'afterDelete', name, fn)
} }
Hooks.beforeUpdate = function(name, fn) { Hooks.beforeUpdate = function(name, fn) {
......
...@@ -9,10 +9,10 @@ module.exports = (function() { ...@@ -9,10 +9,10 @@ module.exports = (function() {
var Migrator = function(sequelize, options) { var Migrator = function(sequelize, options) {
this.sequelize = sequelize this.sequelize = sequelize
this.options = Utils._.extend({ this.options = Utils._.extend({
path: __dirname + '/../migrations', path: __dirname + '/../migrations',
from: null, from: null,
to: null, to: null,
logging: console.log, logging: console.log,
filesFilter: /\.js$/ filesFilter: /\.js$/
}, options || {}) }, options || {})
...@@ -49,7 +49,6 @@ module.exports = (function() { ...@@ -49,7 +49,6 @@ module.exports = (function() {
, from = migrations[0] , from = migrations[0]
if (options.method === 'down') { if (options.method === 'down') {
from = migrations[0]
migrations.reverse() migrations.reverse()
} }
......
...@@ -419,12 +419,19 @@ module.exports = (function() { ...@@ -419,12 +419,19 @@ module.exports = (function() {
Sequelize.prototype.transaction = function(_options, _callback) { Sequelize.prototype.transaction = function(_options, _callback) {
var options = (typeof _options === 'function') ? {} : _options var options = (typeof _options === 'function') ? {} : _options
, callback = (typeof _options === 'function') ? _options : _callback , callback = (typeof _options === 'function') ? _options : _callback
, wantsError = (callback.length === 2)
, transaction = new Transaction(this, options) , transaction = new Transaction(this, options)
, self = this , self = this
Utils.tick(function() { Utils.tick(function() {
if (wantsError) {
transaction.error(function(err) {
callback(err, transaction)
})
}
transaction.prepareEnvironment(function() { transaction.prepareEnvironment(function() {
callback(transaction) wantsError ? callback(null, transaction) : callback(transaction)
}) })
}) })
......
...@@ -55,7 +55,7 @@ Transaction.prototype.begin = function(callback) { ...@@ -55,7 +55,7 @@ Transaction.prototype.begin = function(callback) {
.getQueryInterface() .getQueryInterface()
.startTransaction(this, {}) .startTransaction(this, {})
.success(callback) .success(callback)
.error(onError.bind(this))
} }
Transaction.prototype.setAutocommit = function(callback) { Transaction.prototype.setAutocommit = function(callback) {
...@@ -64,6 +64,7 @@ Transaction.prototype.setAutocommit = function(callback) { ...@@ -64,6 +64,7 @@ Transaction.prototype.setAutocommit = function(callback) {
.getQueryInterface() .getQueryInterface()
.setAutocommit(this, this.options.autocommit) .setAutocommit(this, this.options.autocommit)
.success(callback) .success(callback)
.error(onError.bind(this))
} }
Transaction.prototype.setIsolationLevel = function(callback) { Transaction.prototype.setIsolationLevel = function(callback) {
...@@ -72,5 +73,11 @@ Transaction.prototype.setIsolationLevel = function(callback) { ...@@ -72,5 +73,11 @@ Transaction.prototype.setIsolationLevel = function(callback) {
.getQueryInterface() .getQueryInterface()
.setIsolationLevel(this, this.options.isolationLevel) .setIsolationLevel(this, this.options.isolationLevel)
.success(callback) .success(callback)
.error(function(err) { console.log(err) }) .error(onError.bind(this))
}
// private
var onError = function(err) {
this.emit('error', err)
} }
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
}, },
"devDependencies": { "devDependencies": {
"sqlite3": "~2.1.12", "sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha9", "mysql": "2.0.0-alpha9",
"pg": "~2.8.1", "pg": "~2.8.1",
"watchr": "~2.4.3", "watchr": "~2.4.3",
"yuidocjs": "~0.3.36", "yuidocjs": "~0.3.36",
...@@ -89,4 +89,4 @@ ...@@ -89,4 +89,4 @@
"node": ">=0.6.21" "node": ">=0.6.21"
}, },
"license": "MIT" "license": "MIT"
} }
\ No newline at end of file
...@@ -165,6 +165,55 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -165,6 +165,55 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
}) })
}) })
describe('createAssociation', function() {
it('creates an associated model instance', function(done) {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
, Task = this.sequelize.define('Task', { title: DataTypes.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
Task.create({ title: 'task' }).success(function(task) {
task.createUser({ username: 'bob' }).success(function() {
task.getUser().success(function(user) {
expect(user).not.to.be.null
expect(user.username).to.equal('bob')
done()
})
})
})
})
})
it('supports transactions', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: Support.Sequelize.STRING })
, Group = sequelize.define('Group', { name: Support.Sequelize.STRING })
Group.belongsTo(User)
sequelize.sync({ force: true }).success(function() {
Group.create({ name: 'bar' }).success(function(group) {
sequelize.transaction(function(t) {
group.createUser({ username: 'foo' }, { transaction: t }).success(function() {
group.getUser().success(function(user) {
expect(user).to.be.null
group.getUser({ transaction: t }).success(function(user) {
expect(user).not.to.be.null
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING }) var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
......
...@@ -265,6 +265,40 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -265,6 +265,40 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
describe('addAssociations', function() {
it('supports transactions', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var Article = sequelize.define('Article', { 'title': DataTypes.STRING })
, Label = sequelize.define('Label', { 'text': DataTypes.STRING })
Article.hasMany(Label)
sequelize.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
Label.create({ text: 'bar' }).success(function(label) {
sequelize.transaction(function(t) {
article.addLabel(label, { transaction: t }).success(function() {
Label
.findAll({ where: { ArticleId: article.id }, transaction: undefined })
.success(function(labels) {
expect(labels.length).to.equal(0)
Label
.findAll({ where: { ArticleId: article.id }, transaction: t })
.success(function(labels) {
expect(labels.length).to.equal(1)
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
})
it("clears associations when passing null to the set-method with omitNull set to true", function(done) { it("clears associations when passing null to the set-method with omitNull set to true", function(done) {
this.sequelize.options.omitNull = true this.sequelize.options.omitNull = true
...@@ -295,6 +329,59 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -295,6 +329,59 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
describe('createAssociations', function() {
it('creates a new associated object', function(done) {
var Article = this.sequelize.define('Article', { 'title': DataTypes.STRING })
, Label = this.sequelize.define('Label', { 'text': DataTypes.STRING })
Article.hasMany(Label)
Article.sync({ force: true }).success(function() {
Label.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
article.createLabel({ text: 'bar' }).success(function(label) {
Label
.findAll({ where: { ArticleId: article.id }})
.success(function(labels) {
expect(labels.length).to.equal(1)
done()
})
})
})
})
})
})
it('supports transactions', function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var Article = sequelize.define('Article', { 'title': DataTypes.STRING })
, Label = sequelize.define('Label', { 'text': DataTypes.STRING })
Article.hasMany(Label)
Article.sync({ force: true }).success(function() {
Label.sync({ force: true }).success(function() {
Article.create({ title: 'foo' }).success(function(article) {
sequelize.transaction(function (t) {
article.createLabel({ text: 'bar' }, { transaction: t }).success(function(label) {
Label.findAll({ where: { ArticleId: article.id }}).success(function(labels) {
expect(labels.length).to.equal(0)
Label.findAll({ where: { ArticleId: article.id }}, { transaction: t }).success(function(labels) {
expect(labels.length).to.equal(1)
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
})
describe("getting assocations with options", function() { describe("getting assocations with options", function() {
beforeEach(function(done) { beforeEach(function(done) {
var self = this var self = this
...@@ -620,6 +707,90 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -620,6 +707,90 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
describe('createAssociations', function() {
it('creates a new associated object', function(done) {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
, Task = this.sequelize.define('Task', { title: DataTypes.STRING })
User.hasMany(Task)
Task.hasMany(User)
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
Task.create({ title: 'task' }).success(function(task) {
task.createUser({ username: 'foo' }).success(function() {
task.getUsers().success(function(_users) {
expect(_users).to.have.length(1)
done()
})
})
})
})
})
})
it('supports transactions', function(done) {
var self = this
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: DataTypes.STRING })
, Task = sequelize.define('Task', { title: DataTypes.STRING })
User.hasMany(Task)
Task.hasMany(User)
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
Task.create({ title: 'task' }).success(function(task) {
sequelize.transaction(function (t) {
task.createUser({ username: 'foo' }, { transaction: t }).success(function() {
task.getUsers().success(function(users) {
expect(users).to.have.length(0)
task.getUsers({ transaction: t }).success(function(users) {
expect(users).to.have.length(1)
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
})
describe('addAssociations', function() {
it('supports transactions', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: DataTypes.STRING })
, Task = sequelize.define('Task', { title: DataTypes.STRING })
User.hasMany(Task)
Task.hasMany(User)
sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
sequelize.transaction(function(t){
task.addUser(user, { transaction: t }).success(function() {
task.hasUser(user).success(function(hasUser) {
expect(hasUser).to.be.false
task.hasUser(user, { transaction: t }).success(function(hasUser) {
expect(hasUser).to.be.true
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () { describe('optimizations using bulk create, destroy and update', function () {
beforeEach(function (done) { beforeEach(function (done) {
var self = this var self = this
...@@ -820,7 +991,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -820,7 +991,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Worker.hasMany(Task, { through: WorkerTasks }) Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks }) Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) { this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
Worker.create().done(function (err, worker) { Worker.create().done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
...@@ -878,7 +1049,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -878,7 +1049,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Worker.hasMany(Task, { through: WorkerTasks }) Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks }) Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) { this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
Worker.create().done(function (err, worker) { Worker.create().done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
...@@ -909,7 +1080,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -909,7 +1080,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
Worker.hasMany(Task, { through: WorkerTasks }) Worker.hasMany(Task, { through: WorkerTasks })
Task.hasMany(Worker, { through: WorkerTasks }) Task.hasMany(Worker, { through: WorkerTasks })
this.sequelize.sync().done(function(err) { this.sequelize.sync().done(function(err) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
Worker.create({}).done(function (err, worker) { Worker.create({}).done(function (err, worker) {
expect(err).not.to.be.ok expect(err).not.to.be.ok
...@@ -924,7 +1095,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -924,7 +1095,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
worker.getTasks().done(function (err, tasks) { worker.getTasks().done(function (err, tasks) {
expect(tasks.length).to.equal(1) expect(tasks.length).to.equal(1)
done() done()
}) })
}) })
}) })
}) })
......
...@@ -160,6 +160,57 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -160,6 +160,57 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
}) })
}) })
describe('createAssociation', function() {
it('creates an associated model instance', function(done) {
var User = this.sequelize.define('User', { username: Sequelize.STRING })
, Task = this.sequelize.define('Task', { title: Sequelize.STRING })
User.hasOne(Task)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
user.createTask({ title: 'task' }).success(function() {
user.getTask().success(function(task) {
expect(task).not.to.be.null
expect(task.title).to.equal('task')
done()
})
})
})
})
})
it('supports transactions', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: Sequelize.STRING })
, Group = sequelize.define('Group', { name: Sequelize.STRING })
User.hasOne(Group)
sequelize.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
sequelize.transaction(function(t) {
user.createGroup({ name: 'testgroup' }, { transaction: t }).success(function(group) {
User.all().success(function (users) {
users[0].getGroup().success(function (group) {
expect(group).to.be.null;
User.all({ transaction: t }).success(function (users) {
users[0].getGroup({ transaction: t }).success(function (group) {
expect(group).to.be.not.null;
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) { it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING }) var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
......
...@@ -236,7 +236,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -236,7 +236,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
paranoid: true, paranoid: true,
underscored: true underscored: true
}) })
UserTable.sync({force: true}).success(function() { UserTable.sync({force: true}).success(function() {
UserTable.create({aNumber: 30}).success(function(user) { UserTable.create({aNumber: 30}).success(function(user) {
UserTable.count().success(function(c) { UserTable.count().success(function(c) {
...@@ -466,6 +466,25 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -466,6 +466,25 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
describe('find', function() {
it('supports the transaction option in the first parameter', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) {
var User = sequelize.define('User', { username: Sequelize.STRING, foo: Sequelize.STRING })
User.sync({ force: true }).success(function() {
sequelize.transaction(function(t) {
User.create({ username: 'foo' }, { transaction: t }).success(function() {
User.find({ where: { username: 'foo' }, transaction: t }).success(function(user) {
expect(user).to.not.be.null
t.rollback().success(function() { done() })
})
})
})
})
})
})
})
describe('findOrInitialize', function() { describe('findOrInitialize', function() {
it('supports transactions', function(done) { it('supports transactions', function(done) {
Support.prepareTransactionTest(this.sequelize, function(sequelize) { Support.prepareTransactionTest(this.sequelize, function(sequelize) {
...@@ -1660,4 +1679,105 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1660,4 +1679,105 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
describe('paranoid is true and where is an array', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {username: DataTypes.STRING }, { paranoid: true })
this.Project = this.sequelize.define('Project', { title: DataTypes.STRING }, { paranoid: true })
this.Project.hasMany(this.User)
this.User.hasMany(this.Project)
var self = this
this.sequelize.sync({ force: true }).success(function() {
self.User.bulkCreate([{
username: 'leia'
}, {
username: 'luke'
}, {
username: 'vader'
}]).success(function() {
self.Project.bulkCreate([{
title: 'republic'
},{
title: 'empire'
}]).success(function() {
self.User.findAll().success(function(users){
self.Project.findAll().success(function(projects){
var leia = users[0]
, luke = users[1]
, vader = users[2]
, republic = projects[0]
, empire = projects[1]
leia.setProjects([republic]).success(function(){
luke.setProjects([republic]).success(function(){
vader.setProjects([empire]).success(function(){
leia.destroy().success(function() {
done()
})
})
})
})
})
})
})
})
})
})
it('should not fail with an include', function(done) {
var tableName = ''
, ident = this.sequelize.queryInterface.QueryGenerator.quoteIdentifier
, escape = this.sequelize.queryInterface.QueryGenerator.escape
if(this.Project.tableName) {
tableName = ident(this.Project.tableName) + '.'
}
this.User.findAll({
where: [
tableName + ident('title') + ' = ' + escape('republic')
],
include: [
{model: this.Project}
]
}).success(function(users){
try{
expect(users.length).to.be.equal(1)
expect(users[0].username).to.be.equal('luke')
done()
}catch(e){
done(e)
}
}).error(done)
})
it('should not overwrite a specified deletedAt', function(done) {
var tableName = ''
, ident = this.sequelize.queryInterface.QueryGenerator.quoteIdentifier
if(this.User.tableName) {
tableName = ident(this.User.tableName) + '.'
}
this.User.findAll({
where: [
tableName + ident('deletedAt') + ' IS NOT NULL '
],
include: [
{model: this.Project}
]
}).success(function(users){
try{
expect(users.length).to.be.equal(1)
expect(users[0].username).to.be.equal('leia')
done()
}catch(e){
done(e)
}
}).error(done)
})
})
}) })
...@@ -6994,4 +6994,168 @@ describe(Support.getTestDialectTeaser("Hooks"), function () { ...@@ -6994,4 +6994,168 @@ describe(Support.getTestDialectTeaser("Hooks"), function () {
}) })
}) })
}) })
describe('passing DAO instances', function() {
describe('beforeValidate / afterValidate', function() {
it('should pass a DAO instance to the hook', function(done){
var beforeHooked = false
var afterHooked = false
var User = this.sequelize.define('User', {
username: DataTypes.STRING
}, {
hooks: {
beforeValidate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
beforeHooked = true
fn()
},
afterValidate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
afterHooked = true
fn()
}
}
})
User.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
expect(beforeHooked).to.be.true
expect(afterHooked).to.be.true
done()
})
})
})
})
describe('beforeCreate / afterCreate', function() {
it('should pass a DAO instance to the hook', function(done){
var beforeHooked = false
var afterHooked = false
var User = this.sequelize.define('User', {
username: DataTypes.STRING
}, {
hooks: {
beforeCreate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
beforeHooked = true
fn()
},
afterCreate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
afterHooked = true
fn()
}
}
})
User.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
expect(beforeHooked).to.be.true
expect(afterHooked).to.be.true
done()
})
})
})
})
describe('beforeDestroy / afterDestroy', function() {
it('should pass a DAO instance to the hook', function(done){
var beforeHooked = false
var afterHooked = false
var User = this.sequelize.define('User', {
username: DataTypes.STRING
}, {
hooks: {
beforeDestroy: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
beforeHooked = true
fn()
},
afterDestroy: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
afterHooked = true
fn()
}
}
})
User.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
user.destroy().success(function() {
expect(beforeHooked).to.be.true
expect(afterHooked).to.be.true
done()
})
})
})
})
})
describe('beforeDelete / afterDelete', function() {
it('should pass a DAO instance to the hook', function(done){
var beforeHooked = false
var afterHooked = false
var User = this.sequelize.define('User', {
username: DataTypes.STRING
}, {
hooks: {
beforeDelete: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
beforeHooked = true
fn()
},
afterDelete: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
afterHooked = true
fn()
}
}
})
User.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
user.destroy().success(function() {
expect(beforeHooked).to.be.true
expect(afterHooked).to.be.true
done()
})
})
})
})
})
describe('beforeUpdate / afterUpdate', function() {
it('should pass a DAO instance to the hook', function(done){
var beforeHooked = false
var afterHooked = false
var User = this.sequelize.define('User', {
username: DataTypes.STRING
}, {
hooks: {
beforeUpdate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
beforeHooked = true
fn()
},
afterUpdate: function(user, fn) {
expect(user).to.be.instanceof(User.DAO)
afterHooked = true
fn()
}
}
})
User.sync({ force: true }).success(function() {
User.create({ username: 'bob' }).success(function(user) {
user.save({ username: 'bawb' }).success(function() {
expect(beforeHooked).to.be.true
expect(afterHooked).to.be.true
done()
})
})
})
})
})
})
}) })
...@@ -209,6 +209,106 @@ describe(Support.getTestDialectTeaser("Executable"), function() { ...@@ -209,6 +209,106 @@ describe(Support.getTestDialectTeaser("Executable"), function() {
;(function(flags) { ;(function(flags) {
flags.forEach(function(flag) { flags.forEach(function(flag) {
var execBinary = function(callback, _flag) {
_flag = _flag || flag
var dialect = Support.getTestDialect()
, config = require(__dirname + '/config/config.js')
config.sqlite.storage = __dirname + "/tmp/test.sqlite"
config = _.extend(config, config[dialect], { dialect: dialect })
exec("echo '" + JSON.stringify(config) + "' > config/config.json", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize " + _flag, { cwd: __dirname + "/tmp" }, callback)
})
}
var prepare = function(callback, options) {
options = options || {}
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cp ../assets/migrations/*-createPerson.js ./migrations/", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("cat ../support.js|sed s,/../,/../../, > ./support.js", { cwd: __dirname + '/tmp' }, function(error, stdout) {
if (!options.skipExecBinary) {
execBinary(callback, options.flag)
}
})
})
})
})
}
describe(flag, function() {
it("creates a SequelizeMeta table", function(done) {
var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options)
}
prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort()
expect(tables).to.have.length(1)
expect(tables[0]).to.equal("SequelizeMeta")
done()
})
}.bind(this))
})
it("stops execution if no migrations have been done yet", function(done) {
var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options)
}
prepare(function(err, output) {
expect(err).to.be.null
expect(output).to.include("There are no pending migrations.")
done()
}.bind(this))
})
it("is correctly undoing a migration if they have been done yet", function(done) {
var sequelize = this.sequelize
if (this.sequelize.options.dialect === 'sqlite') {
var options = this.sequelize.options
options.storage = __dirname + "/tmp/test.sqlite"
sequelize = new Support.Sequelize("", "", "", options)
}
prepare(function() {
sequelize.getQueryInterface().showAllTables().success(function(tables) {
tables = tables.sort()
expect(tables).to.have.length(2)
expect(tables[0]).to.equal("Person")
execBinary(function(err, output) {
sequelize.getQueryInterface().showAllTables().success(function(tables) {
expect(tables).to.have.length(1)
expect(tables[0]).to.equal("SequelizeMeta")
done()
})
})
})
}.bind(this), { flag: '-m' })
})
})
})
})(['--migrate --undo', '-mu'])
;(function(flags) {
flags.forEach(function(flag) {
var prepare = function(callback) { var prepare = function(callback) {
exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("rm -rf ./*", { cwd: __dirname + '/tmp' }, function(error, stdout) {
exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) { exec("../../bin/sequelize --init", { cwd: __dirname + '/tmp' }, function(error, stdout) {
......
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/support')
, Transaction = require(__dirname + '/../lib/transaction')
describe(Support.getTestDialectTeaser("Sequelize#transaction"), function () {
describe('success', function() {
it("gets triggered once a transaction has been successfully committed", function(done) {
this
.sequelize
.transaction(function(t) { t.commit() })
.success(function() { done() })
})
it("gets triggered once a transaction has been successfully rollbacked", function(done) {
this
.sequelize
.transaction(function(t) { t.rollback() })
.success(function() { done() })
})
})
describe('error', function() {
if (Support.getTestDialect() === 'sqlite') {
// not sure if we can test this in sqlite ...
// how could we enforce an authentication error in sqlite?
} else {
it("gets triggered once an error occurs", function(done) {
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
// lets overwrite the host to get an error
sequelize.config.username = 'foobarbaz'
sequelize
.transaction(function() {})
.error(function(err) {
expect(err).to.not.be.undefined
done()
})
})
}
})
describe('callback', function() {
it("receives the transaction if only one argument is passed", function(done) {
this.sequelize.transaction(function(t) {
expect(t).to.be.instanceOf(Transaction)
t.commit()
}).done(done)
})
it("receives an error and the transaction if two arguments are passed", function(done) {
this.sequelize.transaction(function(err, t) {
expect(err).to.not.be.instanceOf(Transaction)
expect(t).to.be.instanceOf(Transaction)
t.commit()
}).done(done)
})
})
})
...@@ -16,14 +16,26 @@ describe(Support.getTestDialectTeaser("Transaction"), function () { ...@@ -16,14 +16,26 @@ describe(Support.getTestDialectTeaser("Transaction"), function () {
}) })
}) })
describe('success', function() {
it("is a success method available", function() {
expect(Transaction).to.respondTo("success")
})
})
describe('error', function() {
it("is an error method available", function() {
expect(Transaction).to.respondTo("error")
})
})
describe('commit', function() { describe('commit', function() {
it('is a commit message available', function() { it('is a commit method available', function() {
expect(Transaction).to.respondTo('commit') expect(Transaction).to.respondTo('commit')
}) })
}) })
describe('rollback', function() { describe('rollback', function() {
it('is a rollback message available', function() { it('is a rollback method available', function() {
expect(Transaction).to.respondTo('rollback') expect(Transaction).to.respondTo('rollback')
}) })
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!