From 04460fb4415e97bd3109be23c525f84b4072335b Mon Sep 17 00:00:00 2001 From: katowulf Date: Wed, 20 Mar 2013 15:52:03 -0700 Subject: [PATCH] fixed error when Firebase data is cached locally, caused some records to return synchronously with wrong keys and values. Simplified demo data. --- FirebaseIndex.js | 44 +++++++++++++++++++++++++++++++++++++------- demo/demo.js | 32 ++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/FirebaseIndex.js b/FirebaseIndex.js index 810536e..14786a5 100644 --- a/FirebaseIndex.js +++ b/FirebaseIndex.js @@ -77,7 +77,7 @@ var FirebaseIndex; case 'child_added': fn = addEventListener(this.eventListeners[eventType], callback, context); // mimic Firebase behavior by sending any pre-existing records when on('child_added') is invoked - notifyExistingRecs(this.childRefs, fn); + notifyExistingRecs(this.dataRef, this.childRefs, fn); break; case 'child_changed': case 'child_removed': @@ -266,7 +266,10 @@ var FirebaseIndex; function notifyListeners(list, ss, key, prevId) { list.forEach(function(o) { - o.fn(wrapSnap(ss, key), prevId); + // make the calls async so they match client expectations + // Firebase can call them synchonously if the data is already local + // which messes up Promise.progress() and any async callbacks + defer(function() { o.fn(wrapSnap(ss, key), prevId) }); }); } @@ -280,17 +283,22 @@ var FirebaseIndex; return fn; } - function notifyExistingRecs(refs, callback) { + function notifyExistingRecs(dataPathFn, refs, callback) { var key; for (key in refs) { - if (refs.hasOwnProperty(key)) { - refs[key].ref.once('value', function(ss) { - if( ss.val() !== null ) { callback(ss); } - }); + if (refs.hasOwnProperty(key) && refs[key].loaded) { + // must be external because key is mutable and we use it in a closure + getValAndNotify(dataPathFn(key), key, callback); } } } + function getValAndNotify(ref, key, callback) { + ref.once('value', function(ss) { + if( ss.val() !== null ) { defer(function() { callback(wrapSnap(ss, key)); }); } + }); + } + function storeChildRef(list, cb, ss, prevId) { var key = ss.name(); var childRef = ss.ref(); @@ -345,6 +353,28 @@ var FirebaseIndex; } } + var defer; + if( typeof(_) === 'object' && _ && typeof(_.defer) === 'function' ) { + // if underscore is available, use it + defer = _.defer; + } + else { + // otherwise, hope setTimeout hasn't been tinkered with + defer = function(fn) { + return setTimeout(fn, 0); + } + } + + /** + * Because we're getting the value from a random path, which could be a function, the keys may not match. + * For instance, our reference key could be group/member/1234 and our data path could be user/1234/public/widget, + * in which case, if we use ss.name() on the data path, we get a key of "widget" which is clearly not right. + * So this method normalizes the keys. + * + * @param {Firebase} ss reference to the data path + * @param {String} key key from the index + * @returns {*} + */ function wrapSnap(ss, key) { ss.name = function() { return key; }; return ss; diff --git a/demo/demo.js b/demo/demo.js index f4f089f..6cbed62 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -12,7 +12,7 @@ jQuery(function ($) { // initialize our indexed data var FB = new Firebase('https://i5ubv072aza.firebaseio-demo.com/'); - var IDX = new FirebaseIndex(FB.child('indices/widgets'), FB.child('widgets')); + var IDX = new FirebaseIndex(FB.child('index'), FB.child('widgets')); // refs for our DOM nodes var $master = $('#master'); @@ -88,7 +88,13 @@ jQuery(function ($) { function resortIndex(e) { var $arrow = $(e.target), $el = $arrow.closest('li'), id = $el.attr('data-id'), pri = $el.find('input').val(); - IDX.add(id, pri? parseInt(pri) : null); + if( parseInt(pri) == pri ) { + IDX.add(id, pri? parseInt(pri) : null); + } + else { + alert('must be a number for this demo'); + $el.find('input').val(null); + } } /***************************************** @@ -114,7 +120,7 @@ jQuery(function ($) { } function initIndex() { - var ref = FB.child('indices/widgets'); + var ref = FB.child('index'); ref.on('child_added', function(ss, afterId) { var id = ss.name(); @@ -175,17 +181,15 @@ jQuery(function ($) { "charlie" : "it's yellow and conical", "alpha" : "it's red and square" }, - "indices" : { - "widgets" : { - "delta" : { - ".value" : 1, - ".priority" : 100.0 - }, - "golf" : 1, - "alpha" : { - ".value" : 1, - ".priority" : 150.0 - } + "index" : { + "delta" : { + ".value" : 1, + ".priority" : 100.0 + }, + "golf" : 1, + "alpha" : { + ".value" : 1, + ".priority" : 150.0 } } };