executor: Split model boot into two phases

In the first phase, all models are defined.

In the second phase, models are configured, attached to data-sources
and exposed on the app object.

This way when the `attached` Model event is emitted, all models are
already defined and thus a listener can get reference of any other
model used in the app.
This commit is contained in:
Miroslav Bajtoš 2014-06-16 10:56:55 +02:00
parent b887b33b57
commit ef72efa70b
2 changed files with 42 additions and 7 deletions

View File

@ -95,6 +95,17 @@ function setupDataSources(app, instructions) {
} }
function setupModels(app, instructions) { function setupModels(app, instructions) {
defineModels(instructions);
instructions.models.forEach(function(data) {
// Skip base models that are not exported to the app
if (!data.config) return;
app.model(data._model, data.config);
});
}
function defineModels(instructions) {
instructions.models.forEach(function(data) { instructions.models.forEach(function(data) {
var name = data.name; var name = data.name;
var model; var model;
@ -122,10 +133,7 @@ function setupModels(app, instructions) {
} }
} }
// Skip base models that are not exported to the app data._model = model;
if (!data.config) return;
app.model(model, data.config);
}); });
} }

View File

@ -62,7 +62,6 @@ describe('executor', function() {
}.toString()); }.toString());
boot.execute(app, someInstructions({ boot.execute(app, someInstructions({
dataSources: { db: { connector: 'memory' } },
models: [ models: [
{ {
name: 'Customer', name: 'Customer',
@ -83,7 +82,6 @@ describe('executor', function() {
it('defines model without attaching it', function() { it('defines model without attaching it', function() {
boot.execute(app, someInstructions({ boot.execute(app, someInstructions({
dataSources: { db: { connector: 'memory' } },
models: [ models: [
{ {
name: 'Vehicle', name: 'Vehicle',
@ -113,6 +111,35 @@ describe('executor', function() {
assert.equal(app.models.User.dataSource, app.dataSources.theDb); assert.equal(app.models.User.dataSource, app.dataSources.theDb);
}); });
it('defines all models first before running the config phase', function() {
appdir.writeFileSync('models/Customer.js', 'module.exports = ' +
function(Customer/*, Base*/) {
Customer.on('attached', function() {
Customer._modelsWhenAttached =
Object.keys(Customer.modelBuilder.models);
});
}.toString());
boot.execute(app, someInstructions({
models: [
{
name: 'Customer',
config: { dataSource: 'db' },
definition: { name: 'Customer' },
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js')
},
{
name: 'UniqueName',
config: { dataSource: 'db' },
definition: { name: 'UniqueName' },
sourceFile: undefined
}
]
}));
expect(app.models.Customer._modelsWhenAttached).to.include('UniqueName');
});
it('instantiates data sources', function() { it('instantiates data sources', function() {
boot.execute(app, dummyInstructions); boot.execute(app, dummyInstructions);
assert(app.dataSources); assert(app.dataSources);
@ -257,7 +284,7 @@ function someInstructions(values) {
var result = { var result = {
app: values.app || {}, app: values.app || {},
models: values.models || [], models: values.models || [],
dataSources: values.dataSources || {}, dataSources: values.dataSources || { db: { connector: 'memory' } },
files: { files: {
boot: [] boot: []
} }