dist/globals/ember-data.prod.js in ember-data-source-2.10.0 vs dist/globals/ember-data.prod.js in ember-data-source-2.11.0.beta.1
- old
+ new
@@ -4,11 +4,11 @@
/*!
* @overview Ember Data
* @copyright Copyright 2011-2016 Tilde Inc. and contributors.
* Portions Copyright 2011 LivingSocial Inc.
* @license Licensed under MIT license (see license.js)
- * @version 2.10.0
+ * @version 2.11.0-beta.1
*/
var loader, define, requireModule, require, requirejs;
(function(global) {
@@ -640,15 +640,15 @@
urlForDeleteRecord(id, modelName, snapshot) {
return this._super(...arguments) + '/destroy';
}
});
```
- * @method urlForDeleteRecord
- * @param {String} id
- * @param {String} modelName
- * @param {DS.Snapshot} snapshot
- * @return {String} url
+ @method urlForDeleteRecord
+ @param {String} id
+ @param {String} modelName
+ @param {DS.Snapshot} snapshot
+ @return {String} url
*/
urlForDeleteRecord: function (id, modelName, snapshot) {
return this._buildURL(modelName, id);
},
@@ -789,20 +789,20 @@
function debugSeal() {
return _ember.default.debugSeal.apply(_ember.default, arguments);
}
- function checkPolymorphic(typeClass, addedRecord) {
- if (typeClass.__isMixin) {
+ function checkPolymorphic(modelClass, addedModelClass) {
+ if (modelClass.__isMixin) {
//TODO Need to do this in order to support mixins, should convert to public api
//once it exists in Ember
- return typeClass.__mixin.detect(addedRecord.type.PrototypeMixin);
+ return modelClass.__mixin.detect(addedModelClass.PrototypeMixin);
}
if (_ember.default.MODEL_FACTORY_INJECTIONS) {
- typeClass = typeClass.superclass;
+ modelClass = modelClass.superclass;
}
- return typeClass.detect(addedRecord.type);
+ return modelClass.detect(addedModelClass);
}
/*
Assert that `addedRecord` has a valid type so it can be added to the
relationship of the `record`.
@@ -814,26 +814,25 @@
be an InternalModel and the `relationshipMeta` needs to be the meta
information about the relationship, retrieved via
`record.relationshipFor(key)`.
@method assertPolymorphicType
- @param {InternalModel} record
+ @param {InternalModel} internalModel
@param {RelationshipMeta} relationshipMeta retrieved via
`record.relationshipFor(key)`
@param {InternalModel} addedRecord record which
should be added/set for the relationship
*/
- function assertPolymorphicType(record, relationshipMeta, addedRecord) {
- var addedType = addedRecord.type.modelName;
- var recordType = record.type.modelName;
+ function assertPolymorphicType(parentInternalModel, relationshipMeta, addedInternalModel) {
+ var addedModelName = addedInternalModel.modelName;
+ var parentModelName = parentInternalModel.modelName;
var key = relationshipMeta.key;
- var typeClass = record.store.modelFor(relationshipMeta.type);
+ var relationshipClass = parentInternalModel.store.modelFor(relationshipMeta.type);
+ var assertionMessage = 'You cannot add a record of modelClass \'' + addedModelName + '\' to the \'' + parentModelName + '.' + key + '\' relationship (only \'' + relationshipClass.modelName + '\' allowed)';
- var assertionMessage = 'You cannot add a record of type \'' + addedType + '\' to the \'' + recordType + '.' + key + '\' relationship (only \'' + typeClass.modelName + '\' allowed)';
-
- assert(assertionMessage, checkPolymorphic(typeClass, addedRecord));
+ assert(assertionMessage, checkPolymorphic(relationshipClass, addedInternalModel.modelClass));
}
});
define('ember-data/-private/ext/date', ['exports', 'ember', 'ember-data/-private/debug'], function (exports, _ember, _emberDataPrivateDebug) {
/**
@@ -1225,38 +1224,42 @@
@for DS.Model
@private
*/
_debugInfo: function () {
var attributes = ['id'];
- var relationships = { belongsTo: [], hasMany: [] };
+ var relationships = {};
var expensiveProperties = [];
this.eachAttribute(function (name, meta) {
return attributes.push(name);
});
- this.eachRelationship(function (name, relationship) {
- relationships[relationship.kind].push(name);
- expensiveProperties.push(name);
- });
-
var groups = [{
name: 'Attributes',
properties: attributes,
expand: true
- }, {
- name: 'Belongs To',
- properties: relationships.belongsTo,
- expand: true
- }, {
- name: 'Has Many',
- properties: relationships.hasMany,
- expand: true
- }, {
+ }];
+
+ this.eachRelationship(function (name, relationship) {
+ var properties = relationships[relationship.kind];
+
+ if (properties === undefined) {
+ properties = relationships[relationship.kind] = [];
+ groups.push({
+ name: relationship.name,
+ properties: properties,
+ expand: true
+ });
+ }
+ properties.push(name);
+ expensiveProperties.push(name);
+ });
+
+ groups.push({
name: 'Flags',
properties: ['isLoaded', 'hasDirtyAttributes', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid']
- }];
+ });
return {
propertyInfo: {
// include all other mixins / properties (not just the grouped ones)
includeOtherProperties: true,
@@ -1323,11 +1326,10 @@
}
return false;
}
});
define("ember-data/-private/system/many-array", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/promise-proxies", "ember-data/-private/system/store/common"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemStoreCommon) {
-
var get = _ember.default.get;
var set = _ember.default.set;
/**
A `ManyArray` is a `MutableArray` that represents the contents of a has-many
@@ -1373,34 +1375,90 @@
@uses Ember.MutableArray, Ember.Evented
*/
exports.default = _ember.default.Object.extend(_ember.default.MutableArray, _ember.default.Evented, {
init: function () {
this._super.apply(this, arguments);
- this.currentState = _ember.default.A([]);
- },
- record: null,
+ /**
+ The loading state of this array
+ @property {Boolean} isLoaded
+ */
+ this.isLoaded = false;
+ this.length = 0;
- canonicalState: null,
- currentState: null,
+ /**
+ Used for async `hasMany` arrays
+ to keep track of when they will resolve.
+ @property {Ember.RSVP.Promise} promise
+ @private
+ */
+ this.promise = null;
- length: 0,
+ /**
+ Metadata associated with the request for async hasMany relationships.
+ Example
+ Given that the server returns the following JSON payload when fetching a
+ hasMany relationship:
+ ```js
+ {
+ "comments": [{
+ "id": 1,
+ "comment": "This is the first comment",
+ }, {
+ // ...
+ }],
+ "meta": {
+ "page": 1,
+ "total": 5
+ }
+ }
+ ```
+ You can then access the metadata via the `meta` property:
+ ```js
+ post.get('comments').then(function(comments) {
+ var meta = comments.get('meta');
+ // meta.page => 1
+ // meta.total => 5
+ });
+ ```
+ @property {Object} meta
+ @public
+ */
+ this.meta = this.meta || null;
+ /**
+ `true` if the relationship is polymorphic, `false` otherwise.
+ @property {Boolean} isPolymorphic
+ @private
+ */
+ this.isPolymorphic = this.isPolymorphic || false;
+
+ /**
+ The relationship which manages this array.
+ @property {ManyRelationship} relationship
+ @private
+ */
+ this.relationship = this.relationship || null;
+
+ this.currentState = _ember.default.A([]);
+ this.flushCanonical(false);
+ },
+
objectAt: function (index) {
//Ember observers such as 'firstObject', 'lastObject' might do out of bounds accesses
if (!this.currentState[index]) {
return undefined;
}
+
return this.currentState[index].getRecord();
},
flushCanonical: function () {
- //TODO make this smarter, currently its plenty stupid
- var toSet = this.canonicalState.filter(function (internalModel) {
- return !internalModel.isDeleted();
- });
+ var isInitialized = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
+ var toSet = this.canonicalState;
+
//a hack for not removing new records
//TODO remove once we have proper diffing
var newRecords = this.currentState.filter(
// only add new records which are not yet in the canonical state of this
// relationship (a new record can be in the canonical state if it has
@@ -1415,86 +1473,31 @@
if ((0, _emberDataPrivateSystemStoreCommon._objectIsAlive)(this)) {
this.set('length', toSet.length);
}
this.currentState = toSet;
this.arrayContentDidChange(0, oldLength, this.length);
- //TODO Figure out to notify only on additions and maybe only if unloaded
- this.relationship.notifyHasManyChanged();
- this.record.updateRecordArrays();
- },
- /**
- `true` if the relationship is polymorphic, `false` otherwise.
- @property {Boolean} isPolymorphic
- @private
- */
- isPolymorphic: false,
- /**
- The loading state of this array
- @property {Boolean} isLoaded
- */
- isLoaded: false,
-
- /**
- The relationship which manages this array.
- @property {ManyRelationship} relationship
- @private
- */
- relationship: null,
-
- /**
- Metadata associated with the request for async hasMany relationships.
- Example
- Given that the server returns the following JSON payload when fetching a
- hasMany relationship:
- ```js
- {
- "comments": [{
- "id": 1,
- "comment": "This is the first comment",
- }, {
- // ...
- }],
- "meta": {
- "page": 1,
- "total": 5
- }
+ if (isInitialized) {
+ //TODO Figure out to notify only on additions and maybe only if unloaded
+ this.relationship.notifyHasManyChanged();
}
- ```
- You can then access the metadata via the `meta` property:
- ```js
- post.get('comments').then(function(comments) {
- var meta = comments.get('meta');
- // meta.page => 1
- // meta.total => 5
- });
- ```
- @property {Object} meta
- @public
- */
- meta: null,
+ },
internalReplace: function (idx, amt, objects) {
if (!objects) {
objects = [];
}
this.arrayContentWillChange(idx, amt, objects.length);
this.currentState.splice.apply(this.currentState, [idx, amt].concat(objects));
this.set('length', this.currentState.length);
this.arrayContentDidChange(idx, amt, objects.length);
- if (objects) {
- //TODO(Igor) probably needed only for unloaded records
- this.relationship.notifyHasManyChanged();
- }
- this.record.updateRecordArrays();
},
//TODO(Igor) optimize
internalRemoveRecords: function (records) {
- var index;
for (var i = 0; i < records.length; i++) {
- index = this.currentState.indexOf(records[i]);
+ var index = this.currentState.indexOf(records[i]);
this.internalReplace(index, 1);
}
},
//TODO(Igor) optimize
@@ -1504,28 +1507,21 @@
}
this.internalReplace(idx, 0, records);
},
replace: function (idx, amt, objects) {
- var records;
+ var records = undefined;
if (amt > 0) {
records = this.currentState.slice(idx, idx + amt);
this.get('relationship').removeRecords(records);
}
if (objects) {
this.get('relationship').addRecords(objects.map(function (obj) {
return obj._internalModel;
}), idx);
}
},
- /**
- Used for async `hasMany` arrays
- to keep track of when they will resolve.
- @property {Ember.RSVP.Promise} promise
- @private
- */
- promise: null,
/**
@method loadingRecordsCount
@param {Number} count
@private
@@ -1545,11 +1541,26 @@
this.trigger('didLoad');
}
},
/**
- @method reload
+ Reloads all of the records in the manyArray. If the manyArray
+ holds a relationship that was originally fetched using a links url
+ Ember Data will revisit the original links url to repopulate the
+ relationship.
+ If the manyArray holds the result of a `store.query()` reload will
+ re-run the original query.
+ Example
+ ```javascript
+ var user = store.peekRecord('user', 1)
+ user.login().then(function() {
+ user.get('permissions').then(function(permissions) {
+ return permissions.reload();
+ });
+ });
+ ```
+ @method reload
@public
*/
reload: function () {
return this.relationship.reload();
},
@@ -1570,14 +1581,14 @@
@method save
@return {DS.PromiseArray} promise
*/
save: function () {
var manyArray = this;
- var promiseLabel = "DS: ManyArray#save " + get(this, 'type');
- var promise = _ember.default.RSVP.all(this.invoke("save"), promiseLabel).then(function (array) {
+ var promiseLabel = 'DS: ManyArray#save ' + get(this, 'type');
+ var promise = _ember.default.RSVP.all(this.invoke("save"), promiseLabel).then(function () {
return manyArray;
- }, null, "DS: ManyArray#save return ManyArray");
+ }, null, 'DS: ManyArray#save return ManyArray');
return _emberDataPrivateSystemPromiseProxies.PromiseArray.create({ promise: promise });
},
/**
@@ -2194,18 +2205,40 @@
return !isEmpty(this.errorsFor(attribute));
}
});
});
define("ember-data/-private/system/model/internal-model", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/model/states", "ember-data/-private/system/relationships/state/create", "ember-data/-private/system/snapshot", "ember-data/-private/system/empty-object", "ember-data/-private/features", "ember-data/-private/utils", "ember-data/-private/system/references"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemModelStates, _emberDataPrivateSystemRelationshipsStateCreate, _emberDataPrivateSystemSnapshot, _emberDataPrivateSystemEmptyObject, _emberDataPrivateFeatures, _emberDataPrivateUtils, _emberDataPrivateSystemReferences) {
- exports.default = InternalModel;
+ var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
- var Promise = _ember.default.RSVP.Promise;
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
var get = _ember.default.get;
var set = _ember.default.set;
var copy = _ember.default.copy;
+ var EmberError = _ember.default.Error;
+ var inspect = _ember.default.inspect;
+ var isEmpty = _ember.default.isEmpty;
+ var isEqual = _ember.default.isEqual;
+ var emberRun = _ember.default.run;
+ var setOwner = _ember.default.setOwner;
+ var RSVP = _ember.default.RSVP;
+ var Promise = _ember.default.RSVP.Promise;
+
var assign = _ember.default.assign || _ember.default.merge;
+ /*
+ The TransitionChainMap caches the `state.enters`, `state.setups`, and final state reached
+ when transitioning from one state to another, so that future transitions can replay the
+ transition without needing to walk the state tree, collect these hook calls and determine
+ the state to transition into.
+
+ A future optimization would be to build a single chained method out of the collected enters
+ and setups. It may also be faster to do a two level cache (from: { to }) instead of caching based
+ on a key that adds the two together.
+ */
+ var TransitionChainMap = new _emberDataPrivateSystemEmptyObject.default();
+
var _extractPivotNameCache = new _emberDataPrivateSystemEmptyObject.default();
var _splitOnDotCache = new _emberDataPrivateSystemEmptyObject.default();
function splitOnDot(name) {
return _splitOnDotCache[name] || (_splitOnDotCache[name] = name.split('.'));
@@ -2213,16 +2246,10 @@
function extractPivotName(name) {
return _extractPivotNameCache[name] || (_extractPivotNameCache[name] = splitOnDot(name)[0]);
}
- function retrieveFromCurrentState(key) {
- return function () {
- return get(this.currentState, key);
- };
- }
-
// this (and all heimdall instrumentation) will be stripped by a babel transform
// https://github.com/heimdalljs/babel5-plugin-strip-heimdall
/*
`InternalModel` is the Model class that we use internally inside Ember Data to represent models.
@@ -2239,793 +2266,1004 @@
@private
@class InternalModel
*/
- function InternalModel(type, id, store, _, data) {
- this.type = type;
- this.id = id;
- this.store = store;
- this._data = data || new _emberDataPrivateSystemEmptyObject.default();
- this.modelName = type.modelName;
- this.dataHasInitialized = false;
- //Look into making this lazy
- this._deferredTriggers = [];
- this._attributes = new _emberDataPrivateSystemEmptyObject.default();
- this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
- this._relationships = new _emberDataPrivateSystemRelationshipsStateCreate.default(this);
- this._recordArrays = undefined;
- this.currentState = _emberDataPrivateSystemModelStates.default.empty;
- this.recordReference = new _emberDataPrivateSystemReferences.RecordReference(store, this);
- this.references = {};
- this.isReloading = false;
- this.isError = false;
- this.error = null;
- this.__ember_meta__ = null;
- this[_ember.default.GUID_KEY] = _ember.default.guidFor(this);
- /*
- implicit relationships are relationship which have not been declared but the inverse side exists on
- another record somewhere
- For example if there was
- ```app/models/comment.js
- import DS from 'ember-data';
- export default DS.Model.extend({
- name: DS.attr()
- })
- ```
- but there is also
- ```app/models/post.js
- import DS from 'ember-data';
- export default DS.Model.extend({
- name: DS.attr(),
- comments: DS.hasMany('comment')
- })
- ```
- would have a implicit post relationship in order to be do things like remove ourselves from the post
- when we are deleted
- */
- this._implicitRelationships = new _emberDataPrivateSystemEmptyObject.default();
- }
+ var InternalModel = (function () {
+ function InternalModel(modelClass, id, store, data) {
+ this.modelClass = modelClass;
+ this.id = id;
+ this.store = store;
+ this._data = data || new _emberDataPrivateSystemEmptyObject.default();
+ this.modelName = modelClass.modelName;
+ this.dataHasInitialized = false;
+ this._loadingPromise = null;
+ this._recordArrays = undefined;
+ this._record = null;
+ this.currentState = _emberDataPrivateSystemModelStates.default.empty;
+ this.isReloading = false;
+ this._isDestroyed = false;
+ this.isError = false;
+ this.error = null;
- InternalModel.prototype = {
- isEmpty: retrieveFromCurrentState('isEmpty'),
- isLoading: retrieveFromCurrentState('isLoading'),
- isLoaded: retrieveFromCurrentState('isLoaded'),
- hasDirtyAttributes: retrieveFromCurrentState('hasDirtyAttributes'),
- isSaving: retrieveFromCurrentState('isSaving'),
- isDeleted: retrieveFromCurrentState('isDeleted'),
- isNew: retrieveFromCurrentState('isNew'),
- isValid: retrieveFromCurrentState('isValid'),
- dirtyType: retrieveFromCurrentState('dirtyType'),
+ // caches for lazy getters
+ this.__deferredTriggers = null;
+ this._references = null;
+ this._recordReference = null;
+ this.__inFlightAttributes = null;
+ this.__relationships = null;
+ this.__attributes = null;
+ this.__implicitRelationships = null;
+ }
- constructor: InternalModel,
- materializeRecord: function () {
-
- // lookupFactory should really return an object that creates
- // instances with the injections applied
- var createOptions = {
- store: this.store,
- _internalModel: this,
- id: this.id,
- currentState: get(this, 'currentState'),
- isError: this.isError,
- adapterError: this.error
- };
-
- if (_ember.default.setOwner) {
- // ensure that `Ember.getOwner(this)` works inside a model instance
- _ember.default.setOwner(createOptions, (0, _emberDataPrivateUtils.getOwner)(this.store));
- } else {
- createOptions.container = this.store.container;
+ _createClass(InternalModel, [{
+ key: "isEmpty",
+ value: function isEmpty() {
+ return this.currentState.isEmpty;
}
-
- this.record = this.type._create(createOptions);
-
- this._triggerDeferredTriggers();
- },
-
- recordObjectWillDestroy: function () {
- this.record = null;
- },
-
- deleteRecord: function () {
- this.send('deleteRecord');
- },
-
- save: function (options) {
- var promiseLabel = "DS: Model#save " + this;
- var resolver = _ember.default.RSVP.defer(promiseLabel);
-
- this.store.scheduleSave(this, resolver, options);
- return resolver.promise;
- },
-
- startedReloading: function () {
- this.isReloading = true;
- if (this.record) {
- set(this.record, 'isReloading', true);
+ }, {
+ key: "isLoading",
+ value: function isLoading() {
+ return this.currentState.isLoading;
}
- },
-
- finishedReloading: function () {
- this.isReloading = false;
- if (this.record) {
- set(this.record, 'isReloading', false);
+ }, {
+ key: "isLoaded",
+ value: function isLoaded() {
+ return this.currentState.isLoaded;
}
- },
-
- reload: function () {
- this.startedReloading();
- var record = this;
- var promiseLabel = "DS: Model#reload of " + this;
- return new Promise(function (resolve) {
- record.send('reloadRecord', resolve);
- }, promiseLabel).then(function () {
- record.didCleanError();
- return record;
- }, function (error) {
- record.didError(error);
- throw error;
- }, "DS: Model#reload complete, update flags").finally(function () {
- record.finishedReloading();
- record.updateRecordArrays();
- });
- },
-
- getRecord: function () {
- if (!this.record) {
- this.materializeRecord();
+ }, {
+ key: "hasDirtyAttributes",
+ value: function hasDirtyAttributes() {
+ return this.currentState.hasDirtyAttributes;
}
- return this.record;
- },
+ }, {
+ key: "isSaving",
+ value: function isSaving() {
+ return this.currentState.isSaving;
+ }
+ }, {
+ key: "isDeleted",
+ value: function isDeleted() {
+ return this.currentState.isDeleted;
+ }
+ }, {
+ key: "isNew",
+ value: function isNew() {
+ return this.currentState.isNew;
+ }
+ }, {
+ key: "isValid",
+ value: function isValid() {
+ return this.currentState.isValid;
+ }
+ }, {
+ key: "dirtyType",
+ value: function dirtyType() {
+ return this.currentState.dirtyType;
+ }
+ }, {
+ key: "getRecord",
+ value: function getRecord() {
+ if (!this._record) {
- unloadRecord: function () {
- this.send('unloadRecord');
- },
+ // lookupFactory should really return an object that creates
+ // instances with the injections applied
+ var createOptions = {
+ store: this.store,
+ _internalModel: this,
+ id: this.id,
+ currentState: this.currentState,
+ isError: this.isError,
+ adapterError: this.error
+ };
- eachRelationship: function (callback, binding) {
- return this.type.eachRelationship(callback, binding);
- },
+ if (setOwner) {
+ // ensure that `getOwner(this)` works inside a model instance
+ setOwner(createOptions, (0, _emberDataPrivateUtils.getOwner)(this.store));
+ } else {
+ createOptions.container = this.store.container;
+ }
- eachAttribute: function (callback, binding) {
- return this.type.eachAttribute(callback, binding);
- },
+ this._record = this.modelClass._create(createOptions);
- inverseFor: function (key) {
- return this.type.inverseFor(key);
- },
+ this._triggerDeferredTriggers();
+ }
- setupData: function (data) {
- var changedKeys = this._changedKeys(data.attributes);
- assign(this._data, data.attributes);
- this.pushedData();
- if (this.record) {
- this.record._notifyProperties(changedKeys);
+ return this._record;
}
- this.didInitializeData();
- },
-
- becameReady: function () {
- _ember.default.run.schedule('actions', this.store.recordArrayManager, this.store.recordArrayManager.recordWasLoaded, this);
- },
-
- didInitializeData: function () {
- if (!this.dataHasInitialized) {
- this.becameReady();
- this.dataHasInitialized = true;
+ }, {
+ key: "recordObjectWillDestroy",
+ value: function recordObjectWillDestroy() {
+ this._record = null;
}
- },
+ }, {
+ key: "deleteRecord",
+ value: function deleteRecord() {
+ this.send('deleteRecord');
+ }
+ }, {
+ key: "save",
+ value: function save(options) {
+ var promiseLabel = "DS: Model#save " + this;
+ var resolver = RSVP.defer(promiseLabel);
- destroy: function () {
- if (this.record) {
- return this.record.destroy();
+ this.store.scheduleSave(this, resolver, options);
+ return resolver.promise;
}
- },
+ }, {
+ key: "startedReloading",
+ value: function startedReloading() {
+ this.isReloading = true;
+ if (this.hasRecord) {
+ set(this.record, 'isReloading', true);
+ }
+ }
+ }, {
+ key: "finishedReloading",
+ value: function finishedReloading() {
+ this.isReloading = false;
+ if (this.hasRecord) {
+ set(this.record, 'isReloading', false);
+ }
+ }
+ }, {
+ key: "reload",
+ value: function reload() {
+ this.startedReloading();
+ var internalModel = this;
+ var promiseLabel = "DS: Model#reload of " + this;
- /*
- @method createSnapshot
- @private
- */
- createSnapshot: function (options) {
- return new _emberDataPrivateSystemSnapshot.default(this, options);
- },
-
- /*
- @method loadingData
- @private
- @param {Promise} promise
- */
- loadingData: function (promise) {
- this.send('loadingData', promise);
- },
-
- /*
- @method loadedData
- @private
- */
- loadedData: function () {
- this.send('loadedData');
- this.didInitializeData();
- },
-
- /*
- @method notFound
- @private
- */
- notFound: function () {
- this.send('notFound');
- },
-
- /*
- @method pushedData
- @private
- */
- pushedData: function () {
- this.send('pushedData');
- },
-
- flushChangedAttributes: function () {
- this._inFlightAttributes = this._attributes;
- this._attributes = new _emberDataPrivateSystemEmptyObject.default();
- },
-
- hasChangedAttributes: function () {
- return Object.keys(this._attributes).length > 0;
- },
-
- /*
- Checks if the attributes which are considered as changed are still
- different to the state which is acknowledged by the server.
- This method is needed when data for the internal model is pushed and the
- pushed data might acknowledge dirty attributes as confirmed.
- @method updateChangedAttributes
- @private
- */
- updateChangedAttributes: function () {
- var changedAttributes = this.changedAttributes();
- var changedAttributeNames = Object.keys(changedAttributes);
-
- for (var i = 0, _length = changedAttributeNames.length; i < _length; i++) {
- var attribute = changedAttributeNames[i];
- var data = changedAttributes[attribute];
- var oldData = data[0];
- var newData = data[1];
-
- if (oldData === newData) {
- delete this._attributes[attribute];
+ return new Promise(function (resolve) {
+ internalModel.send('reloadRecord', resolve);
+ }, promiseLabel).then(function () {
+ internalModel.didCleanError();
+ return internalModel;
+ }, function (error) {
+ internalModel.didError(error);
+ throw error;
+ }, "DS: Model#reload complete, update flags").finally(function () {
+ internalModel.finishedReloading();
+ internalModel.updateRecordArrays();
+ });
+ }
+ }, {
+ key: "unloadRecord",
+ value: function unloadRecord() {
+ this.send('unloadRecord');
+ }
+ }, {
+ key: "eachRelationship",
+ value: function eachRelationship(callback, binding) {
+ return this.modelClass.eachRelationship(callback, binding);
+ }
+ }, {
+ key: "eachAttribute",
+ value: function eachAttribute(callback, binding) {
+ return this.modelClass.eachAttribute(callback, binding);
+ }
+ }, {
+ key: "inverseFor",
+ value: function inverseFor(key) {
+ return this.modelClass.inverseFor(key);
+ }
+ }, {
+ key: "setupData",
+ value: function setupData(data) {
+ var changedKeys = this._changedKeys(data.attributes);
+ assign(this._data, data.attributes);
+ this.pushedData();
+ if (this.hasRecord) {
+ this.record._notifyProperties(changedKeys);
}
+ this.didInitializeData();
}
- },
+ }, {
+ key: "becameReady",
+ value: function becameReady() {
+ emberRun.schedule('actions', this.store.recordArrayManager, this.store.recordArrayManager.recordWasLoaded, this);
+ }
+ }, {
+ key: "didInitializeData",
+ value: function didInitializeData() {
+ if (!this.dataHasInitialized) {
+ this.becameReady();
+ this.dataHasInitialized = true;
+ }
+ }
+ }, {
+ key: "destroy",
+ value: function destroy() {
+ this._isDestroyed = true;
+ if (this.hasRecord) {
+ return this.record.destroy();
+ }
+ }
- /*
- Returns an object, whose keys are changed properties, and value is an
- [oldProp, newProp] array.
- @method changedAttributes
- @private
- */
- changedAttributes: function () {
- var oldData = this._data;
- var currentData = this._attributes;
- var inFlightData = this._inFlightAttributes;
- var newData = assign(copy(inFlightData), currentData);
- var diffData = new _emberDataPrivateSystemEmptyObject.default();
+ /*
+ @method createSnapshot
+ @private
+ */
+ }, {
+ key: "createSnapshot",
+ value: function createSnapshot(options) {
+ return new _emberDataPrivateSystemSnapshot.default(this, options);
+ }
- var newDataKeys = Object.keys(newData);
+ /*
+ @method loadingData
+ @private
+ @param {Promise} promise
+ */
+ }, {
+ key: "loadingData",
+ value: function loadingData(promise) {
+ this.send('loadingData', promise);
+ }
- for (var i = 0, _length2 = newDataKeys.length; i < _length2; i++) {
- var key = newDataKeys[i];
- diffData[key] = [oldData[key], newData[key]];
+ /*
+ @method loadedData
+ @private
+ */
+ }, {
+ key: "loadedData",
+ value: function loadedData() {
+ this.send('loadedData');
+ this.didInitializeData();
}
- return diffData;
- },
+ /*
+ @method notFound
+ @private
+ */
+ }, {
+ key: "notFound",
+ value: function notFound() {
+ this.send('notFound');
+ }
- /*
- @method adapterWillCommit
- @private
- */
- adapterWillCommit: function () {
- this.send('willCommit');
- },
+ /*
+ @method pushedData
+ @private
+ */
+ }, {
+ key: "pushedData",
+ value: function pushedData() {
+ this.send('pushedData');
+ }
+ }, {
+ key: "flushChangedAttributes",
+ value: function flushChangedAttributes() {
+ this._inFlightAttributes = this._attributes;
+ this._attributes = new _emberDataPrivateSystemEmptyObject.default();
+ }
+ }, {
+ key: "hasChangedAttributes",
+ value: function hasChangedAttributes() {
+ return Object.keys(this._attributes).length > 0;
+ }
- /*
- @method adapterDidDirty
- @private
- */
- adapterDidDirty: function () {
- this.send('becomeDirty');
- this.updateRecordArraysLater();
- },
+ /*
+ Checks if the attributes which are considered as changed are still
+ different to the state which is acknowledged by the server.
+ This method is needed when data for the internal model is pushed and the
+ pushed data might acknowledge dirty attributes as confirmed.
+ @method updateChangedAttributes
+ @private
+ */
+ }, {
+ key: "updateChangedAttributes",
+ value: function updateChangedAttributes() {
+ var changedAttributes = this.changedAttributes();
+ var changedAttributeNames = Object.keys(changedAttributes);
+ var attrs = this._attributes;
- /*
- @method send
- @private
- @param {String} name
- @param {Object} context
- */
- send: function (name, context) {
- var currentState = get(this, 'currentState');
+ for (var i = 0, _length = changedAttributeNames.length; i < _length; i++) {
+ var attribute = changedAttributeNames[i];
+ var data = changedAttributes[attribute];
+ var oldData = data[0];
+ var newData = data[1];
- if (!currentState[name]) {
- this._unhandledEvent(currentState, name, context);
+ if (oldData === newData) {
+ delete attrs[attribute];
+ }
+ }
}
- return currentState[name](this, context);
- },
+ /*
+ Returns an object, whose keys are changed properties, and value is an
+ [oldProp, newProp] array.
+ @method changedAttributes
+ @private
+ */
+ }, {
+ key: "changedAttributes",
+ value: function changedAttributes() {
+ var oldData = this._data;
+ var currentData = this._attributes;
+ var inFlightData = this._inFlightAttributes;
+ var newData = assign(copy(inFlightData), currentData);
+ var diffData = new _emberDataPrivateSystemEmptyObject.default();
+ var newDataKeys = Object.keys(newData);
- notifyHasManyAdded: function (key, record, idx) {
- if (this.record) {
- this.record.notifyHasManyAdded(key, record, idx);
- }
- },
+ for (var i = 0, _length2 = newDataKeys.length; i < _length2; i++) {
+ var key = newDataKeys[i];
+ diffData[key] = [oldData[key], newData[key]];
+ }
- notifyHasManyRemoved: function (key, record, idx) {
- if (this.record) {
- this.record.notifyHasManyRemoved(key, record, idx);
+ return diffData;
}
- },
- notifyBelongsToChanged: function (key, record) {
- if (this.record) {
- this.record.notifyBelongsToChanged(key, record);
+ /*
+ @method adapterWillCommit
+ @private
+ */
+ }, {
+ key: "adapterWillCommit",
+ value: function adapterWillCommit() {
+ this.send('willCommit');
}
- },
- notifyPropertyChange: function (key) {
- if (this.record) {
- this.record.notifyPropertyChange(key);
+ /*
+ @method adapterDidDirty
+ @private
+ */
+ }, {
+ key: "adapterDidDirty",
+ value: function adapterDidDirty() {
+ this.send('becomeDirty');
+ this.updateRecordArraysLater();
}
- },
- rollbackAttributes: function () {
- var dirtyKeys = Object.keys(this._attributes);
+ /*
+ @method send
+ @private
+ @param {String} name
+ @param {Object} context
+ */
+ }, {
+ key: "send",
+ value: function send(name, context) {
+ var currentState = this.currentState;
- this._attributes = new _emberDataPrivateSystemEmptyObject.default();
+ if (!currentState[name]) {
+ this._unhandledEvent(currentState, name, context);
+ }
- if (get(this, 'isError')) {
- this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
- this.didCleanError();
+ return currentState[name](this, context);
}
-
- //Eventually rollback will always work for relationships
- //For now we support it only out of deleted state, because we
- //have an explicit way of knowing when the server acked the relationship change
- if (this.isDeleted()) {
- //TODO: Should probably move this to the state machine somehow
- this.becameReady();
+ }, {
+ key: "notifyHasManyAdded",
+ value: function notifyHasManyAdded(key, record, idx) {
+ if (this.hasRecord) {
+ this.record.notifyHasManyAdded(key, record, idx);
+ }
}
-
- if (this.isNew()) {
- this.clearRelationships();
+ }, {
+ key: "notifyHasManyRemoved",
+ value: function notifyHasManyRemoved(key, record, idx) {
+ if (this.hasRecord) {
+ this.record.notifyHasManyRemoved(key, record, idx);
+ }
}
-
- if (this.isValid()) {
- this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
+ }, {
+ key: "notifyBelongsToChanged",
+ value: function notifyBelongsToChanged(key, record) {
+ if (this.hasRecord) {
+ this.record.notifyBelongsToChanged(key, record);
+ }
}
+ }, {
+ key: "notifyPropertyChange",
+ value: function notifyPropertyChange(key) {
+ if (this.hasRecord) {
+ this.record.notifyPropertyChange(key);
+ }
+ }
+ }, {
+ key: "rollbackAttributes",
+ value: function rollbackAttributes() {
+ var dirtyKeys = Object.keys(this._attributes);
- this.send('rolledBack');
+ this._attributes = new _emberDataPrivateSystemEmptyObject.default();
- this.record._notifyProperties(dirtyKeys);
- },
+ if (get(this, 'isError')) {
+ this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
+ this.didCleanError();
+ }
- /*
- @method transitionTo
- @private
- @param {String} name
- */
- transitionTo: function (name) {
- // POSSIBLE TODO: Remove this code and replace with
- // always having direct reference to state objects
+ //Eventually rollback will always work for relationships
+ //For now we support it only out of deleted state, because we
+ //have an explicit way of knowing when the server acked the relationship change
+ if (this.isDeleted()) {
+ //TODO: Should probably move this to the state machine somehow
+ this.becameReady();
+ }
- var pivotName = extractPivotName(name);
- var currentState = get(this, 'currentState');
- var state = currentState;
-
- do {
- if (state.exit) {
- state.exit(this);
+ if (this.isNew()) {
+ this.clearRelationships();
}
- state = state.parentState;
- } while (!state.hasOwnProperty(pivotName));
- var path = splitOnDot(name);
- var setups = [];
- var enters = [];
- var i, l;
-
- for (i = 0, l = path.length; i < l; i++) {
- state = state[path[i]];
-
- if (state.enter) {
- enters.push(state);
+ if (this.isValid()) {
+ this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
}
- if (state.setup) {
- setups.push(state);
- }
- }
- for (i = 0, l = enters.length; i < l; i++) {
- enters[i].enter(this);
- }
+ this.send('rolledBack');
- set(this, 'currentState', state);
- //TODO Consider whether this is the best approach for keeping these two in sync
- if (this.record) {
- set(this.record, 'currentState', state);
+ this.record._notifyProperties(dirtyKeys);
}
- for (i = 0, l = setups.length; i < l; i++) {
- setups[i].setup(this);
- }
+ /*
+ @method transitionTo
+ @private
+ @param {String} name
+ */
+ }, {
+ key: "transitionTo",
+ value: function transitionTo(name) {
+ // POSSIBLE TODO: Remove this code and replace with
+ // always having direct reference to state objects
- this.updateRecordArraysLater();
- },
+ var pivotName = extractPivotName(name);
+ var state = this.currentState;
+ var transitionMapId = state.stateName + "->" + name;
- _unhandledEvent: function (state, name, context) {
- var errorMessage = "Attempted to handle event `" + name + "` ";
- errorMessage += "on " + String(this) + " while in state ";
- errorMessage += state.stateName + ". ";
+ do {
+ if (state.exit) {
+ state.exit(this);
+ }
+ state = state.parentState;
+ } while (!state[pivotName]);
- if (context !== undefined) {
- errorMessage += "Called with " + _ember.default.inspect(context) + ".";
- }
+ var setups = undefined;
+ var enters = undefined;
+ var i = undefined;
+ var l = undefined;
+ var map = TransitionChainMap[transitionMapId];
- throw new _ember.default.Error(errorMessage);
- },
+ if (map) {
+ setups = map.setups;
+ enters = map.enters;
+ state = map.state;
+ } else {
+ setups = [];
+ enters = [];
- triggerLater: function () {
- var length = arguments.length;
- var args = new Array(length);
+ var path = splitOnDot(name);
- for (var i = 0; i < length; i++) {
- args[i] = arguments[i];
- }
+ for (i = 0, l = path.length; i < l; i++) {
+ state = state[path[i]];
- if (this._deferredTriggers.push(args) !== 1) {
- return;
- }
- _ember.default.run.scheduleOnce('actions', this, '_triggerDeferredTriggers');
- },
+ if (state.enter) {
+ enters.push(state);
+ }
+ if (state.setup) {
+ setups.push(state);
+ }
+ }
- _triggerDeferredTriggers: function () {
- //TODO: Before 1.0 we want to remove all the events that happen on the pre materialized record,
- //but for now, we queue up all the events triggered before the record was materialized, and flush
- //them once we have the record
- if (!this.record) {
- return;
- }
- for (var i = 0, l = this._deferredTriggers.length; i < l; i++) {
- this.record.trigger.apply(this.record, this._deferredTriggers[i]);
- }
+ TransitionChainMap[transitionMapId] = { setups: setups, enters: enters, state: state };
+ }
- this._deferredTriggers.length = 0;
- },
- /*
- @method clearRelationships
- @private
- */
- clearRelationships: function () {
- var _this = this;
+ for (i = 0, l = enters.length; i < l; i++) {
+ enters[i].enter(this);
+ }
- this.eachRelationship(function (name, relationship) {
- if (_this._relationships.has(name)) {
- var rel = _this._relationships.get(name);
- rel.clear();
- rel.destroy();
+ this.currentState = state;
+ if (this.hasRecord) {
+ set(this.record, 'currentState', state);
}
- });
- Object.keys(this._implicitRelationships).forEach(function (key) {
- _this._implicitRelationships[key].clear();
- _this._implicitRelationships[key].destroy();
- });
- },
- /*
- When a find request is triggered on the store, the user can optionally pass in
- attributes and relationships to be preloaded. These are meant to behave as if they
- came back from the server, except the user obtained them out of band and is informing
- the store of their existence. The most common use case is for supporting client side
- nested URLs, such as `/posts/1/comments/2` so the user can do
- `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post.
- Preloaded data can be attributes and relationships passed in either as IDs or as actual
- models.
- @method _preloadData
- @private
- @param {Object} preload
- */
- _preloadData: function (preload) {
- var _this2 = this;
-
- //TODO(Igor) consider the polymorphic case
- Object.keys(preload).forEach(function (key) {
- var preloadValue = get(preload, key);
- var relationshipMeta = _this2.type.metaForProperty(key);
- if (relationshipMeta.isRelationship) {
- _this2._preloadRelationship(key, preloadValue);
- } else {
- _this2._data[key] = preloadValue;
+ for (i = 0, l = setups.length; i < l; i++) {
+ setups[i].setup(this);
}
- });
- },
- _preloadRelationship: function (key, preloadValue) {
- var relationshipMeta = this.type.metaForProperty(key);
- var type = relationshipMeta.type;
- if (relationshipMeta.kind === 'hasMany') {
- this._preloadHasMany(key, preloadValue, type);
- } else {
- this._preloadBelongsTo(key, preloadValue, type);
+ this.updateRecordArraysLater();
}
- },
+ }, {
+ key: "_unhandledEvent",
+ value: function _unhandledEvent(state, name, context) {
+ var errorMessage = "Attempted to handle event `" + name + "` ";
+ errorMessage += "on " + String(this) + " while in state ";
+ errorMessage += state.stateName + ". ";
- _preloadHasMany: function (key, preloadValue, type) {
- var recordsToSet = new Array(preloadValue.length);
+ if (context !== undefined) {
+ errorMessage += "Called with " + inspect(context) + ".";
+ }
- for (var i = 0; i < preloadValue.length; i++) {
- var recordToPush = preloadValue[i];
- recordsToSet[i] = this._convertStringOrNumberIntoInternalModel(recordToPush, type);
+ throw new EmberError(errorMessage);
}
+ }, {
+ key: "triggerLater",
+ value: function triggerLater() {
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
- //We use the pathway of setting the hasMany as if it came from the adapter
- //because the user told us that they know this relationships exists already
- this._relationships.get(key).updateRecordsFromAdapter(recordsToSet);
- },
-
- _preloadBelongsTo: function (key, preloadValue, type) {
- var recordToSet = this._convertStringOrNumberIntoInternalModel(preloadValue, type);
-
- //We use the pathway of setting the hasMany as if it came from the adapter
- //because the user told us that they know this relationships exists already
- this._relationships.get(key).setRecord(recordToSet);
- },
-
- _convertStringOrNumberIntoInternalModel: function (value, type) {
- if (typeof value === 'string' || typeof value === 'number') {
- return this.store._internalModelForId(type, value);
+ if (this._deferredTriggers.push(args) !== 1) {
+ return;
+ }
+ emberRun.schedule('actions', this, this._triggerDeferredTriggers);
}
- if (value._internalModel) {
- return value._internalModel;
- }
- return value;
- },
+ }, {
+ key: "_triggerDeferredTriggers",
+ value: function _triggerDeferredTriggers() {
+ //TODO: Before 1.0 we want to remove all the events that happen on the pre materialized record,
+ //but for now, we queue up all the events triggered before the record was materialized, and flush
+ //them once we have the record
+ if (!this.hasRecord) {
+ return;
+ }
+ for (var i = 0, l = this._deferredTriggers.length; i < l; i++) {
+ this.record.trigger.apply(this.record, this._deferredTriggers[i]);
+ }
- /*
- @method updateRecordArrays
- @private
- */
- updateRecordArrays: function () {
- this._updatingRecordArraysLater = false;
- this.store.dataWasUpdated(this.type, this);
- },
-
- setId: function (id) {
- this.id = id;
- if (this.record.get('id') !== id) {
- this.record.set('id', id);
+ this._deferredTriggers.length = 0;
}
- },
- didError: function (error) {
- this.error = error;
- this.isError = true;
+ /*
+ @method clearRelationships
+ @private
+ */
+ }, {
+ key: "clearRelationships",
+ value: function clearRelationships() {
+ var _this = this;
- if (this.record) {
- this.record.setProperties({
- isError: true,
- adapterError: error
+ this.eachRelationship(function (name, relationship) {
+ if (_this._relationships.has(name)) {
+ var rel = _this._relationships.get(name);
+ rel.clear();
+ rel.destroy();
+ }
});
+ Object.keys(this._implicitRelationships).forEach(function (key) {
+ _this._implicitRelationships[key].clear();
+ _this._implicitRelationships[key].destroy();
+ });
}
- },
- didCleanError: function () {
- this.error = null;
- this.isError = false;
+ /*
+ When a find request is triggered on the store, the user can optionally pass in
+ attributes and relationships to be preloaded. These are meant to behave as if they
+ came back from the server, except the user obtained them out of band and is informing
+ the store of their existence. The most common use case is for supporting client side
+ nested URLs, such as `/posts/1/comments/2` so the user can do
+ `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post.
+ Preloaded data can be attributes and relationships passed in either as IDs or as actual
+ models.
+ @method preloadData
+ @private
+ @param {Object} preload
+ */
+ }, {
+ key: "preloadData",
+ value: function preloadData(preload) {
+ var _this2 = this;
- if (this.record) {
- this.record.setProperties({
- isError: false,
- adapterError: null
+ //TODO(Igor) consider the polymorphic case
+ Object.keys(preload).forEach(function (key) {
+ var preloadValue = get(preload, key);
+ var relationshipMeta = _this2.modelClass.metaForProperty(key);
+ if (relationshipMeta.isRelationship) {
+ _this2._preloadRelationship(key, preloadValue);
+ } else {
+ _this2._data[key] = preloadValue;
+ }
});
}
- },
- /*
- If the adapter did not return a hash in response to a commit,
- merge the changed attributes and relationships into the existing
- saved data.
- @method adapterDidCommit
- */
- adapterDidCommit: function (data) {
- if (data) {
- data = data.attributes;
+ }, {
+ key: "_preloadRelationship",
+ value: function _preloadRelationship(key, preloadValue) {
+ var relationshipMeta = this.modelClass.metaForProperty(key);
+ var modelClass = relationshipMeta.type;
+ if (relationshipMeta.kind === 'hasMany') {
+ this._preloadHasMany(key, preloadValue, modelClass);
+ } else {
+ this._preloadBelongsTo(key, preloadValue, modelClass);
+ }
}
+ }, {
+ key: "_preloadHasMany",
+ value: function _preloadHasMany(key, preloadValue, modelClass) {
+ var recordsToSet = new Array(preloadValue.length);
- this.didCleanError();
- var changedKeys = this._changedKeys(data);
+ for (var i = 0; i < preloadValue.length; i++) {
+ var recordToPush = preloadValue[i];
+ recordsToSet[i] = this._convertStringOrNumberIntoInternalModel(recordToPush, modelClass);
+ }
- assign(this._data, this._inFlightAttributes);
- if (data) {
- assign(this._data, data);
+ //We use the pathway of setting the hasMany as if it came from the adapter
+ //because the user told us that they know this relationships exists already
+ this._relationships.get(key).updateRecordsFromAdapter(recordsToSet);
}
+ }, {
+ key: "_preloadBelongsTo",
+ value: function _preloadBelongsTo(key, preloadValue, modelClass) {
+ var recordToSet = this._convertStringOrNumberIntoInternalModel(preloadValue, modelClass);
- this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
+ //We use the pathway of setting the hasMany as if it came from the adapter
+ //because the user told us that they know this relationships exists already
+ this._relationships.get(key).setRecord(recordToSet);
+ }
+ }, {
+ key: "_convertStringOrNumberIntoInternalModel",
+ value: function _convertStringOrNumberIntoInternalModel(value, modelClass) {
+ if (typeof value === 'string' || typeof value === 'number') {
+ return this.store._internalModelForId(modelClass, value);
+ }
+ if (value._internalModel) {
+ return value._internalModel;
+ }
+ return value;
+ }
- this.send('didCommit');
- this.updateRecordArraysLater();
+ /*
+ @method updateRecordArrays
+ @private
+ */
+ }, {
+ key: "updateRecordArrays",
+ value: function updateRecordArrays() {
+ this._updatingRecordArraysLater = false;
+ this.store.dataWasUpdated(this.modelClass, this);
+ }
+ }, {
+ key: "setId",
+ value: function setId(id) {
+ this.id = id;
+ if (this.record.get('id') !== id) {
+ this.record.set('id', id);
+ }
+ }
+ }, {
+ key: "didError",
+ value: function didError(error) {
+ this.error = error;
+ this.isError = true;
- if (!data) {
- return;
+ if (this.hasRecord) {
+ this.record.setProperties({
+ isError: true,
+ adapterError: error
+ });
+ }
}
+ }, {
+ key: "didCleanError",
+ value: function didCleanError() {
+ this.error = null;
+ this.isError = false;
- this.record._notifyProperties(changedKeys);
- },
-
- /*
- @method updateRecordArraysLater
- @private
- */
- updateRecordArraysLater: function () {
- // quick hack (something like this could be pushed into run.once
- if (this._updatingRecordArraysLater) {
- return;
+ if (this.hasRecord) {
+ this.record.setProperties({
+ isError: false,
+ adapterError: null
+ });
+ }
}
- this._updatingRecordArraysLater = true;
- _ember.default.run.schedule('actions', this, this.updateRecordArrays);
- },
- addErrorMessageToAttribute: function (attribute, message) {
- var record = this.getRecord();
- get(record, 'errors')._add(attribute, message);
- },
+ /*
+ If the adapter did not return a hash in response to a commit,
+ merge the changed attributes and relationships into the existing
+ saved data.
+ @method adapterDidCommit
+ */
+ }, {
+ key: "adapterDidCommit",
+ value: function adapterDidCommit(data) {
+ if (data) {
+ data = data.attributes;
+ }
- removeErrorMessageFromAttribute: function (attribute) {
- var record = this.getRecord();
- get(record, 'errors')._remove(attribute);
- },
+ this.didCleanError();
+ var changedKeys = this._changedKeys(data);
- clearErrorMessages: function () {
- var record = this.getRecord();
- get(record, 'errors')._clear();
- },
+ assign(this._data, this._inFlightAttributes);
+ if (data) {
+ assign(this._data, data);
+ }
- hasErrors: function () {
- var record = this.getRecord();
- var errors = get(record, 'errors');
+ this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
- return !_ember.default.isEmpty(errors);
- },
+ this.send('didCommit');
+ this.updateRecordArraysLater();
- // FOR USE DURING COMMIT PROCESS
+ if (!data) {
+ return;
+ }
- /*
- @method adapterDidInvalidate
- @private
- */
- adapterDidInvalidate: function (errors) {
- var attribute;
+ this.record._notifyProperties(changedKeys);
+ }
- for (attribute in errors) {
- if (errors.hasOwnProperty(attribute)) {
- this.addErrorMessageToAttribute(attribute, errors[attribute]);
+ /*
+ @method updateRecordArraysLater
+ @private
+ */
+ }, {
+ key: "updateRecordArraysLater",
+ value: function updateRecordArraysLater() {
+ // quick hack (something like this could be pushed into run.once
+ if (this._updatingRecordArraysLater) {
+ return;
}
+ this._updatingRecordArraysLater = true;
+ emberRun.schedule('actions', this, this.updateRecordArrays);
}
+ }, {
+ key: "addErrorMessageToAttribute",
+ value: function addErrorMessageToAttribute(attribute, message) {
+ get(this.getRecord(), 'errors')._add(attribute, message);
+ }
+ }, {
+ key: "removeErrorMessageFromAttribute",
+ value: function removeErrorMessageFromAttribute(attribute) {
+ get(this.getRecord(), 'errors')._remove(attribute);
+ }
+ }, {
+ key: "clearErrorMessages",
+ value: function clearErrorMessages() {
+ get(this.getRecord(), 'errors')._clear();
+ }
+ }, {
+ key: "hasErrors",
+ value: function hasErrors() {
+ var errors = get(this.getRecord(), 'errors');
- this.send('becameInvalid');
+ return !isEmpty(errors);
+ }
- this._saveWasRejected();
- },
+ // FOR USE DURING COMMIT PROCESS
- /*
- @method adapterDidError
- @private
- */
- adapterDidError: function (error) {
- this.send('becameError');
- this.didError(error);
- this._saveWasRejected();
- },
+ /*
+ @method adapterDidInvalidate
+ @private
+ */
+ }, {
+ key: "adapterDidInvalidate",
+ value: function adapterDidInvalidate(errors) {
+ var attribute = undefined;
- _saveWasRejected: function () {
- var keys = Object.keys(this._inFlightAttributes);
- for (var i = 0; i < keys.length; i++) {
- if (this._attributes[keys[i]] === undefined) {
- this._attributes[keys[i]] = this._inFlightAttributes[keys[i]];
+ for (attribute in errors) {
+ if (errors.hasOwnProperty(attribute)) {
+ this.addErrorMessageToAttribute(attribute, errors[attribute]);
+ }
}
+
+ this.send('becameInvalid');
+
+ this._saveWasRejected();
}
- this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
- },
- /*
- Ember Data has 3 buckets for storing the value of an attribute on an internalModel.
- `_data` holds all of the attributes that have been acknowledged by
- a backend via the adapter. When rollbackAttributes is called on a model all
- attributes will revert to the record's state in `_data`.
- `_attributes` holds any change the user has made to an attribute
- that has not been acknowledged by the adapter. Any values in
- `_attributes` are have priority over values in `_data`.
- `_inFlightAttributes`. When a record is being synced with the
- backend the values in `_attributes` are copied to
- `_inFlightAttributes`. This way if the backend acknowledges the
- save but does not return the new state Ember Data can copy the
- values from `_inFlightAttributes` to `_data`. Without having to
- worry about changes made to `_attributes` while the save was
- happenign.
- Changed keys builds a list of all of the values that may have been
- changed by the backend after a successful save.
- It does this by iterating over each key, value pair in the payload
- returned from the server after a save. If the `key` is found in
- `_attributes` then the user has a local changed to the attribute
- that has not been synced with the server and the key is not
- included in the list of changed keys.
-
- If the value, for a key differs from the value in what Ember Data
- believes to be the truth about the backend state (A merger of the
- `_data` and `_inFlightAttributes` objects where
- `_inFlightAttributes` has priority) then that means the backend
- has updated the value and the key is added to the list of changed
- keys.
- @method _changedKeys
- @private
- */
- _changedKeys: function (updates) {
- var changedKeys = [];
+ /*
+ @method adapterDidError
+ @private
+ */
+ }, {
+ key: "adapterDidError",
+ value: function adapterDidError(error) {
+ this.send('becameError');
+ this.didError(error);
+ this._saveWasRejected();
+ }
+ }, {
+ key: "_saveWasRejected",
+ value: function _saveWasRejected() {
+ var keys = Object.keys(this._inFlightAttributes);
+ var attrs = this._attributes;
+ for (var i = 0; i < keys.length; i++) {
+ if (attrs[keys[i]] === undefined) {
+ attrs[keys[i]] = this._inFlightAttributes[keys[i]];
+ }
+ }
+ this._inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
+ }
- if (updates) {
- var original, i, value, key;
- var keys = Object.keys(updates);
- var length = keys.length;
+ /*
+ Ember Data has 3 buckets for storing the value of an attribute on an internalModel.
+ `_data` holds all of the attributes that have been acknowledged by
+ a backend via the adapter. When rollbackAttributes is called on a model all
+ attributes will revert to the record's state in `_data`.
+ `_attributes` holds any change the user has made to an attribute
+ that has not been acknowledged by the adapter. Any values in
+ `_attributes` are have priority over values in `_data`.
+ `_inFlightAttributes`. When a record is being synced with the
+ backend the values in `_attributes` are copied to
+ `_inFlightAttributes`. This way if the backend acknowledges the
+ save but does not return the new state Ember Data can copy the
+ values from `_inFlightAttributes` to `_data`. Without having to
+ worry about changes made to `_attributes` while the save was
+ happenign.
+ Changed keys builds a list of all of the values that may have been
+ changed by the backend after a successful save.
+ It does this by iterating over each key, value pair in the payload
+ returned from the server after a save. If the `key` is found in
+ `_attributes` then the user has a local changed to the attribute
+ that has not been synced with the server and the key is not
+ included in the list of changed keys.
+
+ If the value, for a key differs from the value in what Ember Data
+ believes to be the truth about the backend state (A merger of the
+ `_data` and `_inFlightAttributes` objects where
+ `_inFlightAttributes` has priority) then that means the backend
+ has updated the value and the key is added to the list of changed
+ keys.
+ @method _changedKeys
+ @private
+ */
+ }, {
+ key: "_changedKeys",
+ value: function _changedKeys(updates) {
+ var changedKeys = [];
- original = assign(new _emberDataPrivateSystemEmptyObject.default(), this._data);
- original = assign(original, this._inFlightAttributes);
+ if (updates) {
+ var original = undefined,
+ i = undefined,
+ value = undefined,
+ key = undefined;
+ var keys = Object.keys(updates);
+ var _length3 = keys.length;
+ var attrs = this._attributes;
- for (i = 0; i < length; i++) {
- key = keys[i];
- value = updates[key];
+ original = assign(new _emberDataPrivateSystemEmptyObject.default(), this._data);
+ original = assign(original, this._inFlightAttributes);
- // A value in _attributes means the user has a local change to
- // this attributes. We never override this value when merging
- // updates from the backend so we should not sent a change
- // notification if the server value differs from the original.
- if (this._attributes[key] !== undefined) {
- continue;
- }
+ for (i = 0; i < _length3; i++) {
+ key = keys[i];
+ value = updates[key];
- if (!_ember.default.isEqual(original[key], value)) {
- changedKeys.push(key);
+ // A value in _attributes means the user has a local change to
+ // this attributes. We never override this value when merging
+ // updates from the backend so we should not sent a change
+ // notification if the server value differs from the original.
+ if (attrs[key] !== undefined) {
+ continue;
+ }
+
+ if (!isEqual(original[key], value)) {
+ changedKeys.push(key);
+ }
}
}
- }
- return changedKeys;
- },
-
- toString: function () {
- if (this.record) {
- return this.record.toString();
- } else {
+ return changedKeys;
+ }
+ }, {
+ key: "toString",
+ value: function toString() {
return "<" + this.modelName + ":" + this.id + ">";
}
- },
+ }, {
+ key: "referenceFor",
+ value: function referenceFor(kind, name) {
+ var reference = this.references[name];
- referenceFor: function (type, name) {
- var reference = this.references[name];
+ if (!reference) {
+ var relationship = this._relationships.get(name);
- if (!reference) {
- var relationship = this._relationships.get(name);
+ if (kind === "belongsTo") {
+ reference = new _emberDataPrivateSystemReferences.BelongsToReference(this.store, this, relationship);
+ } else if (kind === "hasMany") {
+ reference = new _emberDataPrivateSystemReferences.HasManyReference(this.store, this, relationship);
+ }
- if (type === "belongsTo") {
- reference = new _emberDataPrivateSystemReferences.BelongsToReference(this.store, this, relationship);
- } else if (type === "hasMany") {
- reference = new _emberDataPrivateSystemReferences.HasManyReference(this.store, this, relationship);
+ this.references[name] = reference;
}
- this.references[name] = reference;
+ return reference;
}
+ }, {
+ key: "type",
+ get: function () {
+ return this.modelClass;
+ }
+ }, {
+ key: "recordReference",
+ get: function () {
+ if (this._recordReference === null) {
+ this._recordReference = new _emberDataPrivateSystemReferences.RecordReference(this.store, this);
+ }
+ return this._recordReference;
+ }
+ }, {
+ key: "references",
+ get: function () {
+ if (this._references === null) {
+ this._references = new _emberDataPrivateSystemEmptyObject.default();
+ }
+ return this._references;
+ }
+ }, {
+ key: "_deferredTriggers",
+ get: function () {
+ if (this.__deferredTriggers === null) {
+ this.__deferredTriggers = [];
+ }
+ return this.__deferredTriggers;
+ }
+ }, {
+ key: "_attributes",
+ get: function () {
+ if (this.__attributes === null) {
+ this.__attributes = new _emberDataPrivateSystemEmptyObject.default();
+ }
+ return this.__attributes;
+ },
+ set: function (v) {
+ this.__attributes = v;
+ }
+ }, {
+ key: "_relationships",
+ get: function () {
+ if (this.__relationships === null) {
+ this.__relationships = new _emberDataPrivateSystemRelationshipsStateCreate.default(this);
+ }
- return reference;
- }
- };
+ return this.__relationships;
+ }
+ }, {
+ key: "_inFlightAttributes",
+ get: function () {
+ if (this.__inFlightAttributes === null) {
+ this.__inFlightAttributes = new _emberDataPrivateSystemEmptyObject.default();
+ }
+ return this.__inFlightAttributes;
+ },
+ set: function (v) {
+ this.__inFlightAttributes = v;
+ }
+ /*
+ implicit relationships are relationship which have not been declared but the inverse side exists on
+ another record somewhere
+ For example if there was
+ ```app/models/comment.js
+ import DS from 'ember-data';
+ export default DS.Model.extend({
+ name: DS.attr()
+ })
+ ```
+ but there is also
+ ```app/models/post.js
+ import DS from 'ember-data';
+ export default DS.Model.extend({
+ name: DS.attr(),
+ comments: DS.hasMany('comment')
+ })
+ ```
+ would have a implicit post relationship in order to be do things like remove ourselves from the post
+ when we are deleted
+ */
+ }, {
+ key: "_implicitRelationships",
+ get: function () {
+ if (this.__implicitRelationships === null) {
+ this.__implicitRelationships = new _emberDataPrivateSystemEmptyObject.default();
+ }
+ return this.__implicitRelationships;
+ }
+ }, {
+ key: "record",
+ get: function () {
+ return this._record;
+ }
+ }, {
+ key: "isDestroyed",
+ get: function () {
+ return this._isDestroyed;
+ }
+ }, {
+ key: "hasRecord",
+ get: function () {
+ return !!this._record;
+ }
+ }]);
+
+ return InternalModel;
+ })();
+
+ exports.default = InternalModel;
+
if (false) {
/*
Returns the latest truth for an attribute - the canonical value, or the
in-flight value.
@method lastAcknowledgedValue
@@ -3038,18 +3276,18 @@
return this._data[key];
}
};
}
});
-define("ember-data/-private/system/model/model", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/promise-proxies", "ember-data/-private/system/model/errors", "ember-data/-private/system/debug/debug-info", "ember-data/-private/system/relationships/belongs-to", "ember-data/-private/system/relationships/has-many", "ember-data/-private/system/relationships/ext", "ember-data/-private/system/model/attr", "ember-data/-private/features"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemModelErrors, _emberDataPrivateSystemDebugDebugInfo, _emberDataPrivateSystemRelationshipsBelongsTo, _emberDataPrivateSystemRelationshipsHasMany, _emberDataPrivateSystemRelationshipsExt, _emberDataPrivateSystemModelAttr, _emberDataPrivateFeatures) {
+define("ember-data/-private/system/model/model", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/promise-proxies", "ember-data/-private/system/model/errors", "ember-data/-private/system/debug/debug-info", "ember-data/-private/system/relationships/belongs-to", "ember-data/-private/system/relationships/has-many", "ember-data/-private/system/relationships/ext", "ember-data/-private/system/model/attr", "ember-data/-private/features", "ember-data/-private/system/model/states"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemModelErrors, _emberDataPrivateSystemDebugDebugInfo, _emberDataPrivateSystemRelationshipsBelongsTo, _emberDataPrivateSystemRelationshipsHasMany, _emberDataPrivateSystemRelationshipsExt, _emberDataPrivateSystemModelAttr, _emberDataPrivateFeatures, _emberDataPrivateSystemModelStates) {
+ var get = _ember.default.get;
+ var computed = _ember.default.computed;
/**
@module ember-data
*/
- var get = _ember.default.get;
-
function intersection(array1, array2) {
var result = [];
array1.forEach(function (element) {
if (array2.indexOf(element) >= 0) {
result.push(element);
@@ -3059,11 +3297,11 @@
return result;
}
var RESERVED_MODEL_PROPS = ['currentState', 'data', 'store'];
- var retrieveFromCurrentState = _ember.default.computed('currentState', function (key) {
+ var retrieveFromCurrentState = computed('currentState', function (key) {
return get(this._internalModel.currentState, key);
}).readOnly();
/**
@@ -3141,11 +3379,11 @@
@since 1.13.0
@property hasDirtyAttributes
@type {Boolean}
@readOnly
*/
- hasDirtyAttributes: _ember.default.computed('currentState.isDirty', function () {
+ hasDirtyAttributes: computed('currentState.isDirty', function () {
return this.get('currentState.isDirty');
}),
/**
If this property is `true` the record is in the `saving` state. A
record enters the saving state when `save` is called, but the
@@ -3296,10 +3534,11 @@
/**
@property currentState
@private
@type {Object}
*/
+ currentState: _emberDataPrivateSystemModelStates.default.empty,
/**
When the record is in the `invalid` state this object will contain
any errors returned by the adapter. When present the errors hash
contains keys corresponding to the invalid property names
@@ -3340,11 +3579,11 @@
{{/each}}
```
@property errors
@type {DS.Errors}
*/
- errors: _ember.default.computed(function () {
+ errors: computed(function () {
var errors = _emberDataPrivateSystemModelErrors.default.create();
errors._registerHandlers(this._internalModel, function () {
this.send('becameInvalid');
}, function () {
@@ -3371,11 +3610,11 @@
@method serialize
@param {Object} options
@return {Object} an object whose values are primitive JSON values only
*/
serialize: function (options) {
- return this.store.serialize(this, options);
+ return this._internalModel.createSnapshot().serialize(options);
},
/**
Use [DS.JSONSerializer](DS.JSONSerializer.html) to
get the JSON representation of a record.
@@ -3398,53 +3637,53 @@
/**
Fired when the record is ready to be interacted with,
that is either loaded from the server or created locally.
@event ready
*/
- ready: _ember.default.K,
+ ready: function () {},
/**
Fired when the record is loaded from the server.
@event didLoad
*/
- didLoad: _ember.default.K,
+ didLoad: function () {},
/**
Fired when the record is updated.
@event didUpdate
*/
- didUpdate: _ember.default.K,
+ didUpdate: function () {},
/**
Fired when a new record is commited to the server.
@event didCreate
*/
- didCreate: _ember.default.K,
+ didCreate: function () {},
/**
Fired when the record is deleted.
@event didDelete
*/
- didDelete: _ember.default.K,
+ didDelete: function () {},
/**
Fired when the record becomes invalid.
@event becameInvalid
*/
- becameInvalid: _ember.default.K,
+ becameInvalid: function () {},
/**
Fired when the record enters the error state.
@event becameError
*/
- becameError: _ember.default.K,
+ becameError: function () {},
/**
Fired when the record is rolled back.
@event rolledBack
*/
- rolledBack: _ember.default.K,
+ rolledBack: function () {},
//TODO Do we want to deprecate these?
/**
@method send
@private
@@ -3762,11 +4001,13 @@
});
var blog = store.push({
type: 'blog',
id: 1,
relationships: {
- user: { type: 'user', id: 1 }
+ user: {
+ data: { type: 'user', id: 1 }
+ }
}
});
var userRef = blog.belongsTo('user');
// check if the user relationship is loaded
var isLoaded = userRef.value() !== null;
@@ -3959,13 +4200,12 @@
Model.reopenClass(_emberDataPrivateSystemRelationshipsExt.RelationshipsClassMethodsMixin);
Model.reopenClass(_emberDataPrivateSystemModelAttr.AttrClassMethodsMixin);
exports.default = Model.extend(_emberDataPrivateSystemDebugDebugInfo.default, _emberDataPrivateSystemRelationshipsBelongsTo.BelongsToMixin, _emberDataPrivateSystemRelationshipsExt.DidDefinePropertyMixin, _emberDataPrivateSystemRelationshipsExt.RelationshipsInstanceMethodsMixin, _emberDataPrivateSystemRelationshipsHasMany.HasManyMixin, _emberDataPrivateSystemModelAttr.AttrInstanceMethodsMixin);
});
-define('ember-data/-private/system/model/states', ['exports', 'ember', 'ember-data/-private/debug'], function (exports, _ember, _emberDataPrivateDebug) {
+define('ember-data/-private/system/model/states', ['exports', 'ember-data/-private/debug'], function (exports, _emberDataPrivateDebug) {
- var get = _ember.default.get;
/*
This file encapsulates the various states that a record can transition
through during its lifecycle.
*/
/**
@@ -4121,11 +4361,11 @@
* [isEmpty](DS.Model.html#property_isEmpty)
* [isLoading](DS.Model.html#property_isLoading)
* [isLoaded](DS.Model.html#property_isLoaded)
- * [isDirty](DS.Model.html#property_isDirty)
+ * [hasDirtyAttributes](DS.Model.html#property_hasDirtyAttributes)
* [isSaving](DS.Model.html#property_isSaving)
* [isDeleted](DS.Model.html#property_isDeleted)
* [isNew](DS.Model.html#property_isNew)
* [isValid](DS.Model.html#property_isValid)
@@ -4198,11 +4438,11 @@
// EVENTS
didSetProperty: didSetProperty,
//TODO(Igor) reloading now triggers a
//loadingData event, though it seems fine?
- loadingData: _ember.default.K,
+ loadingData: function () {},
propertyWasReset: function (internalModel, name) {
if (!internalModel.hasChangedAttributes()) {
internalModel.send('rolledBack');
}
@@ -4214,11 +4454,11 @@
if (!internalModel.hasChangedAttributes()) {
internalModel.transitionTo('loaded.saved');
}
},
- becomeDirty: _ember.default.K,
+ becomeDirty: function () {},
willCommit: function (internalModel) {
internalModel.transitionTo('inFlight');
},
@@ -4247,23 +4487,21 @@
// FLAGS
isSaving: true,
// EVENTS
didSetProperty: didSetProperty,
- becomeDirty: _ember.default.K,
- pushedData: _ember.default.K,
+ becomeDirty: function () {},
+ pushedData: function () {},
unloadRecord: assertAgainstUnloadRecord,
// TODO: More robust semantics around save-while-in-flight
- willCommit: _ember.default.K,
+ willCommit: function () {},
didCommit: function (internalModel) {
- var dirtyType = get(this, 'dirtyType');
-
internalModel.transitionTo('saved');
- internalModel.send('invokeLifecycleCallbacks', dirtyType);
+ internalModel.send('invokeLifecycleCallbacks', this.dirtyType);
},
becameInvalid: function (internalModel) {
internalModel.transitionTo('invalid');
internalModel.send('invokeLifecycleCallbacks');
@@ -4294,13 +4532,13 @@
if (!internalModel.hasErrors()) {
this.becameValid(internalModel);
}
},
- becameInvalid: _ember.default.K,
- becomeDirty: _ember.default.K,
- pushedData: _ember.default.K,
+ becameInvalid: function () {},
+ becomeDirty: function () {},
+ pushedData: function () {},
willCommit: function (internalModel) {
internalModel.clearErrorMessages();
internalModel.transitionTo('inFlight');
},
@@ -4325,11 +4563,11 @@
// chart so we can reopen their substates and add mixins as
// necessary.
function deepClone(object) {
var clone = {};
- var value;
+ var value = undefined;
for (var prop in object) {
value = object[prop];
if (value && typeof value === 'object') {
clone[prop] = deepClone(value);
@@ -4361,10 +4599,11 @@
});
createdState.invalid.rolledBack = function (internalModel) {
internalModel.transitionTo('deleted.saved');
};
+
createdState.uncommitted.rolledBack = function (internalModel) {
internalModel.transitionTo('deleted.saved');
};
var updatedState = dirtyState({
@@ -4388,11 +4627,11 @@
createdState.uncommitted.pushedData = function (internalModel) {
internalModel.transitionTo('loaded.updated.uncommitted');
internalModel.triggerLater('didLoad');
};
- createdState.uncommitted.propertyWasReset = _ember.default.K;
+ createdState.uncommitted.propertyWasReset = function () {};
function assertAgainstUnloadRecord(internalModel) {}
updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord;
@@ -4415,19 +4654,19 @@
// Trying to roll back if you're not in the dirty state
// doesn't change your state. For example, if you're in the
// in-flight state, rolling back the record doesn't move
// you out of the in-flight state.
- rolledBack: _ember.default.K,
+ rolledBack: function () {},
unloadRecord: function (internalModel) {
// clear relationships before moving to deleted state
// otherwise it fails
internalModel.clearRelationships();
internalModel.transitionTo('deleted.saved');
},
- propertyWasReset: _ember.default.K,
+ propertyWasReset: function () {},
// SUBSTATES
// A record begins its lifecycle in the `empty` state.
// If its data will come from the adapter, it will
@@ -4496,11 +4735,11 @@
// FLAGS
isLoaded: true,
//TODO(Igor) Reloading now triggers a loadingData event,
//but it should be ok?
- loadingData: _ember.default.K,
+ loadingData: function () {},
// SUBSTATES
// If there are no local changes to a record, it remains
// in the `saved` state.
@@ -4512,11 +4751,11 @@
},
// EVENTS
didSetProperty: didSetProperty,
- pushedData: _ember.default.K,
+ pushedData: function () {},
becomeDirty: function (internalModel) {
internalModel.transitionTo('updated.uncommitted');
},
@@ -4537,18 +4776,15 @@
// otherwise it fails
internalModel.clearRelationships();
internalModel.transitionTo('deleted.saved');
},
- didCommit: function (internalModel) {
- internalModel.send('invokeLifecycleCallbacks', get(internalModel, 'lastDirtyType'));
- },
+ didCommit: function () {},
// loaded.saved.notFound would be triggered by a failed
// `reload()` on an unchanged record
- notFound: _ember.default.K
-
+ notFound: function () {}
},
// A record is in this state after it has been locally
// created but before the adapter has indicated that
// it has been saved.
@@ -4591,13 +4827,13 @@
rollback: function (internalModel) {
internalModel.rollbackAttributes();
internalModel.triggerLater('ready');
},
- pushedData: _ember.default.K,
- becomeDirty: _ember.default.K,
- deleteRecord: _ember.default.K,
+ pushedData: function () {},
+ becomeDirty: function () {},
+ deleteRecord: function () {},
rolledBack: function (internalModel) {
internalModel.transitionTo('loaded.saved');
internalModel.triggerLater('ready');
}
@@ -4614,11 +4850,11 @@
// EVENTS
unloadRecord: assertAgainstUnloadRecord,
// TODO: More robust semantics around save-while-in-flight
- willCommit: _ember.default.K,
+ willCommit: function () {},
didCommit: function (internalModel) {
internalModel.transitionTo('saved');
internalModel.send('invokeLifecycleCallbacks');
},
@@ -4650,13 +4886,12 @@
invokeLifecycleCallbacks: function (internalModel) {
internalModel.triggerLater('didDelete', internalModel);
internalModel.triggerLater('didCommit', internalModel);
},
- willCommit: _ember.default.K,
-
- didCommit: _ember.default.K
+ willCommit: function () {},
+ didCommit: function () {}
},
invalid: {
isValid: false,
@@ -4668,14 +4903,14 @@
if (!internalModel.hasErrors()) {
this.becameValid(internalModel);
}
},
- becameInvalid: _ember.default.K,
- becomeDirty: _ember.default.K,
- deleteRecord: _ember.default.K,
- willCommit: _ember.default.K,
+ becameInvalid: function () {},
+ becomeDirty: function () {},
+ deleteRecord: function () {},
+ willCommit: function () {},
rolledBack: function (internalModel) {
internalModel.clearErrorMessages();
internalModel.transitionTo('loaded.saved');
internalModel.triggerLater('ready');
@@ -4708,20 +4943,18 @@
for (var prop in object) {
if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') {
continue;
}
if (typeof object[prop] === 'object') {
- object[prop] = wireState(object[prop], object, name + "." + prop);
+ object[prop] = wireState(object[prop], object, name + '.' + prop);
}
}
return object;
}
- RootState = wireState(RootState, null, "root");
-
- exports.default = RootState;
+ exports.default = wireState(RootState, null, 'root');
});
/**
@module ember-data
*/
define('ember-data/-private/system/normalize-link', ['exports'], function (exports) {
@@ -4810,13 +5043,16 @@
return this;
};
});
define('ember-data/-private/system/promise-proxies', ['exports', 'ember', 'ember-data/-private/debug'], function (exports, _ember, _emberDataPrivateDebug) {
-
- var Promise = _ember.default.RSVP.Promise;
+ exports.promiseObject = promiseObject;
+ exports.promiseArray = promiseArray;
+ exports.proxyToContent = proxyToContent;
+ exports.promiseManyArray = promiseManyArray;
var get = _ember.default.get;
+ var Promise = _ember.default.RSVP.Promise;
/**
A `PromiseArray` is an object that acts like both an `Ember.Array`
and a promise. When the promise is resolved the resulting value
will be set to the `PromiseArray`'s `content` property. This makes
@@ -4845,10 +5081,11 @@
@extends Ember.ArrayProxy
@uses Ember.PromiseProxyMixin
*/
var PromiseArray = _ember.default.ArrayProxy.extend(_ember.default.PromiseProxyMixin);
+ exports.PromiseArray = PromiseArray;
/**
A `PromiseObject` is an object that acts like both an `Ember.Object`
and a promise. When the promise is resolved, then the resulting value
will be set to the `PromiseObject`'s `content` property. This makes
it easy to create data bindings with the `PromiseObject` that will
@@ -4876,21 +5113,23 @@
@extends Ember.ObjectProxy
@uses Ember.PromiseProxyMixin
*/
var PromiseObject = _ember.default.ObjectProxy.extend(_ember.default.PromiseProxyMixin);
- var promiseObject = function (promise, label) {
+ exports.PromiseObject = PromiseObject;
+
+ function promiseObject(promise, label) {
return PromiseObject.create({
promise: Promise.resolve(promise, label)
});
- };
+ }
- var promiseArray = function (promise, label) {
+ function promiseArray(promise, label) {
return PromiseArray.create({
promise: Promise.resolve(promise, label)
});
- };
+ }
/**
A PromiseManyArray is a PromiseArray that also proxies certain method calls
to the underlying manyArray.
Right now we proxy:
@@ -4908,12 +5147,13 @@
@extends Ember.ArrayProxy
*/
function proxyToContent(method) {
return function () {
- var content = get(this, 'content');
- return content[method].apply(content, arguments);
+ var _get;
+
+ return (_get = get(this, 'content'))[method].apply(_get, arguments);
};
}
var PromiseManyArray = PromiseArray.extend({
reload: function () {
@@ -4935,28 +5175,23 @@
off: proxyToContent('off'),
has: proxyToContent('has')
});
- var promiseManyArray = function (promise, label) {
+ exports.PromiseManyArray = PromiseManyArray;
+
+ function promiseManyArray(promise, label) {
return PromiseManyArray.create({
promise: Promise.resolve(promise, label)
});
- };
-
- exports.PromiseArray = PromiseArray;
- exports.PromiseObject = PromiseObject;
- exports.PromiseManyArray = PromiseManyArray;
- exports.promiseArray = promiseArray;
- exports.promiseObject = promiseObject;
- exports.promiseManyArray = promiseManyArray;
+ }
});
define("ember-data/-private/system/record-array-manager", ["exports", "ember", "ember-data/-private/system/record-arrays", "ember-data/-private/system/ordered-set"], function (exports, _ember, _emberDataPrivateSystemRecordArrays, _emberDataPrivateSystemOrderedSet) {
+ var get = _ember.default.get;
var MapWithDefault = _ember.default.MapWithDefault;
+ var emberRun = _ember.default.run;
- var get = _ember.default.get;
-
/**
@class RecordArrayManager
@namespace DS
@private
@extends Ember.Object
@@ -4970,12 +5205,12 @@
return [];
}
});
this.liveRecordArrays = MapWithDefault.create({
- defaultValue: function (typeClass) {
- return _this.createRecordArray(typeClass);
+ defaultValue: function (modelClass) {
+ return _this.createRecordArray(modelClass);
}
});
this.changedRecords = [];
this._adapterPopulatedRecordArrays = [];
@@ -4984,11 +5219,11 @@
recordDidChange: function (record) {
if (this.changedRecords.push(record) !== 1) {
return;
}
- _ember.default.run.schedule('actions', this, this.updateRecordArrays);
+ emberRun.schedule('actions', this, this.updateRecordArrays);
},
recordArraysForRecord: function (record) {
record._recordArrays = record._recordArrays || _emberDataPrivateSystemOrderedSet.default.create();
return record._recordArrays;
@@ -5003,11 +5238,12 @@
*/
updateRecordArrays: function () {
var _this2 = this;
this.changedRecords.forEach(function (internalModel) {
- if (get(internalModel, 'record.isDestroyed') || get(internalModel, 'record.isDestroying') || get(internalModel, 'currentState.stateName') === 'root.deleted.saved') {
+
+ if (internalModel.isDestroyed || internalModel.currentState.stateName === 'root.deleted.saved') {
_this2._recordWasDeleted(internalModel);
} else {
_this2._recordWasChanged(internalModel);
}
});
@@ -5021,22 +5257,22 @@
if (!recordArrays) {
return;
}
recordArrays.forEach(function (array) {
- return array.removeInternalModel(record);
+ return array._removeInternalModels([record]);
});
record._recordArrays = null;
},
_recordWasChanged: function (record) {
var _this3 = this;
var typeClass = record.type;
var recordArrays = this.filteredRecordArrays.get(typeClass);
- var filter;
+ var filter = undefined;
recordArrays.forEach(function (array) {
filter = get(array, 'filterFunction');
_this3.updateFilterRecordArray(array, filter, typeClass, record);
});
},
@@ -5045,59 +5281,78 @@
recordWasLoaded: function (record) {
var _this4 = this;
var typeClass = record.type;
var recordArrays = this.filteredRecordArrays.get(typeClass);
- var filter;
+ var filter = undefined;
recordArrays.forEach(function (array) {
filter = get(array, 'filterFunction');
_this4.updateFilterRecordArray(array, filter, typeClass, record);
});
if (this.liveRecordArrays.has(typeClass)) {
var liveRecordArray = this.liveRecordArrays.get(typeClass);
- this._addRecordToRecordArray(liveRecordArray, record);
+ this._addInternalModelToRecordArray(liveRecordArray, record);
}
},
+
/**
Update an individual filter.
@method updateFilterRecordArray
@param {DS.FilteredRecordArray} array
@param {Function} filter
- @param {DS.Model} typeClass
- @param {InternalModel} record
+ @param {DS.Model} modelClass
+ @param {InternalModel} internalModel
*/
- updateFilterRecordArray: function (array, filter, typeClass, record) {
- var shouldBeInArray = filter(record.getRecord());
- var recordArrays = this.recordArraysForRecord(record);
+ updateFilterRecordArray: function (array, filter, modelClass, internalModel) {
+ var shouldBeInArray = filter(internalModel.getRecord());
+ var recordArrays = this.recordArraysForRecord(internalModel);
if (shouldBeInArray) {
- this._addRecordToRecordArray(array, record);
+ this._addInternalModelToRecordArray(array, internalModel);
} else {
recordArrays.delete(array);
- array.removeInternalModel(record);
+ array._removeInternalModels([internalModel]);
}
},
- _addRecordToRecordArray: function (array, record) {
- var recordArrays = this.recordArraysForRecord(record);
+ _addInternalModelToRecordArray: function (array, internalModel) {
+ var recordArrays = this.recordArraysForRecord(internalModel);
if (!recordArrays.has(array)) {
- array.addInternalModel(record);
+ array._pushInternalModels([internalModel]);
recordArrays.add(array);
}
},
- populateLiveRecordArray: function (array, modelName) {
- var typeMap = this.store.typeMapFor(modelName);
+ syncLiveRecordArray: function (array, modelClass) {
+ var hasNoPotentialDeletions = this.changedRecords.length === 0;
+ var typeMap = this.store.typeMapFor(modelClass);
+ var hasNoInsertionsOrRemovals = typeMap.records.length === array.length;
+
+ /*
+ Ideally the recordArrayManager has knowledge of the changes to be applied to
+ liveRecordArrays, and is capable of strategically flushing those changes and applying
+ small diffs if desired. However, until we've refactored recordArrayManager, this dirty
+ check prevents us from unnecessarily wiping out live record arrays returned by peekAll.
+ */
+ if (hasNoPotentialDeletions && hasNoInsertionsOrRemovals) {
+ return;
+ }
+
+ this.populateLiveRecordArray(array, modelClass);
+ },
+
+ populateLiveRecordArray: function (array, modelClass) {
+ var typeMap = this.store.typeMapFor(modelClass);
var records = typeMap.records;
- var record;
+ var record = undefined;
for (var i = 0; i < records.length; i++) {
record = records[i];
if (!record.isDeleted() && !record.isEmpty()) {
- this._addRecordToRecordArray(array, record);
+ this._addInternalModelToRecordArray(array, record);
}
}
},
/**
@@ -5105,23 +5360,23 @@
changed on a `DS.FilteredRecordArray`.
It essentially re-runs the filter from scratch. This same
method is invoked when the filter is created in th first place.
@method updateFilter
@param {Array} array
- @param {String} modelName
+ @param {Class} modelClass
@param {Function} filter
*/
- updateFilter: function (array, modelName, filter) {
- var typeMap = this.store.typeMapFor(modelName);
+ updateFilter: function (array, modelClass, filter) {
+ var typeMap = this.store.typeMapFor(modelClass);
var records = typeMap.records;
- var record;
+ var record = undefined;
for (var i = 0; i < records.length; i++) {
record = records[i];
if (!record.isDeleted() && !record.isEmpty()) {
- this.updateFilterRecordArray(array, filter, modelName, record);
+ this.updateFilterRecordArray(array, filter, modelClass, record);
}
}
},
/**
@@ -5136,23 +5391,21 @@
},
/**
Create a `DS.RecordArray` for a type.
@method createRecordArray
- @param {Class} typeClass
+ @param {Class} modelClass
@return {DS.RecordArray}
*/
- createRecordArray: function (typeClass) {
- var array = _emberDataPrivateSystemRecordArrays.RecordArray.create({
- type: typeClass,
+ createRecordArray: function (modelClass) {
+ return _emberDataPrivateSystemRecordArrays.RecordArray.create({
+ type: modelClass,
content: _ember.default.A(),
store: this.store,
isLoaded: true,
manager: this
});
-
- return array;
},
/**
Create a `DS.FilteredRecordArray` for a type and register it for updates.
@method createFilteredRecordArray
@@ -5219,10 +5472,11 @@
So manager will not update this array.
@method unregisterRecordArray
@param {DS.RecordArray} array
*/
unregisterRecordArray: function (array) {
+
var typeClass = array.type;
// unregister filtered record array
var recordArrays = this.filteredRecordArrays.get(typeClass);
var removedFromFiltered = remove(recordArrays, array);
@@ -5257,11 +5511,11 @@
entry.destroy();
}
function flatten(list) {
var length = list.length;
- var result = _ember.default.A();
+ var result = [];
for (var i = 0; i < length; i++) {
result = result.concat(list[i]);
}
@@ -5288,11 +5542,11 @@
exports.AdapterPopulatedRecordArray = _emberDataPrivateSystemRecordArraysAdapterPopulatedRecordArray.default;
});
/**
@module ember-data
*/
-define("ember-data/-private/system/record-arrays/adapter-populated-record-array", ["exports", "ember", "ember-data/-private/system/record-arrays/record-array", "ember-data/-private/system/clone-null", "ember-data/-private/features"], function (exports, _ember, _emberDataPrivateSystemRecordArraysRecordArray, _emberDataPrivateSystemCloneNull, _emberDataPrivateFeatures) {
+define("ember-data/-private/system/record-arrays/adapter-populated-record-array", ["exports", "ember", "ember-data/-private/system/record-arrays/record-array", "ember-data/-private/system/clone-null"], function (exports, _ember, _emberDataPrivateSystemRecordArraysRecordArray, _emberDataPrivateSystemCloneNull) {
/**
@module ember-data
*/
@@ -5334,12 +5588,19 @@
@class AdapterPopulatedRecordArray
@namespace DS
@extends DS.RecordArray
*/
exports.default = _emberDataPrivateSystemRecordArraysRecordArray.default.extend({
- query: null,
+ init: function () {
+ // yes we are touching `this` before super, but ArrayProxy has a bug that requires this.
+ this.set('content', this.get('content') || _ember.default.A());
+ this._super.apply(this, arguments);
+ this.query = this.query || null;
+ this.links = null;
+ },
+
replace: function () {
var type = get(this, 'type').toString();
throw new Error("The result of a server query (on " + type + ") is immutable.");
},
@@ -5350,33 +5611,32 @@
return store._query(modelName, query, this);
},
/**
- @method loadRecords
- @param {Array} records
+ @method _setInternalModels
+ @param {Array} internalModels
@param {Object} payload normalized payload
@private
*/
- loadRecords: function (records, payload) {
+ _setInternalModels: function (internalModels, payload) {
var _this = this;
- //TODO Optimize
- var internalModels = _ember.default.A(records).mapBy('_internalModel');
+ // TODO: initial load should not cause change events at all, only
+ // subsequent. This requires changing the public api of adapter.query, but
+ // hopefully we can do that soon.
+ this.get('content').setObjects(internalModels);
+
this.setProperties({
- content: _ember.default.A(internalModels),
isLoaded: true,
isUpdating: false,
- meta: (0, _emberDataPrivateSystemCloneNull.default)(payload.meta)
+ meta: (0, _emberDataPrivateSystemCloneNull.default)(payload.meta),
+ links: (0, _emberDataPrivateSystemCloneNull.default)(payload.links)
});
- if (true) {
- this.set('links', (0, _emberDataPrivateSystemCloneNull.default)(payload.links));
- }
-
internalModels.forEach(function (record) {
- _this.manager.recordArraysForRecord(record).add(_this);
+ return _this.manager.recordArraysForRecord(record).add(_this);
});
// TODO: should triggering didLoad event be the last action of the runLoop?
_ember.default.run.once(this, 'trigger', 'didLoad');
}
@@ -5399,10 +5659,16 @@
@class FilteredRecordArray
@namespace DS
@extends DS.RecordArray
*/
exports.default = _emberDataPrivateSystemRecordArraysRecordArray.default.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.set('filterFunction', this.get('filterFunction') || null);
+ this.isLoaded = true;
+ },
/**
The filterFunction is a function used to test records from the store to
determine if they should be part of the record array.
Example
```javascript
@@ -5420,36 +5686,36 @@
```
@method filterFunction
@param {DS.Model} record
@return {Boolean} `true` if the record should be in the array
*/
- filterFunction: null,
- isLoaded: true,
replace: function () {
var type = get(this, 'type').toString();
- throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
+ throw new Error('The result of a client-side filter (on ' + type + ') is immutable.');
},
/**
@method updateFilter
@private
*/
_updateFilter: function () {
- var manager = get(this, 'manager');
- manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
+ if (get(this, 'isDestroying') || get(this, 'isDestroyed')) {
+ return;
+ }
+ get(this, 'manager').updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
},
updateFilter: _ember.default.observer('filterFunction', function () {
_ember.default.run.once(this, this._updateFilter);
})
});
});
define("ember-data/-private/system/record-arrays/record-array", ["exports", "ember", "ember-data/-private/system/promise-proxies", "ember-data/-private/system/snapshot-record-array"], function (exports, _ember, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemSnapshotRecordArray) {
-
var get = _ember.default.get;
var set = _ember.default.set;
+ var Promise = _ember.default.RSVP.Promise;
/**
A record array is an array that contains records of a certain type. The record
array materializes records as needed when they are retrieved for the first
time. You should not create record arrays yourself. Instead, an instance of
@@ -5461,60 +5727,65 @@
@extends Ember.ArrayProxy
@uses Ember.Evented
*/
exports.default = _ember.default.ArrayProxy.extend(_ember.default.Evented, {
- /**
- The model type contained by this record array.
- @property type
- @type DS.Model
- */
- type: null,
+ init: function () {
+ this._super.apply(this, arguments);
- /**
- The array of client ids backing the record array. When a
- record is requested from the record array, the record
- for the client id at the same index is materialized, if
- necessary, by the store.
- @property content
- @private
- @type Ember.Array
- */
- content: null,
+ /**
+ The model type contained by this record array.
+ @property type
+ @type DS.Model
+ */
+ this.type = this.type || null;
- /**
+ /**
+ The array of client ids backing the record array. When a
+ record is requested from the record array, the record
+ for the client id at the same index is materialized, if
+ necessary, by the store.
+ @property content
+ @private
+ @type Ember.Array
+ */
+ this.set('content', this.content || null);
+
+ /**
The flag to signal a `RecordArray` is finished loading data.
Example
```javascript
var people = store.peekAll('person');
people.get('isLoaded'); // true
```
@property isLoaded
@type Boolean
- */
- isLoaded: false,
- /**
+ */
+ this.isLoaded = this.isLoaded || false;
+ /**
The flag to signal a `RecordArray` is currently loading data.
- Example
- ```javascript
+ Example
+ ```javascript
var people = store.peekAll('person');
people.get('isUpdating'); // false
people.update();
people.get('isUpdating'); // true
```
- @property isUpdating
+ @property isUpdating
@type Boolean
- */
- isUpdating: false,
+ */
+ this.isUpdating = false;
- /**
+ /**
The store that created this record array.
- @property store
+ @property store
@private
@type DS.Store
- */
- store: null,
+ */
+ this.store = this.store || null;
+ this._updatingPromise = null;
+ },
replace: function () {
var type = get(this, 'type').toString();
throw new Error("The result of a server query (for all " + type + " types) is immutable. To modify contents, use toArray()");
},
@@ -5525,12 +5796,11 @@
@private
@param {Number} index
@return {DS.Model} record
*/
objectAtContent: function (index) {
- var content = get(this, 'content');
- var internalModel = content.objectAt(index);
+ var internalModel = get(this, 'content').objectAt(index);
return internalModel && internalModel.getRecord();
},
/**
Used to get the latest version of all of the records in this array
@@ -5545,16 +5815,29 @@
people.get('isUpdating'); // true
```
@method update
*/
update: function () {
+ var _this = this;
+
if (get(this, 'isUpdating')) {
- return;
+ return this._updatingPromise;
}
this.set('isUpdating', true);
- return this._update();
+
+ var updatingPromise = this._update().finally(function () {
+ _this._updatingPromise = null;
+ if (_this.get('isDestroying') || _this.get('isDestroyed')) {
+ return;
+ }
+ _this.set('isUpdating', false);
+ });
+
+ this._updatingPromise = updatingPromise;
+
+ return updatingPromise;
},
/*
Update this RecordArray and return a promise which resolves once the update
is finished.
@@ -5569,29 +5852,26 @@
/**
Adds an internal model to the `RecordArray` without duplicates
@method addInternalModel
@private
@param {InternalModel} internalModel
- @param {number} an optional index to insert at
*/
- addInternalModel: function (internalModel, idx) {
- var content = get(this, 'content');
- if (idx === undefined) {
- content.addObject(internalModel);
- } else if (!content.includes(internalModel)) {
- content.insertAt(idx, internalModel);
- }
+ _pushInternalModels: function (internalModels) {
+ // pushObjects because the internalModels._recordArrays set was already
+ // consulted for inclusion, so addObject and its on .contains call is not
+ // required.
+ get(this, 'content').pushObjects(internalModels);
},
/**
Removes an internalModel to the `RecordArray`.
@method removeInternalModel
@private
@param {InternalModel} internalModel
*/
- removeInternalModel: function (internalModel) {
- get(this, 'content').removeObject(internalModel);
+ _removeInternalModels: function (internalModels) {
+ get(this, 'content').removeObjects(internalModels);
},
/**
Saves all of the records in the `RecordArray`.
Example
@@ -5604,51 +5884,72 @@
```
@method save
@return {DS.PromiseArray} promise
*/
save: function () {
- var recordArray = this;
- var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
- var promise = _ember.default.RSVP.all(this.invoke("save"), promiseLabel).then(function (array) {
- return recordArray;
- }, null, "DS: RecordArray#save return RecordArray");
+ var _this2 = this;
+ var promiseLabel = 'DS: RecordArray#save ' + get(this, 'type');
+ var promise = Promise.all(this.invoke('save'), promiseLabel).then(function () {
+ return _this2;
+ }, null, 'DS: RecordArray#save return RecordArray');
+
return _emberDataPrivateSystemPromiseProxies.PromiseArray.create({ promise: promise });
},
_dissociateFromOwnRecords: function () {
- var _this = this;
+ var _this3 = this;
- this.get('content').forEach(function (record) {
- var recordArrays = record._recordArrays;
+ this.get('content').forEach(function (internalModel) {
+ var recordArrays = internalModel._recordArrays;
if (recordArrays) {
- recordArrays.delete(_this);
+ recordArrays.delete(_this3);
}
});
},
/**
@method _unregisterFromManager
@private
*/
_unregisterFromManager: function () {
- var manager = get(this, 'manager');
- manager.unregisterRecordArray(this);
+ get(this, 'manager').unregisterRecordArray(this);
},
willDestroy: function () {
this._unregisterFromManager();
this._dissociateFromOwnRecords();
- set(this, 'content', undefined);
+ // TODO: we should not do work during destroy:
+ // * when objects are destroyed, they should simply be left to do
+ // * if logic errors do to this, that logic needs to be more careful during
+ // teardown (ember provides isDestroying/isDestroyed) for this reason
+ // * the exception being: if an dominator has a reference to this object,
+ // and must be informed to release e.g. e.g. removing itself from th
+ // recordArrayMananger
+ set(this, 'content', null);
set(this, 'length', 0);
this._super.apply(this, arguments);
},
- createSnapshot: function (options) {
- var meta = this.get('meta');
- return new _emberDataPrivateSystemSnapshotRecordArray.default(this, meta, options);
+ /**
+ r @method _createSnapshot
+ @private
+ */
+ _createSnapshot: function (options) {
+ // this is private for users, but public for ember-data internals
+ return new _emberDataPrivateSystemSnapshotRecordArray.default(this, this.get('meta'), options);
+ },
+
+ /**
+ r @method _takeSnapshot
+ @private
+ */
+ _takeSnapshot: function () {
+ return get(this, 'content').map(function (internalModel) {
+ return internalModel.createSnapshot();
+ });
}
});
});
/**
@module ember-data
@@ -5658,10 +5959,19 @@
exports.BelongsToReference = _emberDataPrivateSystemReferencesBelongsTo.default;
exports.HasManyReference = _emberDataPrivateSystemReferencesHasMany.default;
});
define('ember-data/-private/system/references/belongs-to', ['exports', 'ember-data/model', 'ember', 'ember-data/-private/system/references/reference', 'ember-data/-private/features', 'ember-data/-private/debug'], function (exports, _emberDataModel, _ember, _emberDataPrivateSystemReferencesReference, _emberDataPrivateFeatures, _emberDataPrivateDebug) {
+ /**
+ A BelongsToReference is a low level API that allows users and
+ addon author to perform meta-operations on a belongs-to
+ relationship.
+
+ @class BelongsToReference
+ @namespace DS
+ @extends DS.Reference
+ */
var BelongsToReference = function (store, parentInternalModel, belongsToRelationship) {
this._super$constructor(store, parentInternalModel);
this.belongsToRelationship = belongsToRelationship;
this.type = belongsToRelationship.relationshipMeta.type;
this.parent = parentInternalModel.recordReference;
@@ -5671,31 +5981,221 @@
BelongsToReference.prototype = Object.create(_emberDataPrivateSystemReferencesReference.default.prototype);
BelongsToReference.prototype.constructor = BelongsToReference;
BelongsToReference.prototype._super$constructor = _emberDataPrivateSystemReferencesReference.default;
+ /**
+ This returns a string that represents how the reference will be
+ looked up when it is loaded. If the relationship has a link it will
+ use the "link" otherwise it defaults to "id".
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ // get the identifier of the reference
+ if (userRef.remoteType() === "id") {
+ var id = userRef.id();
+ } else if (userRef.remoteType() === "link") {
+ var link = userRef.link();
+ }
+ ```
+
+ @method remoteType
+ @return {String} The name of the remote type. This should either be "link" or "id"
+ */
BelongsToReference.prototype.remoteType = function () {
if (this.belongsToRelationship.link) {
return "link";
}
return "id";
};
+ /**
+ The `id` of the record that this reference refers to. Together, the
+ `type()` and `id()` methods form a composite key for the identity
+ map. This can be used to access the id of an async relationship
+ without triggering a fetch that would normally happen if you
+ attempted to use `record.get('relationship.id')`.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ // get the identifier of the reference
+ if (userRef.remoteType() === "id") {
+ var id = userRef.id();
+ }
+ ```
+
+ @method id
+ @return {String} The id of the record in this belongsTo relationship.
+ */
BelongsToReference.prototype.id = function () {
var inverseRecord = this.belongsToRelationship.inverseRecord;
return inverseRecord && inverseRecord.id;
};
+ /**
+ The link Ember Data will use to fetch or reload this belongs-to
+ relationship.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ links: {
+ related: '/articles/1/author'
+ }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ // get the identifier of the reference
+ if (userRef.remoteType() === "link") {
+ var link = userRef.link();
+ }
+ ```
+
+ @method link
+ @return {String} The link Ember Data will use to fetch or reload this belongs-to relationship.
+ */
BelongsToReference.prototype.link = function () {
return this.belongsToRelationship.link;
};
+ /**
+ The meta data for the belongs-to relationship.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ links: {
+ related: {
+ href: '/articles/1/author',
+ meta: {
+ lastUpdated: 1458014400000
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ var userRef = blog.belongsTo('user');
+
+ userRef.meta() // { lastUpdated: 1458014400000 }
+ ```
+
+ @method meta
+ @return {Object} The meta information for the belongs-oo relationship.
+ */
BelongsToReference.prototype.meta = function () {
return this.belongsToRelationship.meta;
};
+ /**
+ `push` can be used to update the data in the relationship and Ember
+ Data will treat the new data as the conanical value of this
+ relationship on the backend.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ // provide data for reference
+ userRef.push({
+ data: {
+ type: 'user',
+ id: 1,
+ attributes: {
+ username: "@user"
+ }
+ }
+ }).then(function(user) {
+ userRef.value() === user;
+ });
+ ```
+
+ @method push
+ @param {Object|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
+ @return {Promise<record>} A promise that resolves with the new value in this belongs-to relationship.
+ */
BelongsToReference.prototype.push = function (objectOrPromise) {
var _this = this;
return _ember.default.RSVP.resolve(objectOrPromise).then(function (data) {
var record;
@@ -5711,20 +6211,103 @@
return record;
});
};
+ /**
+ `value()` synchronously returns the current value of the belongs-to
+ relationship. Unlike `record.get('relationshipName')`, calling
+ `value()` on a reference does not trigger a fetch if the async
+ relationship is not yet loaded. If the relationship is not loaded
+ it will always return `null`.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ userRef.value(); // null
+
+ // provide data for reference
+ userRef.push({
+ data: {
+ type: 'user',
+ id: 1,
+ attributes: {
+ username: "@user"
+ }
+ }
+ }).then(function(user) {
+ userRef.value(); // user
+ });
+ ```
+
+ @method value
+ @param {Object|Promise} objectOrPromise a promise that resolves to a JSONAPI document object describing the new value of this relationship.
+ @return {DS.Model} the record in this relationship
+ */
BelongsToReference.prototype.value = function () {
var inverseRecord = this.belongsToRelationship.inverseRecord;
- if (inverseRecord && inverseRecord.record) {
- return inverseRecord.record;
+ if (inverseRecord && inverseRecord.isLoaded()) {
+ return inverseRecord.getRecord();
}
return null;
};
+ /**
+ Loads a record in a belongs to relationship if it is not already
+ loaded. If the relationship is already loaded this method does not
+ trigger a new load.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ userRef.value(); // null
+
+ userRef.load().then(function(user) {
+ userRef.value() === user
+ });
+
+ @method load
+ @return {Promise} a promise that resolves with the record in this belongs-to relationship.
+ */
BelongsToReference.prototype.load = function () {
var _this2 = this;
if (this.remoteType() === "id") {
return this.belongsToRelationship.getRecord();
@@ -5735,10 +6318,44 @@
return _this2.value();
});
}
};
+ /**
+ Triggers a reload of the value in this relationship. If the
+ remoteType is `"link"` Ember Data will use the relationship link to
+ reload the relationship. Otherwise it will reload the record by its
+ id.
+
+ Example
+
+ ```javascript
+ // models/blog.js
+ export default DS.Model.extend({
+ user: DS.belongsTo({ async: true })
+ });
+
+ var blog = store.push({
+ data: {
+ type: 'blog',
+ id: 1,
+ relationships: {
+ user: {
+ data: { type: 'user', id: 1 }
+ }
+ }
+ }
+ });
+ var userRef = blog.belongsTo('user');
+
+ userRef.reload().then(function(user) {
+ userRef.value() === user
+ });
+
+ @method reload
+ @return {Promise} a promise that resolves with the record in this belongs-to relationship after the reload has completed.
+ */
BelongsToReference.prototype.reload = function () {
var _this3 = this;
return this.belongsToRelationship.reload().then(function (internalModel) {
return _this3.value();
@@ -5746,11 +6363,11 @@
};
exports.default = BelongsToReference;
});
define('ember-data/-private/system/references/has-many', ['exports', 'ember', 'ember-data/-private/system/references/reference', 'ember-data/-private/debug', 'ember-data/-private/features'], function (exports, _ember, _emberDataPrivateSystemReferencesReference, _emberDataPrivateDebug, _emberDataPrivateFeatures) {
-
+ var resolve = _ember.default.RSVP.resolve;
var get = _ember.default.get;
var HasManyReference = function (store, parentInternalModel, hasManyRelationship) {
this._super$constructor(store, parentInternalModel);
this.hasManyRelationship = hasManyRelationship;
@@ -5775,26 +6392,25 @@
HasManyReference.prototype.link = function () {
return this.hasManyRelationship.link;
};
HasManyReference.prototype.ids = function () {
- var members = this.hasManyRelationship.members;
- var ids = members.toArray().map(function (internalModel) {
+ var members = this.hasManyRelationship.members.toArray();
+
+ return members.map(function (internalModel) {
return internalModel.id;
});
-
- return ids;
};
HasManyReference.prototype.meta = function () {
- return this.hasManyRelationship.manyArray.meta;
+ return this.hasManyRelationship.meta;
};
HasManyReference.prototype.push = function (objectOrPromise) {
var _this = this;
- return _ember.default.RSVP.resolve(objectOrPromise).then(function (payload) {
+ return resolve(objectOrPromise).then(function (payload) {
var array = payload;
if (false) {}
var useLegacyArrayPush = true;
@@ -5821,88 +6437,203 @@
internalModels = _ember.default.A(records).mapBy('_internalModel');
}
_this.hasManyRelationship.computeChanges(internalModels);
- return _this.hasManyRelationship.manyArray;
+ return _this.hasManyRelationship.getManyArray();
});
};
HasManyReference.prototype._isLoaded = function () {
var hasData = get(this.hasManyRelationship, 'hasData');
if (!hasData) {
return false;
}
var members = this.hasManyRelationship.members.toArray();
- var isEveryLoaded = members.every(function (internalModel) {
+
+ return members.every(function (internalModel) {
return internalModel.isLoaded() === true;
});
-
- return isEveryLoaded;
};
HasManyReference.prototype.value = function () {
if (this._isLoaded()) {
- return this.hasManyRelationship.manyArray;
+ return this.hasManyRelationship.getManyArray();
}
return null;
};
HasManyReference.prototype.load = function () {
if (!this._isLoaded()) {
return this.hasManyRelationship.getRecords();
}
- var manyArray = this.hasManyRelationship.manyArray;
- return _ember.default.RSVP.resolve(manyArray);
+ return resolve(this.hasManyRelationship.getManyArray());
};
HasManyReference.prototype.reload = function () {
return this.hasManyRelationship.reload();
};
exports.default = HasManyReference;
});
define('ember-data/-private/system/references/record', ['exports', 'ember', 'ember-data/-private/system/references/reference'], function (exports, _ember, _emberDataPrivateSystemReferencesReference) {
+ /**
+ An RecordReference is a low level API that allows users and
+ addon author to perform meta-operations on a record.
+
+ @class RecordReference
+ @namespace DS
+ */
var RecordReference = function (store, internalModel) {
this._super$constructor(store, internalModel);
this.type = internalModel.modelName;
this._id = internalModel.id;
};
RecordReference.prototype = Object.create(_emberDataPrivateSystemReferencesReference.default.prototype);
RecordReference.prototype.constructor = RecordReference;
RecordReference.prototype._super$constructor = _emberDataPrivateSystemReferencesReference.default;
+ /**
+ The `id` of the record that this reference refers to.
+
+ Together, the `type` and `id` properties form a composite key for
+ the identity map.
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ userRef.id(); // '1'
+ ```
+
+ @method id
+ @return {String} The id of the record.
+ */
RecordReference.prototype.id = function () {
return this._id;
};
+ /**
+ How the reference will be looked up when it is loaded: Currently
+ this always return `identity` to signifying that a record will be
+ loaded by the `type` and `id`.
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ userRef.remoteType(); // 'identity'
+ ```
+
+ @method remoteType
+ @return {String} 'identity'
+ */
RecordReference.prototype.remoteType = function () {
return 'identity';
};
+ /**
+ This API allows you to provide a reference with new data. The
+ simplest usage of this API is similar to `store.push`: you provide a
+ normalized hash of data and the object represented by the reference
+ will update.
+
+ If you pass a promise to `push`, Ember Data will not ask the adapter
+ for the data if another attempt to fetch it is made in the
+ interim. When the promise resolves, the underlying object is updated
+ with the new data, and the promise returned by *this function* is resolved
+ with that object.
+
+ For example, `recordReference.push(promise)` will be resolved with a
+ record.
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ // provide data for reference
+ userRef.push({ data: { id: 1, username: "@user" }}).then(function(user) {
+ userRef.value() === user;
+ });
+ ```
+
+ @method
+ @param {Promise|Object}
+ @returns Promise<record> a promise for the value (record or relationship)
+ */
RecordReference.prototype.push = function (objectOrPromise) {
var _this = this;
return _ember.default.RSVP.resolve(objectOrPromise).then(function (data) {
- var record = _this.store.push(data);
- return record;
+ return _this.store.push(data);
});
};
+ /**
+ If the entity referred to by the reference is already loaded, it is
+ present as `reference.value`. Otherwise the value returned by this function
+ is `null`.
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ userRef.value(); // user
+ ```
+
+ @method value
+ @return {DS.Model} the record for this RecordReference
+ */
RecordReference.prototype.value = function () {
return this.internalModel.record;
};
+ /**
+ Triggers a fetch for the backing entity based on its `remoteType`
+ (see `remoteType` definitions per reference type).
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ // load user (via store.find)
+ userRef.load().then(...)
+ ```
+
+ @method load
+ @return {Promise<record>} the record for this RecordReference
+ */
RecordReference.prototype.load = function () {
return this.store.findRecord(this.type, this._id);
};
+ /**
+ Reloads the record if it is already loaded. If the record is not
+ loaded it will load the record via `store.findRecord`
+
+ Example
+
+ ```javascript
+ var userRef = store.getReference('user', 1);
+
+ // or trigger a reload
+ userRef.reload().then(...)
+ ```
+
+ @method reload
+ @return {Promise<record>} the record for this RecordReference
+ */
RecordReference.prototype.reload = function () {
var record = this.value();
if (record) {
return record.reload();
}
@@ -5942,10 +6673,11 @@
return {
key: meta.key,
kind: meta.kind,
type: typeForRelationshipMeta(meta),
options: meta.options,
+ name: meta.name,
parentType: meta.parentType,
isRelationship: true
};
}
});
@@ -6044,10 +6776,11 @@
var meta = {
type: userEnteredModelName,
isRelationship: true,
options: opts,
kind: 'belongsTo',
+ name: 'Belongs To',
key: null
};
return _ember.default.computed({
get: function (key) {
@@ -6241,11 +6974,11 @@
import DS from 'ember-data';
export default DS.Model.extend({
comments: DS.hasMany('comment')
});
```
- Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
+ Calling `store.modelFor('post').typeForRelationship('comments', store)` will return `Comment`.
@method typeForRelationship
@static
@param {String} name the name of the relationship
@param {store} store an instance of DS.Store
@return {DS.Model} the type of the relationship, or undefined
@@ -6272,15 +7005,16 @@
import DS from 'ember-data';
export default DS.Model.extend({
owner: DS.belongsTo('post')
});
```
- App.Post.inverseFor('comments') -> { type: App.Message, name: 'owner', kind: 'belongsTo' }
- App.Message.inverseFor('owner') -> { type: App.Post, name: 'comments', kind: 'hasMany' }
+ store.modelFor('post').inverseFor('comments', store) -> { type: App.Message, name: 'owner', kind: 'belongsTo' }
+ store.modelFor('message').inverseFor('owner', store) -> { type: App.Post, name: 'comments', kind: 'hasMany' }
@method inverseFor
@static
@param {String} name the name of the relationship
+ @param {DS.Store} store
@return {Object} the inverse relationship, or null
*/
inverseFor: function (name, store) {
var inverseMap = get(this, 'inverseMap');
if (inverseMap[name]) {
@@ -6394,15 +7128,17 @@
This computed property would return a map describing these
relationships, like this:
```javascript
import Ember from 'ember';
import Blog from 'app/models/blog';
+ import User from 'app/models/user';
+ import Post from 'app/models/post';
var relationships = Ember.get(Blog, 'relationships');
- relationships.get(App.User);
+ relationships.get(User);
//=> [ { name: 'users', kind: 'hasMany' },
// { name: 'owner', kind: 'belongsTo' } ]
- relationships.get(App.Post);
+ relationships.get(Post);
//=> [ { name: 'posts', kind: 'hasMany' } ]
```
@property relationships
@static
@type Ember.Map
@@ -6469,11 +7205,11 @@
This property would contain the following:
```javascript
import Ember from 'ember';
import Blog from 'app/models/blog';
var relatedTypes = Ember.get(Blog, 'relatedTypes');
- //=> [ App.User, App.Post ]
+ //=> [ User, Post ]
```
@property relatedTypes
@static
@type Ember.Array
@readOnly
@@ -6798,10 +7534,11 @@
var meta = {
type: type,
isRelationship: true,
options: options,
kind: 'hasMany',
+ name: 'Has Many',
key: null
};
return _ember.default.computed({
get: function (key) {
@@ -6863,12 +7600,10 @@
this.addCanonicalRecord(newRecord);
} else if (this.canonicalState) {
this.removeCanonicalRecord(this.canonicalState);
}
this.flushCanonicalLater();
- this.setHasData(true);
- this.setHasLoaded(true);
};
BelongsToRelationship.prototype._super$addCanonicalRecord = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.addCanonicalRecord;
BelongsToRelationship.prototype.addCanonicalRecord = function (newRecord) {
if (this.canonicalMembers.has(newRecord)) {
@@ -6888,12 +7623,14 @@
//temporary fix to not remove newly created records if server returned null.
//TODO remove once we have proper diffing
if (this.inverseRecord && this.inverseRecord.isNew() && !this.canonicalState) {
return;
}
- this.inverseRecord = this.canonicalState;
- this.record.notifyBelongsToChanged(this.key);
+ if (this.inverseRecord !== this.canonicalState) {
+ this.inverseRecord = this.canonicalState;
+ this.record.notifyBelongsToChanged(this.key);
+ }
this._super$flushCanonical();
};
BelongsToRelationship.prototype._super$addRecord = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.addRecord;
BelongsToRelationship.prototype.addRecord = function (newRecord) {
@@ -6992,16 +7729,21 @@
if (this.link) {
return this.fetchLink();
}
// reload record, if it is already loaded
- if (this.inverseRecord && this.inverseRecord.record) {
+ if (this.inverseRecord && this.inverseRecord.hasRecord) {
return this.inverseRecord.record.reload();
}
return this.findRecord();
};
+
+ BelongsToRelationship.prototype.updateData = function (data) {
+ var internalModel = this.store._pushResourceIdentifier(this, data);
+ this.setCanonicalRecord(internalModel);
+ };
});
define("ember-data/-private/system/relationships/state/create", ["exports", "ember", "ember-data/-private/system/relationships/state/has-many", "ember-data/-private/system/relationships/state/belongs-to", "ember-data/-private/system/empty-object"], function (exports, _ember, _emberDataPrivateSystemRelationshipsStateHasMany, _emberDataPrivateSystemRelationshipsStateBelongsTo, _emberDataPrivateSystemEmptyObject) {
exports.default = Relationships;
var get = _ember.default.get;
@@ -7052,33 +7794,44 @@
function ManyRelationship(store, record, inverseKey, relationshipMeta) {
this._super$constructor(store, record, inverseKey, relationshipMeta);
this.belongsToType = relationshipMeta.type;
this.canonicalState = [];
- this.manyArray = _emberDataPrivateSystemManyArray.default.create({
- canonicalState: this.canonicalState,
- store: this.store,
- relationship: this,
- type: this.store.modelFor(this.belongsToType),
- record: record
- });
this.isPolymorphic = relationshipMeta.options.polymorphic;
- this.manyArray.isPolymorphic = this.isPolymorphic;
}
ManyRelationship.prototype = Object.create(_emberDataPrivateSystemRelationshipsStateRelationship.default.prototype);
+ ManyRelationship.prototype.getManyArray = function () {
+ if (!this._manyArray) {
+ this._manyArray = _emberDataPrivateSystemManyArray.default.create({
+ canonicalState: this.canonicalState,
+ store: this.store,
+ relationship: this,
+ type: this.store.modelFor(this.belongsToType),
+ record: this.record,
+ meta: this.meta,
+ isPolymorphic: this.isPolymorphic
+ });
+ }
+ return this._manyArray;
+ };
+
ManyRelationship.prototype.constructor = ManyRelationship;
ManyRelationship.prototype._super$constructor = _emberDataPrivateSystemRelationshipsStateRelationship.default;
ManyRelationship.prototype.destroy = function () {
- this.manyArray.destroy();
+ if (this._manyArray) {
+ this._manyArray.destroy();
+ }
};
ManyRelationship.prototype._super$updateMeta = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.updateMeta;
ManyRelationship.prototype.updateMeta = function (meta) {
this._super$updateMeta(meta);
- this.manyArray.set('meta', meta);
+ if (this._manyArray) {
+ this._manyArray.set('meta', meta);
+ }
};
ManyRelationship.prototype._super$addCanonicalRecord = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.addCanonicalRecord;
ManyRelationship.prototype.addCanonicalRecord = function (record, idx) {
if (this.canonicalMembers.has(record)) {
@@ -7096,11 +7849,12 @@
ManyRelationship.prototype.addRecord = function (record, idx) {
if (this.members.has(record)) {
return;
}
this._super$addRecord(record, idx);
- this.manyArray.internalAddRecords([record], idx);
+ // make lazy later
+ this.getManyArray().internalAddRecords([record], idx);
};
ManyRelationship.prototype._super$removeCanonicalRecordFromOwn = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.removeCanonicalRecordFromOwn;
ManyRelationship.prototype.removeCanonicalRecordFromOwn = function (record, idx) {
var i = idx;
@@ -7116,53 +7870,55 @@
this._super$removeCanonicalRecordFromOwn(record, idx);
};
ManyRelationship.prototype._super$flushCanonical = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.flushCanonical;
ManyRelationship.prototype.flushCanonical = function () {
- this.manyArray.flushCanonical();
+ if (this._manyArray) {
+ this._manyArray.flushCanonical();
+ }
this._super$flushCanonical();
};
ManyRelationship.prototype._super$removeRecordFromOwn = _emberDataPrivateSystemRelationshipsStateRelationship.default.prototype.removeRecordFromOwn;
ManyRelationship.prototype.removeRecordFromOwn = function (record, idx) {
if (!this.members.has(record)) {
return;
}
this._super$removeRecordFromOwn(record, idx);
+ var manyArray = this.getManyArray();
if (idx !== undefined) {
//TODO(Igor) not used currently, fix
- this.manyArray.currentState.removeAt(idx);
+ manyArray.currentState.removeAt(idx);
} else {
- this.manyArray.internalRemoveRecords([record]);
+ manyArray.internalRemoveRecords([record]);
}
};
ManyRelationship.prototype.notifyRecordRelationshipAdded = function (record, idx) {
this.record.notifyHasManyAdded(this.key, record, idx);
};
ManyRelationship.prototype.reload = function () {
- var _this = this;
+ var manyArray = this.getManyArray();
+ var manyArrayLoadedState = manyArray.get('isLoaded');
- var manyArrayLoadedState = this.manyArray.get('isLoaded');
-
if (this._loadingPromise) {
if (this._loadingPromise.get('isPending')) {
return this._loadingPromise;
}
if (this._loadingPromise.get('isRejected')) {
- this.manyArray.set('isLoaded', manyArrayLoadedState);
+ manyArray.set('isLoaded', manyArrayLoadedState);
}
}
if (this.link) {
this._loadingPromise = (0, _emberDataPrivateSystemPromiseProxies.promiseManyArray)(this.fetchLink(), 'Reload with link');
return this._loadingPromise;
} else {
- this._loadingPromise = (0, _emberDataPrivateSystemPromiseProxies.promiseManyArray)(this.store.scheduleFetchMany(this.manyArray.toArray()).then(function () {
- return _this.manyArray;
+ this._loadingPromise = (0, _emberDataPrivateSystemPromiseProxies.promiseManyArray)(this.store._scheduleFetchMany(manyArray.currentState).then(function () {
+ return manyArray;
}), 'Reload with ids');
return this._loadingPromise;
}
};
@@ -7197,79 +7953,84 @@
this.addCanonicalRecord(record, i);
}
};
ManyRelationship.prototype.fetchLink = function () {
- var _this2 = this;
+ var _this = this;
return this.store.findHasMany(this.record, this.link, this.relationshipMeta).then(function (records) {
if (records.hasOwnProperty('meta')) {
- _this2.updateMeta(records.meta);
+ _this.updateMeta(records.meta);
}
- _this2.store._backburner.join(function () {
- _this2.updateRecordsFromAdapter(records);
- _this2.manyArray.set('isLoaded', true);
+ _this.store._backburner.join(function () {
+ _this.updateRecordsFromAdapter(records);
+ _this.getManyArray().set('isLoaded', true);
});
- return _this2.manyArray;
+ return _this.getManyArray();
});
};
ManyRelationship.prototype.findRecords = function () {
- var _this3 = this;
+ var manyArray = this.getManyArray();
+ var array = manyArray.toArray();
+ var internalModels = new Array(array.length);
- var manyArray = this.manyArray.toArray();
- var internalModels = new Array(manyArray.length);
-
- for (var i = 0; i < manyArray.length; i++) {
- internalModels[i] = manyArray[i]._internalModel;
+ for (var i = 0; i < array.length; i++) {
+ internalModels[i] = array[i]._internalModel;
}
//TODO CLEANUP
return this.store.findMany(internalModels).then(function () {
- if (!_this3.manyArray.get('isDestroyed')) {
+ if (!manyArray.get('isDestroyed')) {
//Goes away after the manyArray refactor
- _this3.manyArray.set('isLoaded', true);
+ manyArray.set('isLoaded', true);
}
- return _this3.manyArray;
+ return manyArray;
});
};
ManyRelationship.prototype.notifyHasManyChanged = function () {
this.record.notifyHasManyAdded(this.key);
};
ManyRelationship.prototype.getRecords = function () {
- var _this4 = this;
+ var _this2 = this;
//TODO(Igor) sync server here, once our syncing is not stupid
+ var manyArray = this.getManyArray();
if (this.isAsync) {
var promise;
if (this.link) {
if (this.hasLoaded) {
promise = this.findRecords();
} else {
promise = this.findLink().then(function () {
- return _this4.findRecords();
+ return _this2.findRecords();
});
}
} else {
promise = this.findRecords();
}
this._loadingPromise = _emberDataPrivateSystemPromiseProxies.PromiseManyArray.create({
- content: this.manyArray,
+ content: manyArray,
promise: promise
});
return this._loadingPromise;
} else {
//TODO(Igor) WTF DO I DO HERE?
- if (!this.manyArray.get('isDestroyed')) {
- this.manyArray.set('isLoaded', true);
+ if (!manyArray.get('isDestroyed')) {
+ manyArray.set('isLoaded', true);
}
- return this.manyArray;
+ return manyArray;
}
};
+ ManyRelationship.prototype.updateData = function (data) {
+ var internalModels = this.store._pushResourceIdentifiers(this, data);
+ this.updateRecordsFromAdapter(internalModels);
+ };
+
function setForArray(array) {
var set = new _emberDataPrivateSystemOrderedSet.default();
if (array) {
for (var i = 0, l = array.length; i < l; i++) {
@@ -7278,11 +8039,11 @@
}
return set;
}
});
-define("ember-data/-private/system/relationships/state/relationship", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/ordered-set"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemOrderedSet) {
+define("ember-data/-private/system/relationships/state/relationship", ["exports", "ember-data/-private/debug", "ember-data/-private/system/ordered-set", "ember-data/-private/system/normalize-link"], function (exports, _emberDataPrivateDebug, _emberDataPrivateSystemOrderedSet, _emberDataPrivateSystemNormalizeLink) {
exports.default = Relationship;
function Relationship(store, record, inverseKey, relationshipMeta) {
var async = relationshipMeta.options.async;
this.members = new _emberDataPrivateSystemOrderedSet.default();
@@ -7303,11 +8064,11 @@
}
Relationship.prototype = {
constructor: Relationship,
- destroy: _ember.default.K,
+ destroy: function () {},
updateMeta: function (meta) {
this.meta = meta;
},
@@ -7475,16 +8236,14 @@
return _this3.store._backburner.schedule('syncRelationships', _this3, _this3.flushCanonical);
});
},
updateLink: function (link) {
- if (link !== this.link) {
- this.link = link;
- this.linkPromise = null;
- this.setHasLoaded(false);
- this.record.notifyPropertyChange(this.key);
- }
+
+ this.link = link;
+ this.linkPromise = null;
+ this.record.notifyPropertyChange(this.key);
},
findLink: function () {
if (this.linkPromise) {
return this.linkPromise;
@@ -7499,16 +8258,14 @@
updateRecordsFromAdapter: function (records) {
//TODO(Igor) move this to a proper place
//TODO Once we have adapter support, we need to handle updated and canonical changes
this.computeChanges(records);
- this.setHasData(true);
- this.setHasLoaded(true);
},
- notifyRecordRelationshipAdded: _ember.default.K,
- notifyRecordRelationshipRemoved: _ember.default.K,
+ notifyRecordRelationshipAdded: function () {},
+ notifyRecordRelationshipRemoved: function () {},
/*
`hasData` for a relationship is a flag to indicate if we consider the
content of this relationship "known". Snapshots uses this to tell the
difference between unknown (`undefined`) or empty (`null`). The reason for
@@ -7529,11 +8286,60 @@
(`hasData === true`).
Updating the link will automatically set `hasLoaded` to `false`.
*/
setHasLoaded: function (value) {
this.hasLoaded = value;
- }
+ },
+
+ /*
+ `push` for a relationship allows the store to push a JSON API Relationship
+ Object onto the relationship. The relationship will then extract and set the
+ meta, data and links of that relationship.
+ `push` use `updateMeta`, `updateData` and `updateLink` to update the state
+ of the relationship.
+ */
+ push: function (payload) {
+
+ var hasData = false;
+ var hasLink = false;
+
+ if (payload.meta) {
+ this.updateMeta(payload.meta);
+ }
+
+ if (payload.data !== undefined) {
+ hasData = true;
+ this.updateData(payload.data);
+ }
+
+ if (payload.links && payload.links.related) {
+ var relatedLink = (0, _emberDataPrivateSystemNormalizeLink.default)(payload.links.related);
+ if (relatedLink && relatedLink.href && relatedLink.href !== this.link) {
+ hasLink = true;
+ this.updateLink(relatedLink.href);
+ }
+ }
+
+ /*
+ Data being pushed into the relationship might contain only data or links,
+ or a combination of both.
+ If we got data we want to set both hasData and hasLoaded to true since
+ this would indicate that we should prefer the local state instead of
+ trying to fetch the link or call findRecord().
+ If we have no data but a link is present we want to set hasLoaded to false
+ without modifying the hasData flag. This will ensure we fetch the updated
+ link next time the relationship is accessed.
+ */
+ if (hasData) {
+ this.setHasData(true);
+ this.setHasLoaded(true);
+ } else if (hasLink) {
+ this.setHasLoaded(false);
+ }
+ },
+
+ updateData: function () {}
};
});
/* global heimdall */
define('ember-data/-private/system/snapshot-record-array', ['exports'], function (exports) {
exports.default = SnapshotRecordArray;
@@ -7599,16 +8405,16 @@
Get snapshots of the underlying record array
@method snapshots
@return {Array} Array of snapshots
*/
SnapshotRecordArray.prototype.snapshots = function () {
- if (this._snapshots) {
+ if (this._snapshots !== null) {
return this._snapshots;
}
- var recordArray = this._recordArray;
- this._snapshots = recordArray.invoke('createSnapshot');
+ this._snapshots = this._recordArray._takeSnapshot();
+
return this._snapshots;
};
});
define("ember-data/-private/system/snapshot", ["exports", "ember", "ember-data/-private/system/empty-object"], function (exports, _ember, _emberDataPrivateSystemEmptyObject) {
exports.default = Snapshot;
@@ -7649,11 +8455,10 @@
A hash of adapter options
@property adapterOptions
@type {Object}
*/
this.adapterOptions = options.adapterOptions;
-
this.include = options.include;
this._changedAttributes = record.changedAttributes();
}
@@ -7934,39 +8739,46 @@
};
});
/**
@module ember-data
*/
-define('ember-data/-private/system/store', ['exports', 'ember', 'ember-data/model', 'ember-data/-private/debug', 'ember-data/-private/system/normalize-link', 'ember-data/-private/system/normalize-model-name', 'ember-data/adapters/errors', 'ember-data/-private/system/promise-proxies', 'ember-data/-private/system/store/common', 'ember-data/-private/system/store/serializer-response', 'ember-data/-private/system/store/serializers', 'ember-data/-private/system/store/finders', 'ember-data/-private/utils', 'ember-data/-private/system/coerce-id', 'ember-data/-private/system/record-array-manager', 'ember-data/-private/system/store/container-instance-cache', 'ember-data/-private/system/model/internal-model', 'ember-data/-private/system/empty-object', 'ember-data/-private/features'], function (exports, _ember, _emberDataModel, _emberDataPrivateDebug, _emberDataPrivateSystemNormalizeLink, _emberDataPrivateSystemNormalizeModelName, _emberDataAdaptersErrors, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemStoreCommon, _emberDataPrivateSystemStoreSerializerResponse, _emberDataPrivateSystemStoreSerializers, _emberDataPrivateSystemStoreFinders, _emberDataPrivateUtils, _emberDataPrivateSystemCoerceId, _emberDataPrivateSystemRecordArrayManager, _emberDataPrivateSystemStoreContainerInstanceCache, _emberDataPrivateSystemModelInternalModel, _emberDataPrivateSystemEmptyObject, _emberDataPrivateFeatures) {
+define('ember-data/-private/system/store', ['exports', 'ember', 'ember-data/model', 'ember-data/-private/debug', 'ember-data/-private/system/normalize-model-name', 'ember-data/adapters/errors', 'ember-data/-private/system/promise-proxies', 'ember-data/-private/system/store/common', 'ember-data/-private/system/store/serializer-response', 'ember-data/-private/system/store/serializers', 'ember-data/-private/system/store/finders', 'ember-data/-private/utils', 'ember-data/-private/system/coerce-id', 'ember-data/-private/system/record-array-manager', 'ember-data/-private/system/store/container-instance-cache', 'ember-data/-private/system/model/internal-model', 'ember-data/-private/system/empty-object', 'ember-data/-private/features'], function (exports, _ember, _emberDataModel, _emberDataPrivateDebug, _emberDataPrivateSystemNormalizeModelName, _emberDataAdaptersErrors, _emberDataPrivateSystemPromiseProxies, _emberDataPrivateSystemStoreCommon, _emberDataPrivateSystemStoreSerializerResponse, _emberDataPrivateSystemStoreSerializers, _emberDataPrivateSystemStoreFinders, _emberDataPrivateUtils, _emberDataPrivateSystemCoerceId, _emberDataPrivateSystemRecordArrayManager, _emberDataPrivateSystemStoreContainerInstanceCache, _emberDataPrivateSystemModelInternalModel, _emberDataPrivateSystemEmptyObject, _emberDataPrivateFeatures) {
var badIdFormatAssertion = '`id` passed to `findRecord()` has to be non-empty string or number';
exports.badIdFormatAssertion = badIdFormatAssertion;
+ var A = _ember.default.A;
var Backburner = _ember.default._Backburner;
- var Map = _ember.default.Map;
+ var computed = _ember.default.computed;
+ var copy = _ember.default.copy;
+ var ENV = _ember.default.ENV;
+ var EmberError = _ember.default.Error;
+ var get = _ember.default.get;
+ var guidFor = _ember.default.guidFor;
+ var inspect = _ember.default.inspect;
+ var isNone = _ember.default.isNone;
+ var isPresent = _ember.default.isPresent;
+ var MapWithDefault = _ember.default.MapWithDefault;
+ var emberRun = _ember.default.run;
+ var set = _ember.default.set;
+ var RSVP = _ember.default.RSVP;
+ var Service = _ember.default.Service;
+ var typeOf = _ember.default.typeOf;
+ var Promise = RSVP.Promise;
//Get the materialized model from the internalModel/promise that returns
//an internal model and return it in a promiseObject. Useful for returning
//from find methods
- function promiseRecord(internalModel, label) {
- var toReturn = internalModel.then(function (model) {
- return model.getRecord();
+ function promiseRecord(internalModelPromise, label) {
+ var toReturn = internalModelPromise.then(function (internalModel) {
+ return internalModel.getRecord();
});
+
return (0, _emberDataPrivateSystemPromiseProxies.promiseObject)(toReturn, label);
}
- var once = _ember.default.run.once;
- var Promise = _ember.default.RSVP.Promise;
- var Store;
+ var Store = undefined;
- var copy = _ember.default.copy;
- var get = _ember.default.get;
- var GUID_KEY = _ember.default.GUID_KEY;
- var isNone = _ember.default.isNone;
- var isPresent = _ember.default.isPresent;
- var set = _ember.default.set;
- var Service = _ember.default.Service;
-
// Implementors Note:
//
// The variables in this file are consistently named according to the following
// scheme:
//
@@ -8066,25 +8878,32 @@
this.typeMaps = {};
this.recordArrayManager = _emberDataPrivateSystemRecordArrayManager.default.create({
store: this
});
this._pendingSave = [];
- this._instanceCache = new _emberDataPrivateSystemStoreContainerInstanceCache.default((0, _emberDataPrivateUtils.getOwner)(this));
+ this._instanceCache = new _emberDataPrivateSystemStoreContainerInstanceCache.default((0, _emberDataPrivateUtils.getOwner)(this), this);
+
//Used to keep track of all the find requests that need to be coalesced
- this._pendingFetch = Map.create();
+ this._pendingFetch = MapWithDefault.create({ defaultValue: function () {
+ return [];
+ } });
},
/**
- The adapter to use to communicate to a backend server or other persistence layer.
- This can be specified as an instance, class, or string.
+ The default adapter to use to communicate to a backend server or
+ other persistence layer. This will be overridden by an application
+ adapter if present.
If you want to specify `app/adapters/custom.js` as a string, do:
```js
- adapter: 'custom'
+ import DS from 'ember-data';
+ export default DS.Store.extend({
+ adapter: 'custom',
+ });
```
@property adapter
- @default DS.JSONAPIAdapter
- @type {(DS.Adapter|String)}
+ @default '-json-api'
+ @type {String}
*/
adapter: '-json-api',
/**
Returns a JSON representation of the record using a custom
@@ -8092,14 +8911,16 @@
The available options are:
* `includeId`: `true` if the record's ID should be included in
the JSON representation
@method serialize
@private
+ @deprecated
@param {DS.Model} record the record to serialize
@param {Object} options an options hash
*/
serialize: function (record, options) {
+ if (true) {}
var snapshot = record._internalModel.createSnapshot();
return snapshot.serialize(options);
},
/**
@@ -8112,16 +8933,14 @@
adapter class should be used for the lifetime of the store.
@property defaultAdapter
@private
@return DS.Adapter
*/
- defaultAdapter: _ember.default.computed('adapter', function () {
+ defaultAdapter: computed('adapter', function () {
var adapter = get(this, 'adapter');
- adapter = this.retrieveManagedInstance('adapter', adapter);
-
- return adapter;
+ return this.adapterFor(adapter);
}),
// .....................
// . CREATE NEW RECORD .
// .....................
@@ -8135,11 +8954,11 @@
title: "Rails is omakase"
});
```
To create a new instance of a `Post` that has a relationship with a `User` record:
```js
- var user = this.store.peekRecord('user', 1);
+ let user = this.store.peekRecord('user', 1);
store.createRecord('post', {
title: "Rails is omakase",
user: user
});
```
@@ -8148,11 +8967,11 @@
@param {Object} inputProperties a hash of properties to set on the
newly created record.
@return {DS.Model} record
*/
createRecord: function (modelName, inputProperties) {
- var typeClass = this.modelFor(modelName);
+ var modelClass = this.modelFor(modelName);
var properties = copy(inputProperties) || new _emberDataPrivateSystemEmptyObject.default();
// If the passed properties do not include a primary key,
// give the adapter an opportunity to generate one. Typically,
// client-side ID generators will use something like uuid.js
@@ -8163,20 +8982,23 @@
}
// Coerce ID to a string
properties.id = (0, _emberDataPrivateSystemCoerceId.default)(properties.id);
- var internalModel = this.buildInternalModel(typeClass, properties.id);
+ var internalModel = this.buildInternalModel(modelClass, properties.id);
var record = internalModel.getRecord();
// Move the record out of its initial `empty` state into
// the `loaded` state.
+ // TODO @runspired this seems really bad, store should not be changing the state
internalModel.loadedData();
// Set the properties specified on the record.
+ // TODO @runspired this is probably why we do the bad thing above
record.setProperties(properties);
+ // TODO @runspired this should also be coalesced into some form of internalModel.setState()
internalModel.eachRelationship(function (key, descriptor) {
internalModel._relationships.get(key).setHasData(true);
});
return record;
@@ -8207,11 +9029,11 @@
/**
For symmetry, a record can be deleted via the store.
Example
```javascript
- var post = store.createRecord('post', {
+ let post = store.createRecord('post', {
title: "Rails is omakase"
});
store.deleteRecord(post);
```
@method deleteRecord
@@ -8254,11 +9076,11 @@
// that's why we have to keep this method around even though `findRecord` is
// the public way to get a record by modelName and id.
if (arguments.length === 1) {}
- if (_ember.default.typeOf(id) === 'object') {}
+ if (typeOf(id) === 'object') {}
if (options) {}
return this.findRecord(modelName, id);
},
@@ -8331,11 +9153,11 @@
id: 1,
type: 'post',
revision: 1
}
});
- var blogPost = store.findRecord('post', 1).then(function(post) {
+ let blogPost = store.findRecord('post', 1).then(function(post) {
post.get('revision'); // 1
});
// later, once adapter#findRecord resolved with
// [
// {
@@ -8379,10 +9201,43 @@
// ...
}
});
```
See [peekRecord](#method_peekRecord) to get the cached version of a record.
+ ### Retrieving Related Model Records
+ If you use an adapter such as Ember's default
+ [`JSONAPIAdapter`](http://emberjs.com/api/data/classes/DS.JSONAPIAdapter.html)
+ that supports the [JSON API specification](http://jsonapi.org/) and if your server
+ endpoint supports the use of an
+ ['include' query parameter](http://jsonapi.org/format/#fetching-includes),
+ you can use `findRecord()` to automatically retrieve additional records related to
+ the one you request by supplying an `include` parameter in the `options` object.
+ For example, given a `post` model that has a `hasMany` relationship with a `comment`
+ model, when we retrieve a specific post we can have the server also return that post's
+ comments in the same request:
+ ```app/routes/post.js
+ import Ember from 'ember';
+ export default Ember.Route.extend({
+ model: function(params) {
+ return this.store.findRecord('post', params.post_id, {include: 'comments'});
+ }
+ });
+ ```
+ In this case, the post's comments would then be available in your template as
+ `model.comments`.
+ Multiple relationships can be requested using an `include` parameter consisting of a
+ comma-separated list (without white-space) while nested relationships can be specified
+ using a dot-separated sequence of relationship names. So to request both the post's
+ comments and the authors of those comments the request would look like this:
+ ```app/routes/post.js
+ import Ember from 'ember';
+ export default Ember.Route.extend({
+ model: function(params) {
+ return this.store.findRecord('post', params.post_id, {include: 'comments,comments.author'});
+ }
+ });
+ ```
@since 1.13.0
@method findRecord
@param {String} modelName
@param {(String|Integer)} id
@param {Object} options
@@ -8403,50 +9258,50 @@
},
_findRecord: function (internalModel, options) {
// Refetch if the reload option is passed
if (options.reload) {
- return this.scheduleFetch(internalModel, options);
+ return this._scheduleFetch(internalModel, options);
}
var snapshot = internalModel.createSnapshot(options);
- var typeClass = internalModel.type;
- var adapter = this.adapterFor(typeClass.modelName);
+ var modelClass = internalModel.type;
+ var adapter = this.adapterFor(modelClass.modelName);
// Refetch the record if the adapter thinks the record is stale
if (adapter.shouldReloadRecord(this, snapshot)) {
- return this.scheduleFetch(internalModel, options);
+ return this._scheduleFetch(internalModel, options);
}
if (options.backgroundReload === false) {
return Promise.resolve(internalModel);
}
// Trigger the background refetch if backgroundReload option is passed
if (options.backgroundReload || adapter.shouldBackgroundReloadRecord(this, snapshot)) {
- this.scheduleFetch(internalModel, options);
+ this._scheduleFetch(internalModel, options);
}
// Return the cached record
return Promise.resolve(internalModel);
},
_findByInternalModel: function (internalModel, options) {
options = options || {};
if (options.preload) {
- internalModel._preloadData(options.preload);
+ internalModel.preloadData(options.preload);
}
var fetchedInternalModel = this._findEmptyInternalModel(internalModel, options);
return promiseRecord(fetchedInternalModel, "DS: Store#findRecord " + internalModel.typeKey + " with id: " + get(internalModel, 'id'));
},
_findEmptyInternalModel: function (internalModel, options) {
if (internalModel.isEmpty()) {
- return this.scheduleFetch(internalModel, options);
+ return this._scheduleFetch(internalModel, options);
}
//TODO double check about reloading
if (internalModel.isLoading()) {
return internalModel._loadingPromise;
@@ -8469,175 +9324,197 @@
for (var i = 0; i < ids.length; i++) {
promises[i] = this.findRecord(modelName, ids[i]);
}
- return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)(_ember.default.RSVP.all(promises).then(_ember.default.A, null, "DS: Store#findByIds of " + modelName + " complete"));
+ return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)(RSVP.all(promises).then(A, null, "DS: Store#findByIds of " + modelName + " complete"));
},
/**
This method is called by `findRecord` if it discovers that a particular
type/id pair hasn't been loaded yet to kick off a request to the
adapter.
- @method fetchRecord
+ @method _fetchRecord
@private
@param {InternalModel} internalModel model
@return {Promise} promise
*/
- // TODO rename this to have an underscore
- fetchRecord: function (internalModel, options) {
- var typeClass = internalModel.type;
+ _fetchRecord: function (internalModel, options) {
+ var modelClass = internalModel.type;
var id = internalModel.id;
- var adapter = this.adapterFor(typeClass.modelName);
+ var adapter = this.adapterFor(modelClass.modelName);
- var promise = (0, _emberDataPrivateSystemStoreFinders._find)(adapter, this, typeClass, id, internalModel, options);
- return promise;
+ return (0, _emberDataPrivateSystemStoreFinders._find)(adapter, this, modelClass, id, internalModel, options);
},
- scheduleFetchMany: function (records) {
- var internalModels = new Array(records.length);
- var fetches = new Array(records.length);
- for (var i = 0; i < records.length; i++) {
- internalModels[i] = records[i]._internalModel;
- }
+ _scheduleFetchMany: function (internalModels) {
+ var fetches = new Array(internalModels.length);
for (var i = 0; i < internalModels.length; i++) {
- fetches[i] = this.scheduleFetch(internalModels[i]);
+ fetches[i] = this._scheduleFetch(internalModels[i]);
}
- return _ember.default.RSVP.Promise.all(fetches);
+ return Promise.all(fetches);
},
- scheduleFetch: function (internalModel, options) {
- var typeClass = internalModel.type;
-
+ _scheduleFetch: function (internalModel, options) {
if (internalModel._loadingPromise) {
return internalModel._loadingPromise;
}
- var resolver = _ember.default.RSVP.defer('Fetching ' + typeClass + 'with id: ' + internalModel.id);
+ var modelClass = internalModel.type;
+ var resolver = RSVP.defer('Fetching ' + modelClass.modelName + ' with id: ' + internalModel.id);
var pendingFetchItem = {
- record: internalModel,
+ internalModel: internalModel,
resolver: resolver,
options: options
};
var promise = resolver.promise;
internalModel.loadingData(promise);
+ this._pendingFetch.get(modelClass).push(pendingFetchItem);
- if (!this._pendingFetch.get(typeClass)) {
- this._pendingFetch.set(typeClass, [pendingFetchItem]);
- } else {
- this._pendingFetch.get(typeClass).push(pendingFetchItem);
- }
- _ember.default.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches);
+ emberRun.scheduleOnce('afterRender', this, this.flushAllPendingFetches);
return promise;
},
flushAllPendingFetches: function () {
if (this.isDestroyed || this.isDestroying) {
return;
}
this._pendingFetch.forEach(this._flushPendingFetchForType, this);
- this._pendingFetch = Map.create();
+ this._pendingFetch.clear();
},
- _flushPendingFetchForType: function (pendingFetchItems, typeClass) {
+ _flushPendingFetchForType: function (pendingFetchItems, modelClass) {
var store = this;
- var adapter = store.adapterFor(typeClass.modelName);
+ var adapter = store.adapterFor(modelClass.modelName);
var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests;
- var records = _ember.default.A(pendingFetchItems).mapBy('record');
+ var totalItems = pendingFetchItems.length;
+ var internalModels = new Array(totalItems);
+ var seeking = new _emberDataPrivateSystemEmptyObject.default();
+ for (var i = 0; i < totalItems; i++) {
+ var pendingItem = pendingFetchItems[i];
+ var internalModel = pendingItem.internalModel;
+ internalModels[i] = internalModel;
+ seeking[internalModel.id] = pendingItem;
+ }
+
function _fetchRecord(recordResolverPair) {
- recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record, recordResolverPair.options)); // TODO adapter options
+ var recordFetch = store._fetchRecord(recordResolverPair.internalModel, recordResolverPair.options); // TODO adapter options
+
+ recordResolverPair.resolver.resolve(recordFetch);
}
- function resolveFoundRecords(records) {
- records.forEach(function (record) {
- var pair = _ember.default.A(pendingFetchItems).findBy('record', record);
+ function handleFoundRecords(foundInternalModels, expectedInternalModels) {
+ // resolve found records
+ var found = new _emberDataPrivateSystemEmptyObject.default();
+ for (var i = 0, l = foundInternalModels.length; i < l; i++) {
+ var internalModel = foundInternalModels[i];
+ var pair = seeking[internalModel.id];
+ found[internalModel.id] = internalModel;
+
if (pair) {
var resolver = pair.resolver;
- resolver.resolve(record);
+ resolver.resolve(internalModel);
}
- });
- return records;
- }
+ }
- function makeMissingRecordsRejector(requestedRecords) {
- return function rejectMissingRecords(resolvedRecords) {
- resolvedRecords = _ember.default.A(resolvedRecords);
- var missingRecords = requestedRecords.reject(function (record) {
- return resolvedRecords.includes(record);
- });
- if (missingRecords.length) {}
- rejectRecords(missingRecords);
- };
- }
+ // reject missing records
+ var missingInternalModels = [];
- function makeRecordsRejector(records) {
- return function (error) {
- rejectRecords(records, error);
- };
+ for (var i = 0, l = expectedInternalModels.length; i < l; i++) {
+ var internalModel = expectedInternalModels[i];
+
+ if (!found[internalModel.id]) {
+ missingInternalModels.push(internalModel);
+ }
+ }
+
+ if (missingInternalModels.length) {
+ rejectInternalModels(missingInternalModels);
+ }
}
- function rejectRecords(records, error) {
- records.forEach(function (record) {
- var pair = _ember.default.A(pendingFetchItems).findBy('record', record);
+ function rejectInternalModels(internalModels, error) {
+ for (var i = 0, l = internalModels.length; i < l; i++) {
+ var pair = seeking[internalModels[i].id];
+
if (pair) {
- var resolver = pair.resolver;
- resolver.reject(error);
+ pair.resolver.reject(error);
}
- });
+ }
}
- if (pendingFetchItems.length === 1) {
- _fetchRecord(pendingFetchItems[0]);
- } else if (shouldCoalesce) {
-
+ if (shouldCoalesce) {
// TODO: Improve records => snapshots => records => snapshots
//
// We want to provide records to all store methods and snapshots to all
// adapter methods. To make sure we're doing that we're providing an array
// of snapshots to adapter.groupRecordsForFindMany(), which in turn will
// return grouped snapshots instead of grouped records.
//
// But since the _findMany() finder is a store method we need to get the
// records from the grouped snapshots even though the _findMany() finder
// will once again convert the records to snapshots for adapter.findMany()
+ var snapshots = new Array(totalItems);
+ for (var i = 0; i < totalItems; i++) {
+ snapshots[i] = internalModels[i].createSnapshot();
+ }
- var snapshots = _ember.default.A(records).invoke('createSnapshot');
var groups = adapter.groupRecordsForFindMany(this, snapshots);
- groups.forEach(function (groupOfSnapshots) {
- var groupOfRecords = _ember.default.A(groupOfSnapshots).mapBy('_internalModel');
- var requestedRecords = _ember.default.A(groupOfRecords);
- var ids = requestedRecords.mapBy('id');
- if (ids.length > 1) {
- (0, _emberDataPrivateSystemStoreFinders._findMany)(adapter, store, typeClass, ids, requestedRecords).then(resolveFoundRecords).then(makeMissingRecordsRejector(requestedRecords)).then(null, makeRecordsRejector(requestedRecords));
+
+ var _loop = function (i, l) {
+ var group = groups[i];
+ var totalInGroup = groups[i].length;
+ var ids = new Array(totalInGroup);
+ var groupedInternalModels = new Array(totalInGroup);
+
+ for (var j = 0; j < totalInGroup; j++) {
+ var internalModel = group[j]._internalModel;
+
+ groupedInternalModels[j] = internalModel;
+ ids[j] = internalModel.id;
+ }
+
+ if (totalInGroup > 1) {
+ (0, _emberDataPrivateSystemStoreFinders._findMany)(adapter, store, modelClass, ids, groupedInternalModels).then(function (foundInternalModels) {
+ handleFoundRecords(foundInternalModels, groupedInternalModels);
+ }).catch(function (error) {
+ rejectInternalModels(groupedInternalModels, error);
+ });
} else if (ids.length === 1) {
- var pair = _ember.default.A(pendingFetchItems).findBy('record', groupOfRecords[0]);
+ var pair = seeking[groupedInternalModels[0].id];
_fetchRecord(pair);
} else {}
- });
+ };
+
+ for (var i = 0, l = groups.length; i < l; i++) {
+ _loop(i, l);
+ }
} else {
- pendingFetchItems.forEach(_fetchRecord);
+ for (var i = 0; i < totalItems; i++) {
+ _fetchRecord(pendingFetchItems[i]);
+ }
}
},
/**
Get the reference for the specified record.
Example
```javascript
- var userRef = store.getReference('user', 1);
+ let userRef = store.getReference('user', 1);
// check if the user is loaded
- var isLoaded = userRef.value() !== null;
+ let isLoaded = userRef.value() !== null;
// get the record of the reference (null if not yet available)
- var user = userRef.value();
+ let user = userRef.value();
// get the identifier of the reference
if (userRef.remoteType() === "id") {
- var id = userRef.id();
+ let id = userRef.id();
}
// load user (via store.find)
userRef.load().then(...)
// or trigger a reload
userRef.reload().then(...)
@@ -8661,11 +9538,11 @@
This method will synchronously return the record if it is available in the store,
otherwise it will return `null`. A record is available if it has been fetched earlier, or
pushed manually into the store.
_Note: This is an synchronous method and does not return a promise._
```js
- var post = store.peekRecord('post', 1);
+ let post = store.peekRecord('post', 1);
post.get('id'); // 1
```
@since 1.13.0
@method peekRecord
@param {String} modelName
@@ -8688,29 +9565,41 @@
@method reloadRecord
@private
@param {DS.Model} internalModel
@return {Promise} promise
*/
+ // TODO @runspired this should be underscored
reloadRecord: function (internalModel) {
var modelName = internalModel.type.modelName;
var adapter = this.adapterFor(modelName);
var id = internalModel.id;
- return this.scheduleFetch(internalModel);
+ return this._scheduleFetch(internalModel);
},
/**
- Returns true if a record for a given type and ID is already loaded.
+ This method returns true if a record for a given modelName and id is already
+ loaded in the store. Use this function to know beforehand if a findRecord()
+ will result in a request or that it will be a cache hit.
+ Example
+ ```javascript
+ store.hasRecordForId('post', 1); // false
+ store.findRecord('post', 1).then(function() {
+ store.hasRecordForId('post', 1); // true
+ });
+ ```
@method hasRecordForId
- @param {(String|DS.Model)} modelName
- @param {(String|Integer)} inputId
+ @param {String} modelName
+ @param {(String|Integer)} id
@return {Boolean}
*/
- hasRecordForId: function (modelName, inputId) {
- var typeClass = this.modelFor(modelName);
- var id = (0, _emberDataPrivateSystemCoerceId.default)(inputId);
- var internalModel = this.typeMapFor(typeClass).idToRecord[id];
+ hasRecordForId: function (modelName, id) {
+
+ var trueId = (0, _emberDataPrivateSystemCoerceId.default)(id);
+ var modelClass = this.modelFor(modelName);
+ var internalModel = this.typeMapFor(modelClass).idToRecord[trueId];
+
return !!internalModel && internalModel.isLoaded();
},
/**
Returns id record for a given type and ID. If one isn't already loaded,
@@ -8723,21 +9612,21 @@
*/
recordForId: function (modelName, id) {
return this._internalModelForId(modelName, id).getRecord();
},
- _internalModelForId: function (typeName, inputId) {
- var typeClass = this.modelFor(typeName);
+ _internalModelForId: function (modelName, inputId) {
+ var modelClass = this.modelFor(modelName);
var id = (0, _emberDataPrivateSystemCoerceId.default)(inputId);
- var idToRecord = this.typeMapFor(typeClass).idToRecord;
- var record = idToRecord[id];
+ var idToRecord = this.typeMapFor(modelClass).idToRecord;
+ var internalModel = idToRecord[id];
- if (!record || !idToRecord[id]) {
- record = this.buildInternalModel(typeClass, id);
+ if (!internalModel || !idToRecord[id]) {
+ internalModel = this.buildInternalModel(modelClass, id);
}
- return record;
+ return internalModel;
},
/**
@method findMany
@private
@@ -8829,23 +9718,25 @@
query: function (modelName, query) {
return this._query(modelName, query);
},
_query: function (modelName, query, array) {
- var typeClass = this.modelFor(modelName);
- array = array || this.recordArrayManager.createAdapterPopulatedRecordArray(typeClass, query);
+ var modelClass = this.modelFor(modelName);
+ array = array || this.recordArrayManager.createAdapterPopulatedRecordArray(modelClass, query);
+
var adapter = this.adapterFor(modelName);
- var pA = (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._query)(adapter, this, typeClass, query, array));
+ var pA = (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._query)(adapter, this, modelClass, query, array));
return pA;
},
/**
This method makes a request for one record, where the `id` is not known
- beforehand (if the `id` is known, use `findRecord` instead).
+ beforehand (if the `id` is known, use [`findRecord`](#method_findRecord)
+ instead).
This method can be used when it is certain that the server will return a
single object for the primary data.
Let's assume our API provides an endpoint for the currently logged in user
via:
```
@@ -8864,12 +9755,11 @@
let username = user.get('username');
console.log(`Currently logged in as ${username}`);
});
```
The request is made through the adapters' `queryRecord`:
- ```javascript
- // app/adapters/user.js
+ ```app/adapters/user.js
import DS from "ember-data";
export default DS.Adapter.extend({
queryRecord(modelName, query) {
return Ember.$.getJSON("/api/current_user");
}
@@ -8918,14 +9808,22 @@
@param {any} query an opaque query to be used by the adapter
@return {Promise} promise which resolves with the found record or `null`
*/
queryRecord: function (modelName, query) {
- var typeClass = this.modelFor(modelName);
+ var modelClass = this.modelFor(modelName);
var adapter = this.adapterFor(modelName);
- return (0, _emberDataPrivateSystemPromiseProxies.promiseObject)((0, _emberDataPrivateSystemStoreFinders._queryRecord)(adapter, this, typeClass, query));
+ return (0, _emberDataPrivateSystemPromiseProxies.promiseObject)((0, _emberDataPrivateSystemStoreFinders._queryRecord)(adapter, this, modelClass, query).then(function (internalModel) {
+ // the promise returned by store.queryRecord is expected to resolve with
+ // an instance of DS.Model
+ if (internalModel) {
+ return internalModel.getRecord();
+ }
+
+ return null;
+ }));
},
/**
`findAll` asks the adapter's `findAll` method to find the records for the
given type, and returns a promise which will resolve with all records of
@@ -8986,11 +9884,11 @@
data: {
id: 'first',
type: 'author'
}
});
- var allAuthors;
+ let allAuthors;
store.findAll('author').then(function(authors) {
authors.getEach('id'); // ['first']
allAuthors = authors;
});
// later, once adapter#findAll resolved with
@@ -9036,69 +9934,101 @@
}
});
```
See [peekAll](#method_peekAll) to get an array of current records in the
store, without waiting until a reload is finished.
+ ### Retrieving Related Model Records
+ If you use an adapter such as Ember's default
+ [`JSONAPIAdapter`](http://emberjs.com/api/data/classes/DS.JSONAPIAdapter.html)
+ that supports the [JSON API specification](http://jsonapi.org/) and if your server
+ endpoint supports the use of an
+ ['include' query parameter](http://jsonapi.org/format/#fetching-includes),
+ you can use `findAll()` to automatically retrieve additional records related to
+ those requested by supplying an `include` parameter in the `options` object.
+ For example, given a `post` model that has a `hasMany` relationship with a `comment`
+ model, when we retrieve all of the post records we can have the server also return
+ all of the posts' comments in the same request:
+ ```app/routes/posts.js
+ import Ember from 'ember';
+ export default Ember.Route.extend({
+ model: function() {
+ return this.store.findAll('post', {include: 'comments'});
+ }
+ });
+ ```
+ Multiple relationships can be requested using an `include` parameter consisting of a
+ comma-separated list (without white-space) while nested relationships can be specified
+ using a dot-separated sequence of relationship names. So to request both the posts'
+ comments and the authors of those comments the request would look like this:
+ ```app/routes/posts.js
+ import Ember from 'ember';
+ export default Ember.Route.extend({
+ model: function() {
+ return this.store.findAll('post', {include: 'comments,comments.author'});
+ }
+ });
+ ```
See [query](#method_query) to only get a subset of records from the server.
@since 1.13.0
@method findAll
@param {String} modelName
@param {Object} options
@return {Promise} promise
*/
findAll: function (modelName, options) {
+ var modelClass = this.modelFor(modelName);
- var typeClass = this.modelFor(modelName);
+ var fetch = this._fetchAll(modelClass, this.peekAll(modelName), options);
- var fetch = this._fetchAll(typeClass, this.peekAll(modelName), options);
-
return fetch;
},
/**
@method _fetchAll
@private
- @param {DS.Model} typeClass
+ @param {DS.Model} modelClass
@param {DS.RecordArray} array
@return {Promise} promise
*/
- _fetchAll: function (typeClass, array, options) {
+ _fetchAll: function (modelClass, array, options) {
options = options || {};
- var adapter = this.adapterFor(typeClass.modelName);
- var sinceToken = this.typeMapFor(typeClass).metadata.since;
+ var adapter = this.adapterFor(modelClass.modelName);
+ var sinceToken = this.typeMapFor(modelClass).metadata.since;
+
if (options.reload) {
set(array, 'isUpdating', true);
- return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, typeClass, sinceToken, options));
+ return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, modelClass, sinceToken, options));
}
- var snapshotArray = array.createSnapshot(options);
+ var snapshotArray = array._createSnapshot(options);
if (adapter.shouldReloadAll(this, snapshotArray)) {
set(array, 'isUpdating', true);
- return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, typeClass, sinceToken, options));
+ return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)((0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, modelClass, sinceToken, options));
}
if (options.backgroundReload === false) {
return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)(Promise.resolve(array));
}
if (options.backgroundReload || adapter.shouldBackgroundReloadAll(this, snapshotArray)) {
set(array, 'isUpdating', true);
- (0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, typeClass, sinceToken, options);
+ (0, _emberDataPrivateSystemStoreFinders._findAll)(adapter, this, modelClass, sinceToken, options);
}
return (0, _emberDataPrivateSystemPromiseProxies.promiseArray)(Promise.resolve(array));
},
/**
@method didUpdateAll
- @param {DS.Model} typeClass
+ @param {DS.Model} modelClass
@private
*/
- didUpdateAll: function (typeClass) {
- var liveRecordArray = this.recordArrayManager.liveRecordArrayFor(typeClass);
+ didUpdateAll: function (modelClass) {
+ var liveRecordArray = this.recordArrayManager.liveRecordArrayFor(modelClass);
+
set(liveRecordArray, 'isUpdating', false);
},
/**
This method returns a filtered array that contains all of the
@@ -9110,37 +10040,39 @@
[store.findAll](#method_findAll).
Also note that multiple calls to `peekAll` for a given type will always
return the same `RecordArray`.
Example
```javascript
- var localPosts = store.peekAll('post');
+ let localPosts = store.peekAll('post');
```
@since 1.13.0
@method peekAll
@param {String} modelName
@return {DS.RecordArray}
*/
peekAll: function (modelName) {
- var typeClass = this.modelFor(modelName);
+ var modelClass = this.modelFor(modelName);
+ var liveRecordArray = this.recordArrayManager.liveRecordArrayFor(modelClass);
- var liveRecordArray = this.recordArrayManager.liveRecordArrayFor(typeClass);
- this.recordArrayManager.populateLiveRecordArray(liveRecordArray, typeClass);
+ this.recordArrayManager.syncLiveRecordArray(liveRecordArray, modelClass);
return liveRecordArray;
},
/**
This method unloads all records in the store.
+ It schedules unloading to happen during the next run loop.
Optionally you can pass a type which unload all records for a given type.
```javascript
store.unloadAll();
store.unloadAll('post');
```
@method unloadAll
@param {String} modelName
*/
unloadAll: function (modelName) {
+
if (arguments.length === 0) {
var typeMaps = this.typeMaps;
var keys = Object.keys(typeMaps);
var types = new Array(keys.length);
@@ -9148,12 +10080,12 @@
types[i] = typeMaps[keys[i]]['type'].modelName;
}
types.forEach(this.unloadAll, this);
} else {
- var typeClass = this.modelFor(modelName);
- var typeMap = this.typeMapFor(typeClass);
+ var modelClass = this.modelFor(modelName);
+ var typeMap = this.typeMapFor(modelClass);
var records = typeMap.records.slice();
var record = undefined;
for (var i = 0; i < records.length; i++) {
record = records[i];
@@ -9194,11 +10126,11 @@
```javascript
store.filter('post', { unread: true }, function(post) {
return post.get('unread');
}).then(function(unreadPosts) {
unreadPosts.get('length'); // 5
- var unreadPost = unreadPosts.objectAt(0);
+ let unreadPost = unreadPosts.objectAt(0);
unreadPost.set('unread', false);
unreadPosts.get('length'); // 4
});
```
@method filter
@@ -9209,15 +10141,15 @@
@return {DS.PromiseArray}
@deprecated
*/
filter: function (modelName, query, filter) {
- if (!_ember.default.ENV.ENABLE_DS_FILTER) {}
+ if (!ENV.ENABLE_DS_FILTER) {}
- var promise;
+ var promise = undefined;
var length = arguments.length;
- var array;
+ var array = undefined;
var hasQuery = length === 3;
// allow an optional server query
if (hasQuery) {
promise = this.query(modelName, query);
@@ -9239,21 +10171,14 @@
return array;
}, null, 'DS: Store#filter of ' + modelName));
},
/**
- This method returns if a certain record is already loaded
- in the store. Use this function to know beforehand if a findRecord()
- will result in a request or that it will be a cache hit.
- Example
- ```javascript
- store.recordIsLoaded('post', 1); // false
- store.findRecord('post', 1).then(function() {
- store.recordIsLoaded('post', 1); // true
- });
- ```
- @method recordIsLoaded
+ This method has been deprecated and is an alias for store.hasRecordForId, which should
+ be used instead.
+ @deprecated
+ @method recordIsLoaded
@param {String} modelName
@param {string} id
@return {boolean}
*/
recordIsLoaded: function (modelName, id) {
@@ -9298,11 +10223,11 @@
internalModel.adapterWillCommit();
this._pendingSave.push({
snapshot: snapshot,
resolver: resolver
});
- once(this, 'flushPendingSave');
+ emberRun.once(this, this.flushPendingSave);
},
/**
This method is called at the end of the run loop, and
flushes any records passed into `scheduleSave`
@@ -9317,12 +10242,12 @@
pending.forEach(function (pendingItem) {
var snapshot = pendingItem.snapshot;
var resolver = pendingItem.resolver;
var record = snapshot._internalModel;
- var adapter = _this.adapterFor(record.type.modelName);
- var operation;
+ var adapter = _this.adapterFor(record.modelClass.modelName);
+ var operation = undefined;
if (get(record, 'currentState.stateName') === 'root.deleted.saved') {
return resolver.resolve();
} else if (record.isNew()) {
operation = 'createRecord';
@@ -9346,17 +10271,17 @@
@private
@param {InternalModel} internalModel the in-flight internal model
@param {Object} data optional data (see above)
*/
didSaveRecord: function (internalModel, dataArg) {
- var data;
+ var data = undefined;
if (dataArg) {
data = dataArg.data;
}
if (data) {
// normalize relationship IDs into records
- this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', internalModel, data);
+ this._backburner.schedule('normalizeRelationships', this, this._setupRelationships, internalModel, data);
this.updateId(internalModel, data);
} else {}
//We first make sure the primary data has been updated
//TODO try to move notification to the user to the end of the runloop
@@ -9419,27 +10344,27 @@
/**
Returns a map of IDs to client IDs for a given type.
@method typeMapFor
@private
- @param {DS.Model} typeClass
+ @param {DS.Model} modelClass
@return {Object} typeMap
*/
- typeMapFor: function (typeClass) {
+ typeMapFor: function (modelClass) {
var typeMaps = get(this, 'typeMaps');
- var guid = _ember.default.guidFor(typeClass);
+ var guid = guidFor(modelClass);
var typeMap = typeMaps[guid];
if (typeMap) {
return typeMap;
}
typeMap = {
idToRecord: new _emberDataPrivateSystemEmptyObject.default(),
records: [],
metadata: new _emberDataPrivateSystemEmptyObject.default(),
- type: typeClass
+ type: modelClass
};
typeMaps[guid] = typeMap;
return typeMap;
@@ -9467,14 +10392,14 @@
},
/*
In case someone defined a relationship to a mixin, for example:
```
- var Comment = DS.Model.extend({
+ let Comment = DS.Model.extend({
owner: belongsTo('commentable'. { polymorphic: true})
});
- var Commentable = Ember.Mixin.create({
+ let Commentable = Ember.Mixin.create({
comments: hasMany('comment')
});
```
we want to look up a Commentable class which has all the necessary
relationship metadata. Thus, we look up the mixin and create a mock
@@ -9519,11 +10444,11 @@
if (!factory) {
//Support looking up mixins as base types for polymorphic relationships
factory = this._modelForMixin(modelName);
}
if (!factory) {
- throw new _ember.default.Error("No model was found for '" + modelName + "'");
+ throw new EmberError("No model was found for '" + modelName + "'");
}
factory.modelName = factory.modelName || (0, _emberDataPrivateSystemNormalizeModelName.default)(modelName);
return factory;
},
@@ -9664,23 +10589,53 @@
@param {Object} data
@return {DS.Model|Array} the record(s) that was created or
updated.
*/
push: function (data) {
+ var pushed = this._push(data);
+
+ if (Array.isArray(pushed)) {
+ var records = pushed.map(function (internalModel) {
+ return internalModel.getRecord();
+ });
+
+ return records;
+ }
+
+ if (pushed === null) {
+ return null;
+ }
+
+ var record = pushed.getRecord();
+
+ return record;
+ },
+
+ /*
+ Push some data into the store, without creating materialized records.
+ @method _push
+ @private
+ @param {Object} data
+ @return {DS.InternalModel|Array<DS.InternalModel>} pushed InternalModel(s)
+ */
+ _push: function (data) {
var included = data.included;
- var i, length;
+ var i = undefined,
+ length = undefined;
+
if (included) {
for (i = 0, length = included.length; i < length; i++) {
this._pushInternalModel(included[i]);
}
}
if (Array.isArray(data.data)) {
length = data.data.length;
var internalModels = new Array(length);
+
for (i = 0; i < length; i++) {
- internalModels[i] = this._pushInternalModel(data.data[i]).getRecord();
+ internalModels[i] = this._pushInternalModel(data.data[i]);
}
return internalModels;
}
@@ -9688,17 +10643,15 @@
return null;
}
var internalModel = this._pushInternalModel(data.data);
- var record = internalModel.getRecord();
-
- return record;
+ return internalModel;
},
- _hasModelFor: function (type) {
- return !!(0, _emberDataPrivateUtils.getOwner)(this)._lookupFactory('model:' + type);
+ _hasModelFor: function (modelName) {
+ return !!(0, _emberDataPrivateUtils.getOwner)(this)._lookupFactory('model:' + modelName);
},
_pushInternalModel: function (data) {
var _this2 = this;
@@ -9706,11 +10659,11 @@
// Actually load the record into the store.
var internalModel = this._load(data);
this._backburner.join(function () {
- _this2._backburner.schedule('normalizeRelationships', _this2, '_setupRelationships', internalModel, data);
+ _this2._backburner.schedule('normalizeRelationships', _this2, _this2._setupRelationships, internalModel, data);
});
return internalModel;
},
@@ -9731,11 +10684,11 @@
```app/serializers/application.js
import DS from 'ember-data';
export default DS.ActiveModelSerializer;
```
```js
- var pushData = {
+ let pushData = {
posts: [
{ id: 1, post_title: "Great post", comment_ids: [2] }
],
comments: [
{ id: 2, comment_body: "Insightful comment" }
@@ -9764,12 +10717,12 @@
@param {Object} inputPayload
*/
pushPayload: function (modelName, inputPayload) {
var _this3 = this;
- var serializer;
- var payload;
+ var serializer = undefined;
+ var payload = undefined;
if (!inputPayload) {
payload = modelName;
serializer = defaultSerializer(this);
} else {
payload = inputPayload;
@@ -9791,12 +10744,12 @@
`normalize` converts a json payload into the normalized form that
[push](#method_push) expects.
Example
```js
socket.on('message', function(message) {
- var modelName = message.model;
- var data = message.data;
+ let modelName = message.model;
+ let data = message.data;
store.push(store.normalize(modelName, data));
});
```
@method normalize
@param {String} modelName The name of the model type for this payload
@@ -9812,22 +10765,22 @@
/**
Build a brand new record for a given type, ID, and
initial data.
@method buildRecord
@private
- @param {DS.Model} type
+ @param {DS.Model} modelClass
@param {String} id
@param {Object} data
@return {InternalModel} internal model
*/
- buildInternalModel: function (type, id, data) {
- var typeMap = this.typeMapFor(type);
+ buildInternalModel: function (modelClass, id, data) {
+ var typeMap = this.typeMapFor(modelClass);
var idToRecord = typeMap.idToRecord;
// lookupFactory should really return an object that creates
// instances with the injections applied
- var internalModel = new _emberDataPrivateSystemModelInternalModel.default(type, id, this, null, data);
+ var internalModel = new _emberDataPrivateSystemModelInternalModel.default(modelClass, id, this, data);
// if we're creating an item, this process will be done
// later, once the object has been persisted.
if (id) {
idToRecord[id] = internalModel;
@@ -9853,12 +10806,12 @@
@method _dematerializeRecord
@private
@param {InternalModel} internalModel
*/
_dematerializeRecord: function (internalModel) {
- var type = internalModel.type;
- var typeMap = this.typeMapFor(type);
+ var modelClass = internalModel.type;
+ var typeMap = this.typeMapFor(modelClass);
var id = internalModel.id;
internalModel.updateRecordArrays();
if (id) {
@@ -9887,11 +10840,13 @@
@param {String} modelName
@return DS.Adapter
*/
adapterFor: function (modelName) {
- return this.lookupAdapter(modelName);
+ var normalizedModelName = (0, _emberDataPrivateSystemNormalizeModelName.default)(modelName);
+
+ return this._instanceCache.get('adapter', normalizedModelName);
},
_adapterRun: function (fn) {
return this._backburner.run(fn);
},
@@ -9917,105 +10872,78 @@
@param {String} modelName the record to serialize
@return {DS.Serializer}
*/
serializerFor: function (modelName) {
- var fallbacks = ['application', this.adapterFor(modelName).get('defaultSerializer'), '-default'];
-
- var serializer = this.lookupSerializer(modelName, fallbacks);
- return serializer;
- },
-
- /**
- Retrieve a particular instance from the
- container cache. If not found, creates it and
- placing it in the cache.
- Enabled a store to manage local instances of
- adapters and serializers.
- @method retrieveManagedInstance
- @private
- @param {String} modelName the object modelName
- @param {String} name the object name
- @param {Array} fallbacks the fallback objects to lookup if the lookup for modelName or 'application' fails
- @return {Ember.Object}
- */
- retrieveManagedInstance: function (type, modelName, fallbacks) {
var normalizedModelName = (0, _emberDataPrivateSystemNormalizeModelName.default)(modelName);
- var instance = this._instanceCache.get(type, normalizedModelName, fallbacks);
- set(instance, 'store', this);
- return instance;
+ return this._instanceCache.get('serializer', normalizedModelName);
},
lookupAdapter: function (name) {
- return this.retrieveManagedInstance('adapter', name, this.get('_adapterFallbacks'));
+ return this.adapterFor(name);
},
- _adapterFallbacks: _ember.default.computed('adapter', function () {
- var adapter = this.get('adapter');
- return ['application', adapter, '-json-api'];
- }),
-
- lookupSerializer: function (name, fallbacks) {
- return this.retrieveManagedInstance('serializer', name, fallbacks);
+ lookupSerializer: function (name) {
+ return this.serializerFor(name);
},
willDestroy: function () {
this._super.apply(this, arguments);
this.recordArrayManager.destroy();
+ this._instanceCache.destroy();
this.unloadAll();
- }
+ },
- });
+ _pushResourceIdentifier: function (relationship, resourceIdentifier) {
+ if (isNone(resourceIdentifier)) {
+ return;
+ }
- function deserializeRecordId(store, key, relationship, id) {
- if (isNone(id)) {
- return;
- }
+ //TODO:Better asserts
+ return this._internalModelForId(resourceIdentifier.type, resourceIdentifier.id);
+ },
- //TODO:Better asserts
- return store._internalModelForId(id.type, id.id);
- }
+ _pushResourceIdentifiers: function (relationship, resourceIdentifiers) {
+ if (isNone(resourceIdentifiers)) {
+ return;
+ }
- function deserializeRecordIds(store, key, relationship, ids) {
- if (isNone(ids)) {
- return;
+ var _internalModels = new Array(resourceIdentifiers.length);
+ for (var i = 0; i < resourceIdentifiers.length; i++) {
+ _internalModels[i] = this._pushResourceIdentifier(relationship, resourceIdentifiers[i]);
+ }
+ return _internalModels;
}
+ });
- var _ids = new Array(ids.length);
-
- for (var i = 0; i < ids.length; i++) {
- _ids[i] = deserializeRecordId(store, key, relationship, ids[i]);
- }
-
- return _ids;
- }
-
// Delegation to the adapter and promise management
function defaultSerializer(store) {
return store.serializerFor('application');
}
function _commit(adapter, store, operation, snapshot) {
var internalModel = snapshot._internalModel;
var modelName = snapshot.modelName;
- var typeClass = store.modelFor(modelName);
- var promise = adapter[operation](store, typeClass, snapshot);
+ var modelClass = store.modelFor(modelName);
+
+ var promise = adapter[operation](store, modelClass, snapshot);
var serializer = (0, _emberDataPrivateSystemStoreSerializers.serializerForAdapter)(store, adapter, modelName);
var label = 'DS: Extract and notify about ' + operation + ' completion of ' + internalModel;
promise = Promise.resolve(promise, label);
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, store));
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, internalModel));
return promise.then(function (adapterPayload) {
store._adapterRun(function () {
- var payload, data;
+ var payload = undefined,
+ data = undefined;
if (adapterPayload) {
- payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, snapshot.id, operation);
+ payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
if (payload.included) {
store.push({ data: payload.included });
}
data = payload.data;
}
@@ -10023,11 +10951,12 @@
});
return internalModel;
}, function (error) {
if (error instanceof _emberDataAdaptersErrors.InvalidError) {
- var errors = serializer.extractErrors(store, typeClass, error, snapshot.id);
+ var errors = serializer.extractErrors(store, modelClass, error, snapshot.id);
+
store.recordWasInvalid(internalModel, errors);
} else {
store.recordWasError(internalModel, error);
}
@@ -10039,70 +10968,27 @@
if (!data.relationships) {
return;
}
record.type.eachRelationship(function (key, descriptor) {
- var kind = descriptor.kind;
-
if (!data.relationships[key]) {
return;
}
- var relationship;
-
- if (data.relationships[key].links && data.relationships[key].links.related) {
- var relatedLink = (0, _emberDataPrivateSystemNormalizeLink.default)(data.relationships[key].links.related);
- if (relatedLink && relatedLink.href) {
- relationship = record._relationships.get(key);
- relationship.updateLink(relatedLink.href);
- }
- }
-
- if (data.relationships[key].meta) {
- relationship = record._relationships.get(key);
- relationship.updateMeta(data.relationships[key].meta);
- }
-
- // If the data contains a relationship that is specified as an ID (or IDs),
- // normalizeRelationship will convert them into DS.Model instances
- // (possibly unloaded) before we push the payload into the store.
- normalizeRelationship(store, key, descriptor, data.relationships[key]);
-
- var value = data.relationships[key].data;
-
- if (value !== undefined) {
- if (kind === 'belongsTo') {
- relationship = record._relationships.get(key);
- relationship.setCanonicalRecord(value);
- } else if (kind === 'hasMany') {
- relationship = record._relationships.get(key);
- relationship.updateRecordsFromAdapter(value);
- }
- }
+ var relationship = record._relationships.get(key);
+ relationship.push(data.relationships[key]);
});
}
- function normalizeRelationship(store, key, relationship, jsonPayload) {
- var data = jsonPayload.data;
- if (data) {
- var kind = relationship.kind;
- if (kind === 'belongsTo') {
- jsonPayload.data = deserializeRecordId(store, key, relationship, data);
- } else if (kind === 'hasMany') {
- jsonPayload.data = deserializeRecordIds(store, key, relationship, data);
- }
- }
- }
-
exports.Store = Store;
exports.default = Store;
});
/**
@module ember-data
*/
-// If Ember.ENV.DS_WARN_ON_UNKNOWN_KEYS is set to true and the payload
+// If ENV.DS_WARN_ON_UNKNOWN_KEYS is set to true and the payload
// contains unknown attributes or relationships, log a warning.
// Check unknown attributes
// Check unknown relationships
@@ -10134,97 +11020,136 @@
function _objectIsAlive(object) {
return !(get(object, "isDestroyed") || get(object, "isDestroying"));
}
});
define('ember-data/-private/system/store/container-instance-cache', ['exports', 'ember', 'ember-data/-private/system/empty-object'], function (exports, _ember, _emberDataPrivateSystemEmptyObject) {
- exports.default = ContainerInstanceCache;
+ var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
- var assign = _ember.default.assign || _ember.default.merge;
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+ var set = _ember.default.set;
+
/*
* The `ContainerInstanceCache` serves as a lazy cache for looking up
* instances of serializers and adapters. It has some additional logic for
* finding the 'fallback' adapter or serializer.
*
* The 'fallback' adapter or serializer is an adapter or serializer that is looked up
* when the preferred lookup fails. For example, say you try to look up `adapter:post`,
* but there is no entry (app/adapters/post.js in EmberCLI) for `adapter:post` in the registry.
*
- * The `fallbacks` array passed will then be used; the first entry in the fallbacks array
- * that exists in the container will then be cached for `adapter:post`. So, the next time you
- * look up `adapter:post`, you'll get the `adapter:application` instance (or whatever the fallback
- * was if `adapter:application` doesn't exist).
+ * When an adapter or serializer is unfound, getFallbacks will be invoked with the current namespace
+ * ('adapter' or 'serializer') and the 'preferredKey' (usually a modelName). The method should return
+ * an array of keys to check against.
*
+ * The first entry in the fallbacks array that exists in the container will then be cached for
+ * `adapter:post`. So, the next time you look up `adapter:post`, you'll get the `adapter:application`
+ * instance (or whatever the fallback was if `adapter:application` doesn't exist).
+ *
* @private
* @class ContainerInstanceCache
*
*/
- function ContainerInstanceCache(owner) {
- this._owner = owner;
- this._cache = new _emberDataPrivateSystemEmptyObject.default();
- }
+ var ContainerInstanceCache = (function () {
+ function ContainerInstanceCache(owner, store) {
+ this._owner = owner;
+ this._store = store;
+ this._namespaces = {
+ adapter: new _emberDataPrivateSystemEmptyObject.default(),
+ serializer: new _emberDataPrivateSystemEmptyObject.default()
+ };
+ }
- ContainerInstanceCache.prototype = new _emberDataPrivateSystemEmptyObject.default();
+ _createClass(ContainerInstanceCache, [{
+ key: 'get',
+ value: function get(namespace, preferredKey) {
+ var cache = this._namespaces[namespace];
- assign(ContainerInstanceCache.prototype, {
- get: function (type, preferredKey, fallbacks) {
- var cache = this._cache;
- var preferredLookupKey = type + ':' + preferredKey;
-
- if (!(preferredLookupKey in cache)) {
- var instance = this.instanceFor(preferredLookupKey) || this._findInstance(type, fallbacks);
- if (instance) {
- cache[preferredLookupKey] = instance;
+ if (cache[preferredKey]) {
+ return cache[preferredKey];
}
- }
- return cache[preferredLookupKey];
- },
- _findInstance: function (type, fallbacks) {
- for (var i = 0, _length = fallbacks.length; i < _length; i++) {
- var fallback = fallbacks[i];
- var lookupKey = type + ':' + fallback;
- var instance = this.instanceFor(lookupKey);
+ var preferredLookupKey = namespace + ':' + preferredKey;
+ var instance = this._instanceFor(preferredLookupKey) || this._findInstance(namespace, this._fallbacksFor(namespace, preferredKey));
if (instance) {
- return instance;
+ cache[preferredKey] = instance;
+ set(instance, 'store', this._store);
}
- }
- },
- instanceFor: function (key) {
- var cache = this._cache;
- if (!cache[key]) {
- var instance = this._owner.lookup(key);
- if (instance) {
- cache[key] = instance;
+ return cache[preferredKey];
+ }
+ }, {
+ key: '_fallbacksFor',
+ value: function _fallbacksFor(namespace, preferredKey) {
+ if (namespace === 'adapter') {
+ return ['application', this._store.get('adapter'), '-json-api'];
}
+
+ // serializer
+ return ['application', this.get('adapter', preferredKey).get('defaultSerializer'), '-default'];
}
- return cache[key];
- },
+ }, {
+ key: '_findInstance',
+ value: function _findInstance(namespace, fallbacks) {
+ var cache = this._namespaces[namespace];
- destroy: function () {
- var cache = this._cache;
- var cacheEntries = Object.keys(cache);
+ for (var i = 0, _length = fallbacks.length; i < _length; i++) {
+ var fallback = fallbacks[i];
- for (var i = 0, _length2 = cacheEntries.length; i < _length2; i++) {
- var cacheKey = cacheEntries[i];
- var cacheEntry = cache[cacheKey];
- if (cacheEntry) {
- cacheEntry.destroy();
+ if (cache[fallback]) {
+ return cache[fallback];
+ }
+
+ var lookupKey = namespace + ':' + fallback;
+ var instance = this._instanceFor(lookupKey);
+
+ if (instance) {
+ cache[fallback] = instance;
+ return instance;
+ }
}
}
- this._owner = null;
- },
+ }, {
+ key: '_instanceFor',
+ value: function _instanceFor(key) {
+ return this._owner.lookup(key);
+ }
+ }, {
+ key: 'destroyCache',
+ value: function destroyCache(cache) {
+ var cacheEntries = Object.keys(cache);
- constructor: ContainerInstanceCache,
+ for (var i = 0, _length2 = cacheEntries.length; i < _length2; i++) {
+ var cacheKey = cacheEntries[i];
+ var cacheEntry = cache[cacheKey];
+ if (cacheEntry) {
+ cacheEntry.destroy();
+ }
+ }
+ }
+ }, {
+ key: 'destroy',
+ value: function destroy() {
+ this.destroyCache(this._namespaces.adapter);
+ this.destroyCache(this._namespaces.serializer);
+ this._namespaces = null;
+ this._store = null;
+ this._owner = null;
+ }
+ }, {
+ key: 'toString',
+ value: function toString() {
+ return 'ContainerInstanceCache';
+ }
+ }]);
- toString: function () {
- return 'ContainerInstanceCache';
- }
- });
+ return ContainerInstanceCache;
+ })();
+
+ exports.default = ContainerInstanceCache;
});
/* global heimdall */
define("ember-data/-private/system/store/finders", ["exports", "ember", "ember-data/-private/debug", "ember-data/-private/system/store/common", "ember-data/-private/system/store/serializer-response", "ember-data/-private/system/store/serializers"], function (exports, _ember, _emberDataPrivateDebug, _emberDataPrivateSystemStoreCommon, _emberDataPrivateSystemStoreSerializerResponse, _emberDataPrivateSystemStoreSerializers) {
exports._find = _find;
exports._findMany = _findMany;
@@ -10255,13 +11180,12 @@
return promise.then(function (adapterPayload) {
return store._adapterRun(function () {
var payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, id, 'findRecord');
- //TODO Optimize
- var record = store.push(payload);
- return record._internalModel;
+ var internalModel = store._push(payload);
+ return internalModel;
});
}, function (error) {
internalModel.notFound();
if (internalModel.isEmpty()) {
internalModel.unloadRecord();
@@ -10285,18 +11209,11 @@
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, store));
return promise.then(function (adapterPayload) {
return store._adapterRun(function () {
var payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, null, 'findMany');
- //TODO Optimize, no need to materialize here
- var records = store.push(payload);
- var internalModels = new Array(records.length);
-
- for (var i = 0; i < records.length; i++) {
- internalModels[i] = records[i]._internalModel;
- }
-
+ var internalModels = store._push(payload);
return internalModels;
});
}, null, "DS: Extract payload of " + typeClass);
}
@@ -10342,33 +11259,31 @@
if (!payload.data) {
return null;
}
- //TODO Optimize
- var record = store.push(payload);
- return record._internalModel;
+ var internalModel = store._push(payload);
+ return internalModel;
});
}, null, "DS: Extract payload of " + internalModel + " : " + relationship.type);
}
function _findAll(adapter, store, typeClass, sinceToken, options) {
var modelName = typeClass.modelName;
var recordArray = store.peekAll(modelName);
- var snapshotArray = recordArray.createSnapshot(options);
+ var snapshotArray = recordArray._createSnapshot(options);
var promise = adapter.findAll(store, typeClass, sinceToken, snapshotArray);
var serializer = (0, _emberDataPrivateSystemStoreSerializers.serializerForAdapter)(store, adapter, modelName);
var label = "DS: Handle Adapter#findAll of " + typeClass;
promise = Promise.resolve(promise, label);
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, store));
return promise.then(function (adapterPayload) {
store._adapterRun(function () {
var payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, null, 'findAll');
- //TODO Optimize
- store.push(payload);
+ store._push(payload);
});
store.didUpdateAll(typeClass);
return store.peekAll(modelName);
}, null, "DS: Extract payload of findAll " + typeClass);
@@ -10377,27 +11292,27 @@
function _query(adapter, store, typeClass, query, recordArray) {
var modelName = typeClass.modelName;
var promise = adapter.query(store, typeClass, query, recordArray);
var serializer = (0, _emberDataPrivateSystemStoreSerializers.serializerForAdapter)(store, adapter, modelName);
- var label = "DS: Handle Adapter#query of " + typeClass;
+ var label = 'DS: Handle Adapter#query of ' + typeClass;
promise = Promise.resolve(promise, label);
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, store));
return promise.then(function (adapterPayload) {
- var records, payload;
+ var internalModels = undefined,
+ payload = undefined;
store._adapterRun(function () {
payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, null, 'query');
- //TODO Optimize
- records = store.push(payload);
+ internalModels = store._push(payload);
});
- recordArray.loadRecords(records, payload);
+ recordArray._setInternalModels(internalModels, payload);
return recordArray;
- }, null, "DS: Extract payload of query " + typeClass);
+ }, null, 'DS: Extract payload of query ' + typeClass);
}
function _queryRecord(adapter, store, typeClass, query) {
var modelName = typeClass.modelName;
var promise = adapter.queryRecord(store, typeClass, query);
@@ -10406,19 +11321,18 @@
promise = Promise.resolve(promise, label);
promise = (0, _emberDataPrivateSystemStoreCommon._guard)(promise, (0, _emberDataPrivateSystemStoreCommon._bind)(_emberDataPrivateSystemStoreCommon._objectIsAlive, store));
return promise.then(function (adapterPayload) {
- var record;
+ var internalModel;
store._adapterRun(function () {
var payload = (0, _emberDataPrivateSystemStoreSerializerResponse.normalizeResponseHelper)(serializer, store, typeClass, adapterPayload, null, 'queryRecord');
- //TODO Optimize
- record = store.push(payload);
+ internalModel = store._push(payload);
});
- return record;
+ return internalModel;
}, null, "DS: Extract payload of queryRecord " + typeClass);
}
});
define('ember-data/-private/system/store/serializer-response', ['exports', 'ember', 'ember-data/-private/debug'], function (exports, _ember, _emberDataPrivateDebug) {
exports.validateDocumentStructure = validateDocumentStructure;
@@ -10528,11 +11442,11 @@
exports.NumberTransform = _emberDataPrivateTransformsNumber.default;
exports.DateTransform = _emberDataPrivateTransformsDate.default;
exports.StringTransform = _emberDataPrivateTransformsString.default;
exports.BooleanTransform = _emberDataPrivateTransformsBoolean.default;
});
-define('ember-data/-private/transforms/boolean', ['exports', 'ember', 'ember-data/transform', 'ember-data/-private/features'], function (exports, _ember, _emberDataTransform, _emberDataPrivateFeatures) {
+define("ember-data/-private/transforms/boolean", ["exports", "ember", "ember-data/transform"], function (exports, _ember, _emberDataTransform) {
var isNone = _ember.default.isNone;
/**
The `DS.BooleanTransform` class is used to serialize and deserialize
boolean attributes on Ember Data record objects. This transform is
@@ -10571,14 +11485,12 @@
*/
exports.default = _emberDataTransform.default.extend({
deserialize: function (serialized, options) {
var type = typeof serialized;
- if (true) {
- if (isNone(serialized) && options.allowNull === true) {
- return null;
- }
+ if (isNone(serialized) && options.allowNull === true) {
+ return null;
}
if (type === "boolean") {
return serialized;
} else if (type === "string") {
@@ -10589,14 +11501,12 @@
return false;
}
},
serialize: function (deserialized, options) {
- if (true) {
- if (isNone(deserialized) && options.allowNull === true) {
- return null;
- }
+ if (isNone(deserialized) && options.allowNull === true) {
+ return null;
}
return Boolean(deserialized);
}
});
@@ -10822,11 +11732,10 @@
return headers;
}
});
define('ember-data/adapter', ['exports', 'ember'], function (exports, _ember) {
- var get = _ember.default.get;
/**
An adapter is an object that receives requests from a store and
translates them into the appropriate action to take against your
persistence layer. The persistence layer is usually an HTTP API, but
@@ -11062,11 +11971,11 @@
@param {DS.Snapshot} snapshot
@param {Object} options
@return {Object} serialized snapshot
*/
serialize: function (snapshot, options) {
- return get(snapshot.record, 'store').serializerFor(snapshot.modelName).serialize(snapshot, options);
+ return snapshot.serialize(options);
},
/**
Implement this method in a subclass to handle the creation of
new records.
@@ -11403,10 +12312,73 @@
var SOURCE_POINTER_REGEXP = /^\/?data\/(attributes|relationships)\/(.*)/;
var SOURCE_POINTER_PRIMARY_REGEXP = /^\/?data/;
var PRIMARY_ATTRIBUTE_KEY = 'base';
/**
+ A `DS.AdapterError` is used by an adapter to signal that an error occurred
+ during a request to an external API. It indicates a generic error, and
+ subclasses are used to indicate specific error states. The following
+ subclasses are provided:
+
+ - `DS.InvalidError`
+ - `DS.TimeoutError`
+ - `DS.AbortError`
+ - `DS.UnauthorizedError`
+ - `DS.ForbiddenError`
+ - `DS.NotFoundError`
+ - `DS.ConflictError`
+ - `DS.ServerError`
+
+ To create a custom error to signal a specific error state in communicating
+ with an external API, extend the `DS.AdapterError`. For example if the
+ external API exclusively used HTTP `503 Service Unavailable` to indicate
+ it was closed for maintenance:
+
+ ```app/adapters/maintenance-error.js
+ import DS from 'ember-data';
+
+ export default DS.AdapterError.extend({ message: "Down for maintenance." });
+ ```
+
+ This error would then be returned by an adapter's `handleResponse` method:
+
+ ```app/adapters/application.js
+ import DS from 'ember-data';
+ import MaintenanceError from './maintenance-error';
+
+ export default DS.JSONAPIAdapter.extend({
+ handleResponse(status) {
+ if (503 === status) {
+ return new MaintenanceError();
+ }
+
+ return this._super(...arguments);
+ }
+ });
+ ```
+
+ And can then be detected in an application and used to send the user to an
+ `under-maintenance` route:
+
+ ```app/routes/application.js
+ import Ember from 'ember';
+ import MaintenanceError from '../adapters/maintenance-error';
+
+ export default Ember.Route.extend({
+ actions: {
+ error(error, transition) {
+ if (error instanceof MaintenanceError) {
+ this.transitionTo('under-maintenance');
+ return;
+ }
+
+ // ...other error handling logic
+ }
+ }
+ });
+ ```
+
@class AdapterError
@namespace DS
*/
function AdapterError(errors) {
@@ -11515,61 +12487,213 @@
*/
var InvalidError = extend(AdapterError, 'The adapter rejected the commit because it was invalid');
exports.InvalidError = InvalidError;
/**
+ A `DS.TimeoutError` is used by an adapter to signal that a request
+ to the external API has timed out. I.e. no response was received from
+ the external API within an allowed time period.
+
+ An example use case would be to warn the user to check their internet
+ connection if an adapter operation has timed out:
+
+ ```app/routes/application.js
+ import Ember from 'ember';
+ import DS from 'ember-data';
+
+ const { TimeoutError } = DS;
+
+ export default Ember.Route.extend({
+ actions: {
+ error(error, transition) {
+ if (error instanceof TimeoutError) {
+ // alert the user
+ alert('Are you still connected to the internet?');
+ return;
+ }
+
+ // ...other error handling logic
+ }
+ }
+ });
+ ```
+
@class TimeoutError
@namespace DS
*/
var TimeoutError = extend(AdapterError, 'The adapter operation timed out');
exports.TimeoutError = TimeoutError;
/**
+ A `DS.AbortError` is used by an adapter to signal that a request to
+ the external API was aborted. For example, this can occur if the user
+ navigates away from the current page after a request to the external API
+ has been initiated but before a response has been received.
+
@class AbortError
@namespace DS
*/
var AbortError = extend(AdapterError, 'The adapter operation was aborted');
exports.AbortError = AbortError;
/**
+ A `DS.UnauthorizedError` equates to a HTTP `401 Unauthorized` response
+ status. It is used by an adapter to signal that a request to the external
+ API was rejected because authorization is required and has failed or has not
+ yet been provided.
+
+ An example use case would be to redirect the user to a log in route if a
+ request is unauthorized:
+
+ ```app/routes/application.js
+ import Ember from 'ember';
+ import DS from 'ember-data';
+
+ const { UnauthorizedError } = DS;
+
+ export default Ember.Route.extend({
+ actions: {
+ error(error, transition) {
+ if (error instanceof UnauthorizedError) {
+ // go to the sign in route
+ this.transitionTo('login');
+ return;
+ }
+
+ // ...other error handling logic
+ }
+ }
+ });
+ ```
+
@class UnauthorizedError
@namespace DS
*/
var UnauthorizedError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation is unauthorized') : null;
exports.UnauthorizedError = UnauthorizedError;
/**
+ A `DS.ForbiddenError` equates to a HTTP `403 Forbidden` response status.
+ It is used by an adapter to signal that a request to the external API was
+ valid but the server is refusing to respond to it. If authorization was
+ provided and is valid, then the authenticated user does not have the
+ necessary permissions for the request.
+
@class ForbiddenError
@namespace DS
*/
var ForbiddenError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation is forbidden') : null;
exports.ForbiddenError = ForbiddenError;
/**
+ A `DS.NotFoundError` equates to a HTTP `404 Not Found` response status.
+ It is used by an adapter to signal that a request to the external API
+ was rejected because the resource could not be found on the API.
+
+ An example use case would be to detect if the user has entered a route
+ for a specific model that does not exist. For example:
+
+ ```app/routes/post.js
+ import Ember from 'ember';
+ import DS from 'ember-data';
+
+ const { NotFoundError } = DS;
+
+ export default Ember.Route.extend({
+ model(params) {
+ return this.get('store').findRecord('post', params.post_id);
+ },
+
+ actions: {
+ error(error, transition) {
+ if (error instanceof NotFoundError) {
+ // redirect to a list of all posts instead
+ this.transitionTo('posts');
+ } else {
+ // otherwise let the error bubble
+ return true;
+ }
+ }
+ }
+ });
+ ```
+
@class NotFoundError
@namespace DS
*/
var NotFoundError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter could not find the resource') : null;
exports.NotFoundError = NotFoundError;
/**
+ A `DS.ConflictError` equates to a HTTP `409 Conflict` response status.
+ It is used by an adapter to indicate that the request could not be processed
+ because of a conflict in the request. An example scenario would be when
+ creating a record with a client generated id but that id is already known
+ to the external API.
+
@class ConflictError
@namespace DS
*/
var ConflictError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation failed due to a conflict') : null;
exports.ConflictError = ConflictError;
/**
+ A `DS.ServerError` equates to a HTTP `500 Internal Server Error` response
+ status. It is used by the adapter to indicate that a request has failed
+ because of an error in the external API.
+
@class ServerError
@namespace DS
*/
var ServerError = extendedErrorsEnabled ? extend(AdapterError, 'The adapter operation failed due to a server error') : null;
exports.ServerError = ServerError;
/**
+ Convert an hash of errors into an array with errors in JSON-API format.
+
+ ```javascript
+ import DS from 'ember-data';
+
+ const { errorsHashToArray } = DS;
+
+ let errors = {
+ base: "Invalid attributes on saving this record",
+ name: "Must be present",
+ age: ["Must be present", "Must be a number"]
+ };
+
+ let errorsArray = errorsHashToArray(errors);
+ // [
+ // {
+ // title: "Invalid Document",
+ // detail: "Invalid attributes on saving this record",
+ // source: { pointer: "/data" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be present",
+ // source: { pointer: "/data/attributes/name" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be present",
+ // source: { pointer: "/data/attributes/age" }
+ // },
+ // {
+ // title: "Invalid Attribute",
+ // detail: "Must be a number",
+ // source: { pointer: "/data/attributes/age" }
+ // }
+ // ]
+ ```
+
@method errorsHashToArray
- @private
+ @public
+ @namespace
+ @for DS
+ @param {Object} errors hash with errors as properties
+ @return {Array} array of errors in JSON-API format
*/
function errorsHashToArray(errors) {
var out = [];
@@ -11596,12 +12720,48 @@
return out;
}
/**
+ Convert an array of errors in JSON-API format into an object.
+
+ ```javascript
+ import DS from 'ember-data';
+
+ const { errorsArrayToHash } = DS;
+
+ let errorsArray = [
+ {
+ title: "Invalid Attribute",
+ detail: "Must be present",
+ source: { pointer: "/data/attributes/name" }
+ },
+ {
+ title: "Invalid Attribute",
+ detail: "Must be present",
+ source: { pointer: "/data/attributes/age" }
+ },
+ {
+ title: "Invalid Attribute",
+ detail: "Must be a number",
+ source: { pointer: "/data/attributes/age" }
+ }
+ ];
+
+ let errors = errorsArrayToHash(errorsArray);
+ // {
+ // "name": ["Must be present"],
+ // "age": ["Must be present", "must be a number"]
+ // }
+ ```
+
@method errorsArrayToHash
- @private
+ @public
+ @namespace
+ @for DS
+ @param {Array} errors array of errors in JSON-API format
+ @return {Object}
*/
function errorsArrayToHash(errors) {
var out = {};
@@ -11628,10 +12788,139 @@
}
});
define('ember-data/adapters/json-api', ['exports', 'ember', 'ember-data/adapters/rest', 'ember-data/-private/features', 'ember-data/-private/debug'], function (exports, _ember, _emberDataAdaptersRest, _emberDataPrivateFeatures, _emberDataPrivateDebug) {
/**
+ The `JSONAPIAdapter` is the default adapter used by Ember Data. It
+ is responsible for transforming the store's requests into HTTP
+ requests that follow the [JSON API](http://jsonapi.org/format/)
+ format.
+
+ ## JSON API Conventions
+
+ The JSONAPIAdapter uses JSON API conventions for building the url
+ for a record and selecting the HTTP verb to use with a request. The
+ actions you can take on a record map onto the following URLs in the
+ JSON API adapter:
+
+ <table>
+ <tr>
+ <th>
+ Action
+ </th>
+ <th>
+ HTTP Verb
+ </th>
+ <th>
+ URL
+ </th>
+ </tr>
+ <tr>
+ <th>
+ `store.findRecord('post', 123)`
+ </th>
+ <td>
+ GET
+ </td>
+ <td>
+ /posts/123
+ </td>
+ </tr>
+ <tr>
+ <th>
+ `store.findAll('post')`
+ </th>
+ <td>
+ GET
+ </td>
+ <td>
+ /posts
+ </td>
+ </tr>
+ <tr>
+ <th>
+ Update `postRecord.save()`
+ </th>
+ <td>
+ PATCH
+ </td>
+ <td>
+ /posts/123
+ </td>
+ </tr>
+ <tr>
+ <th>
+ Create `store.createRecord('post').save()`
+ </th>
+ <td>
+ POST
+ </td>
+ <td>
+ /posts
+ </td>
+ </tr>
+ <tr>
+ <th>
+ Delete `postRecord.destroyRecord()`
+ </th>
+ <td>
+ DELETE
+ </td>
+ <td>
+ /posts/123
+ </td>
+ </tr>
+ </table>
+
+ ## Success and failure
+
+ The JSONAPIAdapter will consider a success any response with a
+ status code of the 2xx family ("Success"), as well as 304 ("Not
+ Modified"). Any other status code will be considered a failure.
+
+ On success, the request promise will be resolved with the full
+ response payload.
+
+ Failed responses with status code 422 ("Unprocessable Entity") will
+ be considered "invalid". The response will be discarded, except for
+ the `errors` key. The request promise will be rejected with a
+ `DS.InvalidError`. This error object will encapsulate the saved
+ `errors` value.
+
+ Any other status codes will be treated as an adapter error. The
+ request promise will be rejected, similarly to the invalid case,
+ but with an instance of `DS.AdapterError` instead.
+
+ ### Endpoint path customization
+
+ Endpoint paths can be prefixed with a `namespace` by setting the
+ namespace property on the adapter:
+
+ ```app/adapters/application.js
+ import DS from 'ember-data';
+
+ export default DS.JSONAPIAdapter.extend({
+ namespace: 'api/1'
+ });
+ ```
+ Requests for the `person` model would now target `/api/1/people/1`.
+
+ ### Host customization
+
+ An adapter can target other hosts by setting the `host` property.
+
+ ```app/adapters/application.js
+ import DS from 'ember-data';
+
+ export default DS.JSONAPIAdapter.extend({
+ host: 'https://api.example.com'
+ });
+ ```
+
+ Requests for the `person` model would now target
+ `https://api.example.com/people/1`.
+
@since 1.13.0
@class JSONAPIAdapter
@constructor
@namespace DS
@extends DS.RESTAdapter
@@ -11671,13 +12960,21 @@
ids as a query string, you can set coalesceFindRequests to true to coalesce all find requests
within a single runloop.
For example, if you have an initial payload of:
```javascript
{
- post: {
+ data: {
id: 1,
- comments: [1, 2]
+ type: 'post',
+ relationship: {
+ comments: {
+ data: [
+ { id: 1, type: 'comment' },
+ { id: 2, type: 'comment' }
+ ]
+ }
+ }
}
}
```
By default calling `post.get('comments')` will trigger the following requests(assuming the
comments haven't been loaded before):
@@ -11701,45 +12998,25 @@
@property coalesceFindRequests
@type {boolean}
*/
coalesceFindRequests: false,
- /**
- @method findMany
- @param {DS.Store} store
- @param {DS.Model} type
- @param {Array} ids
- @param {Array} snapshots
- @return {Promise} promise
- */
findMany: function (store, type, ids, snapshots) {
if (false && !this._hasCustomizedAjax()) {
return this._super.apply(this, arguments);
} else {
var url = this.buildURL(type.modelName, ids, snapshots, 'findMany');
return this.ajax(url, 'GET', { data: { filter: { id: ids.join(',') } } });
}
},
- /**
- @method pathForType
- @param {String} modelName
- @return {String} path
- **/
pathForType: function (modelName) {
var dasherized = _ember.default.String.dasherize(modelName);
return _ember.default.String.pluralize(dasherized);
},
// TODO: Remove this once we have a better way to override HTTP verbs.
- /**
- @method updateRecord
- @param {DS.Store} store
- @param {DS.Model} type
- @param {DS.Snapshot} snapshot
- @return {Promise} promise
- */
updateRecord: function (store, type, snapshot) {
if (false && !this._hasCustomizedAjax()) {
return this._super.apply(this, arguments);
} else {
var data = {};
@@ -11875,11 +13152,11 @@
root property. For example, in response to a `GET` request for
`/posts/1`, the JSON should look like this:
```js
{
- "post": {
+ "posts": {
"id": 1,
"title": "I'm Running to Reform the W3C's Tag",
"author": "Yehuda Katz"
}
}
@@ -11930,19 +13207,64 @@
The JSON returned should look like this:
```js
{
- "person": {
+ "people": {
"id": 5,
"firstName": "Barack",
"lastName": "Obama",
"occupation": "President"
}
}
```
+ #### Relationships
+
+ Relationships are usually represented by ids to the record in the
+ relationship. The related records can then be sideloaded in the
+ response under a key for the type.
+
+ ```js
+ {
+ "posts": {
+ "id": 5,
+ "title": "I'm Running to Reform the W3C's Tag",
+ "author": "Yehuda Katz",
+ "comments": [1, 2]
+ },
+ "comments": [{
+ "id": 1,
+ "author": "User 1",
+ "message": "First!",
+ }, {
+ "id": 2,
+ "author": "User 2",
+ "message": "Good Luck!",
+ }]
+ }
+ ```
+
+ If the records in the relationship are not known when the response
+ is serialized its also possible to represent the relationship as a
+ url using the `links` key in the response. Ember Data will fetch
+ this url to resolve the relationship when it is accessed for the
+ first time.
+
+ ```js
+ {
+ "posts": {
+ "id": 5,
+ "title": "I'm Running to Reform the W3C's Tag",
+ "author": "Yehuda Katz",
+ "links": {
+ "comments": "/posts/5/comments"
+ }
+ }
+ }
+ ```
+
### Errors
If a response is considered a failure, the JSON payload is expected to include
a top-level key `errors`, detailing any specific issues. For example:
@@ -12373,10 +13695,11 @@
* Links beginning with a single `/` will have the current adapter's `host` value prepended to it.
* Links with no beginning `/` will have a parentURL prepended to it, via the current adapter's `buildURL`.
@method findHasMany
@param {DS.Store} store
@param {DS.Snapshot} snapshot
+ @param {Object} relationship meta object describing the relationship
@param {String} url
@return {Promise} promise
*/
findHasMany: function (store, snapshot, url, relationship) {
if (false && !this._hasCustomizedAjax()) {
@@ -13444,11 +14767,11 @@
*/
exports.default = {
name: 'data-adapter',
before: 'store',
- initialize: _ember.default.K
+ initialize: function () {}
};
});
define('ember-data/initializers/ember-data', ['exports', 'ember-data/setup-container', 'ember-data/-private/core'], function (exports, _emberDataSetupContainer, _emberDataPrivateCore) {
/*
@@ -13498,11 +14821,11 @@
*/
exports.default = {
name: 'injectStore',
before: 'store',
- initialize: _ember.default.K
+ initialize: function () {}
};
});
define('ember-data/initializers/store', ['exports', 'ember'], function (exports, _ember) {
/*
@@ -13513,11 +14836,11 @@
*/
exports.default = {
name: 'store',
after: 'ember-data',
- initialize: _ember.default.K
+ initialize: function () {}
};
});
define('ember-data/initializers/transforms', ['exports', 'ember'], function (exports, _ember) {
/*
@@ -13528,11 +14851,11 @@
*/
exports.default = {
name: 'transforms',
before: 'store',
- initialize: _ember.default.K
+ initialize: function () {}
};
});
define("ember-data/instance-initializers/ember-data", ["exports", "ember-data/-private/instance-initializers/initialize-store-service"], function (exports, _emberDataPrivateInstanceInitializersInitializeStoreService) {
exports.default = {
name: "ember-data",
@@ -13572,23 +14895,49 @@
*/
exports.default = _ember.default.Object.extend({
/**
- The `store` property is the application's `store` that contains all records.
- It's injected as a service.
- It can be used to push records from a non flat data structure server
- response.
+ The `store` property is the application's `store` that contains
+ all records. It can be used to look up serializers for other model
+ types that may be nested inside the payload response.
+ Example:
+ ```js
+ Serializer.extend({
+ extractRelationship: function(relationshipModelName, relationshipHash) {
+ var modelClass = this.store.modelFor(relationshipModelName);
+ var relationshipSerializer = this.store.serializerFor(relationshipModelName);
+ return relationshipSerializer.normalize(modelClass, relationshipHash);
+ }
+ });
+ ```
@property store
@type {DS.Store}
@public
*/
/**
The `normalizeResponse` method is used to normalize a payload from the
server to a JSON-API Document.
http://jsonapi.org/format/#document-structure
+ Example:
+ ```js
+ Serializer.extend({
+ normalizeResponse(store, primaryModelClass, payload, id, requestType) {
+ if (requestType === 'findRecord') {
+ return this.normalize(primaryModelClass, payload);
+ } else {
+ return payload.reduce(function(documentHash, item) {
+ let { data, included } = this.normalize(primaryModelClass, item);
+ documentHash.included.push(...included);
+ documentHash.data.push(data);
+ return documentHash;
+ }, { data: [], included: [] })
+ }
+ }
+ });
+ ```
@since 1.13.0
@method normalizeResponse
@param {DS.Store} store
@param {DS.Model} primaryModelClass
@param {Object} payload
@@ -13602,22 +14951,56 @@
The `serialize` method is used when a record is saved in order to convert
the record into the form that your external data source expects.
`serialize` takes an optional `options` hash with a single option:
- `includeId`: If this is `true`, `serialize` should include the ID
in the serialized object it builds.
+ Example:
+ ```js
+ Serializer.extend({
+ serialize(snapshot, options) {
+ var json = {
+ id: snapshot.id
+ };
+ snapshot.eachAttribute((key, attribute) => {
+ json[key] = snapshot.attr(key);
+ });
+ snapshot.eachRelationship((key, relationship) => {
+ if (relationship.kind === 'belongsTo') {
+ json[key] = snapshot.belongsTo(key, { id: true });
+ } else if (relationship.kind === 'hasMany') {
+ json[key] = snapshot.hasMany(key, { ids: true });
+ }
+ });
+ return json;
+ },
+ });
+ ```
@method serialize
- @param {DS.Model} record
+ @param {DS.Snapshot} snapshot
@param {Object} [options]
@return {Object}
*/
serialize: null,
/**
The `normalize` method is used to convert a payload received from your
external data source into the normalized form `store.push()` expects. You
should override this method, munge the hash and return the normalized
payload.
+ Example:
+ ```js
+ Serializer.extend({
+ normalize(modelClass, resourceHash) {
+ var data = {
+ id: resourceHash.id,
+ type: modelClass.modelName,
+ attributes: resourceHash
+ };
+ return { data: data };
+ }
+ })
+ ```
@method normalize
@param {DS.Model} typeClass
@param {Object} hash
@return {Object}
*/
@@ -13988,11 +15371,11 @@
this._serializeHasManyAsIdsAndTypes(snapshot, json, relationship);
}
}
},
- /**
+ /*
Serializes a hasMany relationship as an array of objects containing only `id` and `type`
keys.
This has its use case on polymorphic hasMany relationships where the server is not storing
all records in the same table using STI, and therefore the `id` is not enough information
TODO: Make the default in Ember-data 3.0??
@@ -14297,10 +15680,43 @@
}
```
to the format that the Ember Data store expects.
+ ### Customizing meta
+
+ Since a JSON API Document can have meta defined in multiple locations you can
+ use the specific serializer hooks if you need to customize the meta.
+
+ One scenario would be to camelCase the meta keys of your payload. The example
+ below shows how this could be done using `normalizeArrayResponse` and
+ `extractRelationship`.
+
+ ```app/serializers/application.js
+ export default JSONAPISerializer.extend({
+
+ normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
+ let normalizedDocument = this._super(...arguments);
+
+ // Customize document meta
+ normalizedDocument.meta = camelCaseKeys(normalizedDocument.meta);
+
+ return normalizedDocument;
+ },
+
+ extractRelationship(relationshipHash) {
+ let normalizedRelationship = this._super(...arguments);
+
+ // Customize relationship meta
+ normalizedRelationship.meta = camelCaseKeys(normalizedRelationship.meta);
+
+ return normalizedRelationship;
+ }
+
+ });
+ ```
+
@since 1.13.0
@class JSONAPISerializer
@namespace DS
@extends DS.JSONSerializer
*/
@@ -14441,16 +15857,10 @@
var normalized = this._super.apply(this, arguments);
return normalized;
},
- /**
- @method extractAttributes
- @param {DS.Model} modelClass
- @param {Object} resourceHash
- @return {Object}
- */
extractAttributes: function (modelClass, resourceHash) {
var _this = this;
var attributes = {};
@@ -14464,15 +15874,10 @@
}
return attributes;
},
- /**
- @method extractRelationship
- @param {Object} relationshipHash
- @return {Object}
- */
extractRelationship: function (relationshipHash) {
if (_ember.default.typeOf(relationshipHash.data) === 'object') {
relationshipHash.data = this._normalizeRelationshipDataHelper(relationshipHash.data);
}
@@ -14489,16 +15894,10 @@
}
return relationshipHash;
},
- /**
- @method extractRelationships
- @param {Object} modelClass
- @param {Object} resourceHash
- @return {Object}
- */
extractRelationships: function (modelClass, resourceHash) {
var _this2 = this;
var relationships = {};
@@ -14538,35 +15937,36 @@
return this.modelNameFromPayloadKey(resourceHash.type);
}
},
/**
- @method modelNameFromPayloadKey
+ Dasherizes and singularizes the model name in the payload to match
+ the format Ember Data uses internally for the model name.
+ For example the key `posts` would be converted to `post` and the
+ key `studentAssesments` would be converted to `student-assesment`.
+ @method modelNameFromPayloadKey
@param {String} key
@return {String} the model's modelName
*/
// TODO @deprecated Use modelNameFromPayloadType instead
modelNameFromPayloadKey: function (key) {
return (0, _emberInflector.singularize)((0, _emberDataPrivateSystemNormalizeModelName.default)(key));
},
/**
- @method payloadKeyFromModelName
+ Converts the model name to a pluralized version of the model name.
+ For example `post` would be converted to `posts` and
+ `student-assesment` would be converted to `student-assesments`.
+ @method payloadKeyFromModelName
@param {String} modelName
@return {String}
*/
// TODO @deprecated Use payloadTypeFromModelName instead
payloadKeyFromModelName: function (modelName) {
return (0, _emberInflector.pluralize)(modelName);
},
- /**
- @method normalize
- @param {DS.Model} modelClass
- @param {Object} resourceHash the resource hash from the adapter
- @return {Object} the normalized resource hash
- */
normalize: function (modelClass, resourceHash) {
if (resourceHash.attributes) {
this.normalizeUsingDeclaredMapping(modelClass, resourceHash.attributes);
}
@@ -14635,16 +16035,10 @@
*/
keyForRelationship: function (key, typeClass, method) {
return dasherize(key);
},
- /**
- @method serialize
- @param {DS.Snapshot} snapshot
- @param {Object} options
- @return {Object} json
- */
serialize: function (snapshot, options) {
var data = this._super.apply(this, arguments);
var payloadType = undefined;
if (false) {
@@ -14661,17 +16055,10 @@
data.type = payloadType;
return { data: data };
},
- /**
- @method serializeAttribute
- @param {DS.Snapshot} snapshot
- @param {Object} json
- @param {String} key
- @param {Object} attribute
- */
serializeAttribute: function (snapshot, json, key, attribute) {
var type = attribute.type;
if (this._canSerialize(key)) {
json.attributes = json.attributes || {};
@@ -14690,16 +16077,10 @@
json.attributes[payloadKey] = value;
}
},
- /**
- @method serializeBelongsTo
- @param {DS.Snapshot} snapshot
- @param {Object} json
- @param {Object} relationship
- */
serializeBelongsTo: function (snapshot, json, relationship) {
var key = relationship.key;
if (this._canSerialize(key)) {
var belongsTo = snapshot.belongsTo(key);
@@ -14737,16 +16118,10 @@
json.relationships[payloadKey] = { data: data };
}
}
},
- /**
- @method serializeHasMany
- @param {DS.Snapshot} snapshot
- @param {Object} json
- @param {Object} relationship
- */
serializeHasMany: function (snapshot, json, relationship) {
var key = relationship.key;
var shouldSerializeHasMany = '_shouldSerializeHasMany';
if (false) {
shouldSerializeHasMany = 'shouldSerializeHasMany';
@@ -15578,46 +16953,21 @@
modelNameFromPayloadKey: function (key) {
return (0, _emberDataPrivateSystemNormalizeModelName.default)(key);
},
/**
- @method normalizeAttributes
- @private
- */
- normalizeAttributes: function (typeClass, hash) {
- var _this4 = this;
-
- var payloadKey;
-
- if (this.keyForAttribute) {
- typeClass.eachAttribute(function (key) {
- payloadKey = _this4.keyForAttribute(key, 'deserialize');
- if (key === payloadKey) {
- return;
- }
- if (hash[payloadKey] === undefined) {
- return;
- }
-
- hash[key] = hash[payloadKey];
- delete hash[payloadKey];
- });
- }
- },
-
- /**
@method normalizeRelationships
@private
*/
normalizeRelationships: function (typeClass, hash) {
- var _this5 = this;
+ var _this4 = this;
var payloadKey;
if (this.keyForRelationship) {
typeClass.eachRelationship(function (key, relationship) {
- payloadKey = _this5.keyForRelationship(key, relationship.kind, 'deserialize');
+ payloadKey = _this4.keyForRelationship(key, relationship.kind, 'deserialize');
if (key === payloadKey) {
return;
}
if (hash[payloadKey] === undefined) {
return;
@@ -15862,31 +17212,34 @@
@param {DS.Snapshot} snapshot
@param {Object} options
@return {Object} json
*/
serialize: function (snapshot, options) {
- var _this6 = this;
+ var _this5 = this;
var json = {};
if (options && options.includeId) {
- var id = snapshot.id;
-
- if (id) {
- json[get(this, 'primaryKey')] = id;
+ if (false) {
+ this.serializeId(snapshot, json, get(this, 'primaryKey'));
+ } else {
+ var id = snapshot.id;
+ if (id) {
+ json[get(this, 'primaryKey')] = id;
+ }
}
}
snapshot.eachAttribute(function (key, attribute) {
- _this6.serializeAttribute(snapshot, json, key, attribute);
+ _this5.serializeAttribute(snapshot, json, key, attribute);
});
snapshot.eachRelationship(function (key, relationship) {
if (relationship.kind === 'belongsTo') {
- _this6.serializeBelongsTo(snapshot, json, relationship);
+ _this5.serializeBelongsTo(snapshot, json, relationship);
} else if (relationship.kind === 'hasMany') {
- _this6.serializeHasMany(snapshot, json, relationship);
+ _this5.serializeHasMany(snapshot, json, relationship);
}
});
return json;
},
@@ -16076,11 +17429,11 @@
@method serializePolymorphicType
@param {DS.Snapshot} snapshot
@param {Object} json
@param {Object} relationship
*/
- serializePolymorphicType: _ember.default.K,
+ serializePolymorphicType: function () {},
/**
`extractMeta` is used to deserialize any meta information in the
adapter payload. By default Ember Data expects meta information to
be located on the `meta` property of the payload object.
@@ -16182,27 +17535,27 @@
@param {Object} payload
@param {(String|Number)} id
@return {Object} json The deserialized errors
*/
extractErrors: function (store, typeClass, payload, id) {
- var _this7 = this;
+ var _this6 = this;
if (payload && typeof payload === 'object' && payload.errors) {
payload = (0, _emberDataAdaptersErrors.errorsArrayToHash)(payload.errors);
this.normalizeUsingDeclaredMapping(typeClass, payload);
typeClass.eachAttribute(function (name) {
- var key = _this7.keyForAttribute(name, 'deserialize');
+ var key = _this6.keyForAttribute(name, 'deserialize');
if (key !== name && payload[key] !== undefined) {
payload[name] = payload[key];
delete payload[key];
}
});
typeClass.eachRelationship(function (name) {
- var key = _this7.keyForRelationship(name, 'deserialize');
+ var key = _this6.keyForRelationship(name, 'deserialize');
if (key !== name && payload[key] !== undefined) {
payload[name] = payload[key];
delete payload[key];
}
});
@@ -16302,10 +17655,43 @@
}
});
}
+ if (false) {
+
+ JSONSerializer.reopen({
+
+ /**
+ serializeId can be used to customize how id is serialized
+ For example, your server may expect integer datatype of id
+ By default the snapshot's id (String) is set on the json hash via json[primaryKey] = snapshot.id.
+ ```app/serializers/application.js
+ import DS from 'ember-data';
+ export default DS.JSONSerializer.extend({
+ serializeId(snapshot, json, primaryKey) {
+ var id = snapshot.id;
+ json[primaryKey] = parseInt(id, 10);
+ }
+ });
+ ```
+ @method serializeId
+ @public
+ @param {DS.Snapshot} snapshot
+ @param {Object} json
+ @param {String} primaryKey
+ */
+ serializeId: function (snapshot, json, primaryKey) {
+ var id = snapshot.id;
+
+ if (id) {
+ json[primaryKey] = id;
+ }
+ }
+ });
+ }
+
exports.default = JSONSerializer;
});
define("ember-data/serializers/rest", ["exports", "ember", "ember-data/-private/debug", "ember-data/serializers/json", "ember-data/-private/system/normalize-model-name", "ember-inflector", "ember-data/-private/system/coerce-id", "ember-data/-private/utils", "ember-data/-private/features"], function (exports, _ember, _emberDataPrivateDebug, _emberDataSerializersJson, _emberDataPrivateSystemNormalizeModelName, _emberInflector, _emberDataPrivateSystemCoerceId, _emberDataPrivateUtils, _emberDataPrivateFeatures) {
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
@@ -17278,10 +18664,10 @@
*/
deserialize: null
});
});
define("ember-data/version", ["exports"], function (exports) {
- exports.default = "2.10.0";
+ exports.default = "2.11.0-beta.1";
});
define("ember-inflector", ["exports", "ember", "ember-inflector/lib/system", "ember-inflector/lib/ext/string"], function (exports, _ember, _emberInflectorLibSystem, _emberInflectorLibExtString) {
_emberInflectorLibSystem.Inflector.defaultRules = _emberInflectorLibSystem.defaultRules;
_ember.default.Inflector = _emberInflectorLibSystem.Inflector;