diff --git a/ERR.js b/ERR.js index c5ab510..5fb5cda 100644 --- a/ERR.js +++ b/ERR.js @@ -16,13 +16,27 @@ var header = "Async Stacktrace:"; -module.exports = function (err, callback) +function ERR(err, callback, asyncStackLine) { //there is a error if(err != null) { + // determine the new stacktrace line + if (!asyncStackLine) + asyncStackLine = new Error().stack.split("\n")[2]; + + //if only the callback function is passed + if(typeof err == "function") + { + //wrap callback so ERR() is eventually called w/ the cached stacktrace line + callback = err; + return function(err) { + if (ERR(err, callback, asyncStackLine)) return; + callback.apply(this, arguments); + }; + } //if there is already a stacktrace avaiable - if(err.stack != null) + else if(err.stack != null) { //split stack by line var stackParts = err.stack.split("\n"); @@ -34,7 +48,6 @@ module.exports = function (err, callback) } //add a new stacktrace line - var asyncStackLine = new Error().stack.split("\n")[2]; stackParts.splice(1,0,asyncStackLine); //join the stacktrace @@ -61,3 +74,5 @@ module.exports = function (err, callback) //return true if an error happend return err != null; } + +module.exports = ERR; \ No newline at end of file diff --git a/README.md b/README.md index 979462f..15c165b 100644 --- a/README.md +++ b/README.md @@ -194,3 +194,23 @@ The parameters of `ERR()` are: 2. `callback` (optional) If the callback is set and an error is passed, it will call the callback with the modified stacktrace. Else it will throw the error The return value is true if there is an error. Else its false + +### Alternate Usage + +If all the work that is done in a function is done synchronously before an async call, it can be preferable to simply pass the callback directly to the async call, like this: + +```js +function getUsers(db, callback) { + var sql = 'SELECT * FROM users'; + db.query(sql, callback); +} +``` + +`ERR()` can be used in this scenario as well. If it is passed a callback as its first argument, it will wrap the callback, caching the current stacktrace line. When the callback is executed, it will check for an error object and -- if one is exists -- it will add the cached stacktrace line to it: + +```js +function getUsers(db, callback) { + var sql = 'SELECT * FROM users'; + db.query(sql, ERR(callback)); +} +``` \ No newline at end of file diff --git a/tests.js b/tests.js index 11b1756..ba03655 100644 --- a/tests.js +++ b/tests.js @@ -96,5 +96,40 @@ vows.describe('ERR function').addBatch({ assert.equal(stackLinesBefore+1, stackLinesAfter); } + }, + 'when you call it with a callback as the only argument': { + 'it returns a wrapped callback': function() { + var lineNumberRegex = /tests\.js:(\d+):\d+/; + + var err = new Error(); + var stackLinesBefore = err.stack.split("\n"); + var syncLineNumber = lineNumberRegex.exec(stackLinesBefore[1])[1]; + + var stackLinesAfter; + var asyncLineNumber; + var wrappedCallback = ERR(function(_err) { + stackLinesAfter = _err.stack.split("\n"); + asyncLineNumber = lineNumberRegex.exec(stackLinesAfter[1])[1]; + }); + + wrappedCallback(err); + assert.equal(stackLinesBefore.length+3, stackLinesAfter.length); + + // because `ERR(...)` is 6 lines after `new Error()` in this test + assert.equal(parseInt(asyncLineNumber), parseInt(syncLineNumber)+6); + } + }, + 'when you call it with a callback as the only argument then': { + 'the wrapping does not interfere with `this` binding or passed args': function() { + var wrappedCallback = ERR(function(_err, arg1, arg2) { + assert.equal(this, "test_this"); + assert.equal(err, null); + assert.equal(arg1, "arg1"); + assert.equal(arg2, "arg2"); + }); + + var err = null; + wrappedCallback.call("test_this", err, "arg1", "arg2"); + } } }).run();