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

Commit e3c32c94 by Sascha Depold

Merge branch 'mstorgaard-dirty-tracking'

2 parents e83132dd 5b0d6497
...@@ -382,7 +382,7 @@ module.exports = (function() { ...@@ -382,7 +382,7 @@ module.exports = (function() {
} }
DAOFactory.prototype.build = function(values, options) { DAOFactory.prototype.build = function(values, options) {
options = options || { isNewRecord: true } options = options || { isNewRecord: true, isDirty: true }
var self = this var self = this
, instance = new this.DAO(values, this.options, options.isNewRecord) , instance = new this.DAO(values, this.options, options.isNewRecord)
...@@ -390,6 +390,7 @@ module.exports = (function() { ...@@ -390,6 +390,7 @@ module.exports = (function() {
instance.isNewRecord = options.isNewRecord instance.isNewRecord = options.isNewRecord
instance.daoFactoryName = this.name instance.daoFactoryName = this.name
instance.daoFactory = this instance.daoFactory = this
instance.isDirty = options.isDirty
return instance return instance
} }
......
...@@ -84,10 +84,15 @@ module.exports = (function() { ...@@ -84,10 +84,15 @@ module.exports = (function() {
DAO.prototype.getDataValue = function(name) { DAO.prototype.getDataValue = function(name) {
return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name] return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name]
} }
DAO.prototype.get = DAO.prototype.getDataValue
DAO.prototype.setDataValue = function(name, value) { DAO.prototype.setDataValue = function(name, value) {
if (Utils.hasChanged(this.dataValues[name], value)) {
this.isDirty = true
}
this.dataValues[name] = value this.dataValues[name] = value
} }
DAO.prototype.set = DAO.prototype.setDataValue
// if an array with field names is passed to save() // if an array with field names is passed to save()
// only those fields will be updated // only those fields will be updated
...@@ -149,6 +154,7 @@ module.exports = (function() { ...@@ -149,6 +154,7 @@ module.exports = (function() {
}).run() }).run()
} }
else if (this.isNewRecord) { else if (this.isNewRecord) {
this.isDirty = false
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
} else { } else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id }; var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id };
...@@ -157,6 +163,7 @@ module.exports = (function() { ...@@ -157,6 +163,7 @@ module.exports = (function() {
identifier = this.__options.whereCollection; identifier = this.__options.whereCollection;
} }
this.isDirty = false
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier) , query = this.QueryInterface.update(this, tableName, values, identifier)
...@@ -191,6 +198,7 @@ module.exports = (function() { ...@@ -191,6 +198,7 @@ module.exports = (function() {
this[valueName] = obj.values[valueName] this[valueName] = obj.values[valueName]
} }
} }
this.isDirty = false
emitter.emit('success', this) emitter.emit('success', this)
}.bind(this)) }.bind(this))
}.bind(this)).run() }.bind(this)).run()
...@@ -224,14 +232,21 @@ module.exports = (function() { ...@@ -224,14 +232,21 @@ module.exports = (function() {
readOnlyAttributes.push('updatedAt') readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt') readOnlyAttributes.push('deletedAt')
var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) { Utils._.each(updates, function(value, attr) {
var updateAllowed = ( var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) && (readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) && (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1) (self.attributes.indexOf(attr) > -1)
) )
updateAllowed && (self[attr] = value) if (updateAllowed) {
if (Utils.hasChanged(self[attr], value)) {
isDirty = true
}
self[attr] = value
}
}) })
this.isDirty = isDirty
} }
DAO.prototype.destroy = function() { DAO.prototype.destroy = function() {
...@@ -330,7 +345,19 @@ module.exports = (function() { ...@@ -330,7 +345,19 @@ module.exports = (function() {
// (the same is true for __defineSetter and 'prototype' getters) // (the same is true for __defineSetter and 'prototype' getters)
if (has !== true) { if (has !== true) {
this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; }); this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; });
this.__defineSetter__(attribute, has.set || function(v) { this.dataValues[attribute] = v; }); this.__defineSetter__(attribute, has.set || function(v) {
if (Utils.hasChanged(this.dataValues[attribute], v)) {
//Only dirty the object if the change is not due to id, touchedAt, createdAt or updatedAt being initiated
var updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
, createdAtAttr = this.__options.underscored ? 'created_at' : 'createdAt'
, touchedAtAttr = this.__options.underscored ? 'touched_at' : 'touchedAt'
if (this.dataValues[attribute] || (attribute != 'id' && attribute != touchedAtAttr && attribute != createdAtAttr && attribute != updatedAtAttr)) {
this.isDirty = true
}
}
this.dataValues[attribute] = v
});
} }
this[attribute] = value; this[attribute] = value;
......
...@@ -262,7 +262,7 @@ module.exports = (function() { ...@@ -262,7 +262,7 @@ module.exports = (function() {
result = transformRowsWithEagerLoadingIntoDaos.call(this, results) result = transformRowsWithEagerLoadingIntoDaos.call(this, results)
} else { } else {
result = results.map(function(result) { result = results.map(function(result) {
return this.callee.build(result, { isNewRecord: false }) return this.callee.build(result, { isNewRecord: false, isDirty: false })
}.bind(this)) }.bind(this))
} }
...@@ -287,7 +287,7 @@ module.exports = (function() { ...@@ -287,7 +287,7 @@ module.exports = (function() {
var transformRowWithEagerLoadingIntoDao = function(result, dao) { var transformRowWithEagerLoadingIntoDao = function(result, dao) {
// let's build the actual dao instance first... // let's build the actual dao instance first...
dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false }) dao = dao || this.callee.build(result[this.callee.tableName], { isNewRecord: false, isDirty: false })
// ... and afterwards the prefetched associations // ... and afterwards the prefetched associations
for (var tableName in result) { for (var tableName in result) {
...@@ -323,7 +323,7 @@ module.exports = (function() { ...@@ -323,7 +323,7 @@ module.exports = (function() {
accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1) accessor = accessor.slice(0,1).toLowerCase() + accessor.slice(1)
associationData.forEach(function(data) { associationData.forEach(function(data) {
var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false }) var daoInstance = associatedDaoFactory.build(data, { isNewRecord: false, isDirty: false })
, isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers) , isEmpty = !Utils.firstValueOfHash(daoInstance.identifiers)
if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) { if (['BelongsTo', 'HasOne'].indexOf(association.associationType) > -1) {
......
...@@ -252,6 +252,23 @@ var Utils = module.exports = { ...@@ -252,6 +252,23 @@ var Utils = module.exports = {
isHash: function(obj) { isHash: function(obj) {
return Utils._.isObject(obj) && !Array.isArray(obj); return Utils._.isObject(obj) && !Array.isArray(obj);
}, },
hasChanged: function(attrValue, value) {
//If attribute value is Date, check value as a date
if (Utils._.isDate(attrValue) && !Utils._.isDate(value)) {
value = new Date(value)
}
if (Utils._.isDate(attrValue)) {
return attrValue.valueOf() !== value.valueOf()
}
//If both of them are empty, don't set as changed
if ((attrValue === undefined || attrValue === null || attrValue === '') && (value === undefined || value === null || value === '')) {
return false
}
return attrValue !== value
},
argsArePrimaryKeys: function(args, primaryKeys) { argsArePrimaryKeys: function(args, primaryKeys) {
var result = (args.length == Object.keys(primaryKeys).length) var result = (args.length == Object.keys(primaryKeys).length)
if (result) { if (result) {
......
...@@ -19,6 +19,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -19,6 +19,7 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW }, touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
aNumber: { type: DataTypes.INTEGER }, aNumber: { type: DataTypes.INTEGER },
bNumber: { type: DataTypes.INTEGER }, bNumber: { type: DataTypes.INTEGER },
aDate: { type: DataTypes.DATE },
validateTest: { validateTest: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
...@@ -110,6 +111,132 @@ describe(Support.getTestDialectTeaser("DAO"), function () { ...@@ -110,6 +111,132 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
}) })
}) })
describe('isDirty', function() {
it('returns true for non-saved objects', function(done) {
var user = this.User.build({ username: 'user' })
expect(user.id).to.be.null
expect(user.isDirty).to.be.true
done()
})
it("returns false for saved objects", function(done) {
this.User.build({ username: 'user' }).save().success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'new'
expect(user.isDirty).to.be.true
done()
})
})
it("returns false for non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'user'
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for non-changed date attribute", function(done) {
this.User.create({ aDate: new Date(2013, 6, 31, 14, 25, 21) }).success(function(user) {
user.aDate = '2013-07-31 14:25:21'
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for two empty attributes", function(done) {
this.User.create({ username: null }).success(function(user) {
user.username = ''
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for bulk changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.setAttributes({
username: 'new',
aNumber: 1
})
expect(user.isDirty).to.be.true
done()
})
})
it("returns false for bulk non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.setAttributes({
username: 'user'
})
expect(user.isDirty).to.be.false
done()
})
})
it("returns true for changed and bulk non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.aNumber = 23
user.setAttributes({
username: 'user'
})
expect(user.isDirty).to.be.true
done()
})
})
it("returns true for changed attribute and false for saved object", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.username = 'new'
expect(user.isDirty).to.be.true
user.save().success(function() {
expect(user.isDirty).to.be.false
done()
})
})
})
it("returns false for created objects", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
it("returns false for objects found by find method", function(done) {
var self = this
this.User.create({ username: 'user' }).success(function(user) {
self.User.find(user.id).success(function(user) {
expect(user.isDirty).to.be.false
done()
})
})
})
it("returns false for objects found by findAll method", function(done) {
var self = this
, users = []
for (var i = 0; i < 10; i++) {
users[users.length] = {username: 'user'}
}
this.User.bulkCreate(users).success(function() {
self.User.findAll().success(function(users) {
users.forEach(function(u) {
expect(u.isDirty).to.be.false
})
done()
})
})
})
})
describe('increment', function () { describe('increment', function () {
beforeEach(function(done) { beforeEach(function(done) {
this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(function(){ this.User.create({ id: 1, aNumber: 0, bNumber: 0 }).complete(function(){
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!