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

Commit c20fa816 by Mick Hansen

Merge pull request #1744 from sequelize/addRelations

addRelations feature
2 parents 7e9679be b67058a6
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types')
, Helpers = require('./helpers') , Helpers = require('./helpers')
, Transaction = require('../transaction') , Transaction = require('../transaction')
, _ = require('lodash')
module.exports = (function() { module.exports = (function() {
var BelongsTo = function(source, target, options) { var BelongsTo = function(source, target, options) {
......
...@@ -12,16 +12,12 @@ module.exports = (function() { ...@@ -12,16 +12,12 @@ module.exports = (function() {
HasManyDoubleLinked.prototype.injectGetter = function(options) { HasManyDoubleLinked.prototype.injectGetter = function(options) {
var self = this var self = this
, _options = options
, smart
var customEventEmitter = new Utils.CustomEventEmitter(function() {
var where = []
, through = self.association.through , through = self.association.through
, options = _options || {}
, queryOptions = {} , queryOptions = {}
, targetAssociation = self.association.targetAssociation , targetAssociation = self.association.targetAssociation
options = options || {}
//fully qualify //fully qualify
var instancePrimaryKey = self.instance.Model.primaryKeyAttribute var instancePrimaryKey = self.instance.Model.primaryKeyAttribute
, foreignPrimaryKey = self.association.target.primaryKeyAttribute , foreignPrimaryKey = self.association.target.primaryKeyAttribute
...@@ -70,18 +66,11 @@ module.exports = (function() { ...@@ -70,18 +66,11 @@ module.exports = (function() {
} }
} }
self.association.target.findAllJoin([through.getTableName(), through.name], options, queryOptions) return self.association.target.findAllJoin([through.getTableName(), through.name], options, queryOptions)
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('error', function(err){ customEventEmitter.emit('error', err) })
.on('sql', function(sql) { customEventEmitter.emit('sql', sql)})
})
return customEventEmitter.run()
} }
HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations, defaultAttributes) { HasManyDoubleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
var self = this var self = this
, chainer = new Utils.QueryChainer()
, targetAssociation = self.association.targetAssociation , targetAssociation = self.association.targetAssociation
, foreignIdentifier = self.association.foreignIdentifier , foreignIdentifier = self.association.foreignIdentifier
, sourceKeys = Object.keys(self.association.source.primaryKeys) , sourceKeys = Object.keys(self.association.source.primaryKeys)
...@@ -89,6 +78,7 @@ module.exports = (function() { ...@@ -89,6 +78,7 @@ module.exports = (function() {
, obsoleteAssociations = [] , obsoleteAssociations = []
, changedAssociations = [] , changedAssociations = []
, options = {} , options = {}
, promises = []
, unassociatedObjects; , unassociatedObjects;
if ((defaultAttributes || {}).transaction instanceof Transaction) { if ((defaultAttributes || {}).transaction instanceof Transaction) {
...@@ -140,7 +130,7 @@ module.exports = (function() { ...@@ -140,7 +130,7 @@ module.exports = (function() {
where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id)
where[foreignIdentifier] = foreignIds where[foreignIdentifier] = foreignIds
chainer.add(self.association.through.destroy(where, options)) promises.push(self.association.through.destroy(where, options))
} }
if (unassociatedObjects.length > 0) { if (unassociatedObjects.length > 0) {
...@@ -157,23 +147,19 @@ module.exports = (function() { ...@@ -157,23 +147,19 @@ module.exports = (function() {
return attributes return attributes
}) })
chainer.add(self.association.through.bulkCreate(bulk, options)) promises.push(self.association.through.bulkCreate(bulk, options))
} }
if (changedAssociations.length > 0) { if (changedAssociations.length > 0) {
changedAssociations.forEach(function (assoc) { changedAssociations.forEach(function (assoc) {
chainer.add(self.association.through.update(assoc.attributes, assoc.where, options)) promises.push(self.association.through.update(assoc.attributes, assoc.where, options))
}) })
} }
chainer return Utils.Promise.all(promises)
.run()
.success(function() { emitterProxy.emit('success', newAssociations) })
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
} }
HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) { HasManyDoubleLinked.prototype.injectAdder = function(newAssociation, additionalAttributes, exists) {
var attributes = {} var attributes = {}
, targetAssociation = this.association.targetAssociation , targetAssociation = this.association.targetAssociation
, foreignIdentifier = targetAssociation.identifier , foreignIdentifier = targetAssociation.identifier
...@@ -195,17 +181,14 @@ module.exports = (function() { ...@@ -195,17 +181,14 @@ module.exports = (function() {
attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.name], additionalAttributes) attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.name], additionalAttributes)
if (Object.keys(attributes).length) { if (Object.keys(attributes).length) {
targetAssociation.through.update(attributes, where).proxy(emitterProxy) return targetAssociation.through.update(attributes, where)
} else { } else {
emitterProxy.emit('success') return Utils.Promise.resolve()
} }
} 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, options) return this.association.through.create(attributes, options)
.success(function() { emitterProxy.emit('success', newAssociation) })
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
} }
} }
......
...@@ -24,12 +24,12 @@ module.exports = (function() { ...@@ -24,12 +24,12 @@ module.exports = (function() {
return this.association.target.all(options) return this.association.target.all(options)
} }
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations, defaultAttributes) { HasManySingleLinked.prototype.injectSetter = function(oldAssociations, newAssociations, defaultAttributes) {
var self = this var self = this
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {Model: {primaryKeys: {}}}).Model.primaryKeys || {}) , associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {Model: {primaryKeys: {}}}).Model.primaryKeys || {})
, associationKey = (associationKeys.length === 1) ? associationKeys[0] : 'id' , associationKey = (associationKeys.length === 1) ? associationKeys[0] : 'id'
, chainer = new Utils.QueryChainer()
, options = {} , options = {}
, promises = []
, obsoleteAssociations = oldAssociations.filter(function (old) { , obsoleteAssociations = oldAssociations.filter(function (old) {
return !Utils._.find(newAssociations, function (obj) { return !Utils._.find(newAssociations, function (obj) {
return obj[associationKey] === old[associationKey] return obj[associationKey] === old[associationKey]
...@@ -62,7 +62,7 @@ module.exports = (function() { ...@@ -62,7 +62,7 @@ module.exports = (function() {
, updateWhere = {} , updateWhere = {}
updateWhere[primaryKey] = obsoleteIds updateWhere[primaryKey] = obsoleteIds
chainer.add(this.__factory.target.update( promises.push(this.__factory.target.update(
update, update,
updateWhere, updateWhere,
Utils._.extend(options, { allowNull: [self.__factory.identifier] }) Utils._.extend(options, { allowNull: [self.__factory.identifier] })
...@@ -88,21 +88,17 @@ module.exports = (function() { ...@@ -88,21 +88,17 @@ module.exports = (function() {
update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id) update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id)
updateWhere[primaryKey] = unassociatedIds updateWhere[primaryKey] = unassociatedIds
chainer.add(this.__factory.target.update( promises.push(this.__factory.target.update(
update, update,
updateWhere, updateWhere,
Utils._.extend(options, { allowNull: [self.__factory.identifier] }) Utils._.extend(options, { allowNull: [self.__factory.identifier] })
)) ))
} }
chainer return Utils.Promise.all(promises)
.run()
.success(function() { emitter.emit('success', newAssociations) })
.error(function(err) { emitter.emit('error', err) })
.on('sql', function(sql) { emitter.emit('sql', sql) })
} }
HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes) { HasManySingleLinked.prototype.injectAdder = function(newAssociation, additionalAttributes) {
var primaryKeys = Object.keys(this.instance.Model.primaryKeys) var primaryKeys = Object.keys(this.instance.Model.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id' , primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, options = {} , options = {}
...@@ -114,10 +110,7 @@ module.exports = (function() { ...@@ -114,10 +110,7 @@ module.exports = (function() {
newAssociation[this.__factory.identifier] = this.instance[primaryKey] newAssociation[this.__factory.identifier] = this.instance[primaryKey]
newAssociation.save(options) return newAssociation.save(options)
.success(function() { emitterProxy.emit('success', newAssociation) })
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
} }
return HasManySingleLinked return HasManySingleLinked
......
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types')
, Helpers = require('./helpers') , Helpers = require('./helpers')
, _ = require('lodash') , _ = require('lodash')
, Transaction = require('../transaction') , Transaction = require('../transaction')
...@@ -122,6 +121,7 @@ module.exports = (function() { ...@@ -122,6 +121,7 @@ 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),
addMultiple: Utils._.camelize('add_' + this.as, this.target.options.language),
add: Utils._.camelize(Utils.singularize('add_' + this.as, this.target.options.language)), add: Utils._.camelize(Utils.singularize('add_' + this.as, this.target.options.language)),
create: Utils._.camelize(Utils.singularize('create_' + this.as, this.target.options.language)), create: Utils._.camelize(Utils.singularize('create_' + this.as, this.target.options.language)),
remove: Utils._.camelize(Utils.singularize('remove_' + this.as, this.target.options.language)), remove: Utils._.camelize(Utils.singularize('remove_' + this.as, this.target.options.language)),
...@@ -262,40 +262,26 @@ module.exports = (function() { ...@@ -262,40 +262,26 @@ module.exports = (function() {
obj[this.accessors.hasAll] = function(objects, options) { obj[this.accessors.hasAll] = function(objects, options) {
var instance = this; var instance = this;
var customEventEmitter = new Utils.CustomEventEmitter(function() { return instance[self.accessors.get](options).then(function(associatedObjects) {
instance[self.accessors.get](options) return Utils._.all(objects, function(o) {
.error(function(err) { customEventEmitter.emit('error', err) })
.success(function(associatedObjects) {
customEventEmitter.emit('success',
Utils._.all(objects, function(o) {
return Utils._.any(associatedObjects, function(associatedObject) { return Utils._.any(associatedObjects, function(associatedObject) {
return Utils._.all(associatedObject.identifiers, function(key, identifier) { return Utils._.all(associatedObject.identifiers, function(key, identifier) {
return o[identifier] == associatedObject[identifier]; return o[identifier] == associatedObject[identifier];
}); });
}) })
}) })
)
})
}) })
return customEventEmitter.run()
} }
obj[this.accessors.hasSingle] = function(o, options) { obj[this.accessors.hasSingle] = function(o, options) {
var instance = this var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() { return instance[self.accessors.get](options).then(function(associatedObjects) {
instance[self.accessors.get](options) return Utils._.any(associatedObjects, function(associatedObject) {
.error(function(err){ customEventEmitter.emit('error', err)})
.success(function(associatedObjects) {
customEventEmitter.emit('success',
Utils._.any(associatedObjects, function(associatedObject) {
return Utils._.all(associatedObject.identifiers, function(key, identifier) { return Utils._.all(associatedObject.identifiers, function(key, identifier) {
return o[identifier] == associatedObject[identifier]; return o[identifier] == associatedObject[identifier];
}); });
}) })
)
}) })
})
return customEventEmitter.run()
} }
return this return this
} }
...@@ -303,56 +289,55 @@ module.exports = (function() { ...@@ -303,56 +289,55 @@ module.exports = (function() {
HasMany.prototype.injectSetter = function(obj) { HasMany.prototype.injectSetter = function(obj) {
var self = this var self = this
obj[this.accessors.set] = function(newAssociatedObjects, defaultAttributes) { obj[this.accessors.set] = function (newAssociatedObjects, additionalAttributes) {
if (newAssociatedObjects === null) { if (newAssociatedObjects === null) {
newAssociatedObjects = [] newAssociatedObjects = []
} }
var instance = this var instance = this
// define the returned customEventEmitter, which will emit the success event once everything is done return instance[self.accessors.get]({
return new Utils.CustomEventEmitter(function(emitter) { transaction: (additionalAttributes || {}).transaction
instance[self.accessors.get]({ }).then(function(oldAssociatedObjects) {
transaction: (defaultAttributes || {}).transaction
})
.success(function(oldAssociatedObjects) {
var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes) return new Class(self, instance).injectSetter(oldAssociatedObjects, newAssociatedObjects, additionalAttributes)
}) })
.proxy(emitter, {events: ['error', 'sql']})
}).run()
} }
obj[this.accessors.add] = function(newInstance, additionalAttributes) { obj[this.accessors.add] = function (newInstance, additionalAttributes) {
var instance = this var instance = this
, primaryKey = newInstance.Model.primaryKeyAttribute , primaryKey = newInstance.Model.primaryKeyAttribute
, where = new Utils.where(self.target.rawAttributes[primaryKey], newInstance[primaryKey]) , where = new Utils.where(self.target.rawAttributes[primaryKey], newInstance[primaryKey])
return new Utils.CustomEventEmitter(function(emitter) { if (Array.isArray(newInstance)) {
instance[self.accessors.get]({ return obj[self.accessors.addMultiple](newInstance, additionalAttributes)
} else {
return instance[self.accessors.get]({
where: where, where: where,
transaction: (additionalAttributes || {}).transaction transaction: (additionalAttributes || {}).transaction
}) }).then(function(currentAssociatedObjects) {
.proxy(emitter, {events: ['error', 'sql']})
.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 ? HasManyDoubleLinked : HasManySingleLinked var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
new Class(self, instance).injectAdder(emitter, newInstance, additionalAttributes, !!currentAssociatedObjects.length) return new Class(self, instance).injectAdder(newInstance, additionalAttributes, !!currentAssociatedObjects.length)
} else { } else {
emitter.emit('success', newInstance); return Utils.Promise.resolve(newInstance)
} }
}) })
}).run() }
}
obj[this.accessors.addMultiple] = function (newInstances, additionalAttributes) {
var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
return new Class(self, this).injectSetter([], newInstances, additionalAttributes)
} }
obj[this.accessors.remove] = function(oldAssociatedObject, options) {
obj[this.accessors.remove] = function (oldAssociatedObject, options) {
var instance = this var instance = this
return new Utils.CustomEventEmitter(function(emitter) { return instance[self.accessors.get]({
instance[self.accessors.get]({
transaction: (options || {}).transaction transaction: (options || {}).transaction
}).success(function(currentAssociatedObjects) { }).then(function(currentAssociatedObjects) {
var newAssociations = [] var newAssociations = []
, oldAssociations = []
currentAssociatedObjects.forEach(function(association) { currentAssociatedObjects.forEach(function(association) {
if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) { if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
...@@ -360,36 +345,8 @@ module.exports = (function() { ...@@ -360,36 +345,8 @@ module.exports = (function() {
} }
}) })
var tick = 0 return instance[self.accessors.set](newAssociations)
var next = function(err, i) {
if (!!err || i >= oldAssociations.length) {
return run(err)
}
oldAssociations[i].destroy().error(function(err) {
next(err)
})
.success(function() {
tick++
next(null, tick)
})
}
var run = function(err) {
if (!!err) {
return emitter.emit('error', err)
}
instance[self.accessors.set](newAssociations).proxy(emitter)
}
if (oldAssociations.length > 0) {
next(null, tick)
} else {
run()
}
}) })
}).run()
} }
return this return this
...@@ -407,16 +364,10 @@ module.exports = (function() { ...@@ -407,16 +364,10 @@ module.exports = (function() {
} }
if (Object(self.through) === self.through) { if (Object(self.through) === self.through) {
return new Utils.CustomEventEmitter(function(emitter) {
// Create the related model instance // Create the related model instance
self.target return self.target.create(values, fieldsOrOptions).then(function(newAssociatedObject) {
.create(values, fieldsOrOptions) return instance[self.accessors.add](newAssociatedObject, options)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(newAssociatedObject) {
instance[self.accessors.add](newAssociatedObject, options)
.proxy(emitter)
}) })
}).run()
} else { } else {
values[self.identifier] = instance.get(self.source.primaryKeyAttribute); values[self.identifier] = instance.get(self.source.primaryKeyAttribute);
return self.target.create(values, fieldsOrOptions) return self.target.create(values, fieldsOrOptions)
......
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types')
, Helpers = require("./helpers") , Helpers = require("./helpers")
, Transaction = require("../transaction") , Transaction = require("../transaction")
...@@ -118,15 +117,9 @@ module.exports = (function() { ...@@ -118,15 +117,9 @@ module.exports = (function() {
options.transaction = fieldsOrOptions.transaction options.transaction = fieldsOrOptions.transaction
} }
return new Utils.CustomEventEmitter(function(emitter) { return association.target.create(values, fieldsOrOptions).then(function(associationInstance) {
association.target return instance[association.accessors.set](associationInstance, options)
.create(values, fieldsOrOptions)
.proxy(emitter, { events: ['error', 'sql'] })
.success(function(associationInstance) {
instance[association.accessors.set](associationInstance, options)
.proxy(emitter)
}) })
}).run()
} }
return this return this
......
...@@ -69,7 +69,7 @@ var Mixin = module.exports = function(){} ...@@ -69,7 +69,7 @@ var Mixin = module.exports = function(){}
* * set[AS] - for example setProfile(instance, options). Options are passed to `target.save` * * set[AS] - for example setProfile(instance, options). Options are passed to `target.save`
* * create[AS] - for example createProfile(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to `target.create` * * create[AS] - for example createProfile(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to `target.create`
* *
* All methods return an event emitter. * All methods return a promise
* *
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
...@@ -110,7 +110,7 @@ Mixin.hasOne = function(targetModel, options) { ...@@ -110,7 +110,7 @@ Mixin.hasOne = function(targetModel, options) {
* * set[AS] - for example setUser(instance, options). Options are passed to this.save * * set[AS] - for example setUser(instance, options). Options are passed to this.save
* * create[AS] - for example createUser(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to target.create * * create[AS] - for example createUser(value, options). Builds and saves a new instance of the associated model. Values and options are passed on to target.create
* *
* All methods return an event emitter. * All methods return a promise
* *
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
...@@ -158,13 +158,14 @@ Mixin.belongsTo = function(targetModel, options) { ...@@ -158,13 +158,14 @@ Mixin.belongsTo = function(targetModel, options) {
* *
* * get[AS] - for example getPictures(). * * get[AS] - for example getPictures().
* * set[AS] - for example setPictures(instances, defaultAttributes|options). Update the associations. All currently associated models that are not in instances will be removed. * * set[AS] - for example setPictures(instances, defaultAttributes|options). Update the associations. All currently associated models that are not in instances will be removed.
* * add[AS] - for example addPicture(instance, defaultAttributes|options). Add another association. * * add[AS] - for example addPicture(instance, defaultAttributes|options). Add another associated object.
* * add[AS] [plural] - for example addPictures([instance1, instance2], defaultAttributes|options). Add some more associated objects.
* * create[AS] - for example createPicture(values, options). Build and save a new association. * * create[AS] - for example createPicture(values, options). Build and save a new association.
* * remove[AS] - for example removePicture(instance). Remove a single association * * remove[AS] - for example removePicture(instance). Remove a single association
* * has[AS] - for example hasPicture(instance). Is source associated to this target? * * has[AS] - for example hasPicture(instance). Is source associated to this target?
* * has[AS] [plural] - for example hasPictures(instances). Is source associated to all these targets? * * has[AS] [plural] - for example hasPictures(instances). Is source associated to all these targets?
* *
* All methods return an event emitter. * All methods return a promise
* *
* If you use a through model with custom attributes, these attributes can be set when adding / setting new associations in two ways. Consider users and projects from before * If you use a through model with custom attributes, these attributes can be set when adding / setting new associations in two ways. Consider users and projects from before
* with a join table that stores whether the project has been started yet: * with a join table that stores whether the project has been started yet:
......
...@@ -92,7 +92,9 @@ Hooks.runHooks = function() { ...@@ -92,7 +92,9 @@ Hooks.runHooks = function() {
if (!!arguments[0]) { if (!!arguments[0]) {
return reject(arguments[0]) return reject(arguments[0])
} }
if (arguments.length) {
resolveArgs = Array.prototype.slice.call(arguments, 1) resolveArgs = Array.prototype.slice.call(arguments, 1)
}
return run(hooks[tick]) return run(hooks[tick])
})) }))
...@@ -101,7 +103,9 @@ Hooks.runHooks = function() { ...@@ -101,7 +103,9 @@ Hooks.runHooks = function() {
maybePromise.spread(function () { maybePromise.spread(function () {
tick++ tick++
if (arguments.length) {
resolveArgs = Array.prototype.slice.call(arguments) resolveArgs = Array.prototype.slice.call(arguments)
}
return run(hooks[tick]) return run(hooks[tick])
}, reject) }, reject)
......
...@@ -1266,12 +1266,7 @@ module.exports = (function() { ...@@ -1266,12 +1266,7 @@ module.exports = (function() {
, query = null , query = null
, args = [] , args = []
return new Utils.CustomEventEmitter(function(emitter) { return self.runHooks(self.options.hooks.beforeBulkDestroy, where).then(function(newWhere) {
self.runHooks(self.options.hooks.beforeBulkDestroy, where, function(err, newWhere) {
if (!!err) {
return emitter.emit('error', err)
}
where = newWhere || where where = newWhere || where
if (self._timestampAttributes.deletedAt && options.force === false) { if (self._timestampAttributes.deletedAt && options.force === false) {
...@@ -1284,88 +1279,52 @@ module.exports = (function() { ...@@ -1284,88 +1279,52 @@ module.exports = (function() {
args = [self.getTableName(), where, options, self] args = [self.getTableName(), where, options, self]
} }
var runQuery = function(err, records) { var runQuery = function(records) {
if (!!err) { return self.QueryInterface[query].apply(self.QueryInterface, args).then(function(results) {
return emitter.emit('error', err)
}
query = self.QueryInterface[query].apply(self.QueryInterface, args)
query.on('sql', function(sql) {
emitter.emit('sql', sql)
})
.error(function(err) {
emitter.emit('error', err)
})
.success(function(results) {
var finished = function(err) {
if (!!err) {
return emitter.emit('error', err)
}
self.runHooks(self.options.hooks.afterBulkDestroy, where, function(err) {
if (!!err) {
return emitter.emit('error', err)
}
emitter.emit('success', results)
})
}
if (options && options.hooks === true) { if (options && options.hooks === true) {
var tick = 0 var tick = 0
var next = function(i) { var next = function(i) {
self.runHooks(self.options.hooks.afterDestroy, records[i], function(err, newValues) { return self.runHooks(self.options.hooks.afterDestroy, records[i]).then(function(newValues) {
if (!!err) {
return finished(err)
}
records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
tick++ tick++
if (tick >= records.length) { if (tick >= records.length) {
return finished() return self.runHooks(self.options.hooks.afterBulkDestroy, where).return(results)
} }
next(tick) return next(tick)
}) })
} }
next(tick) return next(tick)
} else { } else {
finished() return self.runHooks(self.options.hooks.afterBulkDestroy, where).return(results)
} }
}) })
} }
if (options && options.hooks === true) { if (options && options.hooks === true) {
var tick = 0 var tick = 0
self.all({where: where}).error(function(err) { emitter.emit('error', err) }) return self.all({where: where}).then(function(records) {
.success(function(records) {
var next = function(i) { var next = function(i) {
self.runHooks(self.options.hooks.beforeDestroy, records[i], function(err, newValues) { return self.runHooks(self.options.hooks.beforeDestroy, records[i]).then(function(newValues) {
if (!!err) {
return runQuery(err)
}
records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
tick++ tick++
if (tick >= records.length) { if (tick >= records.length) {
return runQuery(null, records) return runQuery(records)
} }
next(tick) return next(tick)
}) })
} }
next(tick) return next(tick)
}) })
//
} else { } else {
runQuery() return runQuery()
} }
}) })
}).run()
} }
/** /**
...@@ -1383,7 +1342,6 @@ module.exports = (function() { ...@@ -1383,7 +1342,6 @@ module.exports = (function() {
*/ */
Model.prototype.update = function(attrValueHash, where, options) { Model.prototype.update = function(attrValueHash, where, options) {
var self = this var self = this
, query = null
, tick = 0 , tick = 0
options = options || {} options = options || {}
...@@ -1395,97 +1353,58 @@ module.exports = (function() { ...@@ -1395,97 +1353,58 @@ module.exports = (function() {
attrValueHash[self._timestampAttributes.updatedAt] = Utils.now() attrValueHash[self._timestampAttributes.updatedAt] = Utils.now()
} }
return new Utils.CustomEventEmitter(function(emitter) {
var runSave = function() { var runSave = function() {
self.runHooks(self.options.hooks.beforeBulkUpdate, attrValueHash, where, function(err, attributes, _where) { return self.runHooks(self.options.hooks.beforeBulkUpdate, attrValueHash, where).spread(function(attributes, _where) {
if (!!err) {
return emitter.emit('error', err)
}
where = _where || where where = _where || where
attrValueHash = attributes || attrValueHash attrValueHash = attributes || attrValueHash
var runQuery = function(err, records) { var runQuery = function(records) {
if (!!err) { return self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options, self.rawAttributes).then(function(results) {
return emitter.emit('error', err)
}
query = self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options, self.rawAttributes)
query.on('sql', function(sql) {
emitter.emit('sql', sql)
})
.error(function(err) {
emitter.emit('error', err)
})
.success(function(results) {
var finished = function(err, records) {
if (!!err) {
return emitter.emit('error', err)
}
self.runHooks(self.options.hooks.afterBulkUpdate, attrValueHash, where, function(err) {
if (!!err) {
return emitter.emit('error', err)
}
emitter.emit('success', records)
})
}
if (options && options.hooks === true && !!records && records.length > 0) { if (options && options.hooks === true && !!records && records.length > 0) {
var tick = 0 var tick = 0
var next = function(i) { var next = function(i) {
self.runHooks(self.options.hooks.afterUpdate, records[i], function(err, newValues) { return self.runHooks(self.options.hooks.afterUpdate, records[i]).then(function(newValues) {
if (!!err) { records[i].dataValues = (!!newValues && newValues.dataValues) ? newValues.dataValues : records[i].dataValues
return finished(err)
}
records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
tick++ tick++
if (tick >= records.length) { if (tick >= records.length) {
return finished(null, records) return self.runHooks(self.options.hooks.afterBulkUpdate, attrValueHash, where).return(records)
} }
next(tick) return next(tick)
}) })
} }
next(tick) return next(tick)
} else { } else {
finished(null, results) return self.runHooks(self.options.hooks.afterBulkUpdate, attrValueHash, where).return(results)
} }
}) })
} }
if (options.hooks === true) { if (options.hooks === true) {
self.all({where: where}).error(function(err) { emitter.emit('error', err) }) return self.all({where: where}).then(function(records) {
.success(function(records) {
if (records === null || records.length < 1) { if (records === null || records.length < 1) {
return runQuery(null) return runQuery()
} }
var next = function(i) { var next = function(i) {
self.runHooks(self.options.hooks.beforeUpdate, records[i], function(err, newValues) { return self.runHooks(self.options.hooks.beforeUpdate, records[i]).then(function(newValues) {
if (!!err) { records[i].dataValues = (!!newValues && newValues.dataValues) ? newValues.dataValues : records[i].dataValues
return runQuery(err)
}
records[i].dataValues = !!newValues ? newValues.dataValues : records[i].dataValues
tick++ tick++
if (tick >= records.length) { if (tick >= records.length) {
return runQuery(null, records) return runQuery(records)
} }
next(tick) return next(tick)
}) })
} }
next(tick) return next(tick)
}) })
} else { } else {
runQuery() return runQuery()
} }
}) })
} }
...@@ -1497,19 +1416,16 @@ module.exports = (function() { ...@@ -1497,19 +1416,16 @@ module.exports = (function() {
var updatedFields = Object.keys(attrValueHash) var updatedFields = Object.keys(attrValueHash)
var skippedFields = Utils._.difference(Object.keys(self.attributes), updatedFields) var skippedFields = Utils._.difference(Object.keys(self.attributes), updatedFields)
build.hookValidate({skip: skippedFields}).error(function(err) { return build.hookValidate({skip: skippedFields}).then(function(attributes) {
emitter.emit('error', err)
}).success(function(attributes) {
if (!!attributes && !!attributes.dataValues) { if (!!attributes && !!attributes.dataValues) {
attrValueHash = Utils._.pick.apply(Utils._, [].concat(attributes.dataValues).concat(Object.keys(attrValueHash))) attrValueHash = Utils._.pick.apply(Utils._, [].concat(attributes.dataValues).concat(Object.keys(attrValueHash)))
} }
runSave() return runSave()
}) })
} else { } else {
runSave() return runSave()
} }
}).run()
} }
/** /**
......
var util = require("util") var Promise
, Promise
, EventEmitter = require("events").EventEmitter , EventEmitter = require("events").EventEmitter
, proxyEventKeys = ['success', 'error', 'sql'] , proxyEventKeys = ['success', 'error', 'sql']
, Utils = require('./utils') , Utils = require('./utils')
...@@ -12,7 +11,7 @@ var util = require("util") ...@@ -12,7 +11,7 @@ var util = require("util")
* @mixes https://github.com/petkaantonov/bluebird/blob/master/API.md * @mixes https://github.com/petkaantonov/bluebird/blob/master/API.md
* @class Promise * @class Promise
*/ */
var SequelizePromise = Promise = require('bluebird') var SequelizePromise = Promise = require('sequelize-bluebird')
/** /**
* Listen for events, event emitter style. Mostly for backwards compat. with EventEmitter * Listen for events, event emitter style. Mostly for backwards compat. with EventEmitter
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
"generic-pool": "2.0.4", "generic-pool": "2.0.4",
"sql": "~0.35.0", "sql": "~0.35.0",
"circular-json": "~0.1.5", "circular-json": "~0.1.5",
"bluebird": "git://github.com/sequelize/bluebird.git", "sequelize-bluebird": "git://github.com/sequelize/bluebird.git",
"node-uuid": "~1.4.1" "node-uuid": "~1.4.1"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -293,6 +293,35 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -293,6 +293,35 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
describe('addMultipleAssociations', function () {
it('adds associations without removing the current ones', function () {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
, Task = this.sequelize.define('Task', { title: DataTypes.STRING })
Task.hasMany(User)
return this.sequelize.sync({ force: true }).then(function() {
return User.bulkCreate([
{ username: 'foo '},
{ username: 'bar '},
{ username: 'baz '}
]).then(function () {
return Task.create({ title: 'task' }).then(function (task) {
return User.findAll().then(function(users) {
return task.setUsers([users[0]]).then(function () {
return task.addUsers([users[1], users[2]]).then(function () {
return task.getUsers().then(function (users) {
expect(users).to.have.length(3)
})
})
})
})
})
})
})
})
})
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
...@@ -923,6 +952,36 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -923,6 +952,36 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
describe('addMultipleAssociations', function () {
it('adds associations without removing the current ones', function () {
var User = this.sequelize.define('User', { username: DataTypes.STRING })
, Task = this.sequelize.define('Task', { title: DataTypes.STRING })
User.hasMany(Task)
Task.hasMany(User)
return this.sequelize.sync({ force: true }).then(function() {
return User.bulkCreate([
{ username: 'foo '},
{ username: 'bar '},
{ username: 'baz '}
]).then(function () {
return Task.create({ title: 'task' }).then(function (task) {
return User.findAll().then(function(users) {
return task.setUsers([users[0]]).then(function () {
return task.addUsers([users[1], users[2]]).then(function () {
return task.getUsers().then(function (users) {
expect(users).to.have.length(3)
})
})
})
})
})
})
})
})
})
describe('optimizations using bulk create, destroy and update', function () { describe('optimizations using bulk create, destroy and update', function () {
beforeEach(function (done) { beforeEach(function (done) {
this.User = this.sequelize.define('User', { username: DataTypes.STRING }, {timestamps: false}) this.User = this.sequelize.define('User', { username: DataTypes.STRING }, {timestamps: false})
......
...@@ -3,7 +3,6 @@ var chai = require('chai') ...@@ -3,7 +3,6 @@ var chai = require('chai')
, Support = require(__dirname + '/support') , Support = require(__dirname + '/support')
, DataTypes = require(__dirname + "/../lib/data-types") , DataTypes = require(__dirname + "/../lib/data-types")
, SequelizePromise = require(__dirname + "/../lib/promise") , SequelizePromise = require(__dirname + "/../lib/promise")
, Promise = require('bluebird')
, dialect = Support.getTestDialect() , dialect = Support.getTestDialect()
, _ = require('lodash') , _ = require('lodash')
, sinon = require('sinon') , sinon = require('sinon')
...@@ -389,7 +388,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () { ...@@ -389,7 +388,7 @@ describe(Support.getTestDialectTeaser("Promise"), function () {
it('should still work with .done() when resolving multiple results', function(done) { it('should still work with .done() when resolving multiple results', function(done) {
var spy = sinon.spy() var spy = sinon.spy()
, promise = new SequelizePromise(function (resolve, reject) { , promise = new SequelizePromise(function (resolve, reject) {
resolve(Promise.all(['MyModel', true])); resolve(SequelizePromise.all(['MyModel', true]));
}); });
promise.spread(spy); promise.spread(spy);
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!