express
Advanced tools
Comparing version 2.3.6 to 2.3.7
2.3.7 / 2011-05-23 | ||
================== | ||
* Added route `Collection`, ex: `app.get('/user/:id').remove();` | ||
* Added support for `app.param(fn)` to define param logic | ||
* Removed `app.param()` support for callback with return value | ||
* Removed module.parent check from express(1) generated app. Closes #670 | ||
* Refactored router. Closes #639 | ||
2.3.6 / 2011-05-20 | ||
@@ -3,0 +12,0 @@ ================== |
@@ -31,3 +31,3 @@ | ||
exports.version = '2.3.6'; | ||
exports.version = '2.3.7'; | ||
@@ -34,0 +34,0 @@ /** |
192
lib/http.js
@@ -15,4 +15,6 @@ | ||
, router = require('./router') | ||
, Router = require('./router') | ||
, view = require('./view') | ||
, toArray = require('./utils').toArray | ||
, methods = router.methods.concat('del', 'all') | ||
, view = require('./view') | ||
, url = require('url') | ||
@@ -90,7 +92,7 @@ , utils = connect.utils; | ||
// use router, expose as app.get(), etc | ||
var fn = router(function(app){ self.routes = app; }, this); | ||
// router | ||
this.routes = new Router(this); | ||
this.__defineGetter__('router', function(){ | ||
this.__usedRouter = true; | ||
return fn; | ||
return self.routes.middleware; | ||
}); | ||
@@ -118,37 +120,61 @@ | ||
// route lookup methods | ||
this.remove = function(url){ | ||
return self.remove.all(url); | ||
}; | ||
this.match = function(url){ | ||
return self.match.all(url); | ||
}; | ||
this.lookup = function(url){ | ||
return self.lookup.all(url); | ||
}; | ||
// route manipulation methods | ||
methods.forEach(function(method){ | ||
self.match[method] = function(url){ | ||
return self.router.match(url, 'all' == method | ||
? null | ||
: method); | ||
self.lookup[method] = function(path){ | ||
return self.routes.lookup(method, path); | ||
}; | ||
self.remove[method] = function(url){ | ||
return self.router.remove(url, 'all' == method | ||
? null | ||
: method); | ||
self.match[method] = function(path){ | ||
return self.routes.match(method, path); | ||
}; | ||
self.lookup[method] = function(path){ | ||
return self.router.lookup(path, 'all' == method | ||
? null | ||
: method); | ||
self.remove[method] = function(path){ | ||
return self.routes.lookup(method, path).remove(); | ||
}; | ||
}); | ||
// del -> delete | ||
self.lookup.del = self.lookup.delete; | ||
self.match.del = self.match.delete; | ||
self.remove.del = self.remove.delete; | ||
}; | ||
/** | ||
* Remove routes matching the given `path`. | ||
* | ||
* @param {Route} path | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
app.remove = function(path){ | ||
return this.routes.lookup('all', path).remove(); | ||
}; | ||
/** | ||
* Lookup routes defined with a path | ||
* equivalent to `path`. | ||
* | ||
* @param {Stirng} path | ||
* @return {Array} | ||
* @api public | ||
*/ | ||
app.lookup = function(path){ | ||
return this.routes.lookup('all', path); | ||
}; | ||
/** | ||
* Lookup routes matching the given `url`. | ||
* | ||
* @param {Stirng} url | ||
* @return {Array} | ||
* @api public | ||
*/ | ||
app.match = function(url){ | ||
return this.routes.match('all', url); | ||
}; | ||
/** | ||
* When using the vhost() middleware register error handlers. | ||
@@ -295,36 +321,20 @@ */ | ||
* Param mapping is used to provide pre-conditions to routes | ||
* which us normalized placeholders. For example ":user_id" may | ||
* attempt to load the user from the database, where as ":num" may | ||
* pass the value through `parseInt(num, 10)`. | ||
* which us normalized placeholders. This callback has the same | ||
* signature as regular middleware, for example below when ":userId" | ||
* is used this function will be invoked in an attempt to load the user. | ||
* | ||
* When the callback function accepts only a single argument, the | ||
* value of placeholder is passed: | ||
* app.param('userId', 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')); | ||
* } | ||
* }); | ||
* }); | ||
* | ||
* app.param('page', function(n){ return parseInt(n, 10); }); | ||
* | ||
* After which "/users/:page" would automatically provide us with | ||
* an integer for `req.params.page`. If desired we could use the callback | ||
* signature shown below, and immediately `next(new Error('invalid page'))` | ||
* when `parseInt` fails. | ||
* | ||
* Alternatively the callback may accept the request, response, next, and | ||
* the value, acting like middlware: | ||
* | ||
* app.param('userId', 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')); | ||
* } | ||
* }); | ||
* }); | ||
* | ||
* Now every time ":userId" is present, the associated user object | ||
* will be loaded and assigned before the route handler is invoked. | ||
* | ||
* @param {String|Array} name | ||
* @param {String|Array|Function} name | ||
* @param {Function} fn | ||
@@ -336,2 +346,3 @@ * @return {Server} for chaining | ||
app.param = function(name, fn){ | ||
// array | ||
if (Array.isArray(name)) { | ||
@@ -341,2 +352,6 @@ name.forEach(function(name){ | ||
}, this); | ||
// param logic | ||
} else if ('function' == typeof name) { | ||
this.routes.param(name); | ||
// single | ||
} else { | ||
@@ -346,2 +361,3 @@ if (':' == name[0]) name = name.substr(1); | ||
} | ||
return this; | ||
@@ -474,35 +490,43 @@ }; | ||
app.configure = function(env, fn){ | ||
if ('function' == typeof env) { | ||
fn = env, env = 'all'; | ||
} | ||
if ('all' == env || env == this.settings.env) { | ||
fn.call(this); | ||
} | ||
if ('function' == typeof env) fn = env, env = 'all'; | ||
if ('all' == env || env == this.settings.env) fn.call(this); | ||
return this; | ||
}; | ||
// Generate routing methods | ||
/** | ||
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`. | ||
*/ | ||
methods.forEach(function(method){ | ||
app[method] = function(path){ | ||
var self = this; | ||
if (1 == arguments.length) return this.routes.lookup(method, path); | ||
var args = [method].concat(toArray(arguments)); | ||
if (!this.__usedRouter) this.use(this.router); | ||
return this.routes._route.apply(this.routes, args); | ||
} | ||
}); | ||
// Lookup | ||
if (1 == arguments.length) { | ||
return this.router.lookup(path, 'all' == method | ||
? null | ||
: method); | ||
} | ||
/** | ||
* Special-cased "all" method, applying the given route `path`, | ||
* middleware, and callback to _every_ HTTP method. | ||
* | ||
* @param {String} path | ||
* @param {Function} ... | ||
* @return {Server} for chaining | ||
* @api public | ||
*/ | ||
// Ensure router is mounted | ||
if (!this.__usedRouter) this.use(this.router); | ||
app.all = function(path){ | ||
var args = arguments; | ||
if (1 == args.length) return this.routes.lookup('all', path); | ||
methods.forEach(function(method){ | ||
if ('all' == method) return; | ||
app[method].apply(this, args); | ||
}, this); | ||
return this; | ||
}; | ||
// Generate the route | ||
this.routes[method].apply(this, arguments); | ||
return this; | ||
}; | ||
}); | ||
// del -> delete alias | ||
// Alias delete as "del" | ||
app.del = app.delete; | ||
app.del = app.delete; |
/*! | ||
* Express - router | ||
* Express - Router | ||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> | ||
@@ -12,256 +12,266 @@ * MIT Licensed | ||
var utils = require('../utils') | ||
var Route = require('./route') | ||
, Collection = require('./collection') | ||
, utils = require('../utils') | ||
, parse = require('url').parse | ||
, _methods = require('./methods') | ||
, Route = require('./route'); | ||
, toArray = utils.toArray; | ||
/** | ||
* Expose router. | ||
* Expose `Router` constructor. | ||
*/ | ||
exports = module.exports = router; | ||
exports = module.exports = Router; | ||
/** | ||
* Expose methods. | ||
* Expose HTTP methods. | ||
*/ | ||
exports.methods = _methods; | ||
var methods = exports.methods = require('./methods'); | ||
/** | ||
* Provides Sinatra-like routing capabilities. | ||
* | ||
* @param {Function} fn | ||
* @return {Function} | ||
* Initialize a new `Router` with the given `app`. | ||
* | ||
* @param {express.HTTPServer} app | ||
* @api private | ||
*/ | ||
function router(fn, app){ | ||
var self = this | ||
, methods = {} | ||
, routes = {} | ||
, params = {}; | ||
function Router(app) { | ||
var self = this; | ||
this.app = app; | ||
this.routes = {}; | ||
this.params = {}; | ||
this._params = []; | ||
if (!fn) throw new Error('router provider requires a callback function'); | ||
this.middleware = function(req, res, next){ | ||
self._dispatch(req, res, next); | ||
}; | ||
} | ||
// Generate method functions | ||
_methods.forEach(function(method){ | ||
methods[method] = generateMethodFunction(method.toUpperCase()); | ||
}); | ||
/** | ||
* Register a param callback `fn` for the given `name`. | ||
* | ||
* @param {String|Function} name | ||
* @param {Function} fn | ||
* @return {Router} for chaining | ||
* @api public | ||
*/ | ||
// Alias del -> delete | ||
methods.del = methods.delete; | ||
Router.prototype.param = function(name, fn){ | ||
// param logic | ||
if ('function' == typeof name) { | ||
this._params.push(name); | ||
return; | ||
} | ||
// Apply callback to all methods | ||
methods.all = function(){ | ||
var args = arguments; | ||
_methods.forEach(function(name){ | ||
methods[name].apply(this, args); | ||
}); | ||
return self; | ||
}; | ||
// apply param functions | ||
var params = this._params | ||
, len = params.length | ||
, ret; | ||
// Register param callback | ||
methods.param = function(name, fn){ | ||
params[name] = fn; | ||
}; | ||
fn.call(this, methods); | ||
for (var i = 0; i < len; ++i) { | ||
if (ret = params[i](name, fn)) { | ||
fn = ret; | ||
} | ||
} | ||
function generateMethodFunction(name) { | ||
var localRoutes = routes[name] = routes[name] || []; | ||
return function(path, fn){ | ||
var keys = [] | ||
, middleware = []; | ||
// ensure we end up with a | ||
// middleware function | ||
if ('function' != typeof fn) { | ||
throw new Error('invalid param() call for ' + name + ', got ' + fn); | ||
} | ||
// slice middleware | ||
if (arguments.length > 2) { | ||
middleware = Array.prototype.slice.call(arguments, 1, arguments.length); | ||
fn = middleware.pop(); | ||
middleware = utils.flatten(middleware); | ||
} | ||
this.params[name] = fn; | ||
return this; | ||
}; | ||
if (!path) throw new Error(name + ' route requires a path'); | ||
if (!fn) throw new Error(name + ' route ' + path + ' requires a callback'); | ||
/** | ||
* Remove the given `route`, returns | ||
* a bool indicating if the route was present | ||
* or not. | ||
* | ||
* @param {Route} route | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
var options = { sensitive: app.enabled('case sensitive routes') }; | ||
var route = new Route(name, path, fn, options); | ||
route.middleware = middleware; | ||
localRoutes.push(route); | ||
return self; | ||
}; | ||
Router.prototype.remove = function(route){ | ||
var routes = this.routes[route.method] | ||
, len = routes.length; | ||
for (var i = 0; i < len; ++i) { | ||
if (route == routes[i]) { | ||
routes.splice(i, 1); | ||
return true; | ||
} | ||
} | ||
}; | ||
function router(req, res, next){ | ||
var route | ||
, self = this; | ||
/** | ||
* Return routes with route paths matching `path`. | ||
* | ||
* @param {String} method | ||
* @param {String} path | ||
* @return {Collection} | ||
* @api public | ||
*/ | ||
(function pass(i){ | ||
if (route = match(req, routes, i)) { | ||
var i = 0 | ||
, keys = route.keys; | ||
Router.prototype.lookup = function(method, path){ | ||
return this.find(function(route){ | ||
return path == route.path | ||
&& (route.method == method | ||
|| method == 'all'); | ||
}); | ||
}; | ||
req.params = route.params; | ||
/** | ||
* Return routes with regexps that match the given `url`. | ||
* | ||
* @param {String} method | ||
* @param {String} url | ||
* @return {Collection} | ||
* @api public | ||
*/ | ||
// Param preconditions | ||
(function param(err) { | ||
try { | ||
var key = keys[i++] | ||
, val = req.params[key] | ||
, fn = params[key]; | ||
Router.prototype.match = function(method, url){ | ||
return this.find(function(route){ | ||
return route.match(url) | ||
&& (route.method == method | ||
|| method == 'all'); | ||
}); | ||
}; | ||
if ('route' == err) { | ||
pass(req._route_index + 1); | ||
// Error | ||
} else if (err) { | ||
next(err); | ||
// Param has callback | ||
} else if (fn) { | ||
// Return style | ||
if (1 == fn.length) { | ||
req.params[key] = fn(val); | ||
param(); | ||
// Middleware style | ||
} else { | ||
fn(req, res, param, val); | ||
} | ||
// Finished processing params | ||
} else if (!key) { | ||
// route middleware | ||
i = 0; | ||
(function nextMiddleware(err){ | ||
var fn = route.middleware[i++]; | ||
if ('route' == err) { | ||
pass(req._route_index + 1); | ||
} else if (err) { | ||
next(err); | ||
} else if (fn) { | ||
fn(req, res, nextMiddleware); | ||
} else { | ||
route.callback.call(self, req, res, function(err){ | ||
if (err) { | ||
next(err); | ||
} else { | ||
pass(req._route_index + 1); | ||
} | ||
}); | ||
} | ||
})(); | ||
// More params | ||
} else { | ||
param(); | ||
} | ||
} catch (err) { | ||
next(err); | ||
} | ||
})(); | ||
} else if ('OPTIONS' == req.method) { | ||
options(req, res, routes); | ||
} else { | ||
next(); | ||
} | ||
})(); | ||
}; | ||
/** | ||
* Find routes based on the return value of `fn` | ||
* which is invoked once per route. | ||
* | ||
* @param {Function} fn | ||
* @return {Collection} | ||
* @api public | ||
*/ | ||
router.remove = function(path, method, ret){ | ||
var ret = ret || [] | ||
, route; | ||
Router.prototype.find = function(fn){ | ||
var len = methods.length | ||
, ret = new Collection(this) | ||
, method | ||
, routes | ||
, route; | ||
// method specific remove | ||
if (method) { | ||
method = method.toUpperCase(); | ||
if (routes[method]) { | ||
for (var i = 0; i < routes[method].length; ++i) { | ||
route = routes[method][i]; | ||
if (path == route.path) { | ||
route.index = i; | ||
routes[method].splice(i, 1); | ||
ret.push(route); | ||
--i; | ||
} | ||
} | ||
} | ||
// global remove | ||
} else { | ||
_methods.forEach(function(method){ | ||
router.remove(path, method, ret); | ||
}); | ||
for (var i = 0; i < len; ++i) { | ||
method = methods[i]; | ||
routes = this.routes[method]; | ||
if (!routes) continue; | ||
for (var j = 0, jlen = routes.length; j < jlen; ++j) { | ||
route = routes[j]; | ||
if (fn(route)) ret.push(route); | ||
} | ||
} | ||
return ret; | ||
}; | ||
return ret; | ||
}; | ||
router.lookup = function(path, method, ret){ | ||
ret = ret || []; | ||
/** | ||
* Route dispatcher aka the route "middleware". | ||
* | ||
* @param {IncomingMessage} req | ||
* @param {ServerResponse} res | ||
* @param {Function} next | ||
* @api private | ||
*/ | ||
// method specific lookup | ||
if (method) { | ||
method = method.toUpperCase(); | ||
if (routes[method]) { | ||
routes[method].forEach(function(route, i){ | ||
if (path == route.path) { | ||
route.index = i; | ||
ret.push(route); | ||
} | ||
}); | ||
} | ||
// global lookup | ||
} else { | ||
_methods.forEach(function(method){ | ||
router.lookup(path, method, ret); | ||
}); | ||
Router.prototype._dispatch = function(req, res, next){ | ||
var params = this.params | ||
, self = this; | ||
// route dispatch | ||
(function pass(i){ | ||
var route | ||
, keys | ||
, ret; | ||
// match next route | ||
function nextRoute() { | ||
pass(req._route_index + 1); | ||
} | ||
return ret; | ||
}; | ||
// match route | ||
route = self._match(req, i); | ||
router.match = function(url, method, ret){ | ||
var ret = ret || [] | ||
, i = 0 | ||
, route | ||
, req; | ||
// implied OPTIONS | ||
if ('OPTIONS' == req.method) return self._options(req, res); | ||
// method specific matches | ||
if (method) { | ||
method = method.toUpperCase(); | ||
req = { url: url, method: method }; | ||
while (route = match(req, routes, i)) { | ||
i = req._route_index + 1; | ||
route.index = i; | ||
ret.push(route); | ||
} | ||
// global matches | ||
} else { | ||
_methods.forEach(function(method){ | ||
router.match(url, method, ret); | ||
}); | ||
} | ||
// no route | ||
if (!route) return next(); | ||
return ret; | ||
}; | ||
// we have a route | ||
// start at param 0 | ||
req.params = route.params; | ||
keys = route.keys; | ||
i = 0; | ||
router.routes = routes; | ||
(function param(err) { | ||
var key = keys[i++] | ||
, val = req.params[key] | ||
, fn = params[key] | ||
, ret; | ||
return router; | ||
} | ||
try { | ||
if ('route' == err) { | ||
nextRoute(); | ||
} else if (err) { | ||
next(err); | ||
} else if (fn) { | ||
fn(req, res, param, val); | ||
} else if (key) { | ||
param(); | ||
} else { | ||
i = 0; | ||
middleware(); | ||
} | ||
} catch (err) { | ||
next(err); | ||
} | ||
})(); | ||
// invoke route middleware | ||
function middleware(err) { | ||
var fn = route.middleware[i++]; | ||
if ('route' == err) { | ||
nextRoute(); | ||
} else if (err) { | ||
next(err); | ||
} else if (fn) { | ||
fn(req, res, middleware); | ||
} else { | ||
done(); | ||
} | ||
}; | ||
// invoke middleware callback | ||
function done() { | ||
route.callback.call(self, req, res, function(err){ | ||
if (err) return next(err); | ||
pass(req._route_index + 1); | ||
}); | ||
} | ||
})(0); | ||
}; | ||
/** | ||
* Respond to OPTIONS. | ||
* Respond to __OPTIONS__ method. | ||
* | ||
* @param {ServerRequest} req | ||
* @param {ServerResponse} req | ||
* @param {Array} routes | ||
* @param {IncomingMessage} req | ||
* @param {ServerResponse} res | ||
* @api private | ||
*/ | ||
function options(req, res, routes) { | ||
var pathname = parse(req.url).pathname | ||
, body = optionsFor(pathname, routes).join(','); | ||
Router.prototype._options = function(req, res){ | ||
var path = parse(req.url).pathname | ||
, body = this._optionsFor(path).join(','); | ||
res.send(body, { Allow: body }); | ||
} | ||
}; | ||
/** | ||
* Return OPTIONS array for the given `path`, matching `routes`. | ||
* Return an array of HTTP verbs or "options" for `path`. | ||
* | ||
* @param {String} path | ||
* @param {Array} routes | ||
* @return {Array} | ||
@@ -271,7 +281,9 @@ * @api private | ||
function optionsFor(path, routes) { | ||
return _methods.filter(function(method){ | ||
var arr = routes[method.toUpperCase()]; | ||
for (var i = 0, len = arr.length; i < len; ++i) { | ||
if (arr[i].regexp.test(path)) return true; | ||
Router.prototype._optionsFor = function(path){ | ||
var self = this; | ||
return methods.filter(function(method){ | ||
var routes = self.routes[method]; | ||
if (!routes) return; | ||
for (var i = 0, len = routes.length; i < len; ++i) { | ||
if (routes[i].match(path)) return true; | ||
} | ||
@@ -281,41 +293,40 @@ }).map(function(method){ | ||
}); | ||
} | ||
}; | ||
/** | ||
* Attempt to match the given request to | ||
* one of the routes. When successful | ||
* a route function is returned. | ||
* Attempt to match a route for `req` | ||
* starting from offset `i`. | ||
* | ||
* @param {ServerRequest} req | ||
* @param {Object} routes | ||
* @return {Function} | ||
* @param {IncomingMessage} req | ||
* @param {Number} i | ||
* @return {Route} | ||
* @api private | ||
*/ | ||
function match(req, routes, i) { | ||
var captures | ||
, method = req.method | ||
, i = i || 0; | ||
Router.prototype._match = function(req, i){ | ||
var method = req.method.toLowerCase() | ||
, url = parse(req.url) | ||
, path = url.pathname | ||
, routes = this.routes | ||
, captures | ||
, route | ||
, keys; | ||
// pass HEAD to GET routes | ||
if ('HEAD' == method) method = 'GET'; | ||
if ('head' == method) method = 'get'; | ||
// routes for this method | ||
if (routes = routes[method]) { | ||
var url = parse(req.url) | ||
, pathname = url.pathname; | ||
// matching routes | ||
for (var len = routes.length; i < len; ++i) { | ||
var route = routes[i] | ||
, fn = route.callback | ||
, path = route.regexp | ||
, keys = route.keys; | ||
route = routes[i]; | ||
if (captures = route.match(path)) { | ||
keys = route.keys; | ||
route.params = []; | ||
// match | ||
if (captures = path.exec(pathname)) { | ||
route.params = []; | ||
for (var j = 1, l = captures.length; j < l; ++j) { | ||
var key = keys[j-1], | ||
val = 'string' == typeof captures[j] | ||
// params from capture groups | ||
for (var j = 1, jlen = captures.length; j < jlen; ++j) { | ||
var key = keys[j-1] | ||
, val = 'string' == typeof captures[j] | ||
? decodeURIComponent(captures[j]) | ||
@@ -329,2 +340,4 @@ : captures[j]; | ||
} | ||
// all done | ||
req._route_index = i; | ||
@@ -335,2 +348,41 @@ return route; | ||
} | ||
} | ||
}; | ||
/** | ||
* Route `method`, `path`, and optional middleware | ||
* to the callback `fn`. | ||
* | ||
* @param {String} method | ||
* @param {String} path | ||
* @param {Function} ... | ||
* @param {Function} fn | ||
* @return {Router} for chaining | ||
* @api private | ||
*/ | ||
Router.prototype._route = function(method, path, fn){ | ||
var app = this.app | ||
, middleware = []; | ||
// slice middleware | ||
if (arguments.length > 3) { | ||
middleware = toArray(arguments, 2); | ||
fn = middleware.pop(); | ||
middleware = utils.flatten(middleware); | ||
} | ||
// ensure path and callback are given | ||
if (!path) throw new Error(method + 'route requires a path'); | ||
if (!fn) throw new Error(method + ' route ' + path + ' requires a callback'); | ||
// create the route | ||
var route = new Route(method, path, fn, { | ||
sensitive: app.enabled('case sensitive routes') | ||
, middleware: middleware | ||
}); | ||
// add it | ||
(this.routes[method] = this.routes[method] || []) | ||
.push(route); | ||
return this; | ||
}; |
@@ -21,2 +21,3 @@ | ||
* - `sensitive` enable case-sensitive routes | ||
* - `middleware` array of middleware | ||
* | ||
@@ -34,7 +35,20 @@ * @param {String} method | ||
this.path = path; | ||
this.method = method; | ||
this.regexp = normalize(path, this.keys = [], options.sensitive); | ||
this.method = method; | ||
this.middleware = options.middleware; | ||
} | ||
/** | ||
* Check if this route matches `path` and return captures made. | ||
* | ||
* @param {String} path | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
Route.prototype.match = function(path){ | ||
return this.regexp.exec(path); | ||
}; | ||
/** | ||
* Normalize the given path string, | ||
@@ -41,0 +55,0 @@ * returning a regular expression. |
@@ -123,1 +123,18 @@ | ||
}; | ||
/** | ||
* Fast alternative to `Array.prototype.slice.call()`. | ||
* | ||
* @param {Arguments} args | ||
* @param {Number} n | ||
* @return {Array} | ||
* @api public | ||
*/ | ||
exports.toArray = function(args, i){ | ||
var arr = [] | ||
, len = args.length | ||
, i = i || 0; | ||
for (; i < len; ++i) arr.push(args[i]); | ||
return arr; | ||
}; |
{ | ||
"name": "express", | ||
"description": "Sinatra inspired web development framework", | ||
"version": "2.3.6", | ||
"version": "2.3.7", | ||
"author": "TJ Holowaychuk <tj@vision-media.ca>", | ||
@@ -6,0 +6,0 @@ "contributors": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
102966
21
2410