diff --git a/History.md b/History.md index e7eab150..1c0fcba4 100644 --- a/History.md +++ b/History.md @@ -1,9 +1,23 @@ 4.2.x ===== + * deprecate `app.del()` -- use `app.delete()` instead + * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead + - the edge-case `res.json(status, num)` requires `res.status(status).json(num)` + * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead + - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)` * fix `req.next` when inside router instance * include `ETag` header in `HEAD` requests * keep previous `Content-Type` for `res.jsonp` + * support PURGE method + - add `app.purge` + - add `router.purge` + - include PURGE in `app.all` + * update debug to 0.8.0 + - add `enable()` method + - change from stderr to stdout + * update methods to 1.0.0 + - add PURGE 4.1.2 / 2014-05-08 ================== @@ -80,6 +94,39 @@ - `app.route()` - Proxy to the app's `Router#route()` method to create a new route - Router & Route - public API +3.6.0 / 2014-05-09 +================== + + * deprecate `app.del()` -- use `app.delete()` instead + * deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead + - the edge-case `res.json(status, num)` requires `res.status(status).json(num)` + * deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead + - the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)` + * support PURGE method + - add `app.purge` + - add `router.purge` + - include PURGE in `app.all` + * update connect to 2.15.0 + * Add `res.appendHeader` + * Call error stack even when response has been sent + * Patch `res.headerSent` to return Boolean + * Patch `res.headersSent` for node.js 0.8 + * Prevent default 404 handler after response sent + * dep: compression@1.0.2 + * dep: connect-timeout@1.1.0 + * dep: debug@^0.8.0 + * dep: errorhandler@1.0.1 + * dep: express-session@1.0.4 + * dep: morgan@1.0.1 + * dep: serve-favicon@2.0.0 + * dep: serve-index@1.0.2 + * update debug to 0.8.0 + * add `enable()` method + * change from stderr to stdout + * update methods to 1.0.0 + - add PURGE + * update mkdirp to 0.5.0 + 3.5.3 / 2014-05-08 ================== diff --git a/Readme.md b/Readme.md index ac90eb9a..75f6b8cc 100644 --- a/Readme.md +++ b/Readme.md @@ -2,7 +2,7 @@ Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). - [](https://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/) + [](https://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/) [](http://badge.fury.io/js/express) ```js var express = require('express'); diff --git a/examples/resource/app.js b/examples/resource/app.js index cbceaf6f..1496fcf0 100644 --- a/examples/resource/app.js +++ b/examples/resource/app.js @@ -17,7 +17,7 @@ app.resource = function(path, obj) { obj.range(req, res, a, b, format); }); this.get(path + '/:id', obj.show); - this.del(path + '/:id', obj.destroy); + this.delete(path + '/:id', obj.destroy); }; // Fake records diff --git a/examples/route-map/index.js b/examples/route-map/index.js index 76c68bbd..6e5ddd9d 100644 --- a/examples/route-map/index.js +++ b/examples/route-map/index.js @@ -34,7 +34,7 @@ var users = { res.send('user ' + req.params.uid); }, - del: function(req, res){ + delete: function(req, res){ res.send('delete users'); } }; @@ -44,7 +44,7 @@ var pets = { res.send('user ' + req.params.uid + '\'s pets'); }, - del: function(req, res){ + delete: function(req, res){ res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid); } }; @@ -52,13 +52,13 @@ var pets = { app.map({ '/users': { get: users.list, - del: users.del, + delete: users.delete, '/:uid': { get: users.get, '/pets': { get: pets.list, '/:pid': { - del: pets.del + delete: pets.delete } } } diff --git a/examples/route-middleware/index.js b/examples/route-middleware/index.js index 696c3d34..c4d1d22f 100644 --- a/examples/route-middleware/index.js +++ b/examples/route-middleware/index.js @@ -77,7 +77,7 @@ app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){ res.send('Editing user ' + req.user.name); }); -app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){ +app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){ res.send('Deleted user ' + req.user.name); }); diff --git a/lib/application.js b/lib/application.js index c12b8f15..000eeaf0 100644 --- a/lib/application.js +++ b/lib/application.js @@ -11,6 +11,7 @@ var query = require('./middleware/query'); var debug = require('debug')('express:application'); var View = require('./view'); var http = require('http'); +var deprecate = require('./utils').deprecate; /** * Application prototype. @@ -432,7 +433,7 @@ app.all = function(path){ // del -> delete alias -app.del = app.delete; +app.del = deprecate(app.delete, 'app.del: Use app.delete instead'); /** * Render the given view `name` name with `options` diff --git a/lib/response.js b/lib/response.js index 775482c0..23f24c93 100644 --- a/lib/response.js +++ b/lib/response.js @@ -10,6 +10,7 @@ var sign = require('cookie-signature').sign; var normalizeType = require('./utils').normalizeType; var normalizeTypes = require('./utils').normalizeTypes; var contentDisposition = require('./utils').contentDisposition; +var deprecate = require('./utils').deprecate; var etag = require('./utils').etag; var statusCodes = http.STATUS_CODES; var cookie = require('cookie'); @@ -174,6 +175,9 @@ res.json = function(obj){ // res.json(body, status) backwards compat if ('number' == typeof arguments[1]) { this.statusCode = arguments[1]; + return 'number' === typeof obj + ? jsonNumDeprecated.call(this, obj) + : jsonDeprecated.call(this, obj); } else { this.statusCode = obj; obj = arguments[1]; @@ -192,6 +196,12 @@ res.json = function(obj){ return this.send(body); }; +var jsonDeprecated = deprecate(res.json, + 'res.json(obj, status): Use res.json(status, obj) instead'); + +var jsonNumDeprecated = deprecate(res.json, + 'res.json(num, status): Use res.status(status).json(num) instead'); + /** * Send JSON response with JSONP callback support. * @@ -214,6 +224,9 @@ res.jsonp = function(obj){ // res.json(body, status) backwards compat if ('number' == typeof arguments[1]) { this.statusCode = arguments[1]; + return 'number' === typeof obj + ? jsonpNumDeprecated.call(this, obj) + : jsonpDeprecated.call(this, obj); } else { this.statusCode = obj; obj = arguments[1]; @@ -247,6 +260,12 @@ res.jsonp = function(obj){ return this.send(body); }; +var jsonpDeprecated = deprecate(res.json, + 'res.jsonp(obj, status): Use res.jsonp(status, obj) instead'); + +var jsonpNumDeprecated = deprecate(res.json, + 'res.jsonp(num, status): Use res.status(status).jsonp(num) instead'); + /** * Transfer the file at the given `path`. * diff --git a/lib/utils.js b/lib/utils.js index 4297cbce..fc49533e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -5,6 +5,22 @@ var mime = require('send').mime; var crc32 = require('buffer-crc32'); var basename = require('path').basename; +var deprecate = require('util').deprecate; + +/** + * Deprecate function, like core `util.deprecate` + * + * @param {Function} fn + * @param {String} msg + * @return {Function} + * @api private + */ + +exports.deprecate = function(fn, msg){ + return 'test' !== process.env.NODE_ENV + ? deprecate(fn, 'express: ' + msg) + : fn; +}; /** * Return ETag for `body`. diff --git a/package.json b/package.json index 37ca16e6..c016d547 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" }, + { + "name": "Douglas Christopher Wilson", + "email": "doug@somethingdoug.com" + }, { "name": "Guillermo Rauch", "email": "rauchg@gmail.com" @@ -37,7 +41,7 @@ "cookie": "0.1.2", "buffer-crc32": "0.2.1", "fresh": "0.2.2", - "methods": "0.1.0", + "methods": "1.0.0", "send": "0.3.0", "cookie-signature": "1.0.3", "merge-descriptors": "0.0.2", @@ -46,7 +50,7 @@ "qs": "0.6.6", "serve-static": "1.1.0", "path-to-regexp": "0.1.2", - "debug": ">= 0.7.3 < 1" + "debug": ">= 0.8.0 < 1" }, "devDependencies": { "mocha": "~1.18.2", @@ -58,7 +62,7 @@ "multiparty": "~3.2.4", "hjs": "~0.0.6", "should": "~3.3.1", - "supertest": "~0.11.0", + "supertest": "~0.12.0", "method-override": "1.0.0", "cookie-parser": "1.0.1", "express-session": "1.0.4", diff --git a/test/app.router.js b/test/app.router.js index 6c20d168..3373813d 100644 --- a/test/app.router.js +++ b/test/app.router.js @@ -6,11 +6,10 @@ var express = require('../') describe('app.router', function(){ describe('methods supported', function(){ - methods.forEach(function(method){ + methods.concat('del').forEach(function(method){ if (method === 'connect') return; it('should include ' + method.toUpperCase(), function(done){ - if (method == 'delete') method = 'del'; var app = express(); var calls = []; diff --git a/test/res.format.js b/test/res.format.js index 2c52133b..7b846222 100644 --- a/test/res.format.js +++ b/test/res.format.js @@ -4,9 +4,9 @@ var express = require('../') , utils = require('../lib/utils') , assert = require('assert'); -var app = express(); +var app1 = express(); -app.use(function(req, res, next){ +app1.use(function(req, res, next){ res.format({ 'text/plain': function(){ res.send('hey'); @@ -25,7 +25,7 @@ app.use(function(req, res, next){ }); }); -app.use(function(err, req, res, next){ +app1.use(function(err, req, res, next){ if (!err.types) throw err; res.send(err.status, 'Supports: ' + err.types.join(', ')); }) @@ -53,10 +53,24 @@ app3.use(function(req, res, next){ }) }); +var app4 = express(); + +app4.get('/', function(req, res, next){ + res.format({ + text: function(){ res.send('hey') }, + html: function(){ res.send('
hey
') }, + json: function(){ res.send({ message: 'hey' }) } + }); +}); + +app4.use(function(err, req, res, next){ + res.send(err.status, 'Supports: ' + err.types.join(', ')); +}) + describe('res', function(){ describe('.format(obj)', function(){ describe('with canonicalized mime types', function(){ - test(app); + test(app1); }) describe('with extnames', function(){ @@ -72,6 +86,10 @@ describe('res', function(){ }) }) + describe('in router', function(){ + test(app4); + }) + describe('in router', function(){ var app = express(); var router = express.Router(); diff --git a/test/res.json.js b/test/res.json.js index 97eea7eb..c3a33f56 100644 --- a/test/res.json.js +++ b/test/res.json.js @@ -32,7 +32,7 @@ describe('res', function(){ }) describe('when given primitives', function(){ - it('should respond with json', function(done){ + it('should respond with json for null', function(done){ var app = express(); app.use(function(req, res){ @@ -47,6 +47,40 @@ describe('res', function(){ done(); }) }) + + it('should respond with json for Number', function(done){ + var app = express(); + + app.use(function(req, res){ + res.json(300); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(200); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('300'); + done(); + }) + }) + + it('should respond with json for String', function(done){ + var app = express(); + + app.use(function(req, res){ + res.json('str'); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(200); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('"str"'); + done(); + }) + }) }) describe('when given an array', function(){ @@ -169,5 +203,22 @@ describe('res', function(){ done(); }) }) + + it('should use status as second number for backwards compat', function(done){ + var app = express(); + + app.use(function(req, res){ + res.json(200, 201); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(201); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('200'); + done(); + }) + }) }) }) diff --git a/test/res.jsonp.js b/test/res.jsonp.js index 1b8db297..20f6055e 100644 --- a/test/res.jsonp.js +++ b/test/res.jsonp.js @@ -201,6 +201,58 @@ describe('res', function(){ }) }) + describe('when given primitives', function(){ + it('should respond with json for null', function(done){ + var app = express(); + + app.use(function(req, res){ + res.jsonp(null); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('null'); + done(); + }) + }) + + it('should respond with json for Number', function(done){ + var app = express(); + + app.use(function(req, res){ + res.jsonp(300); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(200); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('300'); + done(); + }) + }) + + it('should respond with json for String', function(done){ + var app = express(); + + app.use(function(req, res){ + res.jsonp('str'); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(200); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('"str"'); + done(); + }) + }) + }) + describe('"json replacer" setting', function(){ it('should be passed to JSON.stringify()', function(done){ var app = express(); @@ -285,5 +337,22 @@ describe('res', function(){ done(); }) }) + + it('should use status as second number for backwards compat', function(done){ + var app = express(); + + app.use(function(req, res){ + res.jsonp(200, 201); + }); + + request(app) + .get('/') + .end(function(err, res){ + res.statusCode.should.equal(201); + res.headers.should.have.property('content-type', 'application/json'); + res.text.should.equal('200'); + done(); + }) + }) }) })