diff --git a/CHANGELOG.md b/CHANGELOG.md
index a44a2bb4..5b46519d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# EmberFire Changelog
+### EmberFire 1.0.5 (April 17, 2014)
+
+* Adding/removing objects to a `hasMany` array now persists to Firebase after `save()`
+* Better error handling
+* Registed EmberFire with Ember.libraries
+* Removed `_enqueue` in `extractSingle` to prevent race condition with embedded records
+* `find()` calls `store.dematerializeRecord()` if the record can't be found
+
### EmberFire 1.0.4 (April 4, 2014)
* _saveHasManyRelationshipRecord bug fix
diff --git a/bower.json b/bower.json
index 1ae5e996..f35013e3 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "emberfire",
- "version": "1.0.4",
+ "version": "1.0.5",
"description": "Firebase bindings for Ember Data",
"main": ["./dist/emberfire.js"],
"dependencies": {
diff --git a/dist/emberfire.js b/dist/emberfire.js
index 9b8052fe..2cc6caa5 100644
--- a/dist/emberfire.js
+++ b/dist/emberfire.js
@@ -6,8 +6,16 @@
return;
}
- var Promise = Ember.RSVP.Promise;
+ var EmberFire = Ember.Namespace.create({
+ VERSION: '1.0.5'
+ });
+ if (Ember.libraries) {
+ Ember.libraries.registerCoreLibrary('EmberFire', EmberFire.VERSION);
+ }
+
+ // Shortcuts
+ var Promise = Ember.RSVP.Promise;
var map = Ember.EnumerableUtils.map;
var forEach = Ember.EnumerableUtils.forEach;
var fmt = Ember.String.fmt;
@@ -51,7 +59,7 @@
var normalizedPayload = this.normalize(type, payload);
// Check for embedded records
type.eachRelationship(function(key, relationship) {
- if (!Ember.isNone(payload[key]) && relationship.options.embedded === true) {
+ if (!Ember.isNone(payload) && !Ember.isNone(payload[key]) && relationship.options.embedded === true) {
var embeddedKey;
var embeddedRecordPayload = normalizedPayload[key];
var records = [];
@@ -64,14 +72,8 @@
records.push(record);
}
normalizedPayload[key] = Ember.keys(normalizedPayload[key]);
- if (adapter._enqueue) {
- adapter._enqueue(function() {
- store.pushMany(relationship.type, records);
- });
- }
- else {
- store.pushMany(relationship.type, records);
- }
+ // Push the embedded records into the store
+ store.pushMany(relationship.type, records);
}
});
return normalizedPayload;
@@ -129,6 +131,8 @@
this._ref = this.firebase.ref();
// Keep track of what types `.findAll()` has been called for
this._findAllMapForType = {};
+ // Keep a cache to check modified relationships against
+ this._recordCacheForType = {};
// Used to batch records into the store
this._queue = [];
},
@@ -174,11 +178,18 @@
var payload = adapter._assignIdToPayload(snapshot);
var record = store.getById(type, snapshot.name());
+ adapter._updateRecordCacheForType(type, payload);
+
if (!resolved) {
resolved = true;
// If this is the first event, resolve the promise.
if (payload === null) {
- adapter._enqueue(reject, [{ message: fmt('no record was found at %@', [ref.toString()]), recordId: id }]);
+ if (store.hasRecordForId(type, id)) {
+ store.dematerializeRecord(record);
+ }
+ var error = new Error(fmt('no record was found at %@', [ref.toString()]));
+ error.recordId = id;
+ adapter._enqueue(reject, [error]);
}
else {
adapter._enqueue(resolve, [payload]);
@@ -192,7 +203,7 @@
});
}
// Otherwise push it into the store
- else {
+ else if (payload !== null) {
adapter._enqueue(function() {
store.push(type, serializer.extractSingle(store, type, payload));
});
@@ -220,10 +231,9 @@
promises = Ember.A(promises);
forEach(promises.filterBy('state', 'rejected'), function(promise) {
var recordId = promise.reason.recordId;
- if(store.hasRecordForId(type, recordId)) {
+ if (store.hasRecordForId(type, recordId)) {
var record = store.getById(type, recordId);
- record.transitionTo('loaded.created.uncommitted');
- store.deleteRecord(record);
+ store.dematerializeRecord(record);
}
});
return Ember.A(promises.filterBy('state', 'fulfilled')).mapBy('value');
@@ -250,20 +260,23 @@
valueEventTriggered = adapter._findAllAddEventListeners(store, type, ref);
}
ref.once('value', function(snapshot) {
+ var results = [];
if (valueEventTriggered) {
Ember.run(null, valueEventTriggered.resolve);
}
if (snapshot.val() === null) {
- adapter._enqueue(reject);
+ adapter._enqueue(resolve, [results]);
}
else {
- var results = [];
snapshot.forEach(function(childSnapshot) {
var payload = adapter._assignIdToPayload(childSnapshot);
+ //adapter._updateRecordCacheForType(type, payload);
results.push(payload);
});
adapter._enqueue(resolve, [results]);
}
+ }, function(error) {
+ adapter._enqueue(reject, [error]);
});
}, fmt('DS: FirebaseAdapter#findAll %@ to %@', [type, ref.toString()]));
},
@@ -349,10 +362,9 @@
*/
updateRecord: function(store, type, record) {
var adapter = this;
- var serializedRecord = record.serialize({
- includeId: false
- });
+ var serializedRecord = this._getSerializedRecord(record);
var recordRef = this._getRef(type, record.id);
+ var recordCache = Ember.get(adapter._recordCacheForType, fmt('%@.%@', [type.typeKey, record.get('id')])) || {};
return new Promise(function(resolve, reject) {
var savedRelationships = Ember.A();
@@ -360,7 +372,7 @@
switch (relationship.kind) {
case 'hasMany':
if (Ember.isArray(serializedRecord[key])) {
- var save = adapter._saveHasManyRelationship(store, relationship, serializedRecord[key], recordRef);
+ var save = adapter._saveHasManyRelationship(store, type, relationship, serializedRecord[key], recordRef, recordCache);
savedRelationships.push(save);
// Remove the relationship from the serializedRecord
delete serializedRecord[key];
@@ -391,22 +403,52 @@
}, fmt('DS: FirebaseAdapter#updateRecord %@ to %@', [type, recordRef.toString()]));
},
+ /**
+ Return a serialized version of the record
+ */
+ _getSerializedRecord: function(record) {
+ return record.serialize({
+ includeId: false
+ });
+ },
+
/**
Call _saveHasManyRelationshipRecord on each record in the relationship
and then resolve once they have all settled
*/
- _saveHasManyRelationship: function(store, relationship, ids, parentRef) {
+ _saveHasManyRelationship: function(store, type, relationship, ids, recordRef, recordCache) {
if (!Ember.isArray(ids)) {
throw new Error('hasMany relationships must must be an array');
}
- // Save each record in the relationship
- var savedRecords = map(ids, function(id) {
- return this._saveHasManyRelationshipRecord(store, relationship, parentRef, id);
- }, this);
+ var adapter = this;
+ var idsCache = Ember.A(recordCache[relationship.key]);
+ ids = Ember.A(ids);
+ // Added
+ var addedRecords = ids.filter(function(id) {
+ return !idsCache.contains(id);
+ });
+ // Dirty
+ var dirtyRecords = ids.filter(function(id) {
+ var type = relationship.type;
+ return store.hasRecordForId(type, id) && store.getById(type, id).get('isDirty') === true;
+ });
+ dirtyRecords = Ember.A(dirtyRecords.concat(addedRecords)).uniq().map(function(id) {
+ return adapter._saveHasManyRecord(store, relationship, recordRef, id);
+ });
+ // Removed
+ var removedRecords = idsCache.filter(function(id) {
+ return !ids.contains(id);
+ }).map(function(id) {
+ return adapter._removeHasManyRecord(store, relationship, recordRef, id);
+ });
+ // Combine all the saved records
+ var savedRecords = dirtyRecords.concat(removedRecords);
// Wait for all the updates to finish
return Ember.RSVP.allSettled(savedRecords).then(function(savedRecords) {
var rejected = Ember.A(Ember.A(savedRecords).filterBy('state', 'rejected'));
if (rejected.get('length') === 0) {
+ // Update the cache
+ recordCache[relationship.key] = ids;
return savedRecords;
}
else {
@@ -419,57 +461,65 @@
/**
If the relationship is `async: true`, create a child ref
- named with the record id and set the value to false
+ named with the record id and set the value to true
If the relationship is `embedded: true`, create a child ref
named with the record id and update the value to the serialized
version of the record
*/
- _saveHasManyRelationshipRecord: function(store, relationship, parentRef, id) {
+ _saveHasManyRecord: function(store, relationship, parentRef, id) {
var adapter = this;
- // Create a reference to the related record
var ref = this._getRelationshipRef(parentRef, relationship.key, id);
- // Get the local version of the related record
- var relatedRecord = store.hasRecordForId(relationship.type, id) ? store.getById(relationship.type, id) : false;
+ var record = store.getById(relationship.type, id);
var isEmbedded = relationship.options.embedded === true;
- var isDirty = relatedRecord ? relatedRecord.get('isDirty') : false;
- var valueToSave = isEmbedded ? relatedRecord.serialize({ includeId: false }) : true;
+ var valueToSave = isEmbedded ? record.serialize({ includeId: false }) : true;
return new Promise(function(resolve, reject) {
- // If the relationship is embedded and a record was found and the and there are changes
- // If the relationship is embedded and a related record was found and its dirty or there is no related record
- // TODO: use a state machine to manager these conditionals
- if ((isEmbedded && relatedRecord && isDirty) || (!isEmbedded && ((relatedRecord && isDirty) || !relatedRecord))) {
- var _saveHandler = function(error) {
- if (error) {
- if (typeof error === 'object') {
- error.location = ref.toString();
- }
- adapter._enqueue(reject, [error]);
- } else {
- adapter._enqueue(resolve);
+ var _saveHandler = function(error) {
+ if (error) {
+ if (typeof error === 'object') {
+ error.location = ref.toString();
}
- };
- if (isEmbedded) {
- ref.update(valueToSave, _saveHandler);
- }
- else {
- ref.set(valueToSave, _saveHandler);
+ adapter._enqueue(reject, [error]);
+ } else {
+ adapter._enqueue(resolve);
}
+ };
+ if (isEmbedded) {
+ ref.update(valueToSave, _saveHandler);
}
else {
- // The related record didn't need to be save
- adapter._enqueue(resolve);
+ ref.set(valueToSave, _saveHandler);
}
});
},
+ /**
+ Remove a relationship
+ */
+ _removeHasManyRecord: function(store, relationship, parentRef, id) {
+ var adapter = this;
+ var ref = this._getRelationshipRef(parentRef, relationship.key, id);
+ return new Promise(function(resolve, reject) {
+ var _removeHandler = function(error) {
+ if (error) {
+ if (typeof error === 'object') {
+ error.location = ref.toString();
+ }
+ adapter._enqueue(reject, [error]);
+ } else {
+ adapter._enqueue(resolve);
+ }
+ };
+ ref.remove(_removeHandler);
+ });
+ },
+
/**
Called by the store when a record is deleted.
*/
deleteRecord: function(store, type, record) {
var adapter = this;
- var ref = this._getRef(type, record.id);
-
+ var ref = this._getRef(type, record.get('id'));
return new Promise(function(resolve, reject) {
ref.remove(function(err) {
if (err) {
@@ -543,6 +593,32 @@
if (length === 1) {
this._queueScheduleFlush();
}
+ },
+
+ /**
+ A cache of hasMany relationships that can be used to
+ diff against new relationships when a model is saved
+ */
+ _recordCacheForType: undefined,
+
+ /**
+ _updateHasManyCacheForType
+ */
+ _updateRecordCacheForType: function(type, payload) {
+ if (!payload) { return; }
+ var adapter = this;
+ var id = payload.id;
+ var cache = adapter._recordCacheForType;
+ var typeKey = type.typeKey;
+ // Only cache relationships for now
+ type.eachRelationship(function(key, relationship) {
+ if (relationship.kind === 'hasMany') {
+ var ids = payload[key];
+ cache[typeKey] = cache[typeKey] || {};
+ cache[typeKey][id] = cache[typeKey][id] || {};
+ cache[typeKey][id][key] = !Ember.isNone(ids) ? Ember.A(Ember.keys(ids)) : Ember.A();
+ }
+ });
}
});
diff --git a/dist/emberfire.min.js b/dist/emberfire.min.js
index eb89346f..f74d21cb 100644
--- a/dist/emberfire.min.js
+++ b/dist/emberfire.min.js
@@ -1 +1 @@
-!function(){"use strict";if(void 0!==window.DS){var a=Ember.RSVP.Promise,b=Ember.EnumerableUtils.map,c=Ember.EnumerableUtils.forEach,d=Ember.String.fmt;DS.FirebaseSerializer=DS.JSONSerializer.extend(Ember.Evented,{normalize:function(a,b){return a.eachRelationship(function(c,e){if("hasMany"===e.kind)if("object"!=typeof b[c]||Ember.isArray(b[c])||e.options.embedded===!0){if(Ember.isArray(b[c]))throw new Error(d('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }',[a.toString(),e.kind,e.type.typeKey,e.key,e.type.typeKey]))}else b[c]=Ember.keys(b[c])}),this._super.apply(this,arguments)},extractSingle:function(a,b,c){var d=a.adapterFor(b),e=this.normalize(b,c);return b.eachRelationship(function(b,f){if(!Ember.isNone(c[b])&&f.options.embedded===!0){var g,h,i=e[b],j=[];for(g in i)h=i[g],null!==h&&"object"==typeof h&&(h.id=g),j.push(h);e[b]=Ember.keys(e[b]),d._enqueue?d._enqueue(function(){a.pushMany(f.type,j)}):a.pushMany(f.type,j)}}),e},extractArray:function(a,c,d){return b(d,function(b){return this.extractSingle(a,c,b)},this)}}),DS.FirebaseAdapter=DS.Adapter.extend(Ember.Evented,{defaultSerializer:"-firebase",init:function(){if(!this.firebase||"object"!=typeof this.firebase)throw new Error("Please set the `firebase` property on the adapter.");this._ref=this.firebase.ref(),this._findAllMapForType={},this._queue=[]},generateIdForRecord:function(){return this._ref.push().name()},_assignIdToPayload:function(a){var b=a.val();return null!==b&&"object"==typeof b&&"undefined"==typeof b.id&&(b.id=a.name()),b},find:function(b,c,e){var f=this,g=!1,h=this._getRef(c,e),i=b.serializerFor(c);return new a(function(a,j){h.on("value",function(k){var l=f._assignIdToPayload(k),m=b.getById(c,k.name());g?f._enqueue(null===l&&m&&!m.get("isDeleted")?function(){b.getById(c,k.name()).deleteRecord()}:function(){b.push(c,i.extractSingle(b,c,l))}):(g=!0,null===l?f._enqueue(j,[{message:d("no record was found at %@",[h.toString()]),recordId:e}]):f._enqueue(a,[l]))},function(a){g||f._enqueue(j,[a])})},d("DS: FirebaseAdapter#find %@ to %@",[c,h.toString()]))},findMany:function(a,d,e){var f=b(e,function(b){return this.find(a,d,b)},this);return Ember.RSVP.allSettled(f).then(function(b){return b=Ember.A(b),c(b.filterBy("state","rejected"),function(b){var c=b.reason.recordId;if(a.hasRecordForId(d,c)){var e=a.getById(d,c);e.transitionTo("loaded.created.uncommitted"),a.deleteRecord(e)}}),Ember.A(b.filterBy("state","fulfilled")).mapBy("value")})},findAll:function(b,c){var e=this,f=this._getRef(c);return new a(function(a,d){var g;e._findAllHasEventsForType(c)||(g=e._findAllAddEventListeners(b,c,f)),f.once("value",function(b){if(g&&Ember.run(null,g.resolve),null===b.val())e._enqueue(d);else{var c=[];b.forEach(function(a){var b=e._assignIdToPayload(a);c.push(b)}),e._enqueue(a,[c])}})},d("DS: FirebaseAdapter#findAll %@ to %@",[c,f.toString()]))},_findAllMapForType:void 0,_findAllHasEventsForType:function(a){return!Ember.isNone(this._findAllMapForType[a])},_findAllAddEventListeners:function(a,b,c){this._findAllMapForType[b]=!0;var d=Ember.RSVP.defer(),e=this,f=a.serializerFor(b),g=!1;return d.promise.then(function(){g=!0}),c.on("child_added",function(c){g&&e._handleChildValue(a,b,f,c)}),c.on("child_changed",function(c){g&&e._handleChildValue(a,b,f,c)}),c.on("child_removed",function(c){if(g){var d=a.getById(b,c.name());d&&!d.get("isDeleted")&&e._enqueue(function(){a.deleteRecord(d)})}}),d},_handleChildValue:function(a,b,c,d){var e=this._assignIdToPayload(d);this._enqueue(function(){a.push(b,c.extractSingle(a,b,e))})},createRecord:function(a,b,c){return this.updateRecord(a,b,c)},updateRecord:function(b,c,e){var f=this,g=e.serialize({includeId:!1}),h=this._getRef(c,e.id);return new a(function(a,i){var j=Ember.A();e.eachRelationship(function(a,c){switch(c.kind){case"hasMany":if(Ember.isArray(g[a])){var d=f._saveHasManyRelationship(b,c,g[a],h);j.push(d),delete g[a]}}}),Ember.RSVP.allSettled(j).then(function(b){b=Ember.A(b);var j=Ember.A(b.filterBy("state","rejected"));if(0!==j.get("length")){var k=new Error(d("Some errors were encountered while saving %@ %@",[c,e.id]));k.errors=j.mapBy("reason"),f._enqueue(i,[k])}h.update(g,function(b){b?f._enqueue(i,[b]):f._enqueue(a)})})},d("DS: FirebaseAdapter#updateRecord %@ to %@",[c,h.toString()]))},_saveHasManyRelationship:function(a,c,e,f){if(!Ember.isArray(e))throw new Error("hasMany relationships must must be an array");var g=b(e,function(b){return this._saveHasManyRelationshipRecord(a,c,f,b)},this);return Ember.RSVP.allSettled(g).then(function(a){var b=Ember.A(Ember.A(a).filterBy("state","rejected"));if(0===b.get("length"))return a;var e=new Error(d("Some errors were encountered while saving a hasMany relationship %@ -> %@",[c.parentType,c.type]));throw e.errors=Ember.A(b).mapBy("reason"),e})},_saveHasManyRelationshipRecord:function(b,c,d,e){var f=this,g=this._getRelationshipRef(d,c.key,e),h=b.hasRecordForId(c.type,e)?b.getById(c.type,e):!1,i=c.options.embedded===!0,j=h?h.get("isDirty"):!1,k=i?h.serialize({includeId:!1}):!0;return new a(function(a,b){if(i&&h&&j||!i&&(h&&j||!h)){var c=function(c){c?("object"==typeof c&&(c.location=g.toString()),f._enqueue(b,[c])):f._enqueue(a)};i?g.update(k,c):g.set(k,c)}else f._enqueue(a)})},deleteRecord:function(b,c,e){var f=this,g=this._getRef(c,e.id);return new a(function(a,b){g.remove(function(c){c?f._enqueue(b,[c]):f._enqueue(a)})},d("DS: FirebaseAdapter#deleteRecord %@ to %@",[c,g.toString()]))},pathForType:function(a){var b=Ember.String.camelize(a);return Ember.String.pluralize(b)},_getRef:function(a,b){var c=this._ref;return a&&(c=c.child(this.pathForType(a.typeKey))),b&&(c=c.child(b)),c},_getRelationshipRef:function(a,b,c){return a.child(b).child(c)},_queueFlushDelay:1e3/60,_queueScheduleFlush:function(){Ember.run.later(this,this._queueFlush,this._queueFlushDelay)},_queueFlush:function(){c(this._queue,function(a){var b=a[0],c=a[1];b.apply(null,c)}),this._queue.length=0},_enqueue:function(a,b){var c=this._queue.push([a,b]);1===c&&this._queueScheduleFlush()}}),Ember.onLoad("Ember.Application",function(a){a.initializer({name:"firebase",after:"store",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}();
\ No newline at end of file
+!function(){"use strict";if(void 0!==window.DS){var a=Ember.Namespace.create({VERSION:"1.0.5"});Ember.libraries&&Ember.libraries.registerCoreLibrary("EmberFire",a.VERSION);var b=Ember.RSVP.Promise,c=Ember.EnumerableUtils.map,d=Ember.EnumerableUtils.forEach,e=Ember.String.fmt;DS.FirebaseSerializer=DS.JSONSerializer.extend(Ember.Evented,{normalize:function(a,b){return a.eachRelationship(function(c,d){if("hasMany"===d.kind)if("object"!=typeof b[c]||Ember.isArray(b[c])||d.options.embedded===!0){if(Ember.isArray(b[c]))throw new Error(e('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }',[a.toString(),d.kind,d.type.typeKey,d.key,d.type.typeKey]))}else b[c]=Ember.keys(b[c])}),this._super.apply(this,arguments)},extractSingle:function(a,b,c){var d=(a.adapterFor(b),this.normalize(b,c));return b.eachRelationship(function(b,e){if(!Ember.isNone(c)&&!Ember.isNone(c[b])&&e.options.embedded===!0){var f,g,h=d[b],i=[];for(f in h)g=h[f],null!==g&&"object"==typeof g&&(g.id=f),i.push(g);d[b]=Ember.keys(d[b]),a.pushMany(e.type,i)}}),d},extractArray:function(a,b,d){return c(d,function(c){return this.extractSingle(a,b,c)},this)}}),DS.FirebaseAdapter=DS.Adapter.extend(Ember.Evented,{defaultSerializer:"-firebase",init:function(){if(!this.firebase||"object"!=typeof this.firebase)throw new Error("Please set the `firebase` property on the adapter.");this._ref=this.firebase.ref(),this._findAllMapForType={},this._recordCacheForType={},this._queue=[]},generateIdForRecord:function(){return this._ref.push().name()},_assignIdToPayload:function(a){var b=a.val();return null!==b&&"object"==typeof b&&"undefined"==typeof b.id&&(b.id=a.name()),b},find:function(a,c,d){var f=this,g=!1,h=this._getRef(c,d),i=a.serializerFor(c);return new b(function(b,j){h.on("value",function(k){var l=f._assignIdToPayload(k),m=a.getById(c,k.name());if(f._updateRecordCacheForType(c,l),g)null===l&&m&&!m.get("isDeleted")?f._enqueue(function(){a.getById(c,k.name()).deleteRecord()}):null!==l&&f._enqueue(function(){a.push(c,i.extractSingle(a,c,l))});else if(g=!0,null===l){a.hasRecordForId(c,d)&&a.dematerializeRecord(m);var n=new Error(e("no record was found at %@",[h.toString()]));n.recordId=d,f._enqueue(j,[n])}else f._enqueue(b,[l])},function(a){g||f._enqueue(j,[a])})},e("DS: FirebaseAdapter#find %@ to %@",[c,h.toString()]))},findMany:function(a,b,e){var f=c(e,function(c){return this.find(a,b,c)},this);return Ember.RSVP.allSettled(f).then(function(c){return c=Ember.A(c),d(c.filterBy("state","rejected"),function(c){var d=c.reason.recordId;if(a.hasRecordForId(b,d)){var e=a.getById(b,d);a.dematerializeRecord(e)}}),Ember.A(c.filterBy("state","fulfilled")).mapBy("value")})},findAll:function(a,c){var d=this,f=this._getRef(c);return new b(function(b,e){var g;d._findAllHasEventsForType(c)||(g=d._findAllAddEventListeners(a,c,f)),f.once("value",function(a){var c=[];g&&Ember.run(null,g.resolve),null===a.val()?d._enqueue(b,[c]):(a.forEach(function(a){var b=d._assignIdToPayload(a);c.push(b)}),d._enqueue(b,[c]))},function(a){d._enqueue(e,[a])})},e("DS: FirebaseAdapter#findAll %@ to %@",[c,f.toString()]))},_findAllMapForType:void 0,_findAllHasEventsForType:function(a){return!Ember.isNone(this._findAllMapForType[a])},_findAllAddEventListeners:function(a,b,c){this._findAllMapForType[b]=!0;var d=Ember.RSVP.defer(),e=this,f=a.serializerFor(b),g=!1;return d.promise.then(function(){g=!0}),c.on("child_added",function(c){g&&e._handleChildValue(a,b,f,c)}),c.on("child_changed",function(c){g&&e._handleChildValue(a,b,f,c)}),c.on("child_removed",function(c){if(g){var d=a.getById(b,c.name());d&&!d.get("isDeleted")&&e._enqueue(function(){a.deleteRecord(d)})}}),d},_handleChildValue:function(a,b,c,d){var e=this._assignIdToPayload(d);this._enqueue(function(){a.push(b,c.extractSingle(a,b,e))})},createRecord:function(a,b,c){return this.updateRecord(a,b,c)},updateRecord:function(a,c,d){var f=this,g=this._getSerializedRecord(d),h=this._getRef(c,d.id),i=Ember.get(f._recordCacheForType,e("%@.%@",[c.typeKey,d.get("id")]))||{};return new b(function(b,j){var k=Ember.A();d.eachRelationship(function(b,d){switch(d.kind){case"hasMany":if(Ember.isArray(g[b])){var e=f._saveHasManyRelationship(a,c,d,g[b],h,i);k.push(e),delete g[b]}}}),Ember.RSVP.allSettled(k).then(function(a){a=Ember.A(a);var i=Ember.A(a.filterBy("state","rejected"));if(0!==i.get("length")){var k=new Error(e("Some errors were encountered while saving %@ %@",[c,d.id]));k.errors=i.mapBy("reason"),f._enqueue(j,[k])}h.update(g,function(a){a?f._enqueue(j,[a]):f._enqueue(b)})})},e("DS: FirebaseAdapter#updateRecord %@ to %@",[c,h.toString()]))},_getSerializedRecord:function(a){return a.serialize({includeId:!1})},_saveHasManyRelationship:function(a,b,c,d,f,g){if(!Ember.isArray(d))throw new Error("hasMany relationships must must be an array");var h=this,i=Ember.A(g[c.key]);d=Ember.A(d);var j=d.filter(function(a){return!i.contains(a)}),k=d.filter(function(b){var d=c.type;return a.hasRecordForId(d,b)&&a.getById(d,b).get("isDirty")===!0});k=Ember.A(k.concat(j)).uniq().map(function(b){return h._saveHasManyRecord(a,c,f,b)});var l=i.filter(function(a){return!d.contains(a)}).map(function(b){return h._removeHasManyRecord(a,c,f,b)}),m=k.concat(l);return Ember.RSVP.allSettled(m).then(function(a){var b=Ember.A(Ember.A(a).filterBy("state","rejected"));if(0===b.get("length"))return g[c.key]=d,a;var f=new Error(e("Some errors were encountered while saving a hasMany relationship %@ -> %@",[c.parentType,c.type]));throw f.errors=Ember.A(b).mapBy("reason"),f})},_saveHasManyRecord:function(a,c,d,e){var f=this,g=this._getRelationshipRef(d,c.key,e),h=a.getById(c.type,e),i=c.options.embedded===!0,j=i?h.serialize({includeId:!1}):!0;return new b(function(a,b){var c=function(c){c?("object"==typeof c&&(c.location=g.toString()),f._enqueue(b,[c])):f._enqueue(a)};i?g.update(j,c):g.set(j,c)})},_removeHasManyRecord:function(a,c,d,e){var f=this,g=this._getRelationshipRef(d,c.key,e);return new b(function(a,b){var c=function(c){c?("object"==typeof c&&(c.location=g.toString()),f._enqueue(b,[c])):f._enqueue(a)};g.remove(c)})},deleteRecord:function(a,c,d){var f=this,g=this._getRef(c,d.get("id"));return new b(function(a,b){g.remove(function(c){c?f._enqueue(b,[c]):f._enqueue(a)})},e("DS: FirebaseAdapter#deleteRecord %@ to %@",[c,g.toString()]))},pathForType:function(a){var b=Ember.String.camelize(a);return Ember.String.pluralize(b)},_getRef:function(a,b){var c=this._ref;return a&&(c=c.child(this.pathForType(a.typeKey))),b&&(c=c.child(b)),c},_getRelationshipRef:function(a,b,c){return a.child(b).child(c)},_queueFlushDelay:1e3/60,_queueScheduleFlush:function(){Ember.run.later(this,this._queueFlush,this._queueFlushDelay)},_queueFlush:function(){d(this._queue,function(a){var b=a[0],c=a[1];b.apply(null,c)}),this._queue.length=0},_enqueue:function(a,b){var c=this._queue.push([a,b]);1===c&&this._queueScheduleFlush()},_recordCacheForType:void 0,_updateRecordCacheForType:function(a,b){if(b){var c=this,d=b.id,e=c._recordCacheForType,f=a.typeKey;a.eachRelationship(function(a,c){if("hasMany"===c.kind){var g=b[a];e[f]=e[f]||{},e[f][d]=e[f][d]||{},e[f][d][a]=Ember.isNone(g)?Ember.A():Ember.A(Ember.keys(g))}})}}}),Ember.onLoad("Ember.Application",function(a){a.initializer({name:"firebase",after:"store",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}();
\ No newline at end of file
diff --git a/package.json b/package.json
index 6220a2e7..f1aebd89 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "EmberFire",
- "version": "1.0.4",
+ "version": "1.0.5",
"description": "An officially supported Ember binding for Firebase.",
"main": "dist/emberfire.js",
"repository": {
diff --git a/src/data.js b/src/data.js
index 044060d0..2cc6caa5 100644
--- a/src/data.js
+++ b/src/data.js
@@ -7,7 +7,7 @@
}
var EmberFire = Ember.Namespace.create({
- VERSION: '1.0.4'
+ VERSION: '1.0.5'
});
if (Ember.libraries) {
diff --git a/test/index.html b/test/index.html
index e2055214..cf9cd7fe 100644
--- a/test/index.html
+++ b/test/index.html
@@ -22,7 +22,7 @@
-
+