Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var DataSource = require('loopback-datasource-juggler').DataSource
, _ = require('underscore')
, RemoteObjects = require('strong-remoting')
, stringUtils = require('underscore.string')
, async = require('async')
, path = require('path');

/**
Expand Down Expand Up @@ -466,3 +467,58 @@ app.listen = function(cb) {

return server;
}

/**
* **Do not call this method** if you are using `loopback-boot` to bootstrap your application.
* `loopback-boot` will call this method for you! Otherwise you must call `app.ready()` to run
* the `Model.ready()` hooks.
*
* Calling `ready()` will call `ready()` on all models attached to the `app`. Override
* the `ready()` method on a `Model` class to ensure you have access to a bootstrapped application.
*
* ```js
* module.exports = function(MyModel) {
* MyModel.setup = function() {
* // setup is called when a model is extended
* // you must call `base.setup()` to extend the base class properly
* this.base.setup();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix this per my comment above.

*
* // add or remove remote methods and otherwise modify the `Model`
* this.remoteMethod('myMethod');
* }
*
* MyModel.beforeReady = function(app, cb) {
* // async setup, runs after all models are `setup()`
* // and before `ready()` is called
* setTimeout(cb, 100);
* }
*
* MyModel.ready = function(app) {
* // MyModel and other classes can be used
* // you should not modify any classes in this method
* console.log(this.sharedClass.methods());
* }
* }
* ```
*/

app.ready = function(cb) {
var app = this;
var models = app.models();

async.each(models, function(Model, cb) {
Model.beforeReady(app, cb);
}, function(err) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is running the hooks in parallel, which may cause race conditions.

Perhaps a safer solution is to serialize the calls and order the models using inheritance (base models first, child models afterwards)?

At minimum, the documentation must be clear about the order in which the hooks are invoked.

if(err) return done(err);

models.forEach(function(Model) {
Model.ready(app);
});

done();
});

function done(err) {
if(typeof cb === 'function') return cb(err);
}
}
9 changes: 9 additions & 0 deletions lib/models/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,15 @@ Model.nestRemoting = function(relationName, options, cb) {
}
};

Model.beforeReady = function(app, cb) {
// noop
cb();
}

Model.ready = function() {
// noop
}

// setup the initial model
Model.setup();

30 changes: 30 additions & 0 deletions test/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,34 @@ describe('app', function() {
var app = loopback();
expect(app.loopback).to.equal(loopback);
});

describe('app.ready()', function() {
it('should call the ready hooks', function(done) {
var app = loopback();
var called = 0;
var TestModel = app.model('TestModel', {}, {base: 'Model', dataSource: null});
TestModel.beforeReady = function(app, cb) {
called++;
cb();
}
TestModel.ready = function() {
called++;
};

app.ready(function() {
called++;
});

process.nextTick(function() {
expect(called).to.equal(3);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a more descriptive flag than a number. When called was 2 on the build-server, how are you going to tell what went wrong?

A better solution is to use an array and push a string in every callback. That way you can also verify the order of calls, which is important too!

expect(called).to.eql(['TestModel.beforeReady', 'TestModel.ready', 'ready callback'])

done();
});
});

it('should call built in methods if none provided', function(done) {
var app = loopback();
var TestModel = app.model('TestModel', {}, {base: 'Model', dataSource: null});
app.ready(done);
})
});
});