Add support for hasMany-through and more tests

This commit is contained in:
Raymond Feng 2013-11-05 09:29:24 -08:00
parent 4fbec288c4
commit 68cf633795
2 changed files with 98 additions and 16 deletions

View File

@ -12,6 +12,7 @@ var EventEmitter = require('events').EventEmitter;
var util = require('util');
var path = require('path');
var fs = require('fs');
var assert = require('assert');
var async = require('async');
var existsSync = fs.existsSync || path.existsSync;
@ -302,6 +303,13 @@ DataSource.prototype.setup = function(name, settings) {
};
};
function isModelClass(cls) {
if(!cls) {
return false;
}
return cls.prototype instanceof ModelBaseClass;
}
/**
* Define a model class
*
@ -383,33 +391,62 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define
var relations = settings.relationships || settings.relations;
// Create a function for the closure in the loop
var createListener = function(name, relation, targetModel) {
targetModel.once('defined', function(model) {
// The target model is resolved
var params = {
foreignKey: relation.foreignKey,
as: name,
model: model
};
NewClass[relation.type].call(NewClass, name, params);
});
var createListener = function (name, relation, targetModel, throughModel) {
if (targetModel && targetModel.settings.unresolved) {
targetModel.once('defined', function (model) {
// Check if the through model doesn't exist or resolved
if (!throughModel || !throughModel.settings.unresolved) {
// The target model is resolved
var params = {
foreignKey: relation.foreignKey,
as: name,
model: model
};
if (throughModel) {
params.through = throughModel;
}
NewClass[relation.type].call(NewClass, name, params);
}
});
}
if (throughModel && throughModel.settings.unresolved) {
// Set up a listener to the through model
throughModel.once('defined', function (model) {
if (!targetModel.settings.unresolved) {
// The target model is resolved
var params = {
foreignKey: relation.foreignKey,
as: name,
model: targetModel,
through: model
};
NewClass[relation.type].call(NewClass, name, params);
}
});
}
};
// Set up the relations
if (relations) {
for (var rn in relations) {
var r = relations[rn];
if (!r.type) {
throw new Error('Relation type is required for ' + r);
}
var targetModel = this.models[r.model];
assert(['belongsTo', 'hasMany', 'hasAndBelongsToMany'].indexOf(r.type) !== -1, "Invalid relation type: " + r.type);
var targetModel = isModelClass(r.model) ? r.model : this.models[r.model];
if(!targetModel) {
// The target model doesn't exist, let create a place holder for it
targetModel = this.define(r.model, {}, {unresolved: true});
}
if(targetModel.settings.unresolved) {
var throughModel = null;
if(r.through) {
throughModel = isModelClass(r.through) ? r.through : this.models[r.through];
if(!throughModel) {
// The through model doesn't exist, let create a place holder for it
throughModel = this.define(r.through, {}, {unresolved: true});
}
}
if(targetModel.settings.unresolved || (throughModel && throughModel.settings.unresolved)) {
// Create a listener to defer the relation set up
createListener(rn, r, targetModel);
createListener(rn, r, targetModel, throughModel);
} else {
// The target model is resolved
var params = {
@ -417,6 +454,9 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define
as: rn,
model: targetModel
};
if(throughModel) {
params.through = throughModel;
}
NewClass[r.type].call(NewClass, rn, params);
}
}

View File

@ -444,6 +444,10 @@ describe('Load models with relations', function () {
var ds = new DataSource('memory');
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}, accounts: {type: 'hasMany', model: 'Account'}}});
assert(!User.relations['posts']);
assert(!User.relations['accounts']);
var Post = ds.define('Post', {userId: Number, content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
var Account = ds.define('Account', {userId: Number, type: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
@ -489,6 +493,44 @@ describe('Load models with relations', function () {
});
it('should throw if the relation type is invalid', function (done) {
var ds = new DataSource('memory');
var Post = ds.define('Post', {userId: Number, content: String});
try {
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasXYZ', model: 'Post'}}});
} catch (e) {
done();
}
});
it('should handle hasMany through', function (done) {
var ds = new DataSource('memory');
var Physician = ds.createModel('Physician', {
name: String
}, {relations: {patients: {model: 'Patient', type: 'hasMany', through: 'Appointment'}}});
var Patient = ds.createModel('Patient', {
name: String
}, {relations: {physicians: {model: 'Physician', type: 'hasMany', through: 'Appointment'}}});
assert(!Physician.relations['patients']); // Appointment hasn't been resolved yet
assert(!Patient.relations['physicians']); // Appointment hasn't been resolved yet
var Appointment = ds.createModel('Appointment', {
physicianId: Number,
patientId: Number,
appointmentDate: Date
}, {relations: {patient: {type: 'belongsTo', model: 'Patient'}, physician: {type: 'belongsTo', model: 'Physician'}}});
assert(Physician.relations['patients']);
assert(Patient.relations['physicians']);
done();
});
});
describe('Load models from json', function () {