Skip to content

Commit

Permalink
Allow the store to pass options to the adapter via a snapshot
Browse files Browse the repository at this point in the history
Add a SnapshotRecordArray object used for passing snapshots, meta and adapterOptions to the adapter.
  • Loading branch information
bmac committed Jun 13, 2015
1 parent b63bb87 commit 4e73e0a
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 43 deletions.
4 changes: 1 addition & 3 deletions packages/ember-data/lib/system/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ var Adapter = Ember.Object.extend({
@param {DS.Store} store
@param {DS.Model} type
@param {String} sinceToken
@param {DS.SnapshotRecordArray} snapshotRecordArray
@return {Promise} promise
*/
findAll: null,
Expand Down Expand Up @@ -282,7 +283,6 @@ var Adapter = Ember.Object.extend({
@param {DS.Store} store
@param {DS.Model} type the DS.Model class of the record
@param {DS.Snapshot} snapshot
@param {Object} options
@return {Promise} promise
*/
createRecord: null,
Expand Down Expand Up @@ -325,7 +325,6 @@ var Adapter = Ember.Object.extend({
@param {DS.Store} store
@param {DS.Model} type the DS.Model class of the record
@param {DS.Snapshot} snapshot
@param {Object} options
@return {Promise} promise
*/
updateRecord: null,
Expand Down Expand Up @@ -368,7 +367,6 @@ var Adapter = Ember.Object.extend({
@param {DS.Store} store
@param {DS.Model} type the DS.Model class of the record
@param {DS.Snapshot} snapshot
@param {Object} options
@return {Promise} promise
*/
deleteRecord: null,
Expand Down
11 changes: 7 additions & 4 deletions packages/ember-data/lib/system/model/internal-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ InternalModel.prototype = {
this.send('deleteRecord');
},

save: function() {
save: function(options) {
var promiseLabel = "DS: Model#save " + this;
var resolver = Ember.RSVP.defer(promiseLabel);

this.store.scheduleSave(this, resolver);
this.store.scheduleSave(this, resolver, options);
return resolver.promise;
},

Expand Down Expand Up @@ -221,8 +221,11 @@ InternalModel.prototype = {
@method createSnapshot
@private
*/
createSnapshot: function() {
return new Snapshot(this);
createSnapshot: function(options) {
var adapterOptions = options && options.adapterOptions;
var snapshot = new Snapshot(this);
snapshot.adapterOptions = adapterOptions;
return snapshot;
},

/**
Expand Down
7 changes: 4 additions & 3 deletions packages/ember-data/lib/system/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,13 @@ var Model = Ember.Object.extend(Ember.Evented, {
```
@method destroyRecord
@param {Object} options
@return {Promise} a promise that will be resolved when the adapter returns
successfully or rejected if the adapter returns with an error.
*/
destroyRecord: function() {
destroyRecord: function(options) {
this.deleteRecord();
return this.save();
return this.save(options);
},

/**
Expand Down Expand Up @@ -675,7 +676,7 @@ var Model = Ember.Object.extend(Ember.Evented, {
});
```
@method save
@param {Object} options a hash specifying save options sent to the server.
@param {Object} options
@return {Promise} a promise that will be resolved when the adapter returns
successfully or rejected if the adapter returns with an error.
*/
Expand Down
34 changes: 34 additions & 0 deletions packages/ember-data/lib/system/snapshot-record-array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
@module ember-data
*/

/**
@class SnapshotRecordArray
@namespace DS
@private
@constructor
@param {Array} snapshots An array of snapshots
@param {Object} meta
*/
function SnapshotRecordArray(snapshots, meta, adapterOptions) {
this.snapshots = snapshots;
this.length = snapshots.length;
this.meta = meta;
this.adapterOptions = adapterOptions;
}

/**
@method fromRecordArray
@private
@static
@param {DS.RecordArray} recordArray
@param {Object} adapterOptions
@return SnapshotRecordArray
*/
SnapshotRecordArray.fromRecordArray = function(recordArray, adapterOptions) {
var meta = recordArray.get('meta');
var snapshots = recordArray.invoke('createSnapshot');
return new SnapshotRecordArray(snapshots, meta, adapterOptions);
};

export default SnapshotRecordArray;
59 changes: 30 additions & 29 deletions packages/ember-data/lib/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ Store = Service.extend({
}

if (internalModel.isEmpty()) {
fetchedInternalModel = this.scheduleFetch(internalModel);
fetchedInternalModel = this.scheduleFetch(internalModel, options);
//TODO double check about reloading
} else if (internalModel.isLoading()) {
fetchedInternalModel = internalModel._loadingPromise;
Expand Down Expand Up @@ -641,16 +641,17 @@ Store = Service.extend({
@private
@param {InternalModel} internalModel model
@return {Promise} promise
*/
fetchRecord: function(internalModel) {
*/
// TODO rename this to have an underscore
fetchRecord: function(internalModel, options) {
var typeClass = internalModel.type;
var id = internalModel.id;
var adapter = this.adapterFor(typeClass.modelName);

Ember.assert("You tried to find a record but you have no adapter (for " + typeClass + ")", adapter);
Ember.assert("You tried to find a record but your adapter (for " + typeClass + ") does not implement 'find'", typeof adapter.find === 'function');

var promise = _find(adapter, this, typeClass, id, internalModel);
var promise = _find(adapter, this, typeClass, id, internalModel, options);
return promise;
},

Expand All @@ -659,24 +660,25 @@ Store = Service.extend({
return Promise.all(map(internalModels, this.scheduleFetch, this));
},

scheduleFetch: function(internalModel) {
scheduleFetch: function(internalModel, options) {
var typeClass = internalModel.type;

if (internalModel._loadingPromise) { return internalModel._loadingPromise; }

var resolver = Ember.RSVP.defer('Fetching ' + typeClass + 'with id: ' + internalModel.id);
var recordResolverPair = {
var pendingFetchItem = {
record: internalModel,
resolver: resolver
resolver: resolver,
options: options
};
var promise = resolver.promise;

internalModel.loadingData(promise);

if (!this._pendingFetch.get(typeClass)) {
this._pendingFetch.set(typeClass, [recordResolverPair]);
this._pendingFetch.set(typeClass, [pendingFetchItem]);
} else {
this._pendingFetch.get(typeClass).push(recordResolverPair);
this._pendingFetch.get(typeClass).push(pendingFetchItem);
}
Ember.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches);

Expand All @@ -692,19 +694,19 @@ Store = Service.extend({
this._pendingFetch = Map.create();
},

_flushPendingFetchForType: function (recordResolverPairs, typeClass) {
_flushPendingFetchForType: function (pendingFetchItems, typeClass) {
var store = this;
var adapter = store.adapterFor(typeClass.modelName);
var shouldCoalesce = !!adapter.findMany && adapter.coalesceFindRequests;
var records = Ember.A(recordResolverPairs).mapBy('record');
var records = Ember.A(pendingFetchItems).mapBy('record');

function _fetchRecord(recordResolverPair) {
recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record));
recordResolverPair.resolver.resolve(store.fetchRecord(recordResolverPair.record, recordResolverPair.options)); // TODO adapter options
}

function resolveFoundRecords(records) {
forEach(records, function(record) {
var pair = Ember.A(recordResolverPairs).findBy('record', record);
var pair = Ember.A(pendingFetchItems).findBy('record', record);
if (pair) {
var resolver = pair.resolver;
resolver.resolve(record);
Expand Down Expand Up @@ -734,16 +736,16 @@ Store = Service.extend({

function rejectRecords(records, error) {
forEach(records, function(record) {
var pair = Ember.A(recordResolverPairs).findBy('record', record);
var pair = Ember.A(pendingFetchItems).findBy('record', record);
if (pair) {
var resolver = pair.resolver;
resolver.reject(error);
}
});
}

if (recordResolverPairs.length === 1) {
_fetchRecord(recordResolverPairs[0]);
if (pendingFetchItems.length === 1) {
_fetchRecord(pendingFetchItems[0]);
} else if (shouldCoalesce) {

// TODO: Improve records => snapshots => records => snapshots
Expand All @@ -769,14 +771,14 @@ Store = Service.extend({
then(makeMissingRecordsRejector(requestedRecords)).
then(null, makeRecordsRejector(requestedRecords));
} else if (ids.length === 1) {
var pair = Ember.A(recordResolverPairs).findBy('record', groupOfRecords[0]);
var pair = Ember.A(pendingFetchItems).findBy('record', groupOfRecords[0]);
_fetchRecord(pair);
} else {
Ember.assert("You cannot return an empty array from adapter's method groupRecordsForFindMany", false);
}
});
} else {
forEach(recordResolverPairs, _fetchRecord);
forEach(pendingFetchItems, _fetchRecord);
}
},

Expand Down Expand Up @@ -1043,13 +1045,14 @@ Store = Service.extend({
@method findAll
@param {String} modelName
@param {Object} options
@return {DS.AdapterPopulatedRecordArray}
*/
findAll: function(modelName) {
findAll: function(modelName, options) {
Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string');
var typeClass = this.modelFor(modelName);

return this._fetchAll(typeClass, this.peekAll(modelName));
return this._fetchAll(typeClass, this.peekAll(modelName), options);
},

/**
Expand All @@ -1059,7 +1062,7 @@ Store = Service.extend({
@param {DS.RecordArray} array
@return {Promise} promise
*/
_fetchAll: function(typeClass, array) {
_fetchAll: function(typeClass, array, options) {
var adapter = this.adapterFor(typeClass.modelName);
var sinceToken = this.typeMapFor(typeClass).metadata.since;

Expand All @@ -1068,7 +1071,7 @@ Store = Service.extend({
Ember.assert("You tried to load all records but you have no adapter (for " + typeClass + ")", adapter);
Ember.assert("You tried to load all records but your adapter does not implement `findAll`", typeof adapter.findAll === 'function');

return promiseArray(_findAll(adapter, this, typeClass, sinceToken));
return promiseArray(_findAll(adapter, this, typeClass, sinceToken, options));
},

/**
Expand Down Expand Up @@ -1352,13 +1355,12 @@ Store = Service.extend({
@param {Object} options
*/
scheduleSave: function(internalModel, resolver, options) {
var snapshot = internalModel.createSnapshot();
var snapshot = internalModel.createSnapshot(options);
internalModel.flushChangedAttributes();
internalModel.adapterWillCommit();
this._pendingSave.push({
snapshot: snapshot,
response: resolver,
options: options
resolver: resolver
});
once(this, 'flushPendingSave');
},
Expand All @@ -1377,7 +1379,6 @@ Store = Service.extend({
forEach(pending, function(pendingItem) {
var snapshot = pendingItem.snapshot;
var resolver = pendingItem.resolver;
var options = pendingItem.options;
var record = snapshot._internalModel;
var adapter = this.adapterFor(record.type.modelName);
var operation;
Expand All @@ -1392,7 +1393,7 @@ Store = Service.extend({
operation = 'updateRecord';
}

resolver.resolve(_commit(adapter, this, operation, snapshot, options));
resolver.resolve(_commit(adapter, this, operation, snapshot));
}, this);
},

Expand Down Expand Up @@ -2167,11 +2168,11 @@ function defaultSerializer(container) {
container.lookup('serializer:-default');
}

function _commit(adapter, store, operation, snapshot, options) {
function _commit(adapter, store, operation, snapshot) {
var record = snapshot._internalModel;
var modelName = snapshot.modelName;
var type = store.modelFor(modelName);
var promise = adapter[operation](store, type, snapshot, options); // TODO check this out
var promise = adapter[operation](store, type, snapshot);
var serializer = serializerForAdapter(store, adapter, modelName);
var label = "DS: Extract and notify about " + operation + " completion of " + record;

Expand Down
12 changes: 8 additions & 4 deletions packages/ember-data/lib/system/store/finders.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import {
serializerForAdapter
} from "ember-data/system/store/serializers";

import SnapshotRecordArray from "ember-data/system/snapshot-record-array";

var Promise = Ember.RSVP.Promise;
var map = Ember.EnumerableUtils.map;

export function _find(adapter, store, typeClass, id, internalModel) {
var snapshot = internalModel.createSnapshot();
export function _find(adapter, store, typeClass, id, internalModel, options) {
var snapshot = internalModel.createSnapshot(options);
var promise = adapter.find(store, typeClass, id, snapshot);
var serializer = serializerForAdapter(store, adapter, internalModel.type.modelName);
var label = "DS: Handle Adapter#find of " + typeClass + " with id: " + id;
Expand Down Expand Up @@ -118,9 +120,11 @@ export function _findBelongsTo(adapter, store, internalModel, link, relationship
}, null, "DS: Extract payload of " + internalModel + " : " + relationship.type);
}

export function _findAll(adapter, store, typeClass, sinceToken) {
var promise = adapter.findAll(store, typeClass, sinceToken);
export function _findAll(adapter, store, typeClass, sinceToken, options) {
var adapterOptions = options && options.adapterOptions;
var modelName = typeClass.modelName;
var snapshotArray = SnapshotRecordArray.fromRecordArray(store.peekAll(modelName), adapterOptions);
var promise = adapter.findAll(store, typeClass, sinceToken, snapshotArray);
var serializer = serializerForAdapter(store, adapter, modelName);
var label = "DS: Handle Adapter#findAll of " + typeClass;

Expand Down
Loading

0 comments on commit 4e73e0a

Please sign in to comment.