UNPKG

@onehilltech/blueprint

Version:

lightweight, simple, elegant framework for building mean applications

312 lines (251 loc) 8.52 kB
var winston = require ('winston') , path = require ('path') , util = require ('util') , fs = require ('fs') , all = require ('require-all') , async = require ('async') ; var Server = require ('./Server') , RouterBuilder = require ('./RouterBuilder') , Configuration = require ('./Configuration') , Database = require ('./Database') , ApplicationModule = require ('./ApplicationModule') , Framework = require ('./Framework') , Path = require ('./Path') , Env = require ('./Environment') ; /** * @class Application * * The main Blueprint.js application. * * @param appPath * @constructor */ function Application (appPath) { // Initialize the base class. ApplicationModule.call (this, appPath); // Load the application configuration. var appConfigPath = path.join (appPath, 'configs', 'app.config.js'); var appConfig = require (appConfigPath); this.name = appConfig.name; this._modules = {}; } util.inherits (Application, ApplicationModule); /** * Initialize the application. */ Application.prototype.init = function (callback) { if (this._is_init) return callback (null, this); callback = callback || function (err, app) {}; async.waterfall ([ async.constant (this), // First, make sure there is a data directory. This is where the application stores // all its internal information. function (app, callback) { var tempPath = Path.resolve (app.appPath, 'temp'); tempPath.createIfNotExists (function (err) { return callback (err, app); }); }, // Load all configurations first. This is because other entities in the // application may need the configuration object for initialization. function (app, callback) { var configPath = path.join (app.appPath, 'configs'); Configuration (configPath, Env.name, function (err, configs) { if (err) return callback (err); app._configs = configs; return callback (null, app); }); }, function (app, callback) { // Store the configurations in the application. if (!app._configs.database) return callback (null, app); winston.log ('info', 'application has database support'); app._db = new Database (app._configs.database); app._db.setMessenger (Framework ().messaging); return callback (null, app); }, // Make the server object for the application. function (app, callback) { var server = new Server (app.appPath); server.configure (app._configs.server, function (err, server) { if (err) return callback (err); app._server = server; return callback (null, app); }); }, // Load the modules for the application. function (app, callback) { if (!app._configs.app['modules']) return callback (null, app); async.each (app._configs.app['modules'], function (name, callback) { var location = path.resolve (app.appPath, '../node_modules', name, 'app'); app.addModule (name, location, callback); }, function (err) { return callback (err, app); }); }, // Let's configure the application module portion of the application. function (app, callback) { ApplicationModule.prototype.init.call (app, callback); }, // Move the views to the correct location. function (app, callback) { if (!app.getSupportsViews ()) return callback (null, app); var viewsPath = app.getViewsPath (); app._server.importViews (viewsPath, function (err) { return callback (err, app); }); }, // Make the router for the application. Then, install the router in the // server object. Part of loading the routers requires force loading of // the controllers. Otherwise, the router builder will not be able to // resolve any of the defined actions. function (app, callback) { var routersPath = path.resolve (app.appPath, 'routers'); var builder = new RouterBuilder (app.controllers, routersPath); var routers = app.routers; app._router = builder .addRouters (routers) .getRouter (); app._server.setMainRouter (app._router); return callback (null, app); } ], function (err, app) { if (err) return callback (err); // Notify all listeners the application is initialized. Framework ().messaging.emit ('app.init', app); app._is_init = true; return callback (null, app); }); }; /** * Start the application. This method connects to the database, creates a * new server, and starts listening for incoming messages. * * @param done */ Application.prototype.start = function (callback) { async.waterfall ([ async.constant (this), // Connect to the database. function (app, callback) { if (!app._db) return callback (null, app); app._db.connect (function (err) { return callback (err, app); }); }, // Listen for events. function (app, callback) { app._server.listen (function (err) { return callback (err, app); }); } ], function (err, app) { if (err) return callback (err); Framework ().messaging.emit ('app.start', app); return callback (null, app); }); }; /** * Add an application module to the application. An application module can only * be added once. Two application modules are different if they have the same * name, not module path. This will ensure we do not have the same module in * different location added to the application more than once. * * @param name * @param path * @param callback */ Application.prototype.addModule = function (name, path, callback) { if (this._modules.hasOwnProperty (name)) throw new Error (util.format ('duplicate module: %s', name)); var appModule = new ApplicationModule (path); async.waterfall ([ async.constant (this), function (app, callback) { appModule.init (function (err, module) { if (err) return callback (err); // Store the module app._modules[name] = module; return callback (null, app); }); }, // Import the views from the module into the application. function (app, callback) { if (!app._server && appModule.getSupportsViews ()) return callback (null, app); app._server.importViews (appModule.getViewsPath (), function (err) { return callback (err, app); }); }, // Merge the listeners function (app, callback) { app.listenerManager.merge (appModule.listenerManager); return callback (null, app); }, // Merge the models function (app, callback) { app.modelManager.merge (appModule.modelManager); return callback (null, app); }, // Merge the policies function (app, callback) { app.policyManager.merge (appModule.policyManager); return callback (null, app); } ], callback); }; /** * Get the application database. */ Application.prototype.__defineGetter__ ('database', function () { if (!this._db) throw new Error ('application did not configure database'); return this._db; }); /** * Get the application database. */ Application.prototype.__defineGetter__ ('configs', function () { return this._configs; }); /** * Get the application server. */ Application.prototype.__defineGetter__ ('server', function () { if (!this._server) throw new Error ('application did not configure server'); return this._server; }); /** * Get the Blueprint modules loaded by the application. */ Application.prototype.__defineGetter__ ('modules', function () { return this._modules; }); /** * Test if the application is initialized. */ Application.prototype.__defineGetter__ ('is_init', function () { return this._is_init; }); /** * Read an application resource. If a callback is provided, then it reads the * resource asynchronously. If a callback is not provided, then the resource is * read synchronously. * * @param path Path to resource * @param opts Options for reading * @param callback Optional callback */ Application.prototype.resource = function (location, opts, callback) { var fullPath = path.resolve (this.appPath, 'resources', location); if (callback) return fs.readfile (fullPath, opts, callback); else return fs.readFileSync (fullPath, opts); }; module.exports = exports = Application;