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

Commit f35d6227 by Sascha Depold

added addModel + removeModel for adding or removing single associations

1 parent be4e58ea
......@@ -12,38 +12,79 @@ HasManyDoubleLinked.prototype.injectGetter = function() {
var where = {}
where[self.definition.identifier] = self.instance.id
var primaryKeys = Utils._.keys(self.definition.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.definition.identifier })[0]
self.definition.connectorModel.findAll({where: where}).on('success', function(associatedObjects) {
customEventEmitter.emit('success', associatedObjects)
var ids = associatedObjects.map(function(obj) { return obj[foreignKey] })
if (ids.length == 0) {
customEventEmitter.emit('success', [])
} else {
self.definition.target.findAll({where: 'id in (' + ids.join(", ") + ')'})
.on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('failure', function(err){ customEventEmitter.emit('failure', err) })
}
})
})
return customEventEmitter.run()
}
HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
HasManyDoubleLinked.prototype.destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
// destroy the old association objects
var emitter = new Utils.CustomEventEmitter(function() {
var chainer = new Utils.QueryChainer
var foreignIdentifier = self.definition.target.associations[self.definition.source.tableName].identifier
var destroyChainer = new Utils.QueryChainer
oldAssociations.forEach(function(associatedObject) { destroyChainer.add(associatedObject.destroy()) })
var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
if(obsoleteAssociations.length == 0)
return emitter.emit('success', null)
obsoleteAssociations.forEach(function(associatedObject) {
var where = {}
, primaryKeys = Utils._.keys(self.definition.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.definition.identifier })[0]
where[self.definition.identifier] = self.instance.id
where[foreignKey] = associatedObject.id
destroyChainer
self.definition.connectorModel.find({where: where}).on('success', function(connector) {
chainer.add(connector.destroy())
if(chainer.emitters.length == obsoleteAssociations.length) {
// found all obsolete connectors and will delete them now
chainer
.run()
.on('success', function() { emitter.emit('success', null) })
.on('failure', function(err) { emitter.emit('failure', err) })
}
})
})
})
return emitter.run()
}
HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
this.destroyObsoleteAssociations(oldAssociations, newAssociations)
.on('failure', function(err) { emitter.emit('failure', err) })
.on('success', function() {
// create new one
var chainer = new Utils.QueryChainer
var foreignIdentifier = self.definition.target.associations[self.definition.source.tableName].identifier
var unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) })
var createChainer = new Utils.QueryChainer
newAssociations.forEach(function(associatedObject) {
unassociatedObjects.forEach(function(unassociatedObject) {
var attributes = {}
attributes[self.definition.identifier] = self.instance.id
attributes[foreignIdentifier] = associatedObject.id
attributes[foreignIdentifier] = unassociatedObject.id
createChainer.add(self.definition.connectorModel.create(attributes))
chainer.add(self.definition.connectorModel.create(attributes))
})
createChainer
chainer
.run()
.on('success', function() { emitter.emit('success', null) })
.on('failure', function(err) { emitter.emit('failure', err) })
......
......@@ -10,9 +10,12 @@ var HasMany = module.exports = function(srcModel, targetModel, options) {
this.options = options
var as = (this.options.as || Utils.pluralize(this.target.tableName))
this.accessors = {
get: Utils._.camelize('get_' + as),
set: Utils._.camelize('set_' + as)
set: Utils._.camelize('set_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as)),
remove: Utils._.camelize(Utils.singularize('remove_' + as))
}
}
......@@ -72,5 +75,41 @@ HasMany.prototype.injectSetter = function(obj) {
return customEventEmitter.run()
}
obj[this.accessors.add] = function(newAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.on('failure', function(err){ customEventEmitter.emit('failure', err)})
.on('success', function(currentAssociatedObjects) {
if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
currentAssociatedObjects.push(newAssociatedObject)
instance[self.accessors.set](currentAssociatedObjects)
.on('success', function() { customEventEmitter.emit('success', null) })
.on('failure', function() { customEventEmitter.emit('failure', null) })
})
})
return customEventEmitter.run()
}
obj[this.accessors.remove] = function(oldAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(currentAssociatedObjects) {
var newAssociations = []
currentAssociatedObjects.forEach(function(association) {
if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers))
newAssociations.push(association)
})
instance[self.accessors.set](newAssociations)
.on('success', function() { customEventEmitter.emit('success', null) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
})
})
return customEventEmitter.run()
}
return this
}
\ No newline at end of file
......@@ -148,5 +148,25 @@ Model.prototype.__defineGetter__('primaryKeyValues', function() {
return result
})
Model.prototype.equals = function(other) {
var result = true
, self = this
Utils._.each(this.values, function(value, key) {
result = result && (value == other[key])
})
return result
}
Model.prototype.equalsOneOf = function(others) {
var result = false
, self = this
others.forEach(function(other) { result = result || self.equals(other) })
return result
}
/* Add the instance methods to Model */
Utils._.map(Mixin.instanceMethods, function(fct, name) { Model.prototype[name] = fct})
\ No newline at end of file
......@@ -24,7 +24,7 @@ QueryChainer.prototype.observeEmitter = function(emitter) {
var self = this
emitter
.on('success', function(){ self.finishedEmits++; self.finish() })
.on('failure', function(){ self.finishedEmits++; self.fails.push(emitter); self.finish() })
.on('failure', function(err){ self.finishedEmits++; self.fails.push(err); self.finish() })
}
QueryChainer.prototype.finish = function(result) {
this.finished = (this.finishedEmits == this.emitters.length)
......
......@@ -152,6 +152,8 @@ var QueryGenerator = module.exports = {
result = Utils.addTicks('id') + "=" + Utils.escape(smth)
else if(typeof smth == "string")
result = smth
else if(Array.isArray(smth))
result = Utils.format(smth)
return result
},
......
......@@ -29,6 +29,9 @@ var Utils = module.exports = {
escape: function(s) {
return client.escape(s)
},
format: function(arr) {
return client.format.apply(client, arr)
},
isHash: function(obj) {
return (typeof obj == 'object') && !obj.hasOwnProperty('length')
},
......
var assert = require("assert")
, config = require("./../config")
, Sequelize = require("./../../index")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
var initialize = function(options, callback) {
options = options || {}
options.taskCount = options.taskCount || 1
options.userCount = options.userCount || 1
var num = parseInt(Math.random() * 9999999999999)
, User = sequelize.define('User' + num, { name: Sequelize.STRING })
, Task = sequelize.define('Task' + num, { name: Sequelize.STRING })
, chainer = new Sequelize.Utils.QueryChainer
User.hasMany(Task, {as:'Tasks'})
Task.hasMany(User, {as:'Users'})
sequelize.sync({force: true}).on('success', function() {
for(var i = 0; i < options.taskCount; i++)
chainer.add(Task.create({name: 'task'+i}))
for(var i = 0; i < options.userCount; i++)
chainer.add(User.create({name: 'user'+i}))
chainer.run().on('success', function() {
callback(Task, User)
})
})
}
module.exports = {
'it should correctly add an association to the model': function(exit) {
initialize({taskCount:5, userCount:2}, function(Task, User) {
User.all.on('success', function(users) {
Task.all.on('success', function(tasks) {
var user = users[0]
user.getTasks().on('success', function(_tasks) {
assert.eql(_tasks.length, 0)
user.addTask(tasks[0]).on('success', function() {
user.getTasks().on('success', function(_tasks) {
assert.eql(_tasks.length, 1)
exit(function(){})
})
})
})
})
})
})
},
'it should correctly remove associated objects': function(exit) {
initialize({taskCount:5, userCount:2}, function(Task, User) {
User.all.on('success', function(users) {
Task.all.on('success', function(tasks) {
var user = users[0]
user.getTasks().on('success', function(_tasks) {
assert.eql(_tasks.length, 0)
user.setTasks(tasks).on('success', function() {
user.getTasks().on('success', function(_tasks) {
assert.eql(_tasks.length, tasks.length)
user.removeTask(tasks[0]).on('success', function() {
user.getTasks().on('success', function(_tasks) {
assert.eql(_tasks.length, tasks.length - 1)
exit(function(){})
})
})
})
})
})
})
})
})
}
}
\ No newline at end of file
var assert = require("assert")
, config = require("./../config")
, Sequelize = require("./../../index")
, sequelize = new Sequelize(config.database, config.username, config.password, {logging: false})
module.exports = {
'it should correctly determine equal objects': function(exit) {
var User = sequelize.define('User' + parseInt(Math.random() * 99999999), { name: Sequelize.STRING, bio: Sequelize.TEXT })
User.sync({force: true}).on('success', function() {
User.create({name: 'hallo', bio: 'welt'}).on('success', function(u) {
assert.eql(u.equals(u), true)
exit(function(){})
})
})
},
'it should correctly work with different primary keys': function(exit) {
var User = sequelize.define('User' + parseInt(Math.random() * 99999999), {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING, bio: Sequelize.TEXT
})
User.sync({force: true}).on('success', function() {
User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
assert.eql(u.equals(u), true)
exit(function(){})
})
})
},
'equalsOneOf should work': function(exit) {
var User = sequelize.define('User' + parseInt(Math.random() * 99999999), {
foo: {type: Sequelize.STRING, primaryKey: true},
bar: {type: Sequelize.STRING, primaryKey: true},
name: Sequelize.STRING, bio: Sequelize.TEXT
})
User.sync({force: true}).on('success', function() {
User.create({foo: '1', bar: '2', name: 'hallo', bio: 'welt'}).on('success', function(u) {
assert.eql(u.equalsOneOf([u, {a:1}]), true)
assert.eql(u.equalsOneOf([{b:2}, {a:1}]), false)
exit(function(){})
})
})
}
}
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!