Merge pull request #286 from fabien/fix/polymorphic-transient

getTransientDataSource helper + embedsMany/polymorphic test case fix
This commit is contained in:
Raymond Feng 2014-09-04 09:21:38 -07:00
commit 5c7a9aee3a
3 changed files with 38 additions and 19 deletions

View File

@ -391,13 +391,13 @@ util.inherits(HasOne, Relation);
* EmbedsOne subclass * EmbedsOne subclass
* @param {RelationDefinition|Object} definition * @param {RelationDefinition|Object} definition
* @param {Object} modelInstance * @param {Object} modelInstance
* @returns {EmbedsMany} * @returns {EmbedsOne}
* @constructor * @constructor
* @class EmbedsOne * @class EmbedsOne
*/ */
function EmbedsOne(definition, modelInstance) { function EmbedsOne(definition, modelInstance) {
if (!(this instanceof EmbedsOne)) { if (!(this instanceof EmbedsOne)) {
return new EmbedsMany(definition, modelInstance); return new EmbedsOne(definition, modelInstance);
} }
assert(definition.type === RelationTypes.embedsOne); assert(definition.type === RelationTypes.embedsOne);
Relation.apply(this, arguments); Relation.apply(this, arguments);
@ -503,9 +503,9 @@ function lookupModelTo(modelFrom, modelTo, params, singularize) {
* @param {Object|String} params Name of the polymorphic relation or params * @param {Object|String} params Name of the polymorphic relation or params
* @returns {Object} The normalized parameters * @returns {Object} The normalized parameters
*/ */
function polymorphicParams(params) { function polymorphicParams(params, as) {
if (typeof params === 'string') params = { as: params }; if (typeof params === 'string') params = { as: params };
if (typeof params.as !== 'string') params.as = 'reference'; // default if (typeof params.as !== 'string') params.as = as || 'reference'; // default
params.foreignKey = params.foreignKey || i8n.camelize(params.as + '_id', true); params.foreignKey = params.foreignKey || i8n.camelize(params.as + '_id', true);
params.discriminator = params.discriminator || i8n.camelize(params.as + '_type', true); params.discriminator = params.discriminator || i8n.camelize(params.as + '_type', true);
return params; return params;
@ -1079,21 +1079,23 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
var idName, relationName, fk; var idName, relationName, fk;
if (params.polymorphic) { if (params.polymorphic) {
relationName = params.as || (typeof modelTo === 'string' ? modelTo : null); // initially
if (params.polymorphic === true) { if (params.polymorphic === true) {
// modelTo arg will be the name of the polymorphic relation (string) // modelTo arg will be the name of the polymorphic relation (string)
polymorphic = polymorphicParams(modelTo); polymorphic = polymorphicParams(modelTo, relationName);
} else { } else {
polymorphic = polymorphicParams(params.polymorphic); polymorphic = polymorphicParams(params.polymorphic, relationName);
} }
modelTo = null; // will lookup dynamically modelTo = null; // will lookup dynamically
idName = params.idName || 'id'; idName = params.idName || 'id';
relationName = params.as || polymorphic.as; relationName = params.as || polymorphic.as; // finally
fk = polymorphic.foreignKey; fk = polymorphic.foreignKey;
discriminator = polymorphic.discriminator; discriminator = polymorphic.discriminator;
if (typeof polymorphic.idType === 'string') { // explicit key type if (polymorphic.idType) { // explicit key type
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true }); modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true });
} else { // try to use the same foreign key type as modelFrom } else { // try to use the same foreign key type as modelFrom
modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName); modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName);
@ -2208,10 +2210,7 @@ EmbedsMany.prototype.build = function(targetModelData) {
var assignId = (forceId || targetModelData[pk] === undefined); var assignId = (forceId || targetModelData[pk] === undefined);
if (assignId && typeof connector.generateId === 'function') { if (assignId && pkType === Number) {
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
targetModelData[pk] = id;
} else if (assignId && pkType === Number) {
var ids = embeddedList.map(function(m) { var ids = embeddedList.map(function(m) {
return (typeof m[pk] === 'number' ? m[pk] : 0); return (typeof m[pk] === 'number' ? m[pk] : 0);
}); });
@ -2220,6 +2219,9 @@ EmbedsMany.prototype.build = function(targetModelData) {
} else { } else {
targetModelData[pk] = 1; targetModelData[pk] = 1;
} }
} else if (assignId && typeof connector.generateId === 'function') {
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
targetModelData[pk] = id;
} }
this.definition.applyProperties(modelInstance, targetModelData); this.definition.applyProperties(modelInstance, targetModelData);

View File

@ -1,5 +1,7 @@
// This test written in mocha+should.js // This test written in mocha+should.js
var should = require('./init.js'); var should = require('./init.js');
var jdb = require('../');
var DataSource = jdb.DataSource;
var db, tmp, Book, Chapter, Author, Reader; var db, tmp, Book, Chapter, Author, Reader;
var Category, Job; var Category, Job;
@ -7,6 +9,10 @@ var Picture, PictureLink;
var Person, Address; var Person, Address;
var Link; var Link;
var getTransientDataSource = function(settings) {
return new DataSource('transient', settings, db.modelBuilder);
};
describe('relations', function () { describe('relations', function () {
describe('hasMany', function () { describe('hasMany', function () {
@ -1679,7 +1685,7 @@ describe('relations', function () {
var Other; var Other;
before(function () { before(function () {
tmp = getSchema('transient'); tmp = getTransientDataSource();
db = getSchema(); db = getSchema();
Person = db.define('Person', {name: String}); Person = db.define('Person', {name: String});
Passport = tmp.define('Passport', Passport = tmp.define('Passport',
@ -1828,7 +1834,7 @@ describe('relations', function () {
var address1, address2; var address1, address2;
before(function (done) { before(function (done) {
tmp = getSchema('transient', {defaultIdType: Number}); tmp = getTransientDataSource({defaultIdType: Number});
db = getSchema(); db = getSchema();
Person = db.define('Person', {name: String}); Person = db.define('Person', {name: String});
Address = tmp.define('Address', {street: String}); Address = tmp.define('Address', {street: String});
@ -2008,7 +2014,7 @@ describe('relations', function () {
describe('embedsMany - explicit ids', function () { describe('embedsMany - explicit ids', function () {
before(function (done) { before(function (done) {
tmp = getSchema('transient'); tmp = getTransientDataSource();
db = getSchema(); db = getSchema();
Person = db.define('Person', {name: String}); Person = db.define('Person', {name: String});
Address = tmp.define('Address', {street: String}); Address = tmp.define('Address', {street: String});
@ -2415,11 +2421,16 @@ describe('relations', function () {
before(function (done) { before(function (done) {
db = getSchema(); db = getSchema();
tmp = getTransientDataSource();
Book = db.define('Book', {name: String}); Book = db.define('Book', {name: String});
Author = db.define('Author', {name: String}); Author = db.define('Author', {name: String});
Reader = db.define('Reader', {name: String}); Reader = db.define('Reader', {name: String});
Link = db.define('Link', {name: String, notes: String}); // generic model Link = tmp.define('Link', {
id: {type: Number, id: true},
name: String, notes: String
}); // generic model
Link.validatesPresenceOf('linkedId'); Link.validatesPresenceOf('linkedId');
Link.validatesPresenceOf('linkedType'); Link.validatesPresenceOf('linkedType');
@ -2433,13 +2444,15 @@ describe('relations', function () {
}); });
it('can be declared', function (done) { it('can be declared', function (done) {
var idType = db.connector.getDefaultIdType();
Book.embedsMany(Link, { as: 'people', Book.embedsMany(Link, { as: 'people',
polymorphic: 'linked', polymorphic: 'linked',
scope: { include: 'linked' } scope: { include: 'linked' }
}); });
Link.belongsTo('linked', { Link.belongsTo('linked', {
polymorphic: true, // needs unique auto-id polymorphic: { idType: idType }, // native type
properties: { name: 'name' }, // denormalized properties: { name: 'name' }, // denormalized
options: { invertProperties: true } options: { invertProperties: true }
}); });
db.automigrate(done); db.automigrate(done);

View File

@ -6,10 +6,14 @@ var should = require('./init.js');
var db, TransientModel, Person, Widget, Item; var db, TransientModel, Person, Widget, Item;
var getTransientDataSource = function(settings) {
return new DataSource('transient', settings);
};
describe('Transient connector', function () { describe('Transient connector', function () {
before(function () { before(function () {
db = getSchema('transient'); db = getTransientDataSource();
TransientModel = db.define('TransientModel', {}, { idInjection: false }); TransientModel = db.define('TransientModel', {}, { idInjection: false });
Person = TransientModel.extend('Person', {name: String}); Person = TransientModel.extend('Person', {name: String});