diff options
Diffstat (limited to 'node_modules/express/lib/application.js')
| -rw-r--r-- | node_modules/express/lib/application.js | 531 | 
1 files changed, 531 insertions, 0 deletions
| diff --git a/node_modules/express/lib/application.js b/node_modules/express/lib/application.js new file mode 100644 index 0000000..e841c12 --- /dev/null +++ b/node_modules/express/lib/application.js @@ -0,0 +1,531 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') +  , Router = require('./router') +  , methods = require('methods') +  , middleware = require('./middleware') +  , debug = require('debug')('express:application') +  , locals = require('./utils').locals +  , View = require('./view') +  , utils = connect.utils +  , path = require('path') +  , http = require('http') +  , join = path.join; + +/** + * Application prototype. + */ + +var app = exports = module.exports = {}; + +/** + * Initialize the server. + * + *   - setup default configuration + *   - setup default middleware + *   - setup route reflection methods + * + * @api private + */ + +app.init = function(){ +  this.cache = {}; +  this.settings = {}; +  this.engines = {}; +  this.defaultConfiguration(); +}; + +/** + * Initialize application configuration. + * + * @api private + */ + +app.defaultConfiguration = function(){ +  // default settings +  this.enable('x-powered-by'); +  this.set('env', process.env.NODE_ENV || 'development'); +  this.set('subdomain offset', 2); +  debug('booting in %s mode', this.get('env')); + +  // implicit middleware +  this.use(connect.query()); +  this.use(middleware.init(this)); + +  // inherit protos +  this.on('mount', function(parent){ +    this.request.__proto__ = parent.request; +    this.response.__proto__ = parent.response; +    this.engines.__proto__ = parent.engines; +    this.settings.__proto__ = parent.settings; +  }); + +  // router +  this._router = new Router(this); +  this.routes = this._router.map; +  this.__defineGetter__('router', function(){ +    this._usedRouter = true; +    this._router.caseSensitive = this.enabled('case sensitive routing'); +    this._router.strict = this.enabled('strict routing'); +    return this._router.middleware; +  }); + +  // setup locals +  this.locals = locals(this); + +  // default locals +  this.locals.settings = this.settings; + +  // default configuration +  this.set('view', View); +  this.set('views', process.cwd() + '/views'); +  this.set('jsonp callback name', 'callback'); + +  this.configure('development', function(){ +    this.set('json spaces', 2); +  }); + +  this.configure('production', function(){ +    this.enable('view cache'); +  }); +}; + +/** + * Proxy `connect#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} fn + * @return {app} for chaining + * @api public + */ + +app.use = function(route, fn){ +  var app; + +  // default route to '/' +  if ('string' != typeof route) fn = route, route = '/'; + +  // express app +  if (fn.handle && fn.set) app = fn; + +  // restore .app property on req and res +  if (app) { +    app.route = route; +    fn = function(req, res, next) { +      var orig = req.app; +      app.handle(req, res, function(err){ +        req.app = res.app = orig; +        req.__proto__ = orig.request; +        res.__proto__ = orig.response; +        next(err); +      }); +    }; +  } + +  connect.proto.use.call(this, route, fn); + +  // mounted an app +  if (app) { +    app.parent = this; +    app.emit('mount', this); +  } + +  return this; +}; + +/** + * Register the given template engine callback `fn` + * as `ext`. + * + * By default will `require()` the engine based on the + * file extension. For example if you try to render + * a "foo.jade" file Express will invoke the following internally: + * + *     app.engine('jade', require('jade').__express); + * + * For engines that do not provide `.__express` out of the box, + * or if you wish to "map" a different extension to the template engine + * you may use this method. For example mapping the EJS template engine to + * ".html" files: + * + *     app.engine('html', require('ejs').renderFile); + * + * In this case EJS provides a `.renderFile()` method with + * the same signature that Express expects: `(path, options, callback)`, + * though note that it aliases this method as `ejs.__express` internally + * so if you're using ".ejs" extensions you dont need to do anything. + * + * Some template engines do not follow this convention, the + * [Consolidate.js](https://github.com/visionmedia/consolidate.js) + * library was created to map all of node's popular template + * engines to follow this convention, thus allowing them to + * work seamlessly within Express. + * + * @param {String} ext + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.engine = function(ext, fn){ +  if ('function' != typeof fn) throw new Error('callback function required'); +  if ('.' != ext[0]) ext = '.' + ext; +  this.engines[ext] = fn; +  return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Parameter mapping is used to provide pre-conditions to routes + * which use normalized placeholders. For example a _:user_id_ parameter + * could automatically load a user's information from the database without + * any additional code, + * + * The callback uses the samesignature as middleware, the only differencing + * being that the value of the placeholder is passed, in this case the _id_ + * of the user. Once the `next()` function is invoked, just like middleware + * it will continue on to execute the route, or subsequent parameter functions. + * + *      app.param('user_id', function(req, res, next, id){ + *        User.find(id, function(err, user){ + *          if (err) { + *            next(err); + *          } else if (user) { + *            req.user = user; + *            next(); + *          } else { + *            next(new Error('failed to load user')); + *          } + *        }); + *      }); + * + * @param {String|Array} name + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.param = function(name, fn){ +  var self = this +    , fns = [].slice.call(arguments, 1); + +  // array +  if (Array.isArray(name)) { +    name.forEach(function(name){ +      fns.forEach(function(fn){ +        self.param(name, fn); +      }); +    }); +  // param logic +  } else if ('function' == typeof name) { +    this._router.param(name); +  // single +  } else { +    if (':' == name[0]) name = name.substr(1); +    fns.forEach(function(fn){ +      self._router.param(name, fn); +    }); +  } + +  return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * + *    app.set('foo', 'bar'); + *    app.get('foo'); + *    // => "bar" + * + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server} for chaining + * @api public + */ + +app.set = function(setting, val){ +  if (1 == arguments.length) { +    return this.settings[setting]; +  } else { +    this.settings[setting] = val; +    return this; +  } +}; + +/** + * Return the app's absolute pathname + * based on the parent(s) that have + * mounted it. + * + * For example if the application was + * mounted as "/admin", which itself + * was mounted as "/blog" then the + * return value would be "/blog/admin". + * + * @return {String} + * @api private + */ + +app.path = function(){ +  return this.parent +    ? this.parent.path() + this.route +    : ''; +}; + +/** + * Check if `setting` is enabled (truthy). + * + *    app.enabled('foo') + *    // => false + * + *    app.enable('foo') + *    app.enabled('foo') + *    // => true + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ +  return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + *    app.disabled('foo') + *    // => true + * + *    app.enable('foo') + *    app.disabled('foo') + *    // => false + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ +  return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.enable = function(setting){ +  return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.disable = function(setting){ +  return this.set(setting, false); +}; + +/** + * Configure callback for zero or more envs, + * when no `env` is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + *    app.configure(function(){ + *      // executed for all envs + *    }); + * + *    app.configure('stage', function(){ + *      // executed staging env + *    }); + * + *    app.configure('stage', 'production', function(){ + *      // executed for stage and production + *    }); + * + * Note: + * + *  These callbacks are invoked immediately, and + *  are effectively sugar for the following: + * + *     var env = process.env.NODE_ENV || 'development'; + * + *      switch (env) { + *        case 'development': + *          ... + *          break; + *        case 'stage': + *          ... + *          break; + *        case 'production': + *          ... + *          break; + *      } + * + * @param {String} env... + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.configure = function(env, fn){ +  var envs = 'all' +    , args = [].slice.call(arguments); +  fn = args.pop(); +  if (args.length) envs = args; +  if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); +  return this; +}; + +/** + * Delegate `.VERB(...)` calls to `router.VERB(...)`. + */ + +methods.forEach(function(method){ +  app[method] = function(path){ +    if ('get' == method && 1 == arguments.length) return this.set(path); + +    // if no router attached yet, attach the router +    if (!this._usedRouter) this.use(this.router); + +    // setup route +    this._router[method].apply(this._router, arguments); +    return this; +  }; +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {app} for chaining + * @api public + */ + +app.all = function(path){ +  var args = arguments; +  methods.forEach(function(method){ +    app[method].apply(this, args); +  }, this); +  return this; +}; + +// del -> delete alias + +app.del = app.delete; + +/** + * Render the given view `name` name with `options` + * and a callback accepting an error and the + * rendered template string. + * + * Example: + * + *    app.render('email', { name: 'Tobi' }, function(err, html){ + *      // ... + *    }) + * + * @param {String} name + * @param {String|Function} options or fn + * @param {Function} fn + * @api public + */ + +app.render = function(name, options, fn){ +  var opts = {} +    , cache = this.cache +    , engines = this.engines +    , view; + +  // support callback function as second arg +  if ('function' == typeof options) { +    fn = options, options = {}; +  } + +  // merge app.locals +  utils.merge(opts, this.locals); + +  // merge options._locals +  if (options._locals) utils.merge(opts, options._locals); + +  // merge options +  utils.merge(opts, options); + +  // set .cache unless explicitly provided +  opts.cache = null == opts.cache +    ? this.enabled('view cache') +    : opts.cache; + +  // primed cache +  if (opts.cache) view = cache[name]; + +  // view +  if (!view) { +    view = new (this.get('view'))(name, { +      defaultEngine: this.get('view engine'), +      root: this.get('views'), +      engines: engines +    }); + +    if (!view.path) { +      var err = new Error('Failed to lookup view "' + name + '"'); +      err.view = view; +      return fn(err); +    } + +    // prime the cache +    if (opts.cache) cache[name] = view; +  } + +  // render +  try { +    view.render(opts, fn); +  } catch (err) { +    fn(err); +  } +}; + +/** + * Listen for connections. + * + * A node `http.Server` is returned, with this + * application (which is a `Function`) as its + * callback. If you wish to create both an HTTP + * and HTTPS server you may do so with the "http" + * and "https" modules as shown here: + * + *    var http = require('http') + *      , https = require('https') + *      , express = require('express') + *      , app = express(); + * + *    http.createServer(app).listen(80); + *    https.createServer({ ... }, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ +  var server = http.createServer(this); +  return server.listen.apply(server, arguments); +}; | 
