diff --git a/lib/waterline/query/finders/basic.js b/lib/waterline/query/finders/basic.js index d7361daeb..b091fca4a 100644 --- a/lib/waterline/query/finders/basic.js +++ b/lib/waterline/query/finders/basic.js @@ -13,7 +13,8 @@ var usageError = require('../../utils/usageError'), waterlineCriteria = require('waterline-criteria'), _ = require('lodash'), async = require('async'), - hasOwnProperty = utils.object.hasOwnProperty; + hasOwnProperty = utils.object.hasOwnProperty, + callbacks = require('../../utils/callbacksRunner'); module.exports = { @@ -40,6 +41,10 @@ module.exports = { // Normalize criteria criteria = normalize.criteria(criteria); + this.beforeCallbacks.call(self,criteria.where,function(err){ + if (err) {return cb(err);} + }); + // Return Deferred or pass to adapter if(typeof cb !== 'function') { return new Deferred(this, this.findOne, criteria); @@ -205,6 +210,10 @@ module.exports = { return usageError('Invalid options specified!', usage, cb); } + this.beforeCallbacks.call(self,criteria.where,function(err){ + if (err) return cb(err); + }); + // Return Deferred or pass to adapter if(typeof cb !== 'function') { return new Deferred(this, this.find, criteria); @@ -353,6 +362,15 @@ module.exports = { }); }, + beforeCallbacks:function(criteria,cb){ + var self = this; + async.series([ + function(cb){ + callbacks.beforeFind(self,criteria,cb); + } + ]); + }, + where: function() { this.find.apply(this, Array.prototype.slice.call(arguments)); }, diff --git a/lib/waterline/utils/callbacks.js b/lib/waterline/utils/callbacks.js index 0f8377c65..f169ccbf6 100644 --- a/lib/waterline/utils/callbacks.js +++ b/lib/waterline/utils/callbacks.js @@ -10,5 +10,6 @@ module.exports = [ 'beforeCreate', 'afterCreate', 'beforeDestroy', - 'afterDestroy' + 'afterDestroy', + 'beforeFind' ]; diff --git a/lib/waterline/utils/callbacksRunner.js b/lib/waterline/utils/callbacksRunner.js index eb28b4bad..5ae008990 100644 --- a/lib/waterline/utils/callbacksRunner.js +++ b/lib/waterline/utils/callbacksRunner.js @@ -138,3 +138,21 @@ runner.afterDestroy = function(context, values, cb) { async.eachSeries(context._callbacks.afterDestroy, fn, cb); }; + +/** + * Run before Find Callbacks + * + * @param {Object} context + * @param {Object} values + * @param {Function} cb + * @api public + */ + +runner.beforeFind = function(context, values, cb) { + + var fn = function(item, next) { + item(values, next); + }; + + async.eachSeries(context._callbacks.beforeFind, fn, cb); +}; diff --git a/test/unit/callbacks/beforeFind.find.js b/test/unit/callbacks/beforeFind.find.js new file mode 100644 index 000000000..728372c58 --- /dev/null +++ b/test/unit/callbacks/beforeFind.find.js @@ -0,0 +1,155 @@ +var Waterline = require('../../../lib/waterline'), + assert = require('assert'); + +describe('.beforeFind()', function() { + + describe('basic function', function() { + var person; + + before(function(done) { + var waterline = new Waterline(); + var Model = Waterline.Collection.extend({ + identity: 'user', + connection: 'foo', + attributes: { + name: 'string', + isDeleted:'boolean' + }, + + beforeFind: function(values, cb) { + values.isDeleted = false; + cb(); + } + }); + + waterline.loadCollection(Model); + + // Fixture Adapter Def + var adapterDef = { + find: function(con, col, values, cb) { return cb(null, [values.where]);}, + findOne:function(conn,col,values,cb){return cb(null,values.where);} + }; + + var connections = { + 'foo': { + adapter: 'foobar' + } + }; + + waterline.initialize({ adapters: { foobar: adapterDef }, connections: connections }, function(err, colls) { + if(err) done(err); + person = colls.collections.user; + done(); + }); + }); + + // /** + // * find + // */ + + describe('.find()', function() { + + it('should run beforeFind and mutate values', function(done) { + + person.find({name:'billy'}, function(err, users) { + assert(!err); + assert(users[0].isDeleted === false); + done(); + }); + + }); + }); + + + // /** + // * findOne + // */ + + describe('.findOne()', function() { + + it('should run beforefind and mutate values', function(done) { + + person.findOne({name:'billy'}, function(err, user) { + assert(!err); + assert(user.isDeleted === false); + done(); + }); + + }); + }); + + + + }); + + + /** + * Test Callbacks can be defined as arrays and run in order. + */ + + describe('array of functions', function() { + var person; + + before(function(done) { + var waterline = new Waterline(); + var Model = Waterline.Collection.extend({ + identity: 'user', + connection: 'foo', + attributes: { + name: 'string' + }, + + beforeFind: [ + // Function 1 + function(values, cb) { + values.name = values.name + ' fn1'; + cb(); + }, + + // Function 2 + function(values, cb) { + values.name = values.name + ' fn2'; + cb(); + } + ] + }); + + waterline.loadCollection(Model); + + // Fixture Adapter Def + var adapterDef = { + find: function(con, col, values, cb) { return cb(null, [values.where,]);}, + findOne:function(conn,col,values,cb){return cb(null,values.where);} + }; + + var connections = { + 'foo': { + adapter: 'foobar' + } + }; + + waterline.initialize({ adapters: { foobar: adapterDef }, connections: connections }, function(err, colls) { + if(err) done(err); + person = colls.collections.user; + done(); + }); + }); + + it('should run the functions in order on find()', function(done) { + person.find({ name: 'test' }, function(err, users) { + assert(!err); + assert(users[0].name === 'test fn1 fn2'); + done(); + }); + }); + it('should run the functions in order on findOne()', function(done) { + person.findOne({ name: 'test' }, function(err, user) { + assert(!err); + assert(user.name === 'test fn1 fn2'); + done(); + }); + }); + + }); + +});