Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# http://editorconfig.org
root = true

[*]
indent_style = tab
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
9 changes: 9 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"env": {
"node": true
},
"rules": {
"no-use-before-define": [2, "nofunc"],
"quotes": [2, "single", "avoid-escape"]
}
}
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,23 @@ throw MyError('wow')
npm install extend-error
```

and in your app.js, just ```require('extend-error')```. It will provide you an extend() method for the Error type.


and in your app.js, just ```require('extend-error').polyfill()```. It will provide you an extend() method for the Error type.

### syntax
- extend() takes two arguments : subTypeName & errorCode [optional]
- it returns the newly created error type

#### polyfilling

### more examples for a web app
All of the examples here assume you ran `polyfill()`. If you're not comfortable with modifying native objects, you can use `extendError()` directly.

```
var extendError = require('extend-error');
var ClientError = extendError('ClientError', 400);
var HttpNotFound = extendError(ClientError, HttpNotFound, 404);
```

something useful
### more examples for a web app

```
var AppError = Error.extend('AppError', 500);
Expand Down Expand Up @@ -67,7 +71,7 @@ throw an error in controller
```
var err = HttpNotFound('user profile not found');

throw err;
throw err;
(or)
callback(err)
```
Expand All @@ -85,4 +89,3 @@ if (err instanceof ClientError) {
}

```

83 changes: 56 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,76 @@
'use strict';

var util = require('util');
var assert = require('assert');


/**
* Add extend() method to Error type
*
* Create subtype of Error object
*
* @param BaseType [optional] defaults to {Error}
* @param subTypeName
* @param errorCode [optional]
* @returns {SubType}
*/
function extendError(BaseType, subTypeName, errorCode /*optional*/) {
if (typeof BaseType === 'string') {
errorCode = subTypeName;
subTypeName = BaseType;
BaseType = Error;
}

if (!this) {
if (typeof BaseType !== 'function') {
throw new Error('`BaseType` must be a Function');
}

return extendError.apply(BaseType, arguments);
}

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)) {

// Define the new type
function SubType(message) {
// Handle constructor called without `new`
if (!(this instanceof SubType)) {
return new SubType(message);
}

//populate error details
this.name = subTypeName;
this.code = errorCode;

// Populate error details
this.name = subTypeName;
// Only set `this.code` if a code is defined for the type (to prevent
// "{code:undefined}" when stringifying)
if (errorCode) {
this.code = errorCode;
}
this.message = message || '';
//include stack trace in error object

// Include stack trace in error object
Error.captureStackTrace(this, this.constructor);
});
//inherit the base prototype chain
}

// 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() {

// Override the toString method to error type name and inspected message (to
// expand objects)
SubType.prototype.toString = function toString() {
return this.name + ': ' + util.inspect(this.message);
};

//attach extend() to the SubType to make it extendable further
SubType.extend = this.extend;


// Attach extend() to the SubType to make it extendable further (but only if
// extend has been monkeypatched onto the Error object).
if (this.extend) {
SubType.extend = this.extend;
}

return SubType;
}

/**
* Add `extend()` method to {Error} type
*/
extendError.monkeypatch = function() {
Error.extend = extendError;
};

module.exports = extendError;
21 changes: 21 additions & 0 deletions test-fixtures/http-errors-functional.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** few custom error types required for web apps **/

var extendError = require('..');

/**
* thrown when there is an app related error
*/
exports.AppError = extendError('AppError', 500);

/**
* thrown when there is error in client request/data
*/
var ClientError = exports.ClientError = extendError(Error, 'ClientError', 400);

/**
* specific http error types
*/
exports.HttpNotFound = extendError(ClientError, 'HttpNotFound', 404);
exports.HttpUnauthorized = extendError(ClientError, 'HttpUnauthorized', 401);
exports.HttpForbidden = extendError(ClientError, 'HttpForbidden', 403);
exports.HttpConflict = extendError(ClientError, 'HttpConflict', 409); //unique constraint error
8 changes: 2 additions & 6 deletions http-errors.js → test-fixtures/http-errors-monkeypatched.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/** few custom error types required for web apps **/

require('./index.js');

require('..').monkeypatch();

/**
* thrown when there is an app related error
* thrown when there is an app related error
*/
exports.AppError = Error.extend('AppError', 500);

Expand All @@ -14,13 +13,10 @@ exports.AppError = Error.extend('AppError', 500);
*/
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


96 changes: 54 additions & 42 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,72 @@
'use strict';

/**
* mocha test cases
*/

var errors = require('../http-errors');
/* eslint-env mocha */

var assert = require('assert');

describe('functional', function() {
var errors = require('../test-fixtures/http-errors-functional');
tests(errors);
assert.ok(typeof errors.ClientError.extend === 'undefined', 'ClientError does not have an extend method');
});

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);
});
describe('monkeypatched', function() {
var errors = require('../test-fixtures/http-errors-monkeypatched');
tests(errors);
assert.ok(typeof errors.ClientError.extend === 'function', 'ClientError does not have an extend method');
});

function tests(errors) {
describe('instantiation', function() {
it('should work with new operator', function() {
var err = new errors.AppError('problem');
assert.ok(err instanceof errors.AppError);
});

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 work without new operator', function() {
/* eslint new-cap: [0] */
var err = errors.AppError('problem');
assert.ok(err instanceof errors.AppError);
});
});

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('inheritance', function() {
it('should maintain prototype hierarchy with one level', function() {
var err = new errors.ClientError('email required');

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);
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('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);
});

});
}