Rewrite hooks API
This commit breaks compatibility, but adds consistent API and allow modify data of update
This commit is contained in:
parent
cee7029445
commit
5ea5da0153
|
@ -74,7 +74,7 @@ AbstractClass.prototype._initProperties = function (data, applySetters) {
|
|||
|
||||
ctor.forEachProperty(function (attr) {
|
||||
|
||||
if (!self.__data.hasOwnProperty(attr)) {
|
||||
if ('undefined' === typeof self.__data[attr]) {
|
||||
self.__data[attr] = self.__dataWas[attr] = getDefault(attr);
|
||||
} else {
|
||||
self.__dataWas[attr] = self.__data[attr];
|
||||
|
@ -110,11 +110,11 @@ AbstractClass.prototype._initProperties = function (data, applySetters) {
|
|||
return def;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
this.trigger("initialize");
|
||||
this.trigger('initialize');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,17 +181,21 @@ AbstractClass.create = function (data, callback) {
|
|||
obj = new this(data);
|
||||
data = obj.toObject(true);
|
||||
|
||||
// validation required
|
||||
obj.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
callback(new Error('Validation error'), obj);
|
||||
} else {
|
||||
create();
|
||||
}
|
||||
});
|
||||
obj.trigger('save', function(saveDone) {
|
||||
|
||||
// validation required
|
||||
obj.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
callback(new Error('Validation error'), obj);
|
||||
} else {
|
||||
create(saveDone);
|
||||
}
|
||||
});
|
||||
|
||||
}, obj);
|
||||
}
|
||||
|
||||
function create() {
|
||||
function create(saveDone) {
|
||||
obj.trigger('create', function (done) {
|
||||
|
||||
var data = this.toObject(true); // Added this to fix the beforeCreate trigger not fire.
|
||||
|
@ -207,12 +211,18 @@ AbstractClass.create = function (data, callback) {
|
|||
obj._rev = rev
|
||||
}
|
||||
done.call(this, function () {
|
||||
if (callback) {
|
||||
if (saveDone) {
|
||||
saveDone.call(obj, function () {
|
||||
if (callback) {
|
||||
callback(err, obj);
|
||||
}
|
||||
});
|
||||
} else if (callback) {
|
||||
callback(err, obj);
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
}, obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -646,7 +656,7 @@ AbstractClass.prototype.save = function (options, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -753,47 +763,50 @@ AbstractClass.prototype.updateAttributes = function updateAttributes(data, cb) {
|
|||
var inst = this;
|
||||
var model = this.constructor.modelName;
|
||||
|
||||
if(!data) data = {};
|
||||
if (typeof data === 'function') {
|
||||
cb = data;
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
// update instance's properties
|
||||
Object.keys(data).forEach(function (key) {
|
||||
inst[key] = data[key];
|
||||
});
|
||||
|
||||
inst.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
if (cb) {
|
||||
cb(new Error('Validation error'), inst);
|
||||
}
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
});
|
||||
inst.trigger('save', function (saveDone) {
|
||||
inst.trigger('update', function (done) {
|
||||
|
||||
function update() {
|
||||
inst.trigger('save', function (saveDone) {
|
||||
inst.trigger('update', function (done) {
|
||||
|
||||
Object.keys(data).forEach(function (key) {
|
||||
data[key] = inst[key];
|
||||
});
|
||||
|
||||
inst._adapter().updateAttributes(model, inst.id, inst.constructor._forDB(data), function (err) {
|
||||
if (!err) {
|
||||
// update _was attrs
|
||||
Object.keys(data).forEach(function (key) {
|
||||
inst.__dataWas[key] = inst.__data[key];
|
||||
});
|
||||
inst.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
if (cb) {
|
||||
cb(new Error('Validation error'), inst);
|
||||
}
|
||||
done.call(inst, function () {
|
||||
saveDone.call(inst, function () {
|
||||
cb(err, inst);
|
||||
} else {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
inst[key] = data[key];
|
||||
});
|
||||
|
||||
inst._adapter().updateAttributes(model, inst.id, inst.constructor._forDB(data), function (err) {
|
||||
if (!err) {
|
||||
// update _was attrs
|
||||
Object.keys(data).forEach(function (key) {
|
||||
inst.__dataWas[key] = inst.__data[key];
|
||||
});
|
||||
}
|
||||
done.call(inst, function () {
|
||||
saveDone.call(inst, function () {
|
||||
cb(err, inst);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, data);
|
||||
}, data);
|
||||
};
|
||||
|
||||
AbstractClass.prototype.fromObject = function (obj) {
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
var j = require('../'),
|
||||
should = require('should'),
|
||||
Schema = j.Schema,
|
||||
AbstractClass = j.AbstractClass,
|
||||
Hookable = j.Hookable,
|
||||
|
||||
db, User;
|
||||
|
||||
describe('hooks', function() {
|
||||
|
||||
before(function() {
|
||||
db = new Schema('memory');
|
||||
|
||||
User = db.define('User', {
|
||||
email: String,
|
||||
name: String,
|
||||
password: String,
|
||||
state: String
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialize', function() {
|
||||
|
||||
afterEach(function() {
|
||||
User.afterInitialize = null;
|
||||
});
|
||||
|
||||
it('should be triggered on new', function(done) {
|
||||
User.afterInitialize = function() {
|
||||
done();
|
||||
};
|
||||
new User;
|
||||
});
|
||||
|
||||
it('should be triggered on create', function(done) {
|
||||
var user;
|
||||
User.afterInitialize = function() {
|
||||
if (this.name === 'Nickolay') {
|
||||
this.name += ' Rozental';
|
||||
}
|
||||
};
|
||||
User.create({name: 'Nickolay'}, function(err, u) {
|
||||
u.id.should.be.a('number');
|
||||
u.name.should.equal('Nickolay Rozental');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create', function() {
|
||||
|
||||
afterEach(removeHooks('Create'));
|
||||
|
||||
it('should be triggered on create', function(done) {
|
||||
addHooks('Create', done);
|
||||
User.create();
|
||||
});
|
||||
|
||||
it('should not be triggered on new', function() {
|
||||
User.beforeCreate = function(next) {
|
||||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
var u = new User;
|
||||
});
|
||||
|
||||
it('should be triggered on new+save', function(done) {
|
||||
addHooks('Create', done);
|
||||
(new User).save();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('save', function() {
|
||||
afterEach(removeHooks('Save'));
|
||||
|
||||
it('should be triggered on create', function(done) {
|
||||
addHooks('Save', done);
|
||||
User.create();
|
||||
});
|
||||
|
||||
it('should be triggered on new+save', function(done) {
|
||||
addHooks('Save', done);
|
||||
(new User).save();
|
||||
});
|
||||
|
||||
it('should be triggered on updateAttributes', function(done) {
|
||||
User.create(function(err, user) {
|
||||
addHooks('Save', done);
|
||||
user.updateAttributes({name: 'Anatoliy'});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be triggered on save', function(done) {
|
||||
User.create(function(err, user) {
|
||||
addHooks('Save', done);
|
||||
user.name = 'Hamburger';
|
||||
user.save();
|
||||
});
|
||||
});
|
||||
|
||||
it('should save full object', function(done) {
|
||||
User.create(function(err, user) {
|
||||
User.beforeSave = function(next, data) {
|
||||
data.toObject().should.have.keys('id', 'name', 'email',
|
||||
'password', 'state')
|
||||
done();
|
||||
};
|
||||
user.save();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', function() {
|
||||
afterEach(removeHooks('Update'));
|
||||
|
||||
it('should not be triggered on create', function() {
|
||||
User.beforeUpdate = function(next) {
|
||||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
User.create();
|
||||
});
|
||||
|
||||
it('should not be triggered on new+save', function() {
|
||||
User.beforeUpdate = function(next) {
|
||||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
(new User).save();
|
||||
});
|
||||
|
||||
it('should be triggered on updateAttributes', function(done) {
|
||||
User.create(function (err, user) {
|
||||
addHooks('Update', done);
|
||||
user.updateAttributes({name: 'Anatoliy'});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be triggered on save', function(done) {
|
||||
User.create(function (err, user) {
|
||||
addHooks('Update', done);
|
||||
user.name = 'Hamburger';
|
||||
user.save();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update limited set of fields', function(done) {
|
||||
User.create(function (err, user) {
|
||||
User.beforeUpdate = function(next, data) {
|
||||
data.should.have.keys('name', 'email');
|
||||
done();
|
||||
};
|
||||
user.updateAttributes({name: 1, email: 2});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroy', function() {
|
||||
afterEach(removeHooks('Destroy'));
|
||||
|
||||
it('should be triggered on destroy', function() {
|
||||
var hook = 'not called';
|
||||
User.beforeDestroy = function() {
|
||||
hook = 'called';
|
||||
};
|
||||
User.afterDestroy = function() {
|
||||
hook.should.eql('called');
|
||||
done();
|
||||
};
|
||||
User.create(function (err, user) {
|
||||
user.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function addHooks(name, done) {
|
||||
var called = false, random = Math.floor(Math.random() * 1000);
|
||||
User['before' + name] = function(next, data) {
|
||||
called = true;
|
||||
data.email = random;
|
||||
next();
|
||||
};
|
||||
User['after' + name] = function(next) {
|
||||
(new Boolean(called)).should.equal(true);
|
||||
this.email.should.equal(random);
|
||||
done();
|
||||
};
|
||||
}
|
||||
|
||||
function removeHooks(name) {
|
||||
return function() {
|
||||
User['after' + name] = null;
|
||||
User['before' + name] = null;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
require('should');
|
||||
|
||||
if (!process.env.TRAVIS) {
|
||||
if (typeof __cov === 'undefined') {
|
||||
process.on('exit', function () {
|
||||
require('semicov').report();
|
||||
});
|
||||
}
|
||||
|
||||
require('semicov').init('lib');
|
||||
}
|
Loading…
Reference in New Issue