1 | /*
|
2 | * Copyright (c) 2011-2013, Yahoo! Inc. All rights reserved.
|
3 | * Copyrights licensed under the New BSD License.
|
4 | * See the accompanying LICENSE file for terms.
|
5 | */
|
6 |
|
7 |
|
8 | /*jslint anon:true, sloppy:true, nomen:true, node:true*/
|
9 |
|
10 | ;
|
11 |
|
12 | // ----------------------------------------------------------------------------
|
13 | // Prerequisites
|
14 | // ----------------------------------------------------------------------------
|
15 |
|
16 |
|
17 | var express = require('express'), // TODO: [Issue 80] go back to connect?
|
18 | http = require('http'),
|
19 | store = require('./store'),
|
20 | OutputHandler = require('./output-handler.server'),
|
21 | libpath = require('path'),
|
22 | requestCounter = 0, // used to scope logs per request
|
23 | Mojito;
|
24 |
|
25 | // ----------------------------------------------------------------------------
|
26 | // Mojito Global
|
27 | // ----------------------------------------------------------------------------
|
28 |
|
29 |
|
30 | /**
|
31 | * Shared global object, which isn't named 'mojito' because 'mojito' is a module
|
32 | * name defined in mojito.common.js and required via Y.use.
|
33 | */
|
34 | // TODO: Merge what we put on this object with the 'mojito' module/namespace.
|
35 | global._mojito = {};
|
36 |
|
37 |
|
38 | // ----------------------------------------------------------------------------
|
39 | // MojitoServer
|
40 | // ----------------------------------------------------------------------------
|
41 |
|
42 |
|
43 | /**
|
44 | * The primary Mojito server type. Invoking the constructor returns an instance
|
45 | * which is not yet running. Use listen to run the server once you have made
|
46 | * any adjustments to its configuration.
|
47 | * @param {{port: number,
|
48 | * dir: string,
|
49 | * context: Object,
|
50 | * appConfig: Object,
|
51 | * verbose: boolean}} options An object containing server options. The
|
52 | * default port is process.env.PORT or port 8666 if no port is given.
|
53 | * Verbose is false by default. Dir is cwd() by default. Both the
|
54 | * context and appConfig will default to empty objects.
|
55 | * @constructor
|
56 | * @return {MojitoServer}
|
57 | */
|
58 | function MojitoServer(options) {
|
59 | var appConfig;
|
60 |
|
61 | this._options = options || {};
|
62 | this._options.port = this._options.port || process.env.PORT || 8666;
|
63 | this._options.dir = this._options.dir || process.cwd();
|
64 | this._options.context = this._options.context || {};
|
65 | this._options.mojitoRoot = __dirname;
|
66 |
|
67 | // TODO: Note we could pass some options to the express server instance.
|
68 | this._app = express.createServer();
|
69 |
|
70 | appConfig = store.getAppConfig(this._options.dir, this._options.context);
|
71 | this._options.Y = this._createYUIInstance(this._options, appConfig);
|
72 | this._configureLogger(this._options.Y);
|
73 | this._app.store = store.createStore(this._options);
|
74 | this._configureAppInstance(this._app, this._options, appConfig);
|
75 | this._app.store.optimizeForEnvironment();
|
76 |
|
77 | return this;
|
78 | }
|
79 |
|
80 |
|
81 | // ---------
|
82 | // Constants
|
83 | // ---------
|
84 |
|
85 | /**
|
86 | * An ordered list of the middleware module names to load for a standard Mojito
|
87 | * server instance.
|
88 | * @type {Array.<string>}
|
89 | */
|
90 | MojitoServer.MOJITO_MIDDLEWARE = [
|
91 | 'mojito-handler-static',
|
92 | 'mojito-parser-body',
|
93 | 'mojito-parser-cookies',
|
94 | 'mojito-contextualizer',
|
95 | 'mojito-handler-tunnel',
|
96 | 'mojito-router',
|
97 | 'mojito-handler-dispatcher',
|
98 | 'mojito-handler-error'
|
99 | ];
|
100 |
|
101 |
|
102 | // ------------------
|
103 | // Private Attributes
|
104 | // ------------------
|
105 |
|
106 |
|
107 | /**
|
108 | * The Express application (server) instance.
|
109 | * @type {Object}
|
110 | */
|
111 | MojitoServer.prototype._app = null;
|
112 |
|
113 |
|
114 | /**
|
115 | * The formatting function for the server's associated logger.
|
116 | * @type {function(string, number, string, Date, Object, number)}
|
117 | */
|
118 | MojitoServer.prototype._logFormatter = null;
|
119 |
|
120 |
|
121 | /**
|
122 | * The publisher function for the server's associated logger.
|
123 | * @type {function(string, number, string, Date, number)}
|
124 | */
|
125 | MojitoServer.prototype._logPublisher = null;
|
126 |
|
127 |
|
128 | /**
|
129 | * The write function for the server's associated logger.
|
130 | * @type {function(function(string, number, string, Date, Object, number))}
|
131 | */
|
132 | MojitoServer.prototype._logWriter = null;
|
133 |
|
134 |
|
135 | /**
|
136 | * The server options container. Common option keys are listed.
|
137 | * @type {{port: number,
|
138 | * dir: string,
|
139 | * context: Object,
|
140 | * appConfig: Object,
|
141 | * verbose: boolean}}
|
142 | */
|
143 | MojitoServer.prototype._options = null;
|
144 |
|
145 |
|
146 | /**
|
147 | * The server startup time. This value is used to both provide startup/uptime
|
148 | * information and as a signifier that the server has been configured/started.
|
149 | * @type {number}
|
150 | */
|
151 | MojitoServer.prototype._startupTime = null;
|
152 |
|
153 |
|
154 | // ---------------
|
155 | // Private Methods
|
156 | // ---------------
|
157 |
|
158 | /**
|
159 | * A utility function for compiling a list of middleware
|
160 | * @method _makeMiddewareList
|
161 | * @private
|
162 | * @param {array} app_mw Middleware list specified by the app's applicatioon.json
|
163 | * @param {array} mojito_mw Middeware list specified Mojito, in this file, by the
|
164 | * MojitoServer.MOJITO_MIDDLEWARE property
|
165 | * @return {array} Complete and ordered list of middleware to load
|
166 | */
|
167 | MojitoServer.prototype._makeMiddewareList = function (app_mw, mojito_mw) {
|
168 | var m,
|
169 | hasMojito = false,
|
170 | midName,
|
171 | middleware = [];
|
172 |
|
173 | // computing middleware pieces
|
174 | if (app_mw && app_mw.length) {
|
175 | for (m = 0; m < app_mw.length; m += 1) {
|
176 | midName = app_mw[m];
|
177 | if (0 === midName.indexOf('mojito-')) {
|
178 | hasMojito = true;
|
179 | break;
|
180 | }
|
181 | }
|
182 |
|
183 | if (hasMojito) {
|
184 | // User has specified at least one of mojito's middleware, so
|
185 | // we assume that they have specified all that they need.
|
186 | middleware = app_mw;
|
187 | } else {
|
188 | // backwards compatibility mode:
|
189 | // middlware = user's, then mojito's
|
190 | middleware = app_mw.concat(mojito_mw);
|
191 | }
|
192 |
|
193 | } else {
|
194 | middleware = mojito_mw;
|
195 | }
|
196 |
|
197 | return middleware;
|
198 | };
|
199 |
|
200 | /**
|
201 | * A utility function to require middleware code, configure it, and tell express
|
202 | * to use() it.
|
203 | * @method _useMiddleware
|
204 | * @private
|
205 | * @param {object} app Express app instance.
|
206 | * @param {function} dispatcher Dispatcher function wrapper, special case middleware.
|
207 | * @param {string} midDir Directory of user-specified middleware, if any.
|
208 | * @param {object} midConfig Configuration object.
|
209 | * @param {array} middleware Middleware names, or pathnames.
|
210 | */
|
211 | MojitoServer.prototype._useMiddleware = function (app, dispatcher, midDir, midConfig, middleware) {
|
212 | var m,
|
213 | midName,
|
214 | midPath,
|
215 | midBase,
|
216 | midFactory;
|
217 |
|
218 | for (m = 0; m < middleware.length; m += 1) {
|
219 | midName = middleware[m];
|
220 | if (0 === midName.indexOf('mojito-')) {
|
221 | // one special one, since it might be difficult to move to a
|
222 | // separate file
|
223 | if (midName === 'mojito-handler-dispatcher') {
|
224 | //console.log("======== MIDDLEWARE mojito -- " +
|
225 | // "builtin mojito-handler-dispatcher");
|
226 | app.use(dispatcher);
|
227 | } else {
|
228 | midPath = libpath.join(__dirname, 'app', 'middleware', midName);
|
229 | //console.log("======== MIDDLEWARE mojito " + midPath);
|
230 | midFactory = require(midPath);
|
231 | app.use(midFactory(midConfig));
|
232 | }
|
233 | } else {
|
234 | // backwards-compatibility: user-provided middleware is
|
235 | // specified by path
|
236 | midPath = libpath.join(midDir, midName);
|
237 | //console.log("======== MIDDLEWARE user " + midPath);
|
238 | midBase = libpath.basename(midPath);
|
239 | if (0 === midBase.indexOf('mojito-')) {
|
240 | // Same as above (case of Mojito's special middlewares)
|
241 | // Gives a user-provided middleware access to the YUI
|
242 | // instance, resource store, logger, context, etc.
|
243 | app.use(require(midPath)(midConfig));
|
244 | } else {
|
245 | app.use(require(midPath));
|
246 | }
|
247 | }
|
248 | }
|
249 | };
|
250 |
|
251 | /**
|
252 | * Creates the YUI instance.
|
253 | * @private
|
254 | * @method _createYUIInstance
|
255 | * @param {Object} options The options as passed to the constructor.
|
256 | * @param {Object} appConfig The static application configuration.
|
257 | * @return {Object} The YUI instances.
|
258 | */
|
259 | MojitoServer.prototype._createYUIInstance = function(options, appConfig) {
|
260 | var yuiConfig = (appConfig.yui && appConfig.yui.config) || {},
|
261 | Y;
|
262 |
|
263 | // redefining "combine" and/or "base" in the server side have side effects
|
264 | // and might try to load yui from CDN, so we bypass them.
|
265 | // TODO: report bug.
|
266 | // is there a better alternative for this delete?
|
267 | // maybe not, but it might introduce a perf penalty
|
268 | // in v8 engine, and we can't use the undefined trick
|
269 | // because loader is doing hasOwnProperty :(
|
270 | delete yuiConfig.combine;
|
271 | delete yuiConfig.base;
|
272 |
|
273 | // in case we want to collect some performance metrics,
|
274 | // we can do that by defining the "perf" object in:
|
275 | // application.json (master)
|
276 | // You can also use the option --perf path/filename.log when
|
277 | // running mojito start to dump metrics to disk.
|
278 | if (appConfig.perf) {
|
279 | yuiConfig.perf = appConfig.perf;
|
280 | yuiConfig.perf.logFile = options.perf || yuiConfig.perf.logFile;
|
281 | }
|
282 |
|
283 | // getting yui module, or yui/debug if needed, and applying
|
284 | // the default configuration from application.json->yui-config
|
285 | Y = require('yui' + (yuiConfig.filter === 'debug' ? '/debug' : '')).YUI(yuiConfig, {
|
286 | useSync: true
|
287 | });
|
288 |
|
289 | return Y;
|
290 | };
|
291 |
|
292 | /**
|
293 | * Adds Mojito framework components to the Express application instance.
|
294 | * @private
|
295 | * @method _configureAppInstance
|
296 | * @param {Object} app The Express application instance to Mojito-enable.
|
297 | * @param {{port: number,
|
298 | * dir: string,
|
299 | * context: Object,
|
300 | * appConfig: Object,
|
301 | * verbose: boolean}} options An object containing server options.
|
302 | * @param {Object} appConfig The static application configuration.
|
303 | */
|
304 | MojitoServer.prototype._configureAppInstance = function(app, options, appConfig) {
|
305 | var store = app.store,
|
306 | Y = options.Y,
|
307 | modules = [],
|
308 | middleware,
|
309 | midConfig;
|
310 |
|
311 | modules = this._configureYUI(Y, store);
|
312 |
|
313 | // attaching all modules available for this application for the server side
|
314 | Y.applyConfig({ useSync: true });
|
315 | Y.use.apply(Y, modules);
|
316 | Y.applyConfig({ useSync: false });
|
317 |
|
318 | middleware = this._makeMiddewareList(appConfig.middleware, MojitoServer.MOJITO_MIDDLEWARE);
|
319 |
|
320 | midConfig = {
|
321 | Y: Y,
|
322 | store: store,
|
323 | logger: {
|
324 | log: Y.log
|
325 | },
|
326 | context: options.context
|
327 | };
|
328 |
|
329 | function dispatcher(req, res, next) {
|
330 | var command = req.command,
|
331 | outputHandler,
|
332 | context = req.context || {};
|
333 |
|
334 | if (!command) {
|
335 | next();
|
336 | return;
|
337 | }
|
338 |
|
339 | outputHandler = new OutputHandler(req, res, next);
|
340 | outputHandler.setLogger({
|
341 | log: Y.log
|
342 | });
|
343 |
|
344 | // storing the static app config as well as contextualized app config per request
|
345 | outputHandler.page.staticAppConfig = store.getStaticAppConfig();
|
346 | outputHandler.page.appConfig = store.getAppConfig(context);
|
347 | // compute routes once per request
|
348 | outputHandler.page.routes = store.getRoutes(context);
|
349 |
|
350 | // HookSystem::StartBlock
|
351 | // enabling perf group
|
352 | if (appConfig.perf) {
|
353 | // in case another middleware has enabled hooks before
|
354 | outputHandler.hook = req.hook || {};
|
355 | Y.mojito.hooks.enableHookGroup(outputHandler.hook, 'mojito-perf');
|
356 | }
|
357 | // HookSystem::EndBlock
|
358 |
|
359 | // HookSystem::StartBlock
|
360 | Y.mojito.hooks.hook('AppDispatch', outputHandler.hook, req, res);
|
361 | // HookSystem::EndBlock
|
362 |
|
363 | Y.mojito.Dispatcher.init(store).dispatch(command, outputHandler);
|
364 | }
|
365 |
|
366 | // attach middleware pieces
|
367 | this._useMiddleware(app, dispatcher, options.dir, midConfig, middleware);
|
368 |
|
369 | Y.log('Mojito HTTP Server initialized in ' +
|
370 | ((new Date().getTime()) - Mojito.MOJITO_INIT) + 'ms.');
|
371 | };
|
372 |
|
373 | /*
|
374 | * Configures YUI logger to honor the logLevel and logLevelOrder
|
375 | * TODO: this should be done at the low level in YUI.
|
376 | */
|
377 | MojitoServer.prototype._configureLogger = function(Y) {
|
378 | var logLevel = (Y.config.logLevel || 'debug').toLowerCase(),
|
379 | logLevelOrder = Y.config.logLevelOrder || [],
|
380 | defaultLogLevel = logLevelOrder[0] || 'info',
|
381 | isatty = process.stdout.isTTY;
|
382 |
|
383 | function log(c, msg, cat, src) {
|
384 | var f,
|
385 | m = (src) ? src + ': ' + msg : msg;
|
386 |
|
387 | // if stdout is bound to the tty, we should try to
|
388 | // use the fancy logs implemented by 'yui-log-nodejs'.
|
389 | // TODO: eventually YUI should take care of this piece.
|
390 | if (isatty && Y.Lang.isFunction(c.logFn)) {
|
391 | c.logFn.call(Y, msg, cat, src);
|
392 | } else if ((typeof console !== 'undefined') && console.log) {
|
393 | f = (cat && console[cat]) ? cat : 'log';
|
394 | console[f](msg);
|
395 | }
|
396 | }
|
397 |
|
398 | // one more hack: we need to make sure that base is attached
|
399 | // to be able to listen for Y.on.
|
400 | Y.use('base');
|
401 |
|
402 | if (Y.config.debug) {
|
403 |
|
404 | logLevel = (logLevelOrder.indexOf(logLevel) >= 0 ? logLevel : logLevelOrder[0]);
|
405 |
|
406 | // logLevel index defines the begining of the logLevelOrder structure
|
407 | // e.g: ['foo', 'bar', 'baz'], and logLevel 'bar' should produce: ['bar', 'baz']
|
408 | logLevelOrder = (logLevel ? logLevelOrder.slice(logLevelOrder.indexOf(logLevel)) : []);
|
409 |
|
410 | Y.applyConfig({
|
411 | useBrowserConsole: false,
|
412 | logLevel: logLevel,
|
413 | logLevelOrder: logLevelOrder
|
414 | });
|
415 |
|
416 | // listening for low level log events to filter some of them.
|
417 | Y.on('yui:log', function (e) {
|
418 | var c = Y.config,
|
419 | cat = e && e.cat && e.cat.toLowerCase();
|
420 |
|
421 | // this covers the case Y.log(msg) without category
|
422 | // by using the low priority category from logLevelOrder.
|
423 | cat = cat || defaultLogLevel;
|
424 |
|
425 | // applying logLevel filters
|
426 | if (cat && ((c.logLevel === cat) || (c.logLevelOrder.indexOf(cat) >= 0))) {
|
427 | log(c, e.msg, cat, e.src);
|
428 | }
|
429 | return true;
|
430 | });
|
431 | }
|
432 |
|
433 | };
|
434 |
|
435 | /**
|
436 | * Configures YUI with both the Mojito framework and all the YUI modules in the
|
437 | * application.
|
438 | * @private
|
439 | * @method _configureYUI
|
440 | * @param {object} Y YUI object to configure
|
441 | * @param {object} store Resource Store which knows what to load
|
442 | * @return {array} array of YUI module names
|
443 | */
|
444 | MojitoServer.prototype._configureYUI = function(Y, store) {
|
445 | var modules,
|
446 | load,
|
447 | lang;
|
448 |
|
449 | modules = store.yui.getModulesConfig('server', false);
|
450 | Y.applyConfig(modules);
|
451 |
|
452 | load = Object.keys(modules.modules);
|
453 |
|
454 | // NOTE: Not all of these module names are guaranteed to be valid,
|
455 | // but the loader tolerates them anyways.
|
456 | for (lang in store.yui.langs) {
|
457 | if (store.yui.langs.hasOwnProperty(lang) && lang) {
|
458 | load.push('lang/datatype-date-format_' + lang);
|
459 | }
|
460 | }
|
461 |
|
462 | return load;
|
463 | };
|
464 |
|
465 |
|
466 | // --------------
|
467 | // Public Methods
|
468 | // --------------
|
469 |
|
470 |
|
471 | /**
|
472 | * Closes (shuts down) the server port and stops the server.
|
473 | */
|
474 | MojitoServer.prototype.close = function() {
|
475 | if (this._options.verbose) {
|
476 | console.warn('Closing Mojito Application');
|
477 | }
|
478 |
|
479 | this._app.close();
|
480 | };
|
481 |
|
482 |
|
483 | /**
|
484 | * Returns the instance of http.Server (or a subtype) which is the true server.
|
485 | * @return {http.Server} The node.js http.Server (or subtype) instance.
|
486 | */
|
487 | MojitoServer.prototype.getHttpServer = function() {
|
488 | return this._app;
|
489 | };
|
490 |
|
491 |
|
492 | /**
|
493 | * Begin listening for inbound connections.
|
494 | * @param {Number} port The port number. Defaults to the server's value for
|
495 | * options.port (which defaults to process.env.PORT followed by 8666).
|
496 | * @param {String} host Optional hostname or IP address in string form.
|
497 | * @param {Function} callback Optional callback to get notified when the
|
498 | * server is ready to server traffic.
|
499 | */
|
500 | MojitoServer.prototype.listen = function(port, host, callback) {
|
501 |
|
502 | var app = this._app,
|
503 | p = port || this._options.port,
|
504 | h = host || this._options.host,
|
505 | listenArgs = [p];
|
506 |
|
507 | // Track startup time and use it to ensure we don't try to listen() twice.
|
508 | if (this._startupTime) {
|
509 | if (this._options.verbose) {
|
510 | console.warn('Mojito Application Already Running');
|
511 | }
|
512 | return;
|
513 | }
|
514 | this._startupTime = new Date().getTime();
|
515 |
|
516 | if (this._options.verbose) {
|
517 | console.warn('Starting Mojito Application');
|
518 | }
|
519 |
|
520 | if (h) {
|
521 | listenArgs.push(h);
|
522 | }
|
523 |
|
524 | // close on app for callback
|
525 | function handler(err) {
|
526 | callback(err, app);
|
527 | }
|
528 |
|
529 | if (callback) {
|
530 | app.on('error', handler);
|
531 | listenArgs.push(handler);
|
532 | }
|
533 |
|
534 | app.listen.apply(app, listenArgs);
|
535 | };
|
536 |
|
537 |
|
538 | /**
|
539 | * Invokes a callback function with the content of the requested url.
|
540 | * @param {string} url A url to fetch.
|
541 | * @param {{host: string, port: number, method: string}|function} opts A list of
|
542 | * options, or a callback function (See @param for cb). When providing
|
543 | * options note that the list here is not exhaustive. Any valid http.request
|
544 | * object option may be provided. See documentation for http.request.
|
545 | * @param {function(Error, string, string)} cb A function called on request
|
546 | * completion. Parameters are any optional Error, the original URL, and the
|
547 | * content of that URL.
|
548 | */
|
549 | MojitoServer.prototype.getWebPage = function(url, opts, cb) {
|
550 | var buffer = '',
|
551 | callback,
|
552 | options = {
|
553 | host: '127.0.0.1',
|
554 | port: this._options.port,
|
555 | path: url,
|
556 | method: 'get'
|
557 | };
|
558 |
|
559 | // Options block is optional, no pun intended. When it's a function we'll
|
560 | // use that as our callback function.
|
561 | if (typeof opts === 'function') {
|
562 | callback = opts;
|
563 | } else {
|
564 | // Don't assume we got a real callback function.
|
565 | callback = cb || Mojito.NOOP;
|
566 |
|
567 | // Map provided options into our request options object.
|
568 | Object.keys(opts).forEach(function(k) {
|
569 | if (opts.hasOwnProperty(k)) {
|
570 | options[k] = opts[k];
|
571 | }
|
572 | });
|
573 | }
|
574 |
|
575 | http.request(options, function(res) {
|
576 | res.setEncoding('utf8');
|
577 | res.on('data', function(chunk) {
|
578 | buffer += chunk;
|
579 | });
|
580 | res.on('end', function() {
|
581 | // TODO: 200 isn't the only success code. Support 304 etc.
|
582 | if (res.statusCode !== 200) {
|
583 | callback('Could not get web page: status code: ' +
|
584 | res.statusCode + '\n' + buffer, url);
|
585 | } else {
|
586 | callback(null, url, buffer);
|
587 | }
|
588 | });
|
589 | }).on('error', function(err) {
|
590 | callback(err, url);
|
591 | }).end();
|
592 | };
|
593 |
|
594 |
|
595 | /**
|
596 | * Invokes a callback function with the content of each url requested.
|
597 | * @param {Array.<string>} urls A list of urls to fetch.
|
598 | * @param {function(Error, string, string)} cb A function called once for each
|
599 | * url in the urls list. Parameters are any optional Error, the original URL
|
600 | * and the URL's content.
|
601 | */
|
602 | MojitoServer.prototype.getWebPages = function(urls, cb) {
|
603 | var server = this,
|
604 | callback,
|
605 | count,
|
606 | len,
|
607 | initOne;
|
608 |
|
609 | // If no array, or an empty array, just exit.
|
610 | if (!urls || urls.length === 0) {
|
611 | return;
|
612 | }
|
613 |
|
614 | // NOTE we could just say this is an error condition. No callback, what's
|
615 | // the point of doing the work?
|
616 | callback = cb || Mojito.NOOP;
|
617 |
|
618 | len = urls.length;
|
619 | count = 0;
|
620 |
|
621 | // Create a function to call getWebPage with an individual URL shifted from
|
622 | // the list. When the list is empty we can stop.
|
623 | initOne = function() {
|
624 | if (count < len) {
|
625 | server.getWebPage(urls[count], function(err, url, data) {
|
626 | count += 1;
|
627 | try {
|
628 | callback(err, url, data);
|
629 | } finally {
|
630 | initOne();
|
631 | }
|
632 | });
|
633 | }
|
634 | };
|
635 |
|
636 | // Start the ball rolling :).
|
637 | initOne();
|
638 | };
|
639 |
|
640 | // ----------------------------------------------------------------------------
|
641 | // Mojito
|
642 | // ----------------------------------------------------------------------------
|
643 |
|
644 | /**
|
645 | * The Mojito object is the primary server construction interface for Mojito.
|
646 | * This object is used to create new server instances but given that the raw
|
647 | * Express application object is expected/returned there's no need for a true
|
648 | * constructor since there are no true instances of a Mojito server object.
|
649 | */
|
650 | // TODO: Merge what we put on this object with the 'mojito' module/namespace.
|
651 | Mojito = {};
|
652 |
|
653 |
|
654 | // ---------
|
655 | // Constants
|
656 | // ---------
|
657 |
|
658 | /**
|
659 | * The date/time the Mojito object was initialized.
|
660 | * @type {Date}
|
661 | */
|
662 | Mojito.MOJITO_INIT = new Date().getTime();
|
663 |
|
664 |
|
665 | /**
|
666 | * A placeholder function used to avoid overhead checking for callbacks.
|
667 | * @type {function()}
|
668 | */
|
669 | Mojito.NOOP = function() {};
|
670 |
|
671 |
|
672 | // --------------
|
673 | // Public Methods
|
674 | // --------------
|
675 |
|
676 |
|
677 | /**
|
678 | * Creates a properly configured MojitoServer instance and returns it.
|
679 | * @method createServer
|
680 | * @param {Object} options Options for starting the app.
|
681 | * @return {Object} Express application.
|
682 | */
|
683 | Mojito.createServer = function(options) {
|
684 | // NOTE that we use the exported name here. This allows us to mock that
|
685 | // object during testing.
|
686 | return new Mojito.Server(options);
|
687 | };
|
688 |
|
689 |
|
690 | /**
|
691 | * Allows the bin/mojito command to leverage the current module's relative path
|
692 | * for initial startup loading.
|
693 | * @method include
|
694 | * @param {string} path The path used to locate resources.
|
695 | * @return {Object} The return value of require() for the adjusted path.
|
696 | */
|
697 | Mojito.include = function(path) {
|
698 | return require('./' + path);
|
699 | };
|
700 |
|
701 |
|
702 | // ----------------------------------------------------------------------------
|
703 | // EXPORT(S)
|
704 | // ----------------------------------------------------------------------------
|
705 |
|
706 | /**
|
707 | * Export Mojito as the return value for any require() calls.
|
708 | * @type {Mojito}
|
709 | */
|
710 | module.exports = Mojito;
|
711 |
|
712 | /**
|
713 | * Export Mojito.Server to support unit testing of the server type. With this
|
714 | * approach the slot for the server can be replaced with a mock, but the actual
|
715 | * MojitoServer type remains private to the module.
|
716 | * @type {MojitoServer}
|
717 | */
|
718 | module.exports.Server = MojitoServer;
|
719 |
|