From ff6652c1c45116b7dfe76329b3e74697f6d47593 Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Wed, 3 Sep 2014 18:37:39 -0700 Subject: [PATCH] Add app.ready() and Model ready hooks --- lib/application.js | 56 +++++++++++++++++++++++++++++++++++++++++++++ lib/models/model.js | 9 ++++++++ test/app.test.js | 30 ++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/lib/application.js b/lib/application.js index 7a3abef5..7f35729a 100644 --- a/lib/application.js +++ b/lib/application.js @@ -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'); /** @@ -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(); + * + * // 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) { + if(err) return done(err); + + models.forEach(function(Model) { + Model.ready(app); + }); + + done(); + }); + + function done(err) { + if(typeof cb === 'function') return cb(err); + } +} diff --git a/lib/models/model.js b/lib/models/model.js index c9c48058..c5af88a4 100644 --- a/lib/models/model.js +++ b/lib/models/model.js @@ -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(); diff --git a/test/app.test.js b/test/app.test.js index 6d5b2937..7c0b0afd 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -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); + 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); + }) + }); });