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

Commit 4dbbb89b by Sascha Depold

private method scoping for associations

1 parent 3e57b469
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types') , DataTypes = require('./../data-types')
var BelongsTo = module.exports = function(srcModel, targetModel, options) { module.exports = (function() {
this.source = srcModel var BelongsTo = function(srcModel, targetModel, options) {
this.target = targetModel this.source = srcModel
this.options = options this.target = targetModel
this.isSelfAssociation = (this.source.tableName == this.target.tableName) this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.source.options.underscored) if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.source.options.underscored)
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName) this.associationAccessor = this.isSelfAssociation
: this.target.tableName ? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
} : this.target.tableName
}
// the id is in the source table
BelongsTo.prototype.injectAttributes = function() { // the id is in the source table
var newAttributes = {} BelongsTo.prototype.injectAttributes = function() {
var newAttributes = {}
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER } this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.target.tableName) + "Id", this.source.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.source.attributes, Utils.simplifyAttributes(newAttributes))
return this Utils._.extend(this.source.attributes, Utils.simplifyAttributes(newAttributes))
} return this
}
BelongsTo.prototype.injectGetter = function(obj) {
var self = this BelongsTo.prototype.injectGetter = function(obj) {
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))) var self = this
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName)))
obj[accessor] = function() {
var id = obj[self.identifier] obj[accessor] = function() {
return self.target.find(id) var id = obj[self.identifier]
return self.target.find(id)
}
return this
} }
return this BelongsTo.prototype.injectSetter = function(obj) {
} var self = this
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
BelongsTo.prototype.injectSetter = function(obj) {
var self = this obj[accessor] = function(associatedObject) {
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName))) obj[self.identifier] = associatedObject ? associatedObject.id : null
return obj.save()
obj[accessor] = function(associatedObject) { }
obj[self.identifier] = associatedObject ? associatedObject.id : null
return obj.save() return this
} }
return this return BelongsTo
} })()
\ No newline at end of file
var Utils = require('./../utils') var Utils = require('./../utils')
var HasManyDoubleLinked = module.exports = function(definition, instance) { module.exports = (function() {
this.__definition = definition var HasManyDoubleLinked = function(definition, instance) {
this.instance = instance this.__definition = definition
} this.instance = instance
}
HasManyDoubleLinked.prototype.injectGetter = function() {
var self = this
var customEventEmitter = new Utils.CustomEventEmitter(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) {
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.destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() { HasManyDoubleLinked.prototype.injectGetter = function() {
var chainer = new Utils.QueryChainer var self = this
var foreignIdentifier = self.__definition.target.associations[self.__definition.associationAccessor].identifier
var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
if(obsoleteAssociations.length == 0) var customEventEmitter = new Utils.CustomEventEmitter(function() {
return emitter.emit('success', null) var where = {}
where[self.__definition.identifier] = self.instance.id
obsoleteAssociations.forEach(function(associatedObject) { var primaryKeys = Utils._.keys(self.__definition.connectorModel.rawAttributes)
var where = {}
, primaryKeys = Utils._.keys(self.__definition.connectorModel.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__definition.identifier })[0] , foreignKey = primaryKeys.filter(function(pk) { return pk != self.__definition.identifier })[0]
where[self.__definition.identifier] = self.instance.id self.__definition.connectorModel.findAll({where: where}).on('success', function(associatedObjects) {
where[foreignKey] = associatedObject.id var ids = associatedObjects.map(function(obj) { return obj[foreignKey] })
self.__definition.connectorModel.find({where: where}).on('success', function(connector) {
chainer.add(connector.destroy())
if(chainer.emitters.length == obsoleteAssociations.length) { if (ids.length == 0) {
// found all obsolete connectors and will delete them now customEventEmitter.emit('success', [])
chainer } else {
.run() self.__definition.target.findAll({where: 'id in (' + ids.join(", ") + ')'})
.on('success', function() { emitter.emit('success', null) }) .on('success', function(objects) { customEventEmitter.emit('success', objects) })
.on('failure', function(err) { emitter.emit('failure', err) }) .on('failure', function(err){ customEventEmitter.emit('failure', err) })
} }
}) })
}) })
})
return emitter.run() return customEventEmitter.run()
} }
HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) { HasManyDoubleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this var self = this
this.destroyObsoleteAssociations(oldAssociations, newAssociations) destroyObsoleteAssociations.call(this, oldAssociations, newAssociations)
.on('failure', function(err) { emitter.emit('failure', err) }) .on('failure', function(err) { emitter.emit('failure', err) })
.on('success', function() { .on('success', function() {
var chainer = new Utils.QueryChainer var chainer = new Utils.QueryChainer
, association = self.__definition.target.associations[self.__definition.associationAccessor] , association = self.__definition.target.associations[self.__definition.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier , foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) }) , unassociatedObjects = newAssociations.filter(function(obj) { return !obj.equalsOneOf(oldAssociations) })
unassociatedObjects.forEach(function(unassociatedObject) { unassociatedObjects.forEach(function(unassociatedObject) {
var attributes = {} var attributes = {}
attributes[self.__definition.identifier] = self.instance.id attributes[self.__definition.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id attributes[foreignIdentifier] = unassociatedObject.id
chainer.add(self.__definition.connectorModel.create(attributes)) chainer.add(self.__definition.connectorModel.create(attributes))
})
chainer
.run()
.on('success', function() { emitter.emit('success', newAssociations) })
.on('failure', function(err) { emitter.emit('failure', err) })
}) })
}
// private
var destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
var emitter = new Utils.CustomEventEmitter(function() {
var chainer = new Utils.QueryChainer
var foreignIdentifier = self.__definition.target.associations[self.__definition.associationAccessor].identifier
var obsoleteAssociations = oldAssociations.filter(function(obj) { return !obj.equalsOneOf(newAssociations) })
if(obsoleteAssociations.length == 0)
return emitter.emit('success', null)
chainer obsoleteAssociations.forEach(function(associatedObject) {
.run() var where = {}
.on('success', function() { emitter.emit('success', newAssociations) }) , primaryKeys = Utils._.keys(self.__definition.connectorModel.rawAttributes)
.on('failure', function(err) { emitter.emit('failure', err) }) , foreignKey = primaryKeys.filter(function(pk) { return pk != self.__definition.identifier })[0]
where[self.__definition.identifier] = self.instance.id
where[foreignKey] = associatedObject.id
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()
}
return HasManyDoubleLinked
})()
var Utils = require('./../utils') var Utils = require('./../utils')
var HasManySingleLinked = module.exports = function(definition, instance) { module.exports = (function() {
this.__definition = definition var HasManySingleLinked = function(definition, instance) {
this.instance = instance this.__definition = definition
} this.instance = instance
}
HasManySingleLinked.prototype.injectGetter = function() { HasManySingleLinked.prototype.injectGetter = function() {
var where = {} var where = {}
where[this.__definition.identifier] = this.instance.id where[this.__definition.identifier] = this.instance.id
return this.__definition.target.findAll({where: where}) return this.__definition.target.findAll({where: where})
} }
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) { HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this var self = this
// clear the old associations // clear the old associations
oldAssociations.forEach(function(associatedObject) { oldAssociations.forEach(function(associatedObject) {
associatedObject[self.__definition.identifier] = null associatedObject[self.__definition.identifier] = null
associatedObject.save() associatedObject.save()
}) })
// set the new one // set the new one
var chainer = new Utils.QueryChainer var chainer = new Utils.QueryChainer
newAssociations.forEach(function(associatedObject) { newAssociations.forEach(function(associatedObject) {
associatedObject[self.__definition.identifier] = self.instance.id associatedObject[self.__definition.identifier] = self.instance.id
chainer.add(associatedObject.save()) chainer.add(associatedObject.save())
}) })
chainer chainer
.run() .run()
.on('success', function() { emitter.emit('success', newAssociations) }) .on('success', function() { emitter.emit('success', newAssociations) })
.on('failure', function(err) { emitter.emit('failure', err) }) .on('failure', function(err) { emitter.emit('failure', err) })
} }
return HasManySingleLinked
})()
...@@ -4,124 +4,128 @@ var Utils = require("./../utils") ...@@ -4,124 +4,128 @@ var Utils = require("./../utils")
var HasManySingleLinked = require("./has-many-single-linked") var HasManySingleLinked = require("./has-many-single-linked")
, HasManyMultiLinked = require("./has-many-double-linked") , HasManyMultiLinked = require("./has-many-double-linked")
var HasMany = module.exports = function(srcModel, targetModel, options) { module.exports = (function() {
this.source = srcModel var HasMany = function(srcModel, targetModel, options) {
this.target = targetModel this.source = srcModel
this.options = options this.target = targetModel
this.isSelfAssociation = (this.source.tableName == this.target.tableName) this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
this.associationAccessor = this.combinedName = Utils.combineTableNames(
this.source.tableName, this.associationAccessor = this.combinedName = Utils.combineTableNames(
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName this.source.tableName,
) this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
)
var as = (this.options.as || Utils.pluralize(this.target.tableName))
var as = (this.options.as || Utils.pluralize(this.target.tableName))
this.accessors = {
get: Utils._.camelize('get_' + as), this.accessors = {
set: Utils._.camelize('set_' + as), get: Utils._.camelize('get_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as)), set: Utils._.camelize('set_' + as),
remove: Utils._.camelize(Utils.singularize('remove_' + as)) add: Utils._.camelize(Utils.singularize('add_' + as)),
remove: Utils._.camelize(Utils.singularize('remove_' + as))
}
} }
}
// the id is in the target table
// the id is in the target table // or in an extra table which connects two tables
// or in an extra table which connects two tables HasMany.prototype.injectAttributes = function() {
HasMany.prototype.injectAttributes = function() { var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor) this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
// is there already a single sided association between the source and the target?
// is there already a single sided association between the source and the target? // or is the association on the model itself?
// or is the association on the model itself? if (this.isSelfAssociation || multiAssociation) {
if (this.isSelfAssociation || multiAssociation) { // remove the obsolete association identifier from the source
// remove the obsolete association identifier from the source if(this.isSelfAssociation) {
if(this.isSelfAssociation) { this.foreignIdentifier = (this.options.as || this.target.tableName) + 'Id'
this.foreignIdentifier = (this.options.as || this.target.tableName) + 'Id' } else {
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
delete this.source.attributes[this.foreignIdentifier]
}
// define a new model, which connects the models
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorModel = this.source.modelManager.sequelize.define(this.combinedName, combinedTableAttributes)
if(!this.isSelfAssociation) this.target.associations[this.associationAccessor].connectorModel = this.connectorModel
this.connectorModel.sync()
} else { } else {
this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier var newAttributes = {}
delete this.source.attributes[this.foreignIdentifier] newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes))
} }
// define a new model, which connects the models return this
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorModel = this.source.modelManager.sequelize.define(this.combinedName, combinedTableAttributes)
if(!this.isSelfAssociation) this.target.associations[this.associationAccessor].connectorModel = this.connectorModel
this.connectorModel.sync()
} else {
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes))
} }
return this HasMany.prototype.injectGetter = function(obj) {
} var self = this
HasMany.prototype.injectGetter = function(obj) { obj[this.accessors.get] = function() {
var self = this var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
return new Class(self, this).injectGetter()
obj[this.accessors.get] = function() { }
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
return new Class(self, this).injectGetter() return this
} }
return this
}
HasMany.prototype.injectSetter = function(obj) { HasMany.prototype.injectSetter = function(obj) {
var self = this var self = this
obj[this.accessors.set] = function(newAssociatedObjects) { obj[this.accessors.set] = function(newAssociatedObjects) {
var instance = this var instance = this
// define the returned customEventEmitter, which will emit the success event once everything is done // define the returned customEventEmitter, which will emit the success event once everything is done
var customEventEmitter = new Utils.CustomEventEmitter(function() { var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(oldAssociatedObjects) { instance[self.accessors.get]().on('success', function(oldAssociatedObjects) {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectSetter(customEventEmitter, oldAssociatedObjects, newAssociatedObjects) new Class(self, instance).injectSetter(customEventEmitter, oldAssociatedObjects, newAssociatedObjects)
})
}) })
}) return customEventEmitter.run()
return customEventEmitter.run() }
}
obj[this.accessors.add] = function(newAssociatedObject) {
obj[this.accessors.add] = function(newAssociatedObject) { var instance = this
var instance = this var customEventEmitter = new Utils.CustomEventEmitter(function() {
var customEventEmitter = new Utils.CustomEventEmitter(function() { instance[self.accessors.get]()
instance[self.accessors.get]() .on('failure', function(err){ customEventEmitter.emit('failure', err)})
.on('failure', function(err){ customEventEmitter.emit('failure', err)}) .on('success', function(currentAssociatedObjects) {
.on('success', function(currentAssociatedObjects) { if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects)) currentAssociatedObjects.push(newAssociatedObject)
currentAssociatedObjects.push(newAssociatedObject)
instance[self.accessors.set](currentAssociatedObjects)
instance[self.accessors.set](currentAssociatedObjects) .on('success', function(instances) { customEventEmitter.emit('success', instances) })
.on('success', function(instances) { customEventEmitter.emit('success', instances) }) .on('failure', function(err) { customEventEmitter.emit('failure', err) })
})
})
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) }) .on('failure', function(err) { customEventEmitter.emit('failure', err) })
}) })
})
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 customEventEmitter.run() }
return this
} }
return this return HasMany
} })()
\ No newline at end of file
var Utils = require("./../utils") var Utils = require("./../utils")
, DataTypes = require('./../data-types') , DataTypes = require('./../data-types')
var HasOne = module.exports = function(srcModel, targetModel, options) { module.exports = (function() {
this.source = srcModel var HasOne = function(srcModel, targetModel, options) {
this.target = targetModel this.source = srcModel
this.options = options this.target = targetModel
this.isSelfAssociation = (this.source.tableName == this.target.tableName) this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.options.underscored) if(this.isSelfAssociation && !this.options.foreignKey && !!this.options.as)
this.options.foreignKey = Utils._.underscoredIf(Utils.singularize(this.options.as) + "Id", this.options.underscored)
this.associationAccessor = this.isSelfAssociation
? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName) this.associationAccessor = this.isSelfAssociation
: this.target.tableName ? Utils.combineTableNames(this.target.tableName, this.options.as || this.target.tableName)
: this.target.tableName
this.accessors = {
get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))), this.accessors = {
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName))) get: Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName))),
set: Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName)))
}
}
// the id is in the target table
HasOne.prototype.injectAttributes = function() {
var newAttributes = {}
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes))
return this
} }
}
HasOne.prototype.injectGetter = function(obj) {
// the id is in the target table var self = this
HasOne.prototype.injectAttributes = function() {
var newAttributes = {} obj[this.accessors.get] = function() {
var id = obj.id
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored) , where = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes)) where[self.identifier] = id
return self.target.find({where: where})
return this }
}
return this
HasOne.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var id = obj.id
, where = {}
where[self.identifier] = id
return self.target.find({where: where})
} }
return this HasOne.prototype.injectSetter = function(obj) {
} var self = this
HasOne.prototype.injectSetter = function(obj) { obj[this.accessors.set] = function(associatedObject) {
var self = this var customEventEmitter = new Utils.CustomEventEmitter(function() {
obj[self.accessors.get]().on('success', function(oldObj) {
obj[this.accessors.set] = function(associatedObject) { if(oldObj) {
var customEventEmitter = new Utils.CustomEventEmitter(function() { oldObj[self.identifier] = null
obj[self.accessors.get]().on('success', function(oldObj) { oldObj.save()
if(oldObj) { }
oldObj[self.identifier] = null
oldObj.save() associatedObject[self.identifier] = obj.id
} associatedObject.save()
.on('success', function() { customEventEmitter.emit('success', associatedObject) })
associatedObject[self.identifier] = obj.id .on('failure', function(err) { customEventEmitter.emit('failure', err) })
associatedObject.save() })
.on('success', function() { customEventEmitter.emit('success', associatedObject) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
}) })
}) return customEventEmitter.run()
return customEventEmitter.run() }
return this
} }
return this return HasOne
} })()
\ 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!