source: Dev/trunk/src/node_modules/express/lib/application.js @ 484

Last change on this file since 484 was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 11.9 KB
Line 
1/**
2 * Module dependencies.
3 */
4
5var connect = require('connect')
6  , Router = require('./router')
7  , methods = require('methods')
8  , middleware = require('./middleware')
9  , debug = require('debug')('express:application')
10  , locals = require('./utils').locals
11  , View = require('./view')
12  , utils = connect.utils
13  , path = require('path')
14  , http = require('http')
15  , join = path.join;
16
17/**
18 * Application prototype.
19 */
20
21var app = exports = module.exports = {};
22
23/**
24 * Initialize the server.
25 *
26 *   - setup default configuration
27 *   - setup default middleware
28 *   - setup route reflection methods
29 *
30 * @api private
31 */
32
33app.init = function(){
34  this.cache = {};
35  this.settings = {};
36  this.engines = {};
37  this.defaultConfiguration();
38};
39
40/**
41 * Initialize application configuration.
42 *
43 * @api private
44 */
45
46app.defaultConfiguration = function(){
47  // default settings
48  this.enable('x-powered-by');
49  this.set('env', process.env.NODE_ENV || 'development');
50  this.set('subdomain offset', 2);
51  debug('booting in %s mode', this.get('env'));
52
53  // implicit middleware
54  this.use(connect.query());
55  this.use(middleware.init(this));
56
57  // inherit protos
58  this.on('mount', function(parent){
59    this.request.__proto__ = parent.request;
60    this.response.__proto__ = parent.response;
61    this.engines.__proto__ = parent.engines;
62    this.settings.__proto__ = parent.settings;
63  });
64
65  // router
66  this._router = new Router(this);
67  this.routes = this._router.map;
68  this.__defineGetter__('router', function(){
69    this._usedRouter = true;
70    this._router.caseSensitive = this.enabled('case sensitive routing');
71    this._router.strict = this.enabled('strict routing');
72    return this._router.middleware;
73  });
74
75  // setup locals
76  this.locals = locals(this);
77
78  // default locals
79  this.locals.settings = this.settings;
80
81  // default configuration
82  this.set('view', View);
83  this.set('views', process.cwd() + '/views');
84  this.set('jsonp callback name', 'callback');
85
86  this.configure('development', function(){
87    this.set('json spaces', 2);
88  });
89
90  this.configure('production', function(){
91    this.enable('view cache');
92  });
93};
94
95/**
96 * Proxy `connect#use()` to apply settings to
97 * mounted applications.
98 *
99 * @param {String|Function|Server} route
100 * @param {Function|Server} fn
101 * @return {app} for chaining
102 * @api public
103 */
104
105app.use = function(route, fn){
106  var app;
107
108  // default route to '/'
109  if ('string' != typeof route) fn = route, route = '/';
110
111  // express app
112  if (fn.handle && fn.set) app = fn;
113
114  // restore .app property on req and res
115  if (app) {
116    app.route = route;
117    fn = function(req, res, next) {
118      var orig = req.app;
119      app.handle(req, res, function(err){
120        req.app = res.app = orig;
121        req.__proto__ = orig.request;
122        res.__proto__ = orig.response;
123        next(err);
124      });
125    };
126  }
127
128  connect.proto.use.call(this, route, fn);
129
130  // mounted an app
131  if (app) {
132    app.parent = this;
133    app.emit('mount', this);
134  }
135
136  return this;
137};
138
139/**
140 * Register the given template engine callback `fn`
141 * as `ext`.
142 *
143 * By default will `require()` the engine based on the
144 * file extension. For example if you try to render
145 * a "foo.jade" file Express will invoke the following internally:
146 *
147 *     app.engine('jade', require('jade').__express);
148 *
149 * For engines that do not provide `.__express` out of the box,
150 * or if you wish to "map" a different extension to the template engine
151 * you may use this method. For example mapping the EJS template engine to
152 * ".html" files:
153 *
154 *     app.engine('html', require('ejs').renderFile);
155 *
156 * In this case EJS provides a `.renderFile()` method with
157 * the same signature that Express expects: `(path, options, callback)`,
158 * though note that it aliases this method as `ejs.__express` internally
159 * so if you're using ".ejs" extensions you dont need to do anything.
160 *
161 * Some template engines do not follow this convention, the
162 * [Consolidate.js](https://github.com/visionmedia/consolidate.js)
163 * library was created to map all of node's popular template
164 * engines to follow this convention, thus allowing them to
165 * work seamlessly within Express.
166 *
167 * @param {String} ext
168 * @param {Function} fn
169 * @return {app} for chaining
170 * @api public
171 */
172
173app.engine = function(ext, fn){
174  if ('function' != typeof fn) throw new Error('callback function required');
175  if ('.' != ext[0]) ext = '.' + ext;
176  this.engines[ext] = fn;
177  return this;
178};
179
180/**
181 * Map the given param placeholder `name`(s) to the given callback(s).
182 *
183 * Parameter mapping is used to provide pre-conditions to routes
184 * which use normalized placeholders. For example a _:user_id_ parameter
185 * could automatically load a user's information from the database without
186 * any additional code,
187 *
188 * The callback uses the samesignature as middleware, the only differencing
189 * being that the value of the placeholder is passed, in this case the _id_
190 * of the user. Once the `next()` function is invoked, just like middleware
191 * it will continue on to execute the route, or subsequent parameter functions.
192 *
193 *      app.param('user_id', function(req, res, next, id){
194 *        User.find(id, function(err, user){
195 *          if (err) {
196 *            next(err);
197 *          } else if (user) {
198 *            req.user = user;
199 *            next();
200 *          } else {
201 *            next(new Error('failed to load user'));
202 *          }
203 *        });
204 *      });
205 *
206 * @param {String|Array} name
207 * @param {Function} fn
208 * @return {app} for chaining
209 * @api public
210 */
211
212app.param = function(name, fn){
213  var self = this
214    , fns = [].slice.call(arguments, 1);
215
216  // array
217  if (Array.isArray(name)) {
218    name.forEach(function(name){
219      fns.forEach(function(fn){
220        self.param(name, fn);
221      });
222    });
223  // param logic
224  } else if ('function' == typeof name) {
225    this._router.param(name);
226  // single
227  } else {
228    if (':' == name[0]) name = name.substr(1);
229    fns.forEach(function(fn){
230      self._router.param(name, fn);
231    });
232  }
233
234  return this;
235};
236
237/**
238 * Assign `setting` to `val`, or return `setting`'s value.
239 *
240 *    app.set('foo', 'bar');
241 *    app.get('foo');
242 *    // => "bar"
243 *
244 * Mounted servers inherit their parent server's settings.
245 *
246 * @param {String} setting
247 * @param {String} val
248 * @return {Server} for chaining
249 * @api public
250 */
251
252app.set = function(setting, val){
253  if (1 == arguments.length) {
254    return this.settings[setting];
255  } else {
256    this.settings[setting] = val;
257    return this;
258  }
259};
260
261/**
262 * Return the app's absolute pathname
263 * based on the parent(s) that have
264 * mounted it.
265 *
266 * For example if the application was
267 * mounted as "/admin", which itself
268 * was mounted as "/blog" then the
269 * return value would be "/blog/admin".
270 *
271 * @return {String}
272 * @api private
273 */
274
275app.path = function(){
276  return this.parent
277    ? this.parent.path() + this.route
278    : '';
279};
280
281/**
282 * Check if `setting` is enabled (truthy).
283 *
284 *    app.enabled('foo')
285 *    // => false
286 *
287 *    app.enable('foo')
288 *    app.enabled('foo')
289 *    // => true
290 *
291 * @param {String} setting
292 * @return {Boolean}
293 * @api public
294 */
295
296app.enabled = function(setting){
297  return !!this.set(setting);
298};
299
300/**
301 * Check if `setting` is disabled.
302 *
303 *    app.disabled('foo')
304 *    // => true
305 *
306 *    app.enable('foo')
307 *    app.disabled('foo')
308 *    // => false
309 *
310 * @param {String} setting
311 * @return {Boolean}
312 * @api public
313 */
314
315app.disabled = function(setting){
316  return !this.set(setting);
317};
318
319/**
320 * Enable `setting`.
321 *
322 * @param {String} setting
323 * @return {app} for chaining
324 * @api public
325 */
326
327app.enable = function(setting){
328  return this.set(setting, true);
329};
330
331/**
332 * Disable `setting`.
333 *
334 * @param {String} setting
335 * @return {app} for chaining
336 * @api public
337 */
338
339app.disable = function(setting){
340  return this.set(setting, false);
341};
342
343/**
344 * Configure callback for zero or more envs,
345 * when no `env` is specified that callback will
346 * be invoked for all environments. Any combination
347 * can be used multiple times, in any order desired.
348 *
349 * Examples:
350 *
351 *    app.configure(function(){
352 *      // executed for all envs
353 *    });
354 *
355 *    app.configure('stage', function(){
356 *      // executed staging env
357 *    });
358 *
359 *    app.configure('stage', 'production', function(){
360 *      // executed for stage and production
361 *    });
362 *
363 * Note:
364 *
365 *  These callbacks are invoked immediately, and
366 *  are effectively sugar for the following:
367 *
368 *     var env = process.env.NODE_ENV || 'development';
369 *
370 *      switch (env) {
371 *        case 'development':
372 *          ...
373 *          break;
374 *        case 'stage':
375 *          ...
376 *          break;
377 *        case 'production':
378 *          ...
379 *          break;
380 *      }
381 *
382 * @param {String} env...
383 * @param {Function} fn
384 * @return {app} for chaining
385 * @api public
386 */
387
388app.configure = function(env, fn){
389  var envs = 'all'
390    , args = [].slice.call(arguments);
391  fn = args.pop();
392  if (args.length) envs = args;
393  if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
394  return this;
395};
396
397/**
398 * Delegate `.VERB(...)` calls to `router.VERB(...)`.
399 */
400
401methods.forEach(function(method){
402  app[method] = function(path){
403    if ('get' == method && 1 == arguments.length) return this.set(path);
404
405    // deprecated
406    if (Array.isArray(path)) {
407      console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
408    }
409
410    // if no router attached yet, attach the router
411    if (!this._usedRouter) this.use(this.router);
412
413    // setup route
414    this._router[method].apply(this._router, arguments);
415    return this;
416  };
417});
418
419/**
420 * Special-cased "all" method, applying the given route `path`,
421 * middleware, and callback to _every_ HTTP method.
422 *
423 * @param {String} path
424 * @param {Function} ...
425 * @return {app} for chaining
426 * @api public
427 */
428
429app.all = function(path){
430  var args = arguments;
431  methods.forEach(function(method){
432    app[method].apply(this, args);
433  }, this);
434  return this;
435};
436
437// del -> delete alias
438
439app.del = app.delete;
440
441/**
442 * Render the given view `name` name with `options`
443 * and a callback accepting an error and the
444 * rendered template string.
445 *
446 * Example:
447 *
448 *    app.render('email', { name: 'Tobi' }, function(err, html){
449 *      // ...
450 *    })
451 *
452 * @param {String} name
453 * @param {String|Function} options or fn
454 * @param {Function} fn
455 * @api public
456 */
457
458app.render = function(name, options, fn){
459  var opts = {}
460    , cache = this.cache
461    , engines = this.engines
462    , view;
463
464  // support callback function as second arg
465  if ('function' == typeof options) {
466    fn = options, options = {};
467  }
468
469  // merge app.locals
470  utils.merge(opts, this.locals);
471
472  // merge options._locals
473  if (options._locals) utils.merge(opts, options._locals);
474
475  // merge options
476  utils.merge(opts, options);
477
478  // set .cache unless explicitly provided
479  opts.cache = null == opts.cache
480    ? this.enabled('view cache')
481    : opts.cache;
482
483  // primed cache
484  if (opts.cache) view = cache[name];
485
486  // view
487  if (!view) {
488    view = new (this.get('view'))(name, {
489      defaultEngine: this.get('view engine'),
490      root: this.get('views'),
491      engines: engines
492    });
493
494    if (!view.path) {
495      var err = new Error('Failed to lookup view "' + name + '"');
496      err.view = view;
497      return fn(err);
498    }
499
500    // prime the cache
501    if (opts.cache) cache[name] = view;
502  }
503
504  // render
505  try {
506    view.render(opts, fn);
507  } catch (err) {
508    fn(err);
509  }
510};
511
512/**
513 * Listen for connections.
514 *
515 * A node `http.Server` is returned, with this
516 * application (which is a `Function`) as its
517 * callback. If you wish to create both an HTTP
518 * and HTTPS server you may do so with the "http"
519 * and "https" modules as shown here:
520 *
521 *    var http = require('http')
522 *      , https = require('https')
523 *      , express = require('express')
524 *      , app = express();
525 *
526 *    http.createServer(app).listen(80);
527 *    https.createServer({ ... }, app).listen(443);
528 *
529 * @return {http.Server}
530 * @api public
531 */
532
533app.listen = function(){
534  var server = http.createServer(this);
535  return server.listen.apply(server, arguments);
536};
Note: See TracBrowser for help on using the repository browser.