From 3dd172d032c4257912656b12cba494c2c4ec9646 Mon Sep 17 00:00:00 2001 From: TJ Holowaychuk Date: Fri, 8 Nov 2013 15:16:51 -0800 Subject: [PATCH] remove content-negotiation accessor methods, replace with method equivalents --- docs/api.md | 69 ++++++++++++++++++++++--- lib/context.js | 60 ++++++++++------------ test/context.js | 130 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 175 insertions(+), 84 deletions(-) diff --git a/docs/api.md b/docs/api.md index e6cdf44..ca0c973 100644 --- a/docs/api.md +++ b/docs/api.md @@ -485,21 +485,74 @@ switch (this.accepts('json', 'html', 'text')) { } ``` -### ctx.accepted +### ctx.acceptsEncodings(encodings) - Return accepted mime types ordered by quality. + Check if `encodings` are acceptable, returning + the best match when true, otherwise `undefined`. -### ctx.acceptedEncodings +```js +// Accept-Encoding: gzip +this.acceptsEncodings('gzip', 'deflate'); +// => "gzip" - Return accepted content encodings ordered by quality. +this.acceptsEncodings(['gzip', 'deflate']); +// => "gzip" +``` -### ctx.acceptedCharsets + When no arguments are given all accepted encodings + are returned as an array: - Return accepted charsets ordered by quality. +```js +// Accept-Encoding: gzip, deflate +this.acceptsEncodings(); +// => ["gzip", "deflate"] +``` -### ctx.acceptedLanguages +### ctx.acceptsCharsets(charsets) - Return accepted languages ordered by quality. + Check if `charsets` are acceptable, returning + the best match when true, otherwise `undefined`. + +```js +// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 +this.acceptsCharsets('utf-8', 'utf-7'); +// => "utf-8" + +this.acceptsCharsets(['utf-7', 'utf-8']); +// => "utf-8" +``` + + When no arguments are given all accepted charsets + are returned as an array: + +```js +// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5 +this.acceptsCharsets(); +// => ["utf-8", "utf-7", "iso-8859-1"] +``` + +### ctx.acceptsLanguages(langs) + + Check if `langs` are acceptable, returning + the best match when true, otherwise `undefined`. + +```js +// Accept-Language: en;q=0.8, es, pt +this.acceptsLanguages('es', 'en'); +// => "es" + +this.acceptsLanguages(['en', 'es']); +// => "es" +``` + + When no arguments are given all accepted languages + are returned as an array: + +```js +// Accept-Language: en;q=0.8, es, pt +this.acceptsLanguages(); +// => ["es", "pt", "en"] +``` ### ctx.headerSent diff --git a/lib/context.js b/lib/context.js index 78d0eb0..aa3c0ee 100644 --- a/lib/context.js +++ b/lib/context.js @@ -542,87 +542,79 @@ Context.prototype = { * // => "json" * * @param {String|Array} type(s)... - * @return {String} + * @return {String|Array|Boolean} * @api public */ accepts: function(types){ - // TODO: memoize if (!Array.isArray(types)) types = [].slice.call(arguments); - var normalized = types.map(extToMime); var n = new Negotiator(this.req); - var accepts = n.preferredMediaTypes(normalized); + if (!types.length) return n.preferredMediaTypes(); + var mimes = types.map(extToMime); + var accepts = n.preferredMediaTypes(mimes); var first = accepts[0]; if (!first) return false; - return types[normalized.indexOf(first)]; + return types[mimes.indexOf(first)]; }, /** - * Return accepted encodings. + * Return accepted encodings or best fit based on `encodings`. * * Given `Accept-Encoding: gzip, deflate` * an array sorted by quality is returned: * * ['gzip', 'deflate'] * - * @return {Array} + * @param {String|Array} encoding(s)... + * @return {String|Array} * @api public */ - get acceptedEncodings() { + acceptsEncodings: function(encodings) { + if (!Array.isArray(encodings)) encodings = [].slice.call(arguments); var n = new Negotiator(this.req); - return n.preferredEncodings(); + if (!encodings.length) return n.preferredEncodings(); + return n.preferredEncodings(encodings)[0]; }, /** - * Return accepted charsets. + * Return accepted charsets or best fit based on `charsets`. * * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` * an array sorted by quality is returned: * * ['utf-8', 'utf-7', 'iso-8859-1'] * - * @return {Array} + * @param {String|Array} charset(s)... + * @return {String|Array} * @api public */ - get acceptedCharsets() { + acceptsCharsets: function(charsets) { + if (!Array.isArray(charsets)) charsets = [].slice.call(arguments); var n = new Negotiator(this.req); - return n.preferredCharsets(); + if (!charsets.length) return n.preferredCharsets(); + return n.preferredCharsets(charsets)[0]; }, /** - * Return accepted languages. + * Return accepted languages or best fit based on `langs`. * * Given `Accept-Language: en;q=0.8, es, pt` * an array sorted by quality is returned: * * ['es', 'pt', 'en'] * - * @return {Array} + * @param {String|Array} lang(s)... + * @return {Array|String} * @api public */ - get acceptedLanguages() { + acceptsLanguages: function(langs) { + if (!Array.isArray(langs)) langs = [].slice.call(arguments); var n = new Negotiator(this.req); - return n.preferredLanguages(); - }, - - /** - * Return accepted media types. - * - * Given `Accept: application/*;q=0.2, image/jpeg;q=0.8, text/html` - * an array sorted by quality is returned: - * - * ['text/html', 'image/jpeg', 'application/*'] - * - * @return {Array} - * @api public - */ - - get accepted() { - var n = new Negotiator(this.req); - return n.preferredMediaTypes(); + if (!langs.length) return n.preferredLanguages(); + return n.preferredLanguages(langs)[0]; }, /** diff --git a/test/context.js b/test/context.js index 33e1c3e..1713918 100644 --- a/test/context.js +++ b/test/context.js @@ -389,23 +389,6 @@ describe('ctx.fresh', function(){ }) }) -describe('ctx.accepted', function(){ - describe('when Accept is populated', function(){ - it('should return accepted types', function(){ - var ctx = context(); - ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; - ctx.accepted.should.eql(['text/html', 'text/plain', 'image/jpeg', 'application/*']); - }) - }) - - describe('when Accept is not populated', function(){ - it('should return an empty array', function(){ - var ctx = context(); - ctx.accepted.should.eql([]); - }) - }) -}) - describe('ctx.vary(field)', function(){ describe('when Vary is not set', function(){ it('should set it', function(){ @@ -437,10 +420,18 @@ describe('ctx.vary(field)', function(){ }) describe('ctx.accepts(types)', function(){ - describe('with no types', function(){ + describe('with no arguments', function(){ + it('should return all accepted types', function(){ + var ctx = context(); + ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; + ctx.accepts().should.eql(['text/html', 'text/plain', 'image/jpeg', 'application/*']); + }) + }) + + describe('with no valid types', function(){ it('should return false', function(){ var ctx = context(); - ctx.accepts('').should.be.false; + ctx.accepts('', 'hey').should.be.false; }) }) @@ -504,53 +495,108 @@ describe('ctx.accepts(types)', function(){ }) }) -describe('ctx.acceptedLanguages', function(){ - describe('when Accept-Language is populated', function(){ - it('should return accepted types', function(){ +describe('ctx.acceptsLanguages(langs)', function(){ + describe('with no arguments', function(){ + describe('when Accept-Language is populated', function(){ + it('should return accepted types', function(){ + var ctx = context(); + ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; + ctx.acceptsLanguages().should.eql(['es', 'pt', 'en']); + }) + }) + + describe('when Accept-Language is not populated', function(){ + it('should return an empty array', function(){ + var ctx = context(); + ctx.acceptsLanguages().should.eql([]); + }) + }) + }) + + describe('with multiple arguments', function(){ + it('should return the best fit', function(){ var ctx = context(); ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; - ctx.acceptedLanguages.should.eql(['es', 'pt', 'en']); + ctx.acceptsLanguages('es', 'en').should.equal('es'); }) }) - describe('when Accept-Language is not populated', function(){ - it('should return an empty array', function(){ + describe('with an array', function(){ + it('should return the best fit', function(){ var ctx = context(); - ctx.acceptedLanguages.should.eql([]); + ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; + ctx.acceptsLanguages(['es', 'en']).should.equal('es'); }) }) }) -describe('ctx.acceptedCharsets', function(){ - describe('when Accept-Charset is populated', function(){ - it('should return accepted types', function(){ +describe('ctx.acceptsCharsts()', function(){ + describe('with no arguments', function(){ + describe('when Accept-Charset is populated', function(){ + it('should return accepted types', function(){ + var ctx = context(); + ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; + ctx.acceptsCharsets().should.eql(['utf-8', 'utf-7', 'iso-8859-1']); + }) + }) + + describe('when Accept-Charset is not populated', function(){ + it('should return an empty array', function(){ + var ctx = context(); + ctx.acceptsCharsets().should.eql([]); + }) + }) + }) + + describe('with multiple arguments', function(){ + it('should return the best fit', function(){ var ctx = context(); ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; - ctx.acceptedCharsets.should.eql(['utf-8', 'utf-7', 'iso-8859-1']); + ctx.acceptsCharsets('utf-7', 'utf-8').should.equal('utf-8'); }) }) - describe('when Accept-Charset is not populated', function(){ - it('should return an empty array', function(){ + describe('with an array', function(){ + it('should return the best fit', function(){ var ctx = context(); - ctx.acceptedCharsets.should.eql([]); + ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; + ctx.acceptsCharsets(['utf-7', 'utf-8']).should.equal('utf-8'); }) }) }) -describe('ctx.acceptedEncodings', function(){ - describe('when Accept-Encoding is populated', function(){ - it('should return accepted types', function(){ - var ctx = context(); - ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; - ctx.acceptedEncodings.should.eql(['gzip', 'compress', 'identity']); +describe('ctx.acceptsEncodings()', function(){ + describe('with no arguments', function(){ + describe('when Accept-Encoding is populated', function(){ + it('should return accepted types', function(){ + var ctx = context(); + ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; + ctx.acceptsEncodings().should.eql(['gzip', 'compress', 'identity']); + }) + }) + + describe('when Accept-Encoding is not populated', function(){ + it('should return identity', function(){ + var ctx = context(); + ctx.acceptsEncodings().should.eql(['identity']); + }) }) }) - describe('when Accept-Encoding is not populated', function(){ - it('should return identity', function(){ + describe('with multiple arguments', function(){ + it('should return the best fit', function(){ var ctx = context(); - ctx.acceptedEncodings.should.eql(['identity']); + ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; + ctx.acceptsEncodings('compress', 'gzip').should.eql('gzip'); + ctx.acceptsEncodings('gzip', 'compress').should.eql('gzip'); + }) + }) + + describe('with an array', function(){ + it('should return the best fit', function(){ + var ctx = context(); + ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; + ctx.acceptsEncodings(['compress', 'gzip']).should.eql('gzip'); }) }) })