Introduce embedsMany persistent: true option

When set, instead of only embedding the model (on creation) it will be
persisted first, and subsequently embedded.
This commit is contained in:
Fabien Franzen 2014-09-07 12:59:47 +02:00
parent ef816d490a
commit 95764232b9
2 changed files with 53 additions and 23 deletions

View File

@ -2199,24 +2199,34 @@ EmbedsMany.prototype.create = function (targetModelData, cb) {
var inst = this.build(targetModelData);
var err = inst.isValid() ? null : new ValidationError(inst);
if (err) {
return process.nextTick(function() {
cb(err);
var updateEmbeddedList = function() {
modelInstance.updateAttribute(propertyName,
embeddedList, function(err, modelInst) {
cb(err, err ? null : inst);
});
}
modelInstance.updateAttribute(propertyName,
embeddedList, function(err, modelInst) {
cb(err, err ? null : inst);
});
if (this.definition.options.persistent) {
inst.save(function(err) { // will validate
if (err) return cb(err, inst);
updateEmbeddedList();
});
} else {
var err = inst.isValid() ? null : new ValidationError(inst);
if (err) {
return process.nextTick(function() {
cb(err);
});
}
updateEmbeddedList();
}
};
EmbedsMany.prototype.build = function(targetModelData) {
var modelTo = this.definition.modelTo;
var modelInstance = this.modelInstance;
var forceId = this.definition.options.forceId;
var persistent = this.definition.options.persistent;
var connector = modelTo.dataSource.connector;
var pk = this.definition.keyTo;
@ -2228,6 +2238,7 @@ EmbedsMany.prototype.build = function(targetModelData) {
targetModelData = targetModelData || {};
var assignId = (forceId || targetModelData[pk] === undefined);
assignId = assignId && !persistent;
if (assignId && pkType === Number) {
var ids = embeddedList.map(function(m) {

View File

@ -13,6 +13,10 @@ var getTransientDataSource = function(settings) {
return new DataSource('transient', settings, db.modelBuilder);
};
var getMemoryDataSource = function(settings) {
return new DataSource('memory', settings, db.modelBuilder);
};
describe('relations', function () {
describe('hasMany', function () {
@ -2188,8 +2192,12 @@ describe('relations', function () {
var address0, address1, address2;
var person;
// This test spefically uses the Memory connector
// in order to test the use of the auto-generated
// id, in the sequence of the related model.
before(function (done) {
db = getSchema();
db = getMemoryDataSource();
Person = db.define('Person', {name: String});
Address = db.define('Address', {street: String});
Address.validatesPresenceOf('street');
@ -2200,12 +2208,18 @@ describe('relations', function () {
});
it('can be declared', function (done) {
Person.embedsMany(Address, {scope: {order: 'street'}});
// to save related model itself, set
// persistent: true
Person.embedsMany(Address, {
scope: {order: 'street'},
options: {persistent: true}
});
db.automigrate(done);
});
it('should create individual items (0)', function(done) {
Address.create({ street: 'Street 0' }, function(err, inst) {
inst.id.should.equal(1); // offset sequence
address0 = inst;
done();
});
@ -2213,6 +2227,7 @@ describe('relations', function () {
it('should create individual items (1)', function(done) {
Address.create({ street: 'Street 1' }, function(err, inst) {
inst.id.should.equal(2);
address1 = inst;
done();
});
@ -2220,21 +2235,29 @@ describe('relations', function () {
it('should create individual items (2)', function(done) {
Address.create({ street: 'Street 2' }, function(err, inst) {
inst.id.should.equal(3);
address2 = inst;
done();
});
});
it('should create individual items (3)', function(done) {
Address.create({ street: 'Street 3' }, function(err, inst) {
inst.id.should.equal(4); // offset sequence
done();
});
});
it('should add embedded items on scope', function(done) {
Person.create({ name: 'Fred' }, function(err, p) {
person = p;
p.addressList.create(address1.toObject(), function(err, address) {
should.not.exist(err);
address.id.should.eql(address1.id);
address.id.should.eql(2);
address.street.should.equal('Street 1');
p.addressList.create(address2.toObject(), function(err, address) {
should.not.exist(err);
address.id.should.eql(address2.id);
address.id.should.eql(3);
address.street.should.equal('Street 2');
done();
});
@ -2244,10 +2267,10 @@ describe('relations', function () {
it('should create embedded items on scope', function(done) {
Person.findById(person.id, function(err, p) {
p.addressList.create({ street: 'Street 3' }, function(err, address) {
p.addressList.create({ street: 'Street 4' }, function(err, address) {
should.not.exist(err);
address.should.have.property('id'); // not within Address seq!
address.street.should.equal('Street 3');
address.id.should.equal(5); // in Address sequence, correct offset
address.street.should.equal('Street 4');
done();
});
});
@ -2260,7 +2283,7 @@ describe('relations', function () {
addresses.should.have.length(3);
addresses[0].street.should.equal('Street 1');
addresses[1].street.should.equal('Street 2');
addresses[2].street.should.equal('Street 3');
addresses[2].street.should.equal('Street 4');
done();
});
});
@ -2269,12 +2292,8 @@ describe('relations', function () {
it('should validate embedded items on scope - id', function(done) {
Person.create({ name: 'Wilma' }, function(err, p) {
p.addressList.create({ id: null, street: 'Street 1' }, function(err, address) {
should.exist(err);
err.name.should.equal('ValidationError');
err.details.codes.addresses.should.eql(['invalid']);
var expected = 'The `Person` instance is not valid. ';
expected += 'Details: `addresses` contains invalid item at index `0`: `id` is blank.';
err.message.should.equal(expected);
should.not.exist(err);
address.street.should.equal('Street 1');
done();
});
});