From c51e6be2706416bbbb1493ea1554c7fb3ce26d92 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 29 Nov 2014 13:37:32 -0500 Subject: [PATCH] Allow custom batching implementations. --- src/firebase.js | 10 ++--- src/utils.js | 10 +++-- tests/unit/firebase.spec.js | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/firebase.js b/src/firebase.js index a65d1632..6c9097e0 100644 --- a/src/firebase.js +++ b/src/firebase.js @@ -139,14 +139,14 @@ $asObject: function () { if (!this._objectSync || this._objectSync.isDestroyed) { - this._objectSync = new SyncObject(this, this._config.objectFactory); + this._objectSync = new SyncObject(this, this._config.objectFactory, this._config.batchFactory()); } return this._objectSync.getObject(); }, $asArray: function () { if (!this._arraySync || this._arraySync.isDestroyed) { - this._arraySync = new SyncArray(this, this._config.arrayFactory); + this._arraySync = new SyncArray(this, this._config.arrayFactory, this._config.batchFactory()); } return this._arraySync.getArray(); }, @@ -175,7 +175,7 @@ } }; - function SyncArray($inst, ArrayFactory) { + function SyncArray($inst, ArrayFactory, batch) { function destroy(err) { self.isDestroyed = true; var ref = $inst.$ref(); @@ -219,7 +219,6 @@ var def = $firebaseUtils.defer(); var array = new ArrayFactory($inst, destroy, def.promise); - var batch = $firebaseUtils.batch(); var created = batch(function(snap, prevChild) { var rec = array.$$added(snap, prevChild); if( rec ) { @@ -264,7 +263,7 @@ init(); } - function SyncObject($inst, ObjectFactory) { + function SyncObject($inst, ObjectFactory, batch) { function destroy(err) { self.isDestroyed = true; ref.off('value', applyUpdate); @@ -289,7 +288,6 @@ var def = $firebaseUtils.defer(); var obj = new ObjectFactory($inst, destroy, def.promise); var ref = $inst.$ref(); - var batch = $firebaseUtils.batch(); var applyUpdate = batch(function(snap) { var changed = obj.$$updated(snap); if( changed ) { diff --git a/src/utils.js b/src/utils.js index a088ecef..b62413c3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,8 +2,8 @@ 'use strict'; angular.module('firebase') - .factory('$firebaseConfig', ["$FirebaseArray", "$FirebaseObject", "$injector", - function($FirebaseArray, $FirebaseObject, $injector) { + .factory('$firebaseConfig', ["$FirebaseArray", "$FirebaseObject", "$injector", "$firebaseUtils", + function($FirebaseArray, $FirebaseObject, $injector, $firebaseUtils) { return function(configOpts) { // make a copy we can modify var opts = angular.extend({}, configOpts); @@ -14,10 +14,14 @@ if( typeof opts.arrayFactory === 'string' ) { opts.arrayFactory = $injector.get(opts.arrayFactory); } + if( typeof opts.batchFactory === 'string' ) { + opts.batchFactory = $injector.get(opts.batchFactory); + } // extend defaults and return return angular.extend({ arrayFactory: $FirebaseArray, - objectFactory: $FirebaseObject + objectFactory: $FirebaseObject, + batchFactory: $firebaseUtils.batch }, opts); }; } diff --git a/tests/unit/firebase.spec.js b/tests/unit/firebase.spec.js index 60ec96cd..57bfdc65 100644 --- a/tests/unit/firebase.spec.js +++ b/tests/unit/firebase.spec.js @@ -616,6 +616,31 @@ describe('$firebase', function () { flushAll(); expect($utils.wait.completed).toHaveCallCount(1); }); + + it('should allow custom batching implementations', function() { + var batchFactory = stubBatchFactory(); + var arrayFactory = stubArrayFactory(); + + $fb = $firebase(new Firebase('Mock://').child('data'), { + arrayFactory:arrayFactory, + batchFactory:batchFactory + }); + + $fb.$asArray(); // creates the listeners + flushAll(); + batchFactory.flush(); + arrayFactory.$myArray.$$added.calls.reset(); + var ref = $fb.$ref(); + ref.push({newa: 'newa'}); + ref.push({newb: 'newb'}); + ref.push({newc: 'newc'}); + ref.push({newd: 'newd'}); + flushAll(); + expect(batchFactory.queue.length).toBe(4); + expect(arrayFactory.$myArray.$$added).not.toHaveBeenCalled(); + batchFactory.flush(); + expect(arrayFactory.$myArray.$$added).toHaveCallCount(4); + }); }); describe('$asObject', function() { @@ -723,8 +748,58 @@ describe('$firebase', function () { flushAll(); expect($utils.wait.completed).toHaveCallCount(1); }); + + it('should allow custom batching implementations', function() { + var batchFactory = stubBatchFactory(); + var objectFactory = stubObjectFactory(); + + $fb = $firebase(new Firebase('Mock://').child('data'), { + objectFactory:objectFactory, + batchFactory:batchFactory + }); + + $fb.$asObject(); // creates the listeners + flushAll(); + batchFactory.flush(); + objectFactory.prototype.$$updated.calls.reset(); + var ref = $fb.$ref(); + ref.push({newa: 'newa'}); + ref.push({newb: 'newb'}); + ref.push({newc: 'newc'}); + ref.push({newd: 'newd'}); + flushAll(); + expect(batchFactory.queue.length).toBe(4); + expect(objectFactory.prototype.$$updated).not.toHaveBeenCalled(); + batchFactory.flush(); + expect(objectFactory.prototype.$$updated).toHaveCallCount(4); + }); }); + function stubBatchFactory() { + function makeBatchFn(fn){ + return function(){ + var args = Array.prototype.slice.call(arguments,0); + factory.queue.push(function(){ + fn.apply(null,args); + }); + } + } + + function flushBatch(){ + var queueCopy = factory.queue; + factory.queue = []; + angular.forEach(queueCopy,function(fn){fn()}); + } + + function factory(){ + return makeBatchFn; + } + + factory.flush = flushBatch; + factory.queue = []; + return factory; + } + function stubArrayFactory() { var arraySpy = []; angular.forEach(['$$added', '$$updated', '$$moved', '$$removed', '$$error', '$getRecord', '$indexFor'], function(m) {