UNPKG

12.3 kBJavaScriptView Raw
1'use strict';
2
3var Path = require('path');
4var Util = require('util');
5var Express = require('express');
6var UUID = require('uuid');
7var Diread = require('diread');
8
9var toArray = require('./helper/toArray');
10var deepFreeze = require('./helper/deepFreeze');
11var pathWithoutExtension = require('./helper/pathWithoutExtension');
12var tryCatch = require('./helper/tryCatch');
13
14var debug = require('debug')('ifnode:application');
15var Log = require('./Log');
16
17var Extension = require('./application/Extension');
18var PLUGIN_TYPES = require('./PLUGIN_TYPES');
19var SchemaFactory = require('./SchemaFactory');
20var ConfigurationBuilder = require('./ConfigurationBuilder');
21
22var SchemasList = require('./application/SchemasList');
23var DAOList = require('./application/DAOList');
24var ModelBuilder = require('./application/ModelBuilder');
25
26var ComponentsBuilder = require('./application/ComponentsBuilder');
27var ControllersBuilder = require('./application/ControllersBuilder');
28
29var Controller = require('./Controller');
30var RestMiddleware = require('./middleware/rest');
31
32var IFNodeVirtualSchema = require('./../plugins/ifnode-virtual');
33var NodeHTTPServer = require('./../plugins/node-http_s-server');
34
35/**
36 * Creates a new Application instance
37 *
38 * @class Application
39 *
40 * @param {ApplicationOptions} options
41 */
42function Application(options) {
43 if(options.alias && typeof options.alias !== 'string') {
44 Log.error('application', 'Alias must be String');
45 }
46
47 this._require_cache = {};
48 this._extensions_cache = {};
49 this._project_folder = options.project_folder || options.projectFolder || Path.dirname(process.argv[1]);
50 this._backend_folder = Path.resolve(this._project_folder, 'protected/');
51
52 this._modules = [
53 IFNodeVirtualSchema
54 ];
55 this._models_builder = null;
56 this._components_builder = null;
57 this._controllers_builder = null;
58
59 this.id = UUID.v4();
60 this.alias = options.alias || this.id;
61 this.project_folder = this.projectFolder = this._project_folder;
62 this.backend_folder = this.backendFolder = this._backend_folder;
63
64 this.config = this._initialize_config(options.env || options.environment);
65 deepFreeze(this.config);
66
67 this.listener = this._initialize_listener();
68 this.connection = this._initialize_connection_server();
69
70 /**
71 * @deprecated Deprecated from 2.0.0 version. Connection server will be presented by app.connection
72 * @type {http.Server}
73 */
74 this.server = this.connection.server;
75
76 this.models = {};
77 this.components = {};
78 this.controllers = {};
79}
80
81require('./application/middleware')(Application);
82
83/**
84 * Require module from start point like application project folder
85 *
86 * @param {string} id
87 * @returns {*}
88 */
89Application.prototype.require = function(id) {
90 if(!(id in this._require_cache)) {
91 this._require_cache[id] = require(Path.resolve(this.project_folder, id));
92 }
93
94 return this._require_cache[id];
95};
96
97/**
98 * Registered plugin(s) for application instance
99 *
100 * @param {string|Extension|Array.<string|Extension>} module
101 * @returns {Application}
102 */
103Application.prototype.register = function(module) {
104 var type_of = typeof module;
105
106 if(!(
107 type_of === 'string' ||
108 Array.isArray(module) ||
109 (type_of !== 'undefined' && type_of !== 'number')
110 )) {
111 Log.error('plugins', 'Wrong plugin type');
112 }
113
114 var self = this;
115 var modules = this._modules;
116
117 toArray(module).forEach(function(module) {
118 modules.push(typeof module === 'string' ?
119 self._require_module(module) :
120 module
121 );
122 });
123
124 return this;
125};
126
127/**
128 * Loads all maintenance parts of application
129 *
130 * @returns {Application}
131 */
132Application.prototype.load = function() {
133 this.models = this._initialize_models();
134 Object.freeze(this.models);
135
136 this.components = this._initialize_components();
137 // Object.freeze(this.components);
138
139 this.controllers = this._initialize_controllers();
140 Object.freeze(this.controllers);
141
142 this._is_loaded = true;
143
144 return this;
145};
146
147/**
148 *
149 * @param {string} id
150 * @returns {*}
151 */
152Application.prototype.extension = function(id) {
153 var cache = this._extensions_cache;
154
155 if(!(id in cache)) {
156 var custom_folder = this.config.application.folders.extensions;
157 var custom_full_path = Path.resolve(this._project_folder, custom_folder);
158 var extension_loader = new Extension(custom_full_path);
159
160 cache[id] = extension_loader.require(id);
161 }
162
163 return cache[id];
164};
165
166/**
167 *
168 * @param {string} id
169 * @returns {*}
170 */
171Application.prototype.ext = Util.deprecate(
172 Application.prototype.extension,
173 'Deprecated from 2.0.0 version. Needs to use app.extension() method'
174);
175
176/**
177 *
178 * @param {string} id
179 * @returns {Object}
180 */
181Application.prototype.component = function(id) {
182 if(id in this.components) {
183 return this.components[id];
184 }
185
186 var full_path = Path.resolve(this.config.application.folders.components, id);
187
188 if(!(full_path in this.components)) {
189 this.components[full_path] = this._components_builder.read_and_build_component(full_path, {
190 name: id
191 });
192 }
193
194 return this.components[full_path];
195};
196
197/**
198 *
199 * @param {Object} model_config
200 * @param {Object} [options]
201 * @returns {Function}
202 * @constructor
203 */
204Application.prototype.Model = function(model_config, options) {
205 return this._models_builder.make(model_config, options);
206};
207
208/**
209 *
210 * @param {Object} [custom_component_config]
211 * @returns {Component}
212 */
213Application.prototype.Component = function(custom_component_config) {
214 var builder = this._components_builder;
215
216 return builder.make(
217 builder.build_component_config(custom_component_config || {}, this.config.components)
218 );
219};
220
221/**
222 *
223 * @param {Object} controller_config
224 * @returns {Controller}
225 */
226Application.prototype.Controller = function(controller_config) {
227 return this._controllers_builder.make(controller_config);
228};
229
230/**
231 * Start web-server
232 *
233 * @deprecated
234 * @param {function} callback
235 */
236Application.prototype.run = Util.deprecate(function(callback) {
237 if(!this._is_loaded) {
238 this.load();
239 }
240
241 var self = this;
242 var connection = this.connection;
243
244 if(typeof callback === 'function') {
245 connection.listen(function() {
246 callback.call(self, self.config);
247 });
248 } else {
249 connection.listen();
250 }
251}, 'Deprecated from 2.0.0 version. Server will started by app.connection.listen()');
252
253/**
254 * Stop web-server
255 *
256 * @deprecated
257 * @param {function} callback
258 */
259Application.prototype.down = Util.deprecate(function(callback) {
260 this.connection.close(callback);
261}, 'Deprecated from 2.0.0 version. Server will be stopped by app.connection.close()');
262
263/**
264 * Initialize application instance configuration
265 *
266 * @param {string} environment
267 * @private
268 */
269Application.prototype._initialize_config = function(environment) {
270 var config_path;
271
272 if(environment) {
273 config_path = Path.resolve(this._project_folder, 'config/', environment);
274 }
275
276 return ConfigurationBuilder({
277 environment: environment,
278 project_folder: this._project_folder,
279 backend_folder: this._backend_folder,
280 config_path: config_path
281 });
282};
283
284/**
285 * Initialize server listener
286 *
287 * @returns {Express}
288 * @private
289 */
290Application.prototype._initialize_listener = function() {
291 var app = Express();
292 var config = this.config;
293 var app_config = config.application;
294
295 var middleware_configs = app_config.middleware;
296
297 app.use(RestMiddleware.response());
298 if(middleware_configs) {
299 this._initialize_middleware(middleware_configs, app);
300 app.use(RestMiddleware.request());
301 }
302
303 var express_configs = app_config.express;
304
305 Object.keys(express_configs).forEach(function(express_option) {
306 app.set(express_option, express_configs[express_option]);
307 });
308
309 return app;
310};
311
312/**
313 * Initialize native node.js (http,https, etc) server
314 *
315 * @returns {IConnectionServer}
316 * @private
317 */
318Application.prototype._initialize_connection_server = function() {
319 var site_config = this.config.site;
320 var connection = site_config.connection;
321
322 switch(connection) {
323 case 'http':
324 case 'https':
325 return new NodeHTTPServer(this.listener, site_config);
326 default:
327 /**
328 *
329 * @class IConnectionServer
330 */
331 var ServerCreator = this._require_module(connection);
332
333 return new ServerCreator(this.listener, site_config);
334 }
335};
336
337/**
338 *
339 * @private
340 */
341Application.prototype._initialize_models = function _initialize_models() {
342 var db = this.config.db;
343
344 if(!(db && Object.keys(db).length)) {
345 return;
346 }
347
348 var modules = this._modules;
349 var schemas_list = new SchemasList;
350
351 for(var i = 0; i < modules.length; ++i) {
352 var module = modules[i][PLUGIN_TYPES.SCHEMA];
353
354 if(module) {
355 var schema = SchemaFactory();
356
357 module(this, schema);
358 schemas_list.attach_schema(schema);
359 }
360 }
361
362 var models_builder = this._models_builder = new ModelBuilder(
363 new DAOList(schemas_list, db)
364 );
365
366 Diread({
367 src: this.config.application.folders.models
368 }).each(function(model_file_path) {
369 require(model_file_path);
370 });
371
372 return models_builder.compile_models(this);
373};
374
375/**
376 *
377 * @returns {Object.<string, Component>}
378 * @private
379 */
380Application.prototype._initialize_components = function _initialize_components() {
381 var components_builder = this._components_builder = new ComponentsBuilder;
382 var Component = this.Component.bind(this);
383 var modules = this._modules;
384
385 for(var i = 0; i < modules.length; ++i) {
386 var module = modules[i][PLUGIN_TYPES.COMPONENT];
387
388 if(module) {
389 module(this, Component);
390 }
391 }
392
393 var components_config = this.config.components;
394
395 Diread({
396 src: this.config.application.folders.components,
397 directories: true,
398 level: 1,
399 mask: function(path) {
400 return path === pathWithoutExtension(path) ||
401 path.indexOf('.js') !== -1;
402 }
403 }).each(function(component_path) {
404 var autoformed_config = components_builder.build_and_memorize_config(component_path);
405
406 try {
407 components_builder.read_and_build_component(
408 component_path,
409 components_builder.build_component_config({}, components_config)
410 );
411 } catch(error) {
412 /**
413 * Errors inside component will not catch by this handle
414 */
415 if(error.message.indexOf(component_path) === -1) {
416 throw error;
417 } else {
418 Log.warning(
419 'components',
420 'Cannot load component [' + autoformed_config.name + '] by path [' + component_path + ']'
421 );
422 }
423 }
424 });
425
426 return components_builder.compile(this);
427};
428
429/**
430 *
431 * @private
432 */
433Application.prototype._initialize_controllers = function _initialize_controllers() {
434 var controllers_builder = this._controllers_builder = new ControllersBuilder();
435 var modules = this._modules;
436
437 for(var i = 0; i < modules.length; ++i) {
438 var module = modules[i][PLUGIN_TYPES.CONTROLLER];
439
440 if(module) {
441 module(this, Controller);
442 }
443 }
444
445 controllers_builder.read_and_initialize_controllers(
446 this.config.application.folders.controllers, this
447 );
448
449 return controllers_builder.compile(this.listener);
450};
451
452/**
453 * Require node_module or application extension
454 *
455 * @returns {*}
456 * @private
457 */
458Application.prototype._require_module = function(module_name) {
459 var Module;
460
461 Module = tryCatch(function() {
462 return require(module_name);
463 });
464
465 if(Module) {
466 return Module;
467 }
468
469 var self = this;
470
471 Module = tryCatch(function() {
472 return self.extension(module_name);
473 });
474
475 if(Module) {
476 return Module;
477 }
478
479 Log.error('application', 'Cannot find node module or extension [' + module_name + '].');
480};
481
482module.exports = Application;