From 1e8f96c13ea3492d858c09d29a2fec490c15eb84 Mon Sep 17 00:00:00 2001 From: tangxinfa Date: Fri, 6 Nov 2015 11:10:22 +0800 Subject: [PATCH 1/3] prototype.message is the default error message. --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 966b18a..97d832d 100644 --- a/index.js +++ b/index.js @@ -24,7 +24,9 @@ Error.extend = function(subTypeName, errorCode /*optional*/) { //populate error details this.name = subTypeName; this.code = errorCode; - this.message = message || ''; + if (typeof(message) !== 'undefined') { + this.message = message; + } //include stack trace in error object Error.captureStackTrace(this, this.constructor); From b60a6ac4ce3db3605cfe3bec4c7a11344abc8a0c Mon Sep 17 00:00:00 2001 From: tangxinfa Date: Mon, 9 Nov 2015 19:33:46 +0800 Subject: [PATCH 2/3] Extended error type support any properties. --- README.md | 113 ++++++++++++++++++---------------------------- http-errors.js | 26 ----------- index.js | 50 ++------------------ lib/CodedError.js | 19 ++++++++ lib/extend.js | 75 ++++++++++++++++++++++++++++++ package.json | 33 +++++++------- test/test.js | 84 ++++++++++++++++------------------ 7 files changed, 196 insertions(+), 204 deletions(-) delete mode 100644 http-errors.js create mode 100644 lib/CodedError.js create mode 100644 lib/extend.js diff --git a/README.md b/README.md index 9eabb62..893cd0e 100644 --- a/README.md +++ b/README.md @@ -2,87 +2,62 @@ Ever tried to create custom error types in Node.js and wished it should be this simple? -``` -var MyError = Error.extend('MyError'); -``` + var MyError = extend(Error, 'MyError'); + throw MyError('wow') -``` -throw MyError('wow') -``` +### Installation -### installation + npm install tangxinfa/extend-error -``` -npm install extend-error -``` +#### Test -and in your app.js, just ```require('extend-error')```. It will provide you an extend() method for the Error type. + npm test +### Documentation + /** + * extend Error type. + * + * @param BaseError base error type to extend. Must be Error or subclass of Error. + * @param extendedErrorOptions extended error type options. + * Object: + * - name [required] string, extended error type name. + * - message [optional] string, default error message. + * - toString [optional] function, overwrite toString method. + * String: extended error type name. + * @return Extended error type. + */ + function extend(BaseError, extendedErrorOptions) -### syntax -- extend() takes two arguments : subTypeName & errorCode [optional] -- it returns the newly created error type + /** + * CodedError type + * - name: CodedError + * - code: Error code. + */ + var CodedError = extend(Error, {...}); +### Examples for a web app -### more examples for a web app +#### Extend error types + var http = require('http'); + var util = require('util'); + var extend = require('extend-error'); + + var NotFound = extend(extend.CodedError, {name: 'NotFoundError', message: http.STATUS_CODES[404], code: 404}); + var InternalServerError = extend(extend.CodedError, {name: 'InternalServerError', message: http.STATUS_CODES[500], code: 500}); -something useful +#### Throw errors -``` -var AppError = Error.extend('AppError', 500); -var ClientError = Error.extend('ClientError', 400); -``` + throw new NotFound({resource: '/subject/1'}); -extend ClientError further for specific http types + // 'new' keyword is optional. + throw InternalServerError(); -``` -var HttpNotFound = ClientError.extend('HttpNotFoundError', 404); -var HttpUnauthorized = ClientError.extend('HttpUnauthorized', 401); -``` - -### throwing errors - -``` -throw new AppError('unable to connect db due to error: ' + err); - -throw new ClientError({'message':'required field missing', field: 'email'}) - -throw new HttpNotFound('no post found with id: ' + id); - -throw new HttpNotFound({'message': 'no such post', 'id': id}); -``` - -### don't worry when you forget 'new' - -``` -throw ClientError('bad request'); -``` - -### instanceof - -throw an error in controller - -``` -var err = HttpNotFound('user profile not found'); - -throw err; -(or) -callback(err) -``` - -handle it easily in global error handler (in case of express.js error middleware) - -``` -if (err instanceof ClientError) { - //send out the actual message - res.send(err.code, err.message); -} else { - //send out a generic message - res.send(500, 'oops! something went wrong'); - log.error(err); -} - -``` +#### Handle errors + if (err instanceof NotFound) { + res.send(err.code, "Please create subject" + err.resource + ") first."); + } else { + res.send(err.code, err.message); + } diff --git a/http-errors.js b/http-errors.js deleted file mode 100644 index f0b0c6f..0000000 --- a/http-errors.js +++ /dev/null @@ -1,26 +0,0 @@ -/** few custom error types required for web apps **/ - -require('./index.js'); - - -/** - * thrown when there is an app related error - */ -exports.AppError = Error.extend('AppError', 500); - - -/** - * thrown when there is error in client request/data - */ -var ClientError = exports.ClientError = Error.extend('ClientError', 400); - - -/** - * specific http error types - */ -exports.HttpNotFound = ClientError.extend('HttpNotFound', 404); -exports.HttpUnauthorized = ClientError.extend('HttpUnauthorized', 401); -exports.HttpForbidden = ClientError.extend('HttpForbidden', 403); -exports.HttpConflict = ClientError.extend('HttpConflict', 409); //unique constraint error - - diff --git a/index.js b/index.js index 97d832d..ae9b342 100644 --- a/index.js +++ b/index.js @@ -1,49 +1,5 @@ -var util = require('util'); -var assert = require('assert'); +var extend = require('./lib/extend'); +extend.CodedError = require('./lib/CodedError'); -/** - * Add extend() method to Error type - * - * @param subTypeName - * @param errorCode [optional] - * @returns {SubType} - */ - -Error.extend = function(subTypeName, errorCode /*optional*/) { - assert(subTypeName, 'subTypeName is required'); - - //define new error type - - var SubType = (function(message) { - //handle constructor call without 'new' - if (! (this instanceof SubType)) { - return new SubType(message); - } - - //populate error details - this.name = subTypeName; - this.code = errorCode; - if (typeof(message) !== 'undefined') { - this.message = message; - } - - //include stack trace in error object - Error.captureStackTrace(this, this.constructor); - }); - - //inherit the base prototype chain - util.inherits(SubType, this); - - - //override the toString method to error type name and inspected message (to expand objects) - SubType.prototype.toString = function() { - return this.name + ': ' + util.inspect(this.message); - }; - - //attach extend() to the SubType to make it extendable further - SubType.extend = this.extend; - - return SubType; -}; - +module.exports = extend; diff --git a/lib/CodedError.js b/lib/CodedError.js new file mode 100644 index 0000000..c92e7cb --- /dev/null +++ b/lib/CodedError.js @@ -0,0 +1,19 @@ +var extend = require('./extend.js'); +var util = require('util'); + + +/** + * CodedError type + * - name: CodedError + * - code: Error code. + */ +var CodedError = extend(Error, { + name: 'CodedError', + code: undefined, + toString: function () { + return this.name + ' ' + this.code + (typeof(this.message) == 'undefined' ? '' : ': ' + util.inspect(this.message)); + } +}); + + +module.exports = CodedError; diff --git a/lib/extend.js b/lib/extend.js new file mode 100644 index 0000000..8267f58 --- /dev/null +++ b/lib/extend.js @@ -0,0 +1,75 @@ +var util = require('util'); +var assert = require('assert'); + + +/** + * extend Error type. + * + * @param BaseError base error type to extend. Must be Error or subclass of Error. + * @param extendedErrorOptions extended error type options. + * Object: + * - name [required] string, extended error type name. + * - message [optional] string, default error message. + * - toString [optional] function, overwrite toString method. + * String: extended error type name. + * @return Extended error type. + */ +function extend(BaseError, extendedErrorOptions) { + assert(BaseError.prototype instanceof Error || BaseError === Error, 'BaseError must be Error type'); + + if (typeof(extendedErrorOptions) == 'string') { + extendedErrorOptions = {name: extendedErrorOptions}; + } + + assert(extendedErrorOptions.name, 'Extended error type name is required'); + + if (! extendedErrorOptions.toString) { + // override the toString method to error type name and inspected message (to expand objects) + extendedErrorOptions.toString = function() { + return this.name + (typeof(this.message) == 'undefined' ? '' : ': ' + util.inspect(this.message)); + }; + } + + // define extended error type. + var ExtendedError = (function(objectOptions) { + // handle constructor call without 'new' + if (! (this instanceof ExtendedError)) { + return new ExtendedError(objectOptions); + } + + // compitable with Error constructor. + if (util.isPrimitive(objectOptions)) { + if (typeof(objectOptions) == 'undefined') { + objectOptions = {}; + } else { + objectOptions = {message: objectOptions}; + } + } + + // assign object options. + for(var optionName in objectOptions) { + if (objectOptions.hasOwnProperty(optionName)) { + this[optionName] = objectOptions[optionName]; + } + } + + //include stack trace in error object + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + }); + + // inherit the base prototype chain + util.inherits(ExtendedError, BaseError); + + // assign prototype options. + for(var optionName in extendedErrorOptions) { + if (extendedErrorOptions.hasOwnProperty(optionName)) { + ExtendedError.prototype[optionName] = extendedErrorOptions[optionName]; + } + } + + return ExtendedError; +} + +module.exports = extend; diff --git a/package.json b/package.json index a310946..ca4a4df 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,18 @@ { - "name": "extend-error", - "version": "0.0.2", - "description": "Easily define custom error types in Node.js", - "main": "index.js", - "scripts": { - "test": "node_modules/.bin/mocha --reporter spec" - }, - "author": "Jayy Vis ", - "devDependencies": { - "mocha": "*" - }, - "repository": { - "type" : "git", - "url" : "http://github.com/jayyvis/extend-error.git" - }, - "license": "MIT" + "name": "extend-error", + "version": "0.0.2", + "description": "Easily define custom error types in Node.js", + "main": "index.js", + "scripts": { + "test": "node_modules/.bin/mocha --reporter spec" + }, + "author": "tangxinfa ", + "devDependencies": { + "mocha": "*" + }, + "repository": { + "type" : "git", + "url" : "http://github.com/tangxinfa/extend-error.git" + }, + "license": "MIT" } - diff --git a/test/test.js b/test/test.js index 1507a37..e1735da 100644 --- a/test/test.js +++ b/test/test.js @@ -2,59 +2,53 @@ * mocha test cases */ -var errors = require('../http-errors'); - var assert = require('assert'); +var extend = require('../'); +var http = require('http'); -describe('instantiation', function() { - it('should work with new operator', function() { - var err = new errors.AppError('problem'); - assert.ok(err instanceof errors.AppError); - }); - - it('should work without new operator', function() { - var err = errors.AppError('problem'); - assert.ok(err instanceof errors.AppError); - }); -}); +var NotFound = extend(extend.CodedError, {name: 'NotFound', message: http.STATUS_CODES[404], code: 404}); +var InternalServerError = extend(extend.CodedError, {name: 'InternalServerError', message: http.STATUS_CODES[500], code: 500}); -describe('inheritance', function() { - it('should maintain prototype hierarchy with one level', function() { - var err = new errors.ClientError('email required'); - - assert.ok(err instanceof errors.ClientError, 'ClientError is not an instance of ClientError'); - assert.ok(err instanceof Error, 'ClientError is not an instance of Error'); - }); - - it('should maintain prototype hierarchy with two levels', function() { - var notfound = new errors.HttpNotFound('item not found'); - - assert.ok(notfound instanceof errors.HttpNotFound, 'HttpNotFound is not an instance of HttpNotFound'); - assert.ok(notfound instanceof errors.ClientError, 'HttpNotFound is not an instance of ClientError'); - assert.ok(notfound instanceof Error, 'HttpNotFound is not an instance of Error'); - }); +describe('instantiation', function() { + it('should work with new operator', function() { + var err = new NotFound('not found'); + assert.ok(err instanceof NotFound); + assert.ok(err instanceof extend.CodedError); + assert.ok(err instanceof Error); + }); + + it('should work without new operator', function() { + var err = NotFound('not found'); + assert.ok(err instanceof NotFound); + assert.ok(err instanceof extend.CodedError); + assert.ok(err instanceof Error); + }); + + it('should work like original error', function () { + var err = new NotFound("not found"); + assert.ok(err.message == "not found"); + }); }); -describe('error details', function() { - it('should have message', function() { - var err; - - err = new errors.ClientError('name required'); - assert.equal(err.message, 'name required'); - - err = new errors.ClientError(); - assert.equal(err.message, ''); - }); - - it('should have code', function() { - var err = new errors.ClientError(); - assert.equal(err.code, 400); - }); - -}); +describe('properties', function() { + it('should have message', function() { + var err = new NotFound('not found'); + assert.equal(err.message, 'not found'); + err = new NotFound(); + assert.equal(err.message, http.STATUS_CODES[404]); + }); + it('should have code', function() { + var err = new NotFound(); + assert.equal(err.code, 404); + }); + it('should have other property', function() { + var err = new NotFound({other: 'yes'}); + assert.equal(err.other, 'yes'); + }); +}); From 9a19abb4c89feaddb4d56094aabef202489215cf Mon Sep 17 00:00:00 2001 From: tangxinfa Date: Tue, 10 Nov 2015 13:33:03 +0800 Subject: [PATCH 3/3] More examples. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 893cd0e..b5d30ba 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,8 @@ Ever tried to create custom error types in Node.js and wished it should be this var http = require('http'); var util = require('util'); var extend = require('extend-error'); - + + var AppError = extend(Error, "AppError"); var NotFound = extend(extend.CodedError, {name: 'NotFoundError', message: http.STATUS_CODES[404], code: 404}); var InternalServerError = extend(extend.CodedError, {name: 'InternalServerError', message: http.STATUS_CODES[500], code: 500}); @@ -54,10 +55,13 @@ Ever tried to create custom error types in Node.js and wished it should be this // 'new' keyword is optional. throw InternalServerError(); + // compitable with raw Error constructor. + throw InternalServerError('connection closed); + #### Handle errors if (err instanceof NotFound) { - res.send(err.code, "Please create subject" + err.resource + ") first."); + res.send(err.code, "Please create subject(" + err.resource + ") first."); } else { res.send(err.code, err.message); }