Comparing version 1.2.5 to 1.2.6
@@ -6,3 +6,3 @@ /*! | ||
* | ||
* EmberFire 1.2.5 | ||
* EmberFire 1.2.6 | ||
* https://github.com/firebase/emberfire/ | ||
@@ -20,3 +20,3 @@ * License: MIT | ||
var EmberFire = Ember.Namespace.create({ | ||
VERSION: '1.2.5' | ||
VERSION: '1.2.6' | ||
}); | ||
@@ -28,2 +28,26 @@ | ||
//Monkeypatch the store until ED gives us a good way to listen to push events | ||
DS.Store.reopen({ | ||
push: function(typeName, data, _partial) { | ||
var record = this._super(typeName, data, _partial); | ||
var adapter = this.adapterFor(record.constructor); | ||
if (adapter.recordWasPushed) { | ||
adapter.recordWasPushed(this, typeName, record); | ||
} | ||
return record; | ||
}, | ||
recordWillUnload: function(record) { | ||
var adapter = this.adapterFor(record.constructor); | ||
if (adapter.recordWillUnload) { | ||
adapter.recordWillUnload(this, record); | ||
} | ||
} | ||
}); | ||
DS.Model.reopen({ | ||
unloadRecord: function() { | ||
this.store.recordWillUnload(this); | ||
return this._super(); | ||
} | ||
}); | ||
// Shortcuts | ||
@@ -35,2 +59,21 @@ var Promise = Ember.RSVP.Promise; | ||
var toPromise = function(fn, context, _args, errorMsg) { | ||
var args = _args || []; | ||
return new Promise(function(resolve, reject) { | ||
var callback = function(error) { | ||
if (error) { | ||
if (errorMsg && typeof error === 'object') { | ||
error.location = errorMsg; | ||
} | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
}; | ||
args.push(callback); | ||
fn.apply(context, args); | ||
}); | ||
}; | ||
/** | ||
@@ -42,2 +85,61 @@ The Firebase serializer helps normalize relationships and can be extended on | ||
//We need to account for Firebase turning key/value pairs with ids '1' and '0' into arrays | ||
//See https://github.com/firebase/emberfire/issues/124 | ||
_normalizeNumberIDs: function(hash, key) { | ||
var newHash = []; | ||
if (hash[key][0] === true) { | ||
newHash.push('0'); | ||
} | ||
if (hash[key][1] === true) { | ||
newHash.push('1'); | ||
} | ||
hash[key] = newHash; | ||
}, | ||
normalizeHasMany: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
if (typeof hash[key] === 'object' && !Ember.isArray(hash[key])) { | ||
hash[key] = Ember.keys(hash[key]); | ||
} | ||
//We need to account for Firebase turning key/value pairs with ids '1' and '0' into arrays | ||
//See https://github.com/firebase/emberfire/issues/124 | ||
else if (Ember.isArray(hash[key]) && hash[key].length < 3 && (hash[key][0] === true || hash[key][1] === true)) { | ||
this._normalizeNumberIDs(hash, key); | ||
} | ||
else if (Ember.isArray(hash[key])) { | ||
throw new Error(fmt('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }', [type.toString(), relationship.kind, relationship.type.typeKey, key, relationship.type.typeKey])); | ||
} | ||
}, | ||
normalizeEmbeddedHasMany: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
var embeddedRecordPayload = hash[key]; | ||
var embeddedKey; | ||
if (!hash[key]) { | ||
return; | ||
} | ||
for (embeddedKey in embeddedRecordPayload) { | ||
var record = embeddedRecordPayload[embeddedKey]; | ||
if (record !== null && typeof record === 'object') { | ||
record.id = embeddedKey; | ||
} | ||
this.store.push(relationship.type, this.normalize(relationship.type, record)); | ||
} | ||
hash[key] = Ember.keys(hash[key]); | ||
}, | ||
normalizeEmbeddedBelongsTo: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
if (!hash[key]) { | ||
return; | ||
} | ||
var embeddedRecordPayload = hash[key]; | ||
if (typeof embeddedRecordPayload.id !== 'string') { | ||
throw new Error(fmt('Embedded relationship "%@" of "%@" must contain an "id" property in the payload', [relationship.type.typeKey, type])); | ||
} | ||
this.store.push(relationship.type, this.normalize(relationship.type, embeddedRecordPayload)); | ||
hash[key] = embeddedRecordPayload.id; | ||
}, | ||
normalizeBelongsTo: Ember.K, | ||
/** | ||
@@ -49,10 +151,16 @@ Called after `extractSingle()`. This method checks the model | ||
normalize: function(type, hash) { | ||
var serializer = this; | ||
// Check if the model contains any 'hasMany' relationships | ||
type.eachRelationship(function(key, relationship) { | ||
if (relationship.kind === 'hasMany') { | ||
if (typeof hash[key] === 'object' && !Ember.isArray(hash[key]) && relationship.options.embedded !== true) { | ||
hash[key] = Ember.keys(hash[key]); | ||
if (relationship.options.embedded) { | ||
serializer.normalizeEmbeddedHasMany(type, hash, relationship); | ||
} else { | ||
serializer.normalizeHasMany(type, hash, relationship); | ||
} | ||
else if (Ember.isArray(hash[key])) { | ||
throw new Error(fmt('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }', [type.toString(), relationship.kind, relationship.type.typeKey, relationship.key, relationship.type.typeKey])); | ||
} else { | ||
if (relationship.options.embedded) { | ||
serializer.normalizeEmbeddedBelongsTo(type, hash, relationship); | ||
} else { | ||
serializer.normalizeBelongsTo(type, hash, relationship); | ||
} | ||
@@ -73,33 +181,3 @@ } | ||
extractSingle: function(store, type, payload) { | ||
var adapter = store.adapterFor(type); | ||
var normalizedPayload = this.normalize(type, payload); | ||
// Check for embedded records | ||
type.eachRelationship(function(key, relationship) { | ||
if (!Ember.isNone(payload) && !Ember.isNone(payload[key]) && relationship.options.embedded === true) { | ||
var embeddedKey; | ||
var embeddedRecordPayload = normalizedPayload[key]; | ||
var records = []; | ||
var record; | ||
if (relationship.kind === 'belongsTo') { | ||
if (typeof embeddedRecordPayload.id !== 'string') { | ||
throw new Error(fmt('Embedded relationship "%@" of "%@" must contain an "id" property in the payload', [relationship.type.typeKey, type])); | ||
} | ||
records.push(embeddedRecordPayload); | ||
normalizedPayload[key] = embeddedRecordPayload.id; | ||
} | ||
else { | ||
for (embeddedKey in embeddedRecordPayload) { | ||
record = embeddedRecordPayload[embeddedKey]; | ||
if (record !== null && typeof record === 'object') { | ||
record.id = embeddedKey; | ||
} | ||
records.push(record); | ||
} | ||
normalizedPayload[key] = Ember.keys(normalizedPayload[key]); | ||
} | ||
// Push the embedded records into the store | ||
store.pushMany(relationship.type, records); | ||
} | ||
}); | ||
return normalizedPayload; | ||
return this.normalize(type, payload); | ||
}, | ||
@@ -125,7 +203,11 @@ | ||
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key; | ||
var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship); | ||
var relationshipTypes = ['manyToNone', 'manyToMany', 'manyToOne']; | ||
json[payloadKey] = Ember.A(record.get(key)).mapBy('id'); | ||
}, | ||
if (relationshipTypes.indexOf(relationshipType) > -1) { | ||
json[payloadKey] = Ember.A(record.get(key)).mapBy('id'); | ||
serializeBelongsTo: function(record, json, relationship) { | ||
this._super(record, json, relationship); | ||
var key = relationship.key; | ||
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : relationship.key; | ||
if (typeof json[key] === "undefined" || json[key] === '') { | ||
delete json[key]; | ||
} | ||
@@ -213,3 +295,2 @@ } | ||
var adapter = this; | ||
var resolved = false; | ||
var ref = this._getRef(type, id); | ||
@@ -219,45 +300,16 @@ var serializer = store.serializerFor(type); | ||
return new Promise(function(resolve, reject) { | ||
ref.on('value', function(snapshot) { | ||
ref.once('value', function(snapshot) { | ||
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) { | ||
if (store.hasRecordForId(type, id)) { | ||
adapter._enqueue(function() { | ||
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]); | ||
} | ||
if (payload === null) { | ||
var error = new Error(fmt('no record was found at %@', [ref.toString()])); | ||
error.recordId = id; | ||
reject(error); | ||
} | ||
else { | ||
// If the snapshot is null, delete the record from the store | ||
if (payload === null && record && !record.get('isDeleted')) { | ||
adapter._enqueue(function() { | ||
store.getById(type, snapshot.name()).deleteRecord(); | ||
}); | ||
} | ||
// Otherwise push it into the store | ||
else if (payload !== null) { | ||
adapter._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
} | ||
resolve(payload); | ||
} | ||
}, | ||
function(err) { | ||
// Only called in cases of permission related errors. | ||
if (!resolved) { | ||
adapter._enqueue(reject, [err]); | ||
} | ||
reject(err); | ||
}); | ||
@@ -267,22 +319,31 @@ }, fmt('DS: FirebaseAdapter#find %@ to %@', [type, ref.toString()])); | ||
recordWasPushed: function(store, type, record) { | ||
if (!record.__listening) { | ||
this.listenForChanges(store, type, record); | ||
} | ||
}, | ||
recordWillUnload: function(store, record) { | ||
var ref = this._getRef(record.typeKey, record.get('id')); | ||
ref.off('value'); | ||
}, | ||
listenForChanges: function(store, type, record) { | ||
record.__listening = true; | ||
var serializer = store.serializerFor(type); | ||
var adapter = this; | ||
var ref = this._getRef(type, record.get('id')); | ||
var called = false; | ||
ref.on('value', function(snapshot) { | ||
if (called) { | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
} | ||
called = true; | ||
}); | ||
}, | ||
/** | ||
findMany | ||
*/ | ||
findMany: function(store, type, ids) { | ||
var promises = map(ids, function(id) { | ||
return this.find(store, type, id); | ||
}, this); | ||
return Ember.RSVP.allSettled(promises).then(function(promises) { | ||
// Remove any records that couldn't be fetched | ||
promises = Ember.A(promises); | ||
forEach(promises.filterBy('state', 'rejected'), function(promise) { | ||
var recordId = promise.reason.recordId; | ||
if (store.hasRecordForId(type, recordId)) { | ||
var record = store.getById(type, recordId); | ||
store.dematerializeRecord(record); | ||
} | ||
}); | ||
return Ember.A(promises.filterBy('state', 'fulfilled')).mapBy('value'); | ||
}); | ||
}, | ||
findMany: undefined, | ||
@@ -304,24 +365,15 @@ /** | ||
// Listen for child events on the type | ||
var valueEventTriggered; | ||
if (!adapter._findAllHasEventsForType(type)) { | ||
valueEventTriggered = adapter._findAllAddEventListeners(store, type, ref); | ||
} | ||
ref.once('value', function(snapshot) { | ||
if (!adapter._findAllHasEventsForType(type)) { | ||
adapter._findAllAddEventListeners(store, type, ref); | ||
} | ||
var results = []; | ||
if (valueEventTriggered) { | ||
Ember.run(null, valueEventTriggered.resolve); | ||
} | ||
if (snapshot.val() === null) { | ||
adapter._enqueue(resolve, [results]); | ||
} | ||
else { | ||
snapshot.forEach(function(childSnapshot) { | ||
var payload = adapter._assignIdToPayload(childSnapshot); | ||
adapter._updateRecordCacheForType(type, payload); | ||
results.push(payload); | ||
}); | ||
adapter._enqueue(resolve, [results]); | ||
} | ||
snapshot.forEach(function(childSnapshot) { | ||
var payload = adapter._assignIdToPayload(childSnapshot); | ||
adapter._updateRecordCacheForType(type, payload); | ||
results.push(payload); | ||
}); | ||
resolve(results); | ||
}, function(error) { | ||
adapter._enqueue(reject, [error]); | ||
reject(error); | ||
}); | ||
@@ -351,32 +403,10 @@ }, fmt('DS: FirebaseAdapter#findAll %@ to %@', [type, ref.toString()])); | ||
var deferred = Ember.RSVP.defer(); | ||
var adapter = this; | ||
var serializer = store.serializerFor(type); | ||
var valueEventTriggered = false; | ||
deferred.promise.then(function() { | ||
valueEventTriggered = true; | ||
}); | ||
ref.on('child_added', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
}); | ||
ref.on('child_changed', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
}); | ||
ref.on('child_removed', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
var record = store.getById(type, snapshot.name()); | ||
if (record && !record.get('isDeleted')) { | ||
adapter._enqueue(function() { | ||
store.deleteRecord(record); | ||
}); | ||
if (!store.hasRecordForId(type, snapshot.name())) { | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
} | ||
}); | ||
return deferred; | ||
}, | ||
@@ -388,6 +418,21 @@ | ||
_handleChildValue: function(store, type, serializer, snapshot) { | ||
var payload = this._assignIdToPayload(snapshot); | ||
this._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
//No idea why we need this, we are alredy turning off the callback by | ||
//calling ref.off in recordWillUnload. Something is fishy here | ||
if (store.isDestroying) { | ||
return; | ||
} | ||
var value = snapshot.val(); | ||
if (value === null) { | ||
var id = snapshot.name(); | ||
var record = store.getById(type, id); | ||
//TODO refactor using ED | ||
if (!record.get('isDeleted')) { | ||
record.deleteRecord(); | ||
} | ||
} else { | ||
var payload = this._assignIdToPayload(snapshot); | ||
this._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
} | ||
}, | ||
@@ -400,3 +445,6 @@ | ||
createRecord: function(store, type, record) { | ||
return this.updateRecord(store, type, record); | ||
var adapter = this; | ||
return this.updateRecord(store, type, record).then(function() { | ||
adapter.listenForChanges(store, type, record); | ||
}); | ||
}, | ||
@@ -412,76 +460,57 @@ | ||
any relationships have been successfully saved to Firebase. | ||
We take an optional record reference, in order for this method to be usable | ||
for saving nested records as well. | ||
*/ | ||
updateRecord: function(store, type, record) { | ||
updateRecord: function(store, type, record, _recordRef) { | ||
var adapter = this; | ||
var recordRef = this._getRef(type, record.id); | ||
var recordRef = _recordRef || this._getRef(type, record.id); | ||
var recordCache = Ember.get(adapter._recordCacheForType, fmt('%@.%@', [type.typeKey, record.get('id')])) || {}; | ||
return this._getSerializedRecord(record).then(function(serializedRecord) { | ||
return new Promise(function(resolve, reject) { | ||
var savedRelationships = Ember.A(); | ||
record.eachRelationship(function(key, relationship) { | ||
var save ; | ||
switch (relationship.kind) { | ||
case 'hasMany': | ||
if (Ember.isArray(serializedRecord[key])) { | ||
save = adapter._saveHasManyRelationship(store, type, relationship, serializedRecord[key], recordRef, recordCache); | ||
savedRelationships.push(save); | ||
// Remove the relationship from the serializedRecord | ||
delete serializedRecord[key]; | ||
} | ||
break; | ||
case 'belongsTo': | ||
if (typeof serializedRecord[key] === "undefined" || serializedRecord[key] === null || serializedRecord[key] === '') { | ||
delete serializedRecord[key]; | ||
} | ||
else if (relationship.options.embedded === true) { | ||
save = adapter._saveBelongsToRecord(store, type, relationship, serializedRecord[key], recordRef); | ||
savedRelationships.push(save); | ||
delete serializedRecord[key]; | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
}); | ||
// Save the record once all the relationships have saved | ||
Ember.RSVP.allSettled(savedRelationships).then(function(savedRelationships) { | ||
savedRelationships = Ember.A(savedRelationships); | ||
var rejected = Ember.A(savedRelationships.filterBy('state', 'rejected')); | ||
// Throw an error if any of the relationships failed to save | ||
if (rejected.get('length') !== 0) { | ||
var error = new Error(fmt('Some errors were encountered while saving %@ %@', [type, record.id])); | ||
error.errors = rejected.mapBy('reason'); | ||
adapter._enqueue(reject, [error]); | ||
} | ||
recordRef.update(serializedRecord, function(error) { | ||
if (error) { | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
var serializedRecord = record.serialize({includeId:false}); | ||
return new Promise(function(resolve, reject) { | ||
var savedRelationships = Ember.A(); | ||
record.eachRelationship(function(key, relationship) { | ||
var save; | ||
if (relationship.kind === 'hasMany') { | ||
if (serializedRecord[key]) { | ||
save = adapter._saveHasManyRelationship(store, type, relationship, serializedRecord[key], recordRef, recordCache); | ||
savedRelationships.push(save); | ||
// Remove the relationship from the serializedRecord because otherwise we would clobber the entire hasMany | ||
delete serializedRecord[key]; | ||
} | ||
}); | ||
}); | ||
} else { | ||
if (relationship.options.embedded === true && serializedRecord[key]) { | ||
save = adapter._saveBelongsToRecord(store, type, relationship, serializedRecord[key], recordRef); | ||
savedRelationships.push(save); | ||
delete serializedRecord[key]; | ||
} | ||
} | ||
}); | ||
var relationshipsPromise = Ember.RSVP.allSettled(savedRelationships); | ||
var recordPromise = adapter._updateRecord(recordRef, serializedRecord); | ||
Ember.RSVP.hashSettled({relationships: relationshipsPromise, record: recordPromise}).then(function(promises) { | ||
var rejected = Ember.A(promises.relationships.value).filterBy('state', 'rejected'); | ||
if (promises.record.state === 'rejected') { | ||
rejected.push(promises.record); | ||
} | ||
// Throw an error if any of the relationships failed to save | ||
if (rejected.length !== 0) { | ||
var error = new Error(fmt('Some errors were encountered while saving %@ %@', [type, record.id])); | ||
error.errors = rejected.mapBy('reason'); | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}, fmt('DS: FirebaseAdapter#updateRecord %@ to %@', [type, recordRef.toString()])); | ||
}, | ||
/** | ||
Return a serialized version of the record | ||
*/ | ||
_getSerializedRecord: function(record) { | ||
var json = record.serialize({ includeId: false }); | ||
var relationships = []; | ||
record.eachRelationship(function(key, relationship) { | ||
switch (relationship.kind) { | ||
case 'hasMany': | ||
relationships.push(Promise.cast(record.get(key)).then(function(hasManyRecords) { | ||
json[key] = Ember.A(hasManyRecords).mapBy('id'); | ||
})); | ||
break; | ||
} | ||
}); | ||
return Ember.RSVP.all(relationships).then(function() { | ||
return json; | ||
}); | ||
//Just update the record itself without caring for the relationships | ||
_updateRecord: function(recordRef, serializedRecord) { | ||
return toPromise(recordRef.update, recordRef, [serializedRecord]); | ||
}, | ||
@@ -499,3 +528,5 @@ | ||
var idsCache = Ember.A(recordCache[relationship.key]); | ||
ids = Ember.A(ids); | ||
ids = Ember.A(ids); | ||
var dirtyRecords = []; | ||
// Added | ||
@@ -505,4 +536,5 @@ var addedRecords = ids.filter(function(id) { | ||
}); | ||
// Dirty | ||
var dirtyRecords = ids.filter(function(id) { | ||
dirtyRecords = ids.filter(function(id) { | ||
var type = relationship.type; | ||
@@ -548,25 +580,10 @@ return store.hasRecordForId(type, id) && store.getById(type, id).get('isDirty') === true; | ||
_saveHasManyRecord: function(store, relationship, parentRef, id) { | ||
var adapter = this; | ||
var ref = this._getRelationshipRef(parentRef, relationship.key, id); | ||
var record = store.getById(relationship.type, id); | ||
var isEmbedded = relationship.options.embedded === true; | ||
var valueToSave = isEmbedded ? record.serialize({ includeId: false }) : true; | ||
return new Promise(function(resolve, reject) { | ||
var _saveHandler = function(error) { | ||
if (error) { | ||
if (typeof error === 'object') { | ||
error.location = ref.toString(); | ||
} | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}; | ||
if (isEmbedded) { | ||
ref.update(valueToSave, _saveHandler); | ||
} | ||
else { | ||
ref.set(valueToSave, _saveHandler); | ||
} | ||
}); | ||
if (isEmbedded) { | ||
return this.updateRecord(store, relationship.type, record, ref); | ||
} | ||
return toPromise(ref.set, ref, [true]); | ||
}, | ||
@@ -578,17 +595,4 @@ | ||
_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); | ||
}); | ||
return toPromise(ref.remove, ref, [], ref.toString()); | ||
}, | ||
@@ -600,25 +604,5 @@ | ||
_saveBelongsToRecord: function(store, type, relationship, id, parentRef) { | ||
var adapter = this; | ||
var ref = parentRef.child(relationship.key); | ||
var record = store.getById(relationship.type, id); | ||
var isEmbedded = relationship.options.embedded === true; | ||
var valueToSave = isEmbedded ? record.serialize({ includeId: true }) : true; | ||
return new Promise(function(resolve, reject) { | ||
var _saveHandler = function(error) { | ||
if (error) { | ||
if (typeof error === 'object') { | ||
error.location = ref.toString(); | ||
} | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}; | ||
if (isEmbedded) { | ||
ref.update(valueToSave, _saveHandler); | ||
} | ||
else { | ||
ref.set(valueToSave, _saveHandler); | ||
} | ||
}); | ||
return this.updateRecord(store, relationship.type, record, ref); | ||
}, | ||
@@ -630,13 +614,4 @@ | ||
deleteRecord: function(store, type, record) { | ||
var adapter = this; | ||
var ref = this._getRef(type, record.get('id')); | ||
return new Promise(function(resolve, reject) { | ||
ref.remove(function(err) { | ||
if (err) { | ||
adapter._enqueue(reject, [err]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}); | ||
}, fmt('DS: FirebaseAdapter#deleteRecord %@ to %@', [type, ref.toString()])); | ||
return toPromise(ref.remove, ref); | ||
}, | ||
@@ -702,5 +677,10 @@ | ||
_enqueue: function(callback, args) { | ||
var length = this._queue.push([callback, args]); | ||
if (length === 1) { | ||
this._queueScheduleFlush(); | ||
//Only do the queueing if we scheduled a delay | ||
if (this._queueFlushDelay) { | ||
var length = this._queue.push([callback, args]); | ||
if (length === 1) { | ||
this._queueScheduleFlush(); | ||
} | ||
} else { | ||
callback.apply(null, args); | ||
} | ||
@@ -707,0 +687,0 @@ }, |
@@ -6,6 +6,6 @@ /*! | ||
* | ||
* EmberFire 1.2.5 | ||
* EmberFire 1.2.6 | ||
* https://github.com/firebase/emberfire/ | ||
* License: MIT | ||
*/ | ||
!function(){"use strict";if(void 0!==window.DS){var a=Ember.Namespace.create({VERSION:"1.2.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(f,g){if(!Ember.isNone(c)&&!Ember.isNone(c[f])&&g.options.embedded===!0){var h,i,j=d[f],k=[];if("belongsTo"===g.kind){if("string"!=typeof j.id)throw new Error(e('Embedded relationship "%@" of "%@" must contain an "id" property in the payload',[g.type.typeKey,b]));k.push(j),d[f]=j.id}else{for(h in j)i=j[h],null!==i&&"object"==typeof i&&(i.id=h),k.push(i);d[f]=Ember.keys(d[f])}a.pushMany(g.type,k)}}),d},extractArray:function(a,b,d){return c(d,function(c){return this.extractSingle(a,b,c)},this)},serializeHasMany:function(a,b,c){var d=c.key,e=this.keyForRelationship?this.keyForRelationship(d,"hasMany"):d,f=DS.RelationshipChange.determineRelationshipType(a.constructor,c),g=["manyToNone","manyToMany","manyToOne"];g.indexOf(f)>-1&&(b[e]=Ember.A(a.get(d)).mapBy("id"))}}),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)&&f._enqueue(function(){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 e=[];g&&Ember.run(null,g.resolve),null===a.val()?d._enqueue(b,[e]):(a.forEach(function(a){var b=d._assignIdToPayload(a);d._updateRecordCacheForType(c,b),e.push(b)}),d._enqueue(b,[e]))},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._getRef(c,d.id),h=Ember.get(f._recordCacheForType,e("%@.%@",[c.typeKey,d.get("id")]))||{};return this._getSerializedRecord(d).then(function(i){return new b(function(b,j){var k=Ember.A();d.eachRelationship(function(b,d){var e;switch(d.kind){case"hasMany":Ember.isArray(i[b])&&(e=f._saveHasManyRelationship(a,c,d,i[b],g,h),k.push(e),delete i[b]);break;case"belongsTo":"undefined"==typeof i[b]||null===i[b]||""===i[b]?delete i[b]:d.options.embedded===!0&&(e=f._saveBelongsToRecord(a,c,d,i[b],g),k.push(e),delete i[b])}}),Ember.RSVP.allSettled(k).then(function(a){a=Ember.A(a);var h=Ember.A(a.filterBy("state","rejected"));if(0!==h.get("length")){var k=new Error(e("Some errors were encountered while saving %@ %@",[c,d.id]));k.errors=h.mapBy("reason"),f._enqueue(j,[k])}g.update(i,function(a){a?f._enqueue(j,[a]):f._enqueue(b)})})})},e("DS: FirebaseAdapter#updateRecord %@ to %@",[c,g.toString()]))},_getSerializedRecord:function(a){var c=a.serialize({includeId:!1}),d=[];return a.eachRelationship(function(e,f){switch(f.kind){case"hasMany":d.push(b.cast(a.get(e)).then(function(a){c[e]=Ember.A(a).mapBy("id")}))}}),Ember.RSVP.all(d).then(function(){return c})},_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)});l=Ember.A(l).map(function(b){return h._removeHasManyRecord(a,c,f,b)});var 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)})},_saveBelongsToRecord:function(a,c,d,e,f){var g=this,h=f.child(d.key),i=a.getById(d.type,e),j=d.options.embedded===!0,k=j?i.serialize({includeId:!0}):!0;return new b(function(a,b){var c=function(c){c?("object"==typeof c&&(c.location=h.toString()),g._enqueue(b,[c])):g._enqueue(a)};j?h.update(k,c):h.set(k,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",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}(); | ||
!function(){"use strict";if(void 0!==window.DS){var a=Ember.Namespace.create({VERSION:"1.2.6"});Ember.libraries&&Ember.libraries.registerCoreLibrary("EmberFire",a.VERSION),DS.Store.reopen({push:function(a,b,c){var d=this._super(a,b,c),e=this.adapterFor(d.constructor);return e.recordWasPushed&&e.recordWasPushed(this,a,d),d},recordWillUnload:function(a){var b=this.adapterFor(a.constructor);b.recordWillUnload&&b.recordWillUnload(this,a)}}),DS.Model.reopen({unloadRecord:function(){return this.store.recordWillUnload(this),this._super()}});var b=Ember.RSVP.Promise,c=Ember.EnumerableUtils.map,d=Ember.EnumerableUtils.forEach,e=Ember.String.fmt,f=function(a,c,d,e){var f=d||[];return new b(function(b,d){var g=function(a){a?(e&&"object"==typeof a&&(a.location=e),d(a)):b()};f.push(g),a.apply(c,f)})};DS.FirebaseSerializer=DS.JSONSerializer.extend(Ember.Evented,{_normalizeNumberIDs:function(a,b){var c=[];a[b][0]===!0&&c.push("0"),a[b][1]===!0&&c.push("1"),a[b]=c},normalizeHasMany:function(a,b,c){var d=c.key;if("object"!=typeof b[d]||Ember.isArray(b[d])){if(Ember.isArray(b[d])&&b[d].length<3&&(b[d][0]===!0||b[d][1]===!0))this._normalizeNumberIDs(b,d);else if(Ember.isArray(b[d]))throw new Error(e('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }',[a.toString(),c.kind,c.type.typeKey,d,c.type.typeKey]))}else b[d]=Ember.keys(b[d])},normalizeEmbeddedHasMany:function(a,b,c){var d,e=c.key,f=b[e];if(b[e]){for(d in f){var g=f[d];null!==g&&"object"==typeof g&&(g.id=d),this.store.push(c.type,this.normalize(c.type,g))}b[e]=Ember.keys(b[e])}},normalizeEmbeddedBelongsTo:function(a,b,c){var d=c.key;if(b[d]){var f=b[d];if("string"!=typeof f.id)throw new Error(e('Embedded relationship "%@" of "%@" must contain an "id" property in the payload',[c.type.typeKey,a]));this.store.push(c.type,this.normalize(c.type,f)),b[d]=f.id}},normalizeBelongsTo:Ember.K,normalize:function(a,b){var c=this;return a.eachRelationship(function(d,e){"hasMany"===e.kind?e.options.embedded?c.normalizeEmbeddedHasMany(a,b,e):c.normalizeHasMany(a,b,e):e.options.embedded?c.normalizeEmbeddedBelongsTo(a,b,e):c.normalizeBelongsTo(a,b,e)}),this._super.apply(this,arguments)},extractSingle:function(a,b,c){return this.normalize(b,c)},extractArray:function(a,b,d){return c(d,function(c){return this.extractSingle(a,b,c)},this)},serializeHasMany:function(a,b,c){var d=c.key,e=this.keyForRelationship?this.keyForRelationship(d,"hasMany"):d;b[e]=Ember.A(a.get(d)).mapBy("id")},serializeBelongsTo:function(a,b,c){this._super(a,b,c);{var d=c.key;this.keyForRelationship?this.keyForRelationship(d,"belongsTo"):c.key}("undefined"==typeof b[d]||""===b[d])&&delete b[d]}}),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=this._getRef(c,d);a.serializerFor(c)}return new b(function(a,b){g.once("value",function(h){var i=f._assignIdToPayload(h);if(f._updateRecordCacheForType(c,i),null===i){var j=new Error(e("no record was found at %@",[g.toString()]));j.recordId=d,b(j)}else a(i)},function(a){b(a)})},e("DS: FirebaseAdapter#find %@ to %@",[c,g.toString()]))},recordWasPushed:function(a,b,c){c.__listening||this.listenForChanges(a,b,c)},recordWillUnload:function(a,b){var c=this._getRef(b.typeKey,b.get("id"));c.off("value")},listenForChanges:function(a,b,c){c.__listening=!0;var d=a.serializerFor(b),e=this,f=this._getRef(b,c.get("id")),g=!1;f.on("value",function(c){g&&e._handleChildValue(a,b,d,c),g=!0})},findMany:void 0,findAll:function(a,c){var d=this,f=this._getRef(c);return new b(function(b,e){f.once("value",function(e){d._findAllHasEventsForType(c)||d._findAllAddEventListeners(a,c,f);var g=[];e.forEach(function(a){var b=d._assignIdToPayload(a);d._updateRecordCacheForType(c,b),g.push(b)}),b(g)},function(a){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=this,e=a.serializerFor(b);c.on("child_added",function(c){a.hasRecordForId(b,c.name())||d._handleChildValue(a,b,e,c)})},_handleChildValue:function(a,b,c,d){if(!a.isDestroying){var e=d.val();if(null===e){var f=d.name(),g=a.getById(b,f);g.get("isDeleted")||g.deleteRecord()}else{var h=this._assignIdToPayload(d);this._enqueue(function(){a.push(b,c.extractSingle(a,b,h))})}}},createRecord:function(a,b,c){var d=this;return this.updateRecord(a,b,c).then(function(){d.listenForChanges(a,b,c)})},updateRecord:function(a,c,d,f){var g=this,h=f||this._getRef(c,d.id),i=Ember.get(g._recordCacheForType,e("%@.%@",[c.typeKey,d.get("id")]))||{},j=d.serialize({includeId:!1});return new b(function(b,f){var k=Ember.A();d.eachRelationship(function(b,d){var e;"hasMany"===d.kind?j[b]&&(e=g._saveHasManyRelationship(a,c,d,j[b],h,i),k.push(e),delete j[b]):d.options.embedded===!0&&j[b]&&(e=g._saveBelongsToRecord(a,c,d,j[b],h),k.push(e),delete j[b])});var l=Ember.RSVP.allSettled(k),m=g._updateRecord(h,j);Ember.RSVP.hashSettled({relationships:l,record:m}).then(function(a){var g=Ember.A(a.relationships.value).filterBy("state","rejected");if("rejected"===a.record.state&&g.push(a.record),0!==g.length){var h=new Error(e("Some errors were encountered while saving %@ %@",[c,d.id]));h.errors=g.mapBy("reason"),f(h)}else b()})},e("DS: FirebaseAdapter#updateRecord %@ to %@",[c,h.toString()]))},_updateRecord:function(a,b){return f(a.update,a,[b])},_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=[],k=d.filter(function(a){return!i.contains(a)});j=d.filter(function(b){var d=c.type;return a.hasRecordForId(d,b)&&a.getById(d,b).get("isDirty")===!0}),j=Ember.A(j.concat(k)).uniq().map(function(b){return h._saveHasManyRecord(a,c,f,b)});var l=i.filter(function(a){return!d.contains(a)});l=Ember.A(l).map(function(b){return h._removeHasManyRecord(a,c,f,b)});var m=j.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,b,c,d){var e=this._getRelationshipRef(c,b.key,d),g=a.getById(b.type,d),h=b.options.embedded===!0;return h?this.updateRecord(a,b.type,g,e):f(e.set,e,[!0])},_removeHasManyRecord:function(a,b,c,d){var e=this._getRelationshipRef(c,b.key,d);return f(e.remove,e,[],e.toString())},_saveBelongsToRecord:function(a,b,c,d,e){var f=e.child(c.key),g=a.getById(c.type,d);return this.updateRecord(a,c.type,g,f)},deleteRecord:function(a,b,c){var d=this._getRef(b,c.get("id"));return f(d.remove,d)},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){if(this._queueFlushDelay){var c=this._queue.push([a,b]);1===c&&this._queueScheduleFlush()}else a.apply(null,b)},_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",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}(); |
@@ -11,4 +11,4 @@ 'use strict'; | ||
afterInstall: function() { | ||
return this.addBowerPackageToProject('emberfire', '~1.2.5'); | ||
return this.addBowerPackageToProject('emberfire', '~1.2.6'); | ||
} | ||
}; |
{ | ||
"name": "emberfire", | ||
"description": "The officially supported Ember binding for Firebase", | ||
"version": "1.2.5", | ||
"version": "1.2.6", | ||
"author": "Firebase <support@firebase.com> (https://www.firebase.com/)", | ||
@@ -6,0 +6,0 @@ "homepage": "https://github.com/firebase/emberfire/", |
@@ -35,3 +35,3 @@ # EmberFire (Firebase + Ember Data) | ||
<!-- EmberFire --> | ||
<script src="https://cdn.firebase.com/libs/emberfire/1.2.5/emberfire.min.js"></script> | ||
<script src="https://cdn.firebase.com/libs/emberfire/1.2.6/emberfire.min.js"></script> | ||
``` | ||
@@ -38,0 +38,0 @@ |
@@ -26,2 +26,26 @@ /*! | ||
//Monkeypatch the store until ED gives us a good way to listen to push events | ||
DS.Store.reopen({ | ||
push: function(typeName, data, _partial) { | ||
var record = this._super(typeName, data, _partial); | ||
var adapter = this.adapterFor(record.constructor); | ||
if (adapter.recordWasPushed) { | ||
adapter.recordWasPushed(this, typeName, record); | ||
} | ||
return record; | ||
}, | ||
recordWillUnload: function(record) { | ||
var adapter = this.adapterFor(record.constructor); | ||
if (adapter.recordWillUnload) { | ||
adapter.recordWillUnload(this, record); | ||
} | ||
} | ||
}); | ||
DS.Model.reopen({ | ||
unloadRecord: function() { | ||
this.store.recordWillUnload(this); | ||
return this._super(); | ||
} | ||
}); | ||
// Shortcuts | ||
@@ -33,2 +57,21 @@ var Promise = Ember.RSVP.Promise; | ||
var toPromise = function(fn, context, _args, errorMsg) { | ||
var args = _args || []; | ||
return new Promise(function(resolve, reject) { | ||
var callback = function(error) { | ||
if (error) { | ||
if (errorMsg && typeof error === 'object') { | ||
error.location = errorMsg; | ||
} | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
}; | ||
args.push(callback); | ||
fn.apply(context, args); | ||
}); | ||
}; | ||
/** | ||
@@ -40,2 +83,61 @@ The Firebase serializer helps normalize relationships and can be extended on | ||
//We need to account for Firebase turning key/value pairs with ids '1' and '0' into arrays | ||
//See https://github.com/firebase/emberfire/issues/124 | ||
_normalizeNumberIDs: function(hash, key) { | ||
var newHash = []; | ||
if (hash[key][0] === true) { | ||
newHash.push('0'); | ||
} | ||
if (hash[key][1] === true) { | ||
newHash.push('1'); | ||
} | ||
hash[key] = newHash; | ||
}, | ||
normalizeHasMany: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
if (typeof hash[key] === 'object' && !Ember.isArray(hash[key])) { | ||
hash[key] = Ember.keys(hash[key]); | ||
} | ||
//We need to account for Firebase turning key/value pairs with ids '1' and '0' into arrays | ||
//See https://github.com/firebase/emberfire/issues/124 | ||
else if (Ember.isArray(hash[key]) && hash[key].length < 3 && (hash[key][0] === true || hash[key][1] === true)) { | ||
this._normalizeNumberIDs(hash, key); | ||
} | ||
else if (Ember.isArray(hash[key])) { | ||
throw new Error(fmt('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }', [type.toString(), relationship.kind, relationship.type.typeKey, key, relationship.type.typeKey])); | ||
} | ||
}, | ||
normalizeEmbeddedHasMany: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
var embeddedRecordPayload = hash[key]; | ||
var embeddedKey; | ||
if (!hash[key]) { | ||
return; | ||
} | ||
for (embeddedKey in embeddedRecordPayload) { | ||
var record = embeddedRecordPayload[embeddedKey]; | ||
if (record !== null && typeof record === 'object') { | ||
record.id = embeddedKey; | ||
} | ||
this.store.push(relationship.type, this.normalize(relationship.type, record)); | ||
} | ||
hash[key] = Ember.keys(hash[key]); | ||
}, | ||
normalizeEmbeddedBelongsTo: function(type, hash, relationship) { | ||
var key = relationship.key; | ||
if (!hash[key]) { | ||
return; | ||
} | ||
var embeddedRecordPayload = hash[key]; | ||
if (typeof embeddedRecordPayload.id !== 'string') { | ||
throw new Error(fmt('Embedded relationship "%@" of "%@" must contain an "id" property in the payload', [relationship.type.typeKey, type])); | ||
} | ||
this.store.push(relationship.type, this.normalize(relationship.type, embeddedRecordPayload)); | ||
hash[key] = embeddedRecordPayload.id; | ||
}, | ||
normalizeBelongsTo: Ember.K, | ||
/** | ||
@@ -47,10 +149,16 @@ Called after `extractSingle()`. This method checks the model | ||
normalize: function(type, hash) { | ||
var serializer = this; | ||
// Check if the model contains any 'hasMany' relationships | ||
type.eachRelationship(function(key, relationship) { | ||
if (relationship.kind === 'hasMany') { | ||
if (typeof hash[key] === 'object' && !Ember.isArray(hash[key]) && relationship.options.embedded !== true) { | ||
hash[key] = Ember.keys(hash[key]); | ||
if (relationship.options.embedded) { | ||
serializer.normalizeEmbeddedHasMany(type, hash, relationship); | ||
} else { | ||
serializer.normalizeHasMany(type, hash, relationship); | ||
} | ||
else if (Ember.isArray(hash[key])) { | ||
throw new Error(fmt('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }', [type.toString(), relationship.kind, relationship.type.typeKey, relationship.key, relationship.type.typeKey])); | ||
} else { | ||
if (relationship.options.embedded) { | ||
serializer.normalizeEmbeddedBelongsTo(type, hash, relationship); | ||
} else { | ||
serializer.normalizeBelongsTo(type, hash, relationship); | ||
} | ||
@@ -71,33 +179,3 @@ } | ||
extractSingle: function(store, type, payload) { | ||
var adapter = store.adapterFor(type); | ||
var normalizedPayload = this.normalize(type, payload); | ||
// Check for embedded records | ||
type.eachRelationship(function(key, relationship) { | ||
if (!Ember.isNone(payload) && !Ember.isNone(payload[key]) && relationship.options.embedded === true) { | ||
var embeddedKey; | ||
var embeddedRecordPayload = normalizedPayload[key]; | ||
var records = []; | ||
var record; | ||
if (relationship.kind === 'belongsTo') { | ||
if (typeof embeddedRecordPayload.id !== 'string') { | ||
throw new Error(fmt('Embedded relationship "%@" of "%@" must contain an "id" property in the payload', [relationship.type.typeKey, type])); | ||
} | ||
records.push(embeddedRecordPayload); | ||
normalizedPayload[key] = embeddedRecordPayload.id; | ||
} | ||
else { | ||
for (embeddedKey in embeddedRecordPayload) { | ||
record = embeddedRecordPayload[embeddedKey]; | ||
if (record !== null && typeof record === 'object') { | ||
record.id = embeddedKey; | ||
} | ||
records.push(record); | ||
} | ||
normalizedPayload[key] = Ember.keys(normalizedPayload[key]); | ||
} | ||
// Push the embedded records into the store | ||
store.pushMany(relationship.type, records); | ||
} | ||
}); | ||
return normalizedPayload; | ||
return this.normalize(type, payload); | ||
}, | ||
@@ -123,7 +201,11 @@ | ||
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key; | ||
var relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship); | ||
var relationshipTypes = ['manyToNone', 'manyToMany', 'manyToOne']; | ||
json[payloadKey] = Ember.A(record.get(key)).mapBy('id'); | ||
}, | ||
if (relationshipTypes.indexOf(relationshipType) > -1) { | ||
json[payloadKey] = Ember.A(record.get(key)).mapBy('id'); | ||
serializeBelongsTo: function(record, json, relationship) { | ||
this._super(record, json, relationship); | ||
var key = relationship.key; | ||
var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : relationship.key; | ||
if (typeof json[key] === "undefined" || json[key] === '') { | ||
delete json[key]; | ||
} | ||
@@ -211,3 +293,2 @@ } | ||
var adapter = this; | ||
var resolved = false; | ||
var ref = this._getRef(type, id); | ||
@@ -217,45 +298,16 @@ var serializer = store.serializerFor(type); | ||
return new Promise(function(resolve, reject) { | ||
ref.on('value', function(snapshot) { | ||
ref.once('value', function(snapshot) { | ||
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) { | ||
if (store.hasRecordForId(type, id)) { | ||
adapter._enqueue(function() { | ||
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]); | ||
} | ||
if (payload === null) { | ||
var error = new Error(fmt('no record was found at %@', [ref.toString()])); | ||
error.recordId = id; | ||
reject(error); | ||
} | ||
else { | ||
// If the snapshot is null, delete the record from the store | ||
if (payload === null && record && !record.get('isDeleted')) { | ||
adapter._enqueue(function() { | ||
store.getById(type, snapshot.name()).deleteRecord(); | ||
}); | ||
} | ||
// Otherwise push it into the store | ||
else if (payload !== null) { | ||
adapter._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
} | ||
resolve(payload); | ||
} | ||
}, | ||
function(err) { | ||
// Only called in cases of permission related errors. | ||
if (!resolved) { | ||
adapter._enqueue(reject, [err]); | ||
} | ||
reject(err); | ||
}); | ||
@@ -265,22 +317,31 @@ }, fmt('DS: FirebaseAdapter#find %@ to %@', [type, ref.toString()])); | ||
recordWasPushed: function(store, type, record) { | ||
if (!record.__listening) { | ||
this.listenForChanges(store, type, record); | ||
} | ||
}, | ||
recordWillUnload: function(store, record) { | ||
var ref = this._getRef(record.typeKey, record.get('id')); | ||
ref.off('value'); | ||
}, | ||
listenForChanges: function(store, type, record) { | ||
record.__listening = true; | ||
var serializer = store.serializerFor(type); | ||
var adapter = this; | ||
var ref = this._getRef(type, record.get('id')); | ||
var called = false; | ||
ref.on('value', function(snapshot) { | ||
if (called) { | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
} | ||
called = true; | ||
}); | ||
}, | ||
/** | ||
findMany | ||
*/ | ||
findMany: function(store, type, ids) { | ||
var promises = map(ids, function(id) { | ||
return this.find(store, type, id); | ||
}, this); | ||
return Ember.RSVP.allSettled(promises).then(function(promises) { | ||
// Remove any records that couldn't be fetched | ||
promises = Ember.A(promises); | ||
forEach(promises.filterBy('state', 'rejected'), function(promise) { | ||
var recordId = promise.reason.recordId; | ||
if (store.hasRecordForId(type, recordId)) { | ||
var record = store.getById(type, recordId); | ||
store.dematerializeRecord(record); | ||
} | ||
}); | ||
return Ember.A(promises.filterBy('state', 'fulfilled')).mapBy('value'); | ||
}); | ||
}, | ||
findMany: undefined, | ||
@@ -302,24 +363,15 @@ /** | ||
// Listen for child events on the type | ||
var valueEventTriggered; | ||
if (!adapter._findAllHasEventsForType(type)) { | ||
valueEventTriggered = adapter._findAllAddEventListeners(store, type, ref); | ||
} | ||
ref.once('value', function(snapshot) { | ||
if (!adapter._findAllHasEventsForType(type)) { | ||
adapter._findAllAddEventListeners(store, type, ref); | ||
} | ||
var results = []; | ||
if (valueEventTriggered) { | ||
Ember.run(null, valueEventTriggered.resolve); | ||
} | ||
if (snapshot.val() === null) { | ||
adapter._enqueue(resolve, [results]); | ||
} | ||
else { | ||
snapshot.forEach(function(childSnapshot) { | ||
var payload = adapter._assignIdToPayload(childSnapshot); | ||
adapter._updateRecordCacheForType(type, payload); | ||
results.push(payload); | ||
}); | ||
adapter._enqueue(resolve, [results]); | ||
} | ||
snapshot.forEach(function(childSnapshot) { | ||
var payload = adapter._assignIdToPayload(childSnapshot); | ||
adapter._updateRecordCacheForType(type, payload); | ||
results.push(payload); | ||
}); | ||
resolve(results); | ||
}, function(error) { | ||
adapter._enqueue(reject, [error]); | ||
reject(error); | ||
}); | ||
@@ -349,32 +401,10 @@ }, fmt('DS: FirebaseAdapter#findAll %@ to %@', [type, ref.toString()])); | ||
var deferred = Ember.RSVP.defer(); | ||
var adapter = this; | ||
var serializer = store.serializerFor(type); | ||
var valueEventTriggered = false; | ||
deferred.promise.then(function() { | ||
valueEventTriggered = true; | ||
}); | ||
ref.on('child_added', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
}); | ||
ref.on('child_changed', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
}); | ||
ref.on('child_removed', function(snapshot) { | ||
if (!valueEventTriggered) { return; } | ||
var record = store.getById(type, snapshot.name()); | ||
if (record && !record.get('isDeleted')) { | ||
adapter._enqueue(function() { | ||
store.deleteRecord(record); | ||
}); | ||
if (!store.hasRecordForId(type, snapshot.name())) { | ||
adapter._handleChildValue(store, type, serializer, snapshot); | ||
} | ||
}); | ||
return deferred; | ||
}, | ||
@@ -386,6 +416,21 @@ | ||
_handleChildValue: function(store, type, serializer, snapshot) { | ||
var payload = this._assignIdToPayload(snapshot); | ||
this._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
//No idea why we need this, we are alredy turning off the callback by | ||
//calling ref.off in recordWillUnload. Something is fishy here | ||
if (store.isDestroying) { | ||
return; | ||
} | ||
var value = snapshot.val(); | ||
if (value === null) { | ||
var id = snapshot.name(); | ||
var record = store.getById(type, id); | ||
//TODO refactor using ED | ||
if (!record.get('isDeleted')) { | ||
record.deleteRecord(); | ||
} | ||
} else { | ||
var payload = this._assignIdToPayload(snapshot); | ||
this._enqueue(function() { | ||
store.push(type, serializer.extractSingle(store, type, payload)); | ||
}); | ||
} | ||
}, | ||
@@ -398,3 +443,6 @@ | ||
createRecord: function(store, type, record) { | ||
return this.updateRecord(store, type, record); | ||
var adapter = this; | ||
return this.updateRecord(store, type, record).then(function() { | ||
adapter.listenForChanges(store, type, record); | ||
}); | ||
}, | ||
@@ -410,76 +458,57 @@ | ||
any relationships have been successfully saved to Firebase. | ||
We take an optional record reference, in order for this method to be usable | ||
for saving nested records as well. | ||
*/ | ||
updateRecord: function(store, type, record) { | ||
updateRecord: function(store, type, record, _recordRef) { | ||
var adapter = this; | ||
var recordRef = this._getRef(type, record.id); | ||
var recordRef = _recordRef || this._getRef(type, record.id); | ||
var recordCache = Ember.get(adapter._recordCacheForType, fmt('%@.%@', [type.typeKey, record.get('id')])) || {}; | ||
return this._getSerializedRecord(record).then(function(serializedRecord) { | ||
return new Promise(function(resolve, reject) { | ||
var savedRelationships = Ember.A(); | ||
record.eachRelationship(function(key, relationship) { | ||
var save ; | ||
switch (relationship.kind) { | ||
case 'hasMany': | ||
if (Ember.isArray(serializedRecord[key])) { | ||
save = adapter._saveHasManyRelationship(store, type, relationship, serializedRecord[key], recordRef, recordCache); | ||
savedRelationships.push(save); | ||
// Remove the relationship from the serializedRecord | ||
delete serializedRecord[key]; | ||
} | ||
break; | ||
case 'belongsTo': | ||
if (typeof serializedRecord[key] === "undefined" || serializedRecord[key] === null || serializedRecord[key] === '') { | ||
delete serializedRecord[key]; | ||
} | ||
else if (relationship.options.embedded === true) { | ||
save = adapter._saveBelongsToRecord(store, type, relationship, serializedRecord[key], recordRef); | ||
savedRelationships.push(save); | ||
delete serializedRecord[key]; | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
}); | ||
// Save the record once all the relationships have saved | ||
Ember.RSVP.allSettled(savedRelationships).then(function(savedRelationships) { | ||
savedRelationships = Ember.A(savedRelationships); | ||
var rejected = Ember.A(savedRelationships.filterBy('state', 'rejected')); | ||
// Throw an error if any of the relationships failed to save | ||
if (rejected.get('length') !== 0) { | ||
var error = new Error(fmt('Some errors were encountered while saving %@ %@', [type, record.id])); | ||
error.errors = rejected.mapBy('reason'); | ||
adapter._enqueue(reject, [error]); | ||
} | ||
recordRef.update(serializedRecord, function(error) { | ||
if (error) { | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
var serializedRecord = record.serialize({includeId:false}); | ||
return new Promise(function(resolve, reject) { | ||
var savedRelationships = Ember.A(); | ||
record.eachRelationship(function(key, relationship) { | ||
var save; | ||
if (relationship.kind === 'hasMany') { | ||
if (serializedRecord[key]) { | ||
save = adapter._saveHasManyRelationship(store, type, relationship, serializedRecord[key], recordRef, recordCache); | ||
savedRelationships.push(save); | ||
// Remove the relationship from the serializedRecord because otherwise we would clobber the entire hasMany | ||
delete serializedRecord[key]; | ||
} | ||
}); | ||
}); | ||
} else { | ||
if (relationship.options.embedded === true && serializedRecord[key]) { | ||
save = adapter._saveBelongsToRecord(store, type, relationship, serializedRecord[key], recordRef); | ||
savedRelationships.push(save); | ||
delete serializedRecord[key]; | ||
} | ||
} | ||
}); | ||
var relationshipsPromise = Ember.RSVP.allSettled(savedRelationships); | ||
var recordPromise = adapter._updateRecord(recordRef, serializedRecord); | ||
Ember.RSVP.hashSettled({relationships: relationshipsPromise, record: recordPromise}).then(function(promises) { | ||
var rejected = Ember.A(promises.relationships.value).filterBy('state', 'rejected'); | ||
if (promises.record.state === 'rejected') { | ||
rejected.push(promises.record); | ||
} | ||
// Throw an error if any of the relationships failed to save | ||
if (rejected.length !== 0) { | ||
var error = new Error(fmt('Some errors were encountered while saving %@ %@', [type, record.id])); | ||
error.errors = rejected.mapBy('reason'); | ||
reject(error); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}, fmt('DS: FirebaseAdapter#updateRecord %@ to %@', [type, recordRef.toString()])); | ||
}, | ||
/** | ||
Return a serialized version of the record | ||
*/ | ||
_getSerializedRecord: function(record) { | ||
var json = record.serialize({ includeId: false }); | ||
var relationships = []; | ||
record.eachRelationship(function(key, relationship) { | ||
switch (relationship.kind) { | ||
case 'hasMany': | ||
relationships.push(Promise.cast(record.get(key)).then(function(hasManyRecords) { | ||
json[key] = Ember.A(hasManyRecords).mapBy('id'); | ||
})); | ||
break; | ||
} | ||
}); | ||
return Ember.RSVP.all(relationships).then(function() { | ||
return json; | ||
}); | ||
//Just update the record itself without caring for the relationships | ||
_updateRecord: function(recordRef, serializedRecord) { | ||
return toPromise(recordRef.update, recordRef, [serializedRecord]); | ||
}, | ||
@@ -497,3 +526,5 @@ | ||
var idsCache = Ember.A(recordCache[relationship.key]); | ||
ids = Ember.A(ids); | ||
ids = Ember.A(ids); | ||
var dirtyRecords = []; | ||
// Added | ||
@@ -503,4 +534,5 @@ var addedRecords = ids.filter(function(id) { | ||
}); | ||
// Dirty | ||
var dirtyRecords = ids.filter(function(id) { | ||
dirtyRecords = ids.filter(function(id) { | ||
var type = relationship.type; | ||
@@ -546,25 +578,10 @@ return store.hasRecordForId(type, id) && store.getById(type, id).get('isDirty') === true; | ||
_saveHasManyRecord: function(store, relationship, parentRef, id) { | ||
var adapter = this; | ||
var ref = this._getRelationshipRef(parentRef, relationship.key, id); | ||
var record = store.getById(relationship.type, id); | ||
var isEmbedded = relationship.options.embedded === true; | ||
var valueToSave = isEmbedded ? record.serialize({ includeId: false }) : true; | ||
return new Promise(function(resolve, reject) { | ||
var _saveHandler = function(error) { | ||
if (error) { | ||
if (typeof error === 'object') { | ||
error.location = ref.toString(); | ||
} | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}; | ||
if (isEmbedded) { | ||
ref.update(valueToSave, _saveHandler); | ||
} | ||
else { | ||
ref.set(valueToSave, _saveHandler); | ||
} | ||
}); | ||
if (isEmbedded) { | ||
return this.updateRecord(store, relationship.type, record, ref); | ||
} | ||
return toPromise(ref.set, ref, [true]); | ||
}, | ||
@@ -576,17 +593,4 @@ | ||
_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); | ||
}); | ||
return toPromise(ref.remove, ref, [], ref.toString()); | ||
}, | ||
@@ -598,25 +602,5 @@ | ||
_saveBelongsToRecord: function(store, type, relationship, id, parentRef) { | ||
var adapter = this; | ||
var ref = parentRef.child(relationship.key); | ||
var record = store.getById(relationship.type, id); | ||
var isEmbedded = relationship.options.embedded === true; | ||
var valueToSave = isEmbedded ? record.serialize({ includeId: true }) : true; | ||
return new Promise(function(resolve, reject) { | ||
var _saveHandler = function(error) { | ||
if (error) { | ||
if (typeof error === 'object') { | ||
error.location = ref.toString(); | ||
} | ||
adapter._enqueue(reject, [error]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}; | ||
if (isEmbedded) { | ||
ref.update(valueToSave, _saveHandler); | ||
} | ||
else { | ||
ref.set(valueToSave, _saveHandler); | ||
} | ||
}); | ||
return this.updateRecord(store, relationship.type, record, ref); | ||
}, | ||
@@ -628,13 +612,4 @@ | ||
deleteRecord: function(store, type, record) { | ||
var adapter = this; | ||
var ref = this._getRef(type, record.get('id')); | ||
return new Promise(function(resolve, reject) { | ||
ref.remove(function(err) { | ||
if (err) { | ||
adapter._enqueue(reject, [err]); | ||
} else { | ||
adapter._enqueue(resolve); | ||
} | ||
}); | ||
}, fmt('DS: FirebaseAdapter#deleteRecord %@ to %@', [type, ref.toString()])); | ||
return toPromise(ref.remove, ref); | ||
}, | ||
@@ -700,5 +675,10 @@ | ||
_enqueue: function(callback, args) { | ||
var length = this._queue.push([callback, args]); | ||
if (length === 1) { | ||
this._queueScheduleFlush(); | ||
//Only do the queueing if we scheduled a delay | ||
if (this._queueFlushDelay) { | ||
var length = this._queue.push([callback, args]); | ||
if (length === 1) { | ||
this._queueScheduleFlush(); | ||
} | ||
} else { | ||
callback.apply(null, args); | ||
} | ||
@@ -705,0 +685,0 @@ }, |
@@ -10,2 +10,2 @@ /*! | ||
*/ | ||
!function(){"use strict";if(void 0!==window.DS){var a=Ember.Namespace.create({VERSION:"0.0.0"});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(f,g){if(!Ember.isNone(c)&&!Ember.isNone(c[f])&&g.options.embedded===!0){var h,i,j=d[f],k=[];if("belongsTo"===g.kind){if("string"!=typeof j.id)throw new Error(e('Embedded relationship "%@" of "%@" must contain an "id" property in the payload',[g.type.typeKey,b]));k.push(j),d[f]=j.id}else{for(h in j)i=j[h],null!==i&&"object"==typeof i&&(i.id=h),k.push(i);d[f]=Ember.keys(d[f])}a.pushMany(g.type,k)}}),d},extractArray:function(a,b,d){return c(d,function(c){return this.extractSingle(a,b,c)},this)},serializeHasMany:function(a,b,c){var d=c.key,e=this.keyForRelationship?this.keyForRelationship(d,"hasMany"):d,f=DS.RelationshipChange.determineRelationshipType(a.constructor,c),g=["manyToNone","manyToMany","manyToOne"];g.indexOf(f)>-1&&(b[e]=Ember.A(a.get(d)).mapBy("id"))}}),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)&&f._enqueue(function(){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 e=[];g&&Ember.run(null,g.resolve),null===a.val()?d._enqueue(b,[e]):(a.forEach(function(a){var b=d._assignIdToPayload(a);d._updateRecordCacheForType(c,b),e.push(b)}),d._enqueue(b,[e]))},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._getRef(c,d.id),h=Ember.get(f._recordCacheForType,e("%@.%@",[c.typeKey,d.get("id")]))||{};return this._getSerializedRecord(d).then(function(i){return new b(function(b,j){var k=Ember.A();d.eachRelationship(function(b,d){var e;switch(d.kind){case"hasMany":Ember.isArray(i[b])&&(e=f._saveHasManyRelationship(a,c,d,i[b],g,h),k.push(e),delete i[b]);break;case"belongsTo":"undefined"==typeof i[b]||null===i[b]||""===i[b]?delete i[b]:d.options.embedded===!0&&(e=f._saveBelongsToRecord(a,c,d,i[b],g),k.push(e),delete i[b])}}),Ember.RSVP.allSettled(k).then(function(a){a=Ember.A(a);var h=Ember.A(a.filterBy("state","rejected"));if(0!==h.get("length")){var k=new Error(e("Some errors were encountered while saving %@ %@",[c,d.id]));k.errors=h.mapBy("reason"),f._enqueue(j,[k])}g.update(i,function(a){a?f._enqueue(j,[a]):f._enqueue(b)})})})},e("DS: FirebaseAdapter#updateRecord %@ to %@",[c,g.toString()]))},_getSerializedRecord:function(a){var c=a.serialize({includeId:!1}),d=[];return a.eachRelationship(function(e,f){switch(f.kind){case"hasMany":d.push(b.cast(a.get(e)).then(function(a){c[e]=Ember.A(a).mapBy("id")}))}}),Ember.RSVP.all(d).then(function(){return c})},_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)});l=Ember.A(l).map(function(b){return h._removeHasManyRecord(a,c,f,b)});var 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)})},_saveBelongsToRecord:function(a,c,d,e,f){var g=this,h=f.child(d.key),i=a.getById(d.type,e),j=d.options.embedded===!0,k=j?i.serialize({includeId:!0}):!0;return new b(function(a,b){var c=function(c){c?("object"==typeof c&&(c.location=h.toString()),g._enqueue(b,[c])):g._enqueue(a)};j?h.update(k,c):h.set(k,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",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}(); | ||
!function(){"use strict";if(void 0!==window.DS){var a=Ember.Namespace.create({VERSION:"0.0.0"});Ember.libraries&&Ember.libraries.registerCoreLibrary("EmberFire",a.VERSION),DS.Store.reopen({push:function(a,b,c){var d=this._super(a,b,c),e=this.adapterFor(d.constructor);return e.recordWasPushed&&e.recordWasPushed(this,a,d),d},recordWillUnload:function(a){var b=this.adapterFor(a.constructor);b.recordWillUnload&&b.recordWillUnload(this,a)}}),DS.Model.reopen({unloadRecord:function(){return this.store.recordWillUnload(this),this._super()}});var b=Ember.RSVP.Promise,c=Ember.EnumerableUtils.map,d=Ember.EnumerableUtils.forEach,e=Ember.String.fmt,f=function(a,c,d,e){var f=d||[];return new b(function(b,d){var g=function(a){a?(e&&"object"==typeof a&&(a.location=e),d(a)):b()};f.push(g),a.apply(c,f)})};DS.FirebaseSerializer=DS.JSONSerializer.extend(Ember.Evented,{_normalizeNumberIDs:function(a,b){var c=[];a[b][0]===!0&&c.push("0"),a[b][1]===!0&&c.push("1"),a[b]=c},normalizeHasMany:function(a,b,c){var d=c.key;if("object"!=typeof b[d]||Ember.isArray(b[d])){if(Ember.isArray(b[d])&&b[d].length<3&&(b[d][0]===!0||b[d][1]===!0))this._normalizeNumberIDs(b,d);else if(Ember.isArray(b[d]))throw new Error(e('%@ relationship %@(\'%@\') must be a key/value map in Firebase. Example: { "%@": { "%@_id": true } }',[a.toString(),c.kind,c.type.typeKey,d,c.type.typeKey]))}else b[d]=Ember.keys(b[d])},normalizeEmbeddedHasMany:function(a,b,c){var d,e=c.key,f=b[e];if(b[e]){for(d in f){var g=f[d];null!==g&&"object"==typeof g&&(g.id=d),this.store.push(c.type,this.normalize(c.type,g))}b[e]=Ember.keys(b[e])}},normalizeEmbeddedBelongsTo:function(a,b,c){var d=c.key;if(b[d]){var f=b[d];if("string"!=typeof f.id)throw new Error(e('Embedded relationship "%@" of "%@" must contain an "id" property in the payload',[c.type.typeKey,a]));this.store.push(c.type,this.normalize(c.type,f)),b[d]=f.id}},normalizeBelongsTo:Ember.K,normalize:function(a,b){var c=this;return a.eachRelationship(function(d,e){"hasMany"===e.kind?e.options.embedded?c.normalizeEmbeddedHasMany(a,b,e):c.normalizeHasMany(a,b,e):e.options.embedded?c.normalizeEmbeddedBelongsTo(a,b,e):c.normalizeBelongsTo(a,b,e)}),this._super.apply(this,arguments)},extractSingle:function(a,b,c){return this.normalize(b,c)},extractArray:function(a,b,d){return c(d,function(c){return this.extractSingle(a,b,c)},this)},serializeHasMany:function(a,b,c){var d=c.key,e=this.keyForRelationship?this.keyForRelationship(d,"hasMany"):d;b[e]=Ember.A(a.get(d)).mapBy("id")},serializeBelongsTo:function(a,b,c){this._super(a,b,c);{var d=c.key;this.keyForRelationship?this.keyForRelationship(d,"belongsTo"):c.key}("undefined"==typeof b[d]||""===b[d])&&delete b[d]}}),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=this._getRef(c,d);a.serializerFor(c)}return new b(function(a,b){g.once("value",function(h){var i=f._assignIdToPayload(h);if(f._updateRecordCacheForType(c,i),null===i){var j=new Error(e("no record was found at %@",[g.toString()]));j.recordId=d,b(j)}else a(i)},function(a){b(a)})},e("DS: FirebaseAdapter#find %@ to %@",[c,g.toString()]))},recordWasPushed:function(a,b,c){c.__listening||this.listenForChanges(a,b,c)},recordWillUnload:function(a,b){var c=this._getRef(b.typeKey,b.get("id"));c.off("value")},listenForChanges:function(a,b,c){c.__listening=!0;var d=a.serializerFor(b),e=this,f=this._getRef(b,c.get("id")),g=!1;f.on("value",function(c){g&&e._handleChildValue(a,b,d,c),g=!0})},findMany:void 0,findAll:function(a,c){var d=this,f=this._getRef(c);return new b(function(b,e){f.once("value",function(e){d._findAllHasEventsForType(c)||d._findAllAddEventListeners(a,c,f);var g=[];e.forEach(function(a){var b=d._assignIdToPayload(a);d._updateRecordCacheForType(c,b),g.push(b)}),b(g)},function(a){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=this,e=a.serializerFor(b);c.on("child_added",function(c){a.hasRecordForId(b,c.name())||d._handleChildValue(a,b,e,c)})},_handleChildValue:function(a,b,c,d){if(!a.isDestroying){var e=d.val();if(null===e){var f=d.name(),g=a.getById(b,f);g.get("isDeleted")||g.deleteRecord()}else{var h=this._assignIdToPayload(d);this._enqueue(function(){a.push(b,c.extractSingle(a,b,h))})}}},createRecord:function(a,b,c){var d=this;return this.updateRecord(a,b,c).then(function(){d.listenForChanges(a,b,c)})},updateRecord:function(a,c,d,f){var g=this,h=f||this._getRef(c,d.id),i=Ember.get(g._recordCacheForType,e("%@.%@",[c.typeKey,d.get("id")]))||{},j=d.serialize({includeId:!1});return new b(function(b,f){var k=Ember.A();d.eachRelationship(function(b,d){var e;"hasMany"===d.kind?j[b]&&(e=g._saveHasManyRelationship(a,c,d,j[b],h,i),k.push(e),delete j[b]):d.options.embedded===!0&&j[b]&&(e=g._saveBelongsToRecord(a,c,d,j[b],h),k.push(e),delete j[b])});var l=Ember.RSVP.allSettled(k),m=g._updateRecord(h,j);Ember.RSVP.hashSettled({relationships:l,record:m}).then(function(a){var g=Ember.A(a.relationships.value).filterBy("state","rejected");if("rejected"===a.record.state&&g.push(a.record),0!==g.length){var h=new Error(e("Some errors were encountered while saving %@ %@",[c,d.id]));h.errors=g.mapBy("reason"),f(h)}else b()})},e("DS: FirebaseAdapter#updateRecord %@ to %@",[c,h.toString()]))},_updateRecord:function(a,b){return f(a.update,a,[b])},_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=[],k=d.filter(function(a){return!i.contains(a)});j=d.filter(function(b){var d=c.type;return a.hasRecordForId(d,b)&&a.getById(d,b).get("isDirty")===!0}),j=Ember.A(j.concat(k)).uniq().map(function(b){return h._saveHasManyRecord(a,c,f,b)});var l=i.filter(function(a){return!d.contains(a)});l=Ember.A(l).map(function(b){return h._removeHasManyRecord(a,c,f,b)});var m=j.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,b,c,d){var e=this._getRelationshipRef(c,b.key,d),g=a.getById(b.type,d),h=b.options.embedded===!0;return h?this.updateRecord(a,b.type,g,e):f(e.set,e,[!0])},_removeHasManyRecord:function(a,b,c,d){var e=this._getRelationshipRef(c,b.key,d);return f(e.remove,e,[],e.toString())},_saveBelongsToRecord:function(a,b,c,d,e){var f=e.child(c.key),g=a.getById(c.type,d);return this.updateRecord(a,c.type,g,f)},deleteRecord:function(a,b,c){var d=this._getRef(b,c.get("id"));return f(d.remove,d)},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){if(this._queueFlushDelay){var c=this._queue.push([a,b]);1===c&&this._queueScheduleFlush()}else a.apply(null,b)},_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",initialize:function(a,b){b.register("adapter:-firebase",DS.FirebaseAdapter),b.register("serializer:-firebase",DS.FirebaseSerializer)}})})}}(); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
479962
11
10449