diff --git a/.gitignore b/.gitignore index c1d40255..1b834e50 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ test/config.js test/coverage.html lib-cov/ *~ +.idea \ No newline at end of file diff --git a/lib/ChainFind.js b/lib/ChainFind.js index 7edaba9e..70a374d9 100644 --- a/lib/ChainFind.js +++ b/lib/ChainFind.js @@ -112,12 +112,12 @@ function ChainFind(Model, opts) { run: function (cb) { return this.all(cb); }, - success: function (cb) { + done: function (cb) { if (!promise) { promise = new Promise(); promise.handle(this.all); } - return promise.success(cb); + return promise.done(cb); }, fail: function (cb) { if (!promise) { @@ -126,6 +126,20 @@ function ChainFind(Model, opts) { } return promise.fail(cb); }, + always: function (cb) { + if (!promise) { + promise = new Promise(); + promise.handle(this.all); + } + return promise.always(cb); + }, + then: function (done_cb, fail_cb) { + if (!promise) { + promise = new Promise(); + promise.handle(this.all); + } + return promise.then(done_cb, fail_cb); + }, all: function (cb) { opts.driver.find(opts.only, opts.table, opts.conditions, { limit : opts.limit, diff --git a/lib/Promise.js b/lib/Promise.js index 0008adea..3a92ba0f 100644 --- a/lib/Promise.js +++ b/lib/Promise.js @@ -3,27 +3,74 @@ exports.Promise = Promise; function Promise(opts) { opts = opts || {}; - var success_cb = opts.success || null; - var fail_cb = opts.fail || null; + var resolved = false; + var revoked = false; + var run = false; + + var done_cb = []; + var fail_cb = []; + var always_cb = []; + + done_cb.push(opts.done); + fail_cb.push(opts.fail); + always_cb.push(opts.always); + + var revoke = function (err) { + + if (run) return; + + revoked = run = true; + + for (var i = 0, len = always_cb.length; i < len; i++) { + if (always_cb[i]) always_cb[i](err, null); + } + for (var i = 0, len = fail_cb.length; i < len; i++) { + if (fail_cb[i]) fail_cb[i](err); + } + }; + + var resolve = function () { + + if (run) return; + + resolved = run = true; + + var args = Array.prototype.slice.call(arguments); + for (var i = 0, len = always_cb.length; i < len; i++) { + if (always_cb[i]) always_cb[i].apply(null, args); + } + args = args.slice(1); + for (var i = 0, len = done_cb.length; i < len; i++) { + if (done_cb[i]) done_cb[i].apply(null, args); + } + }; return { handle: function (promise) { + promise(function (err) { if (err) { - if (fail_cb) fail_cb(err); + revoke(err); } else { - var args = Array.prototype.slice.call(arguments, 1); - - if (success_cb) success_cb.apply(null, args); + resolve.apply(null, arguments); } }); }, - success: function (cb) { - success_cb = cb; + done: function (cb) { + done_cb.push(cb); return this; }, fail: function (cb) { - fail_cb = cb; + fail_cb.push(cb); + return this; + }, + always: function (cb) { + always_cb.push(cb); + return this; + }, + then: function (done, fail) { + done_cb.push(done); + fail_cb.push(fail); return this; } }; diff --git a/test/integration/model-find-chain.js b/test/integration/model-find-chain.js index 53b335da..0763a1f2 100644 --- a/test/integration/model-find-chain.js +++ b/test/integration/model-find-chain.js @@ -462,31 +462,144 @@ describe("Model.find() chaining", function() { }); }); - describe(".success()", function () { + describe(".done()", function () { before(setup()); - it("should return a Promise with .fail() method", function (done) { - Person.find().success(function (people) { + it("should return a Promise with .done() method", function (done) { + Person.find().done(function (people) { should(Array.isArray(people)); - - return done(); }).fail(function (err) { // never called.. + }).always(function () { + return done() }); }); + + it("should allow multiple invocations", function (done) { + var invocations = 0; + + Person.find().done(function (people) { + invocations++; + should(Array.isArray(people)); + }).done(function (people) { + invocations++; + should(Array.isArray(people)); + }); + + setTimeout(function () { + should.equal(invocations, 2); + return done(); + }, 500); + }); }); describe(".fail()", function () { before(setup()); - it("should return a Promise with .success() method", function (done) { - Person.find().fail(function (err) { - // never called.. - }).success(function (people) { +// it("should return a Promise with .fail() method that calles the callback on query failure", function (done) { +// Person.find().fail(function (err) { +// should.exist(err); +// }).done(function (people) { +// // never called... +// }).always(function () { +// return done(); +// }); +// }); + +// it("should allow multiple invocations", function (done) { +// var invocations = 0; +// +// Person.find().fail(function (err) { +// invocations++; +// should.exist(err); +// }).fail(function (err) { +// invocations++; +// should.exist(err); +// }); +// +// setTimeout(function () { +// should.equal(invocations, 2); +// return done(); +// }, 500); +// }); + }); + + describe(".always()", function () { + before(setup()); + + it("should return a Promise with .always() method", function (done) { + Person.find().always(function (err, people) { + should(Array.isArray(people)); + should.equal(err, null); + return done(); + }); + }); + + it("should allow multiple invocations", function (done) { + var invocations = 0; + + Person.find().always(function (err, people) { + invocations++; should(Array.isArray(people)); + should.equal(err, null); + }).always(function (err, people) { + invocations++; + should(Array.isArray(people)); + should.equal(err, null); + }); + setTimeout(function () { + should.equal(invocations, 2); + return done(); + }, 500); + }); + }); + + describe(".then()", function () { + before(setup()); + + it("should return a Promise with .then() method", function (done) { + Person.find().then(function (people) { + should(Array.isArray(people)); + }, function (err) { + // never called + }).always(function (err, people) { + should(Array.isArray(people)); + should.equal(err, null); return done(); }); }); + +// it("should call the provided fail callback on failure", function (done) { +// Person.find().remove().then(function (people) { +// // never called +// }, function (err) { +// should.exist(err); +// }).always(function (err, people) { +// should.exist(err); +// return done(); +// }); +// }); + + it("should allow multiple invocations", function (done) { + var invocations = 0; + + Person.find().then(function (people) { + invocations++; + should(Array.isArray(people)); + }, function (err) { + // never called + }).then(function (people) { + invocations++; + should(Array.isArray(people)); + }, function (err) { + // never called + }); + + setTimeout(function () { + should.equal(invocations, 2); + return done(); + }, 500); + }); }); });