Skip to content

Commit

Permalink
#49 want convenience functions for MultiErrors
Browse files Browse the repository at this point in the history
Reviewed by: Joshua M. Clulow <[email protected]>
Reviewed by: Trent Mick <[email protected]>
Approved by: Joshua M. Clulow <[email protected]>
  • Loading branch information
David Pacheco committed Apr 18, 2017
1 parent 29b7ce4 commit ecddeda
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

None yet.

## v1.10.0

* #49 want convenience functions for MultiErrors

## v1.9.0

* #47 could use VError.hasCauseWithName()
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,35 @@ Returns true if and only if `VError.findCauseByName(err, name)` would return
a non-null value. This essentially determines whether `err` has any cause in
its cause chain that has name `name`.

### `VError.errorFromList(errors)`

Given an array of Error objects (possibly empty), return a single error
representing the whole collection of errors. If the list has:

* 0 elements, returns `null`
* 1 element, returns the sole error
* more than 1 element, returns a MultiError referencing the whole list

This is useful for cases where an operation may produce any number of errors,
and you ultimately want to implement the usual `callback(err)` pattern. You can
accumulate the errors in an array and then invoke
`callback(VError.errorFromList(errors))` when the operation is complete.


### `VError.errorForEach(err, func)`

Convenience function for iterating an error that may itself be a MultiError.

In all cases, `err` must be an Error. If `err` is a MultiError, then `func` is
invoked as `func(errorN)` for each of the underlying errors of the MultiError.
If `err` is any other kind of error, `func` is invoked once as `func(err)`. In
all cases, `func` is invoked synchronously.

This is useful for cases where an operation may produce any number of warnings
that may be encapsulated with a MultiError -- but may not be.

This function does not iterate an error's cause chain.


## Examples

Expand Down Expand Up @@ -483,6 +512,9 @@ outputs:

first of 2 errors: failed to resolve DNS name "abc.example.com"

See the convenience function `VError.errorFromList`, which is sometimes simpler
to use than this constructor.

## Public methods


Expand Down
31 changes: 31 additions & 0 deletions lib/verror.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,37 @@ VError.fullStack = function (err)
return (err.stack);
};

VError.errorFromList = function (errors)
{
mod_assertplus.arrayOfObject(errors, 'errors');

if (errors.length === 0) {
return (null);
}

errors.forEach(function (e) {
mod_assertplus.ok(mod_isError(e));
});

if (errors.length == 1) {
return (errors[0]);
}

return (new MultiError(errors));
};

VError.errorForEach = function (err, func)
{
mod_assertplus.ok(mod_isError(err), 'err must be an Error');
mod_assertplus.func(func, 'func');

if (err instanceof MultiError) {
err.errors().forEach(function iterError(e) { func(e); });
} else {
func(err);
}
};


/*
* SError is like VError, but stricter about types. You cannot pass "null" or
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "verror",
"version": "1.9.0",
"version": "1.10.0",
"description": "richer JavaScript errors",
"main": "./lib/verror.js",
"repository": {
Expand Down
75 changes: 75 additions & 0 deletions test/tst.multierror.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ var mod_verror = require('../lib/verror');
var mod_testcommon = require('./common');

var MultiError = mod_verror.MultiError;
var errorFromList = mod_verror.errorFromList;
var errorForEach = mod_verror.errorForEach;

/*
* Save the generic parts of all stack traces so we can avoid hardcoding
Expand All @@ -20,6 +22,7 @@ var nodestack = new Error().stack.split('\n').slice(2).join('\n');
function main()
{
var err1, err2, err3, merr, stack;
var accum, doAccum;

mod_assert.throws(function () {
console.error(new MultiError());
Expand Down Expand Up @@ -52,6 +55,78 @@ function main()
' at main (dummy filename)',
' at Object.<anonymous> (dummy filename)'
].join('\n') + '\n' + nodestack);


/* errorFromList */
mod_assert.throws(function () {
console.error(errorFromList());
}, /^AssertionError: errors \(\[object\]\) is required$/);

mod_assert.throws(function () {
console.error(errorFromList(null));
}, /^AssertionError: errors \(\[object\]\) is required$/);

mod_assert.throws(function () {
console.error(errorFromList({}));
}, /^AssertionError: errors \(\[object\]\) is required$/);

mod_assert.throws(function () {
console.error(errorFromList('asdf'));
}, /^AssertionError: errors \(\[object\]\) is required$/);

mod_assert.throws(function () {
console.error(errorFromList([ new Error(), 17 ]));
}, /^AssertionError: errors \(\[object\]\) is required$/);

mod_assert.throws(function () {
console.error(errorFromList([ new Error(), {} ]));
}, /^AssertionError/);

mod_assert.strictEqual(null, errorFromList([]));
mod_assert.ok(err1 == errorFromList([ err1 ]));
mod_assert.ok(err2 == errorFromList([ err2 ]));
merr = errorFromList([ err1, err2, err3 ]);
mod_assert.ok(merr instanceof MultiError);
mod_assert.ok(merr.errors()[0] == err1);
mod_assert.ok(merr.errors()[1] == err2);
mod_assert.ok(merr.errors()[2] == err3);


/* errorForEach */
mod_assert.throws(function () {
console.error(errorForEach());
}, /^AssertionError: err must be an Error$/);

mod_assert.throws(function () {
console.error(errorForEach(null));
}, /^AssertionError: err must be an Error$/);

mod_assert.throws(function () {
console.error(errorForEach(err1));
}, /^AssertionError: func \(func\) is required$/);

mod_assert.throws(function () {
console.error(errorForEach(err1, {}));
}, /^AssertionError: func \(func\) is required$/);

mod_assert.throws(function () {
console.error(errorForEach({}, function () {}));
}, /^AssertionError: err must be an Error$/);

accum = [];
doAccum = function (e) { accum.push(e); };

accum = [];
errorForEach(err1, doAccum);
mod_assert.equal(accum.length, 1);
mod_assert.ok(accum[0] == err1);

accum = [];
errorForEach(merr, doAccum);
mod_assert.equal(accum.length, 3);
mod_assert.ok(accum[0] == err1);
mod_assert.ok(accum[1] == err2);
mod_assert.ok(accum[2] == err3);
}

main();

0 comments on commit ecddeda

Please sign in to comment.