diff --git a/packages/ember-data/lib/system/adapter.js b/packages/ember-data/lib/system/adapter.js index 850eb00f3e7..1ee98a0c2ba 100644 --- a/packages/ember-data/lib/system/adapter.js +++ b/packages/ember-data/lib/system/adapter.js @@ -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, @@ -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, @@ -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, @@ -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, diff --git a/packages/ember-data/lib/system/model/internal-model.js b/packages/ember-data/lib/system/model/internal-model.js index 14d782019ef..0f33c5c2187 100644 --- a/packages/ember-data/lib/system/model/internal-model.js +++ b/packages/ember-data/lib/system/model/internal-model.js @@ -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; }, @@ -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; }, /** diff --git a/packages/ember-data/lib/system/model/model.js b/packages/ember-data/lib/system/model/model.js index fbccf8e9d83..8d66dd44f7d 100644 --- a/packages/ember-data/lib/system/model/model.js +++ b/packages/ember-data/lib/system/model/model.js @@ -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); }, /** @@ -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. */ diff --git a/packages/ember-data/lib/system/snapshot-record-array.js b/packages/ember-data/lib/system/snapshot-record-array.js new file mode 100644 index 00000000000..daea0cb45f0 --- /dev/null +++ b/packages/ember-data/lib/system/snapshot-record-array.js @@ -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; diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index fdbfdddd0da..3b746779f8c 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -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; @@ -641,8 +641,9 @@ 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); @@ -650,7 +651,7 @@ Store = Service.extend({ 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; }, @@ -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); @@ -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); @@ -734,7 +736,7 @@ 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); @@ -742,8 +744,8 @@ Store = Service.extend({ }); } - if (recordResolverPairs.length === 1) { - _fetchRecord(recordResolverPairs[0]); + if (pendingFetchItems.length === 1) { + _fetchRecord(pendingFetchItems[0]); } else if (shouldCoalesce) { // TODO: Improve records => snapshots => records => snapshots @@ -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); } }, @@ -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); }, /** @@ -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; @@ -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)); }, /** @@ -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'); }, @@ -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; @@ -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); }, @@ -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; diff --git a/packages/ember-data/lib/system/store/finders.js b/packages/ember-data/lib/system/store/finders.js index 2d156f476b4..8e57bf31c6c 100644 --- a/packages/ember-data/lib/system/store/finders.js +++ b/packages/ember-data/lib/system/store/finders.js @@ -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; @@ -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; diff --git a/packages/ember-data/tests/integration/adapter/store-adapter-test.js b/packages/ember-data/tests/integration/adapter/store-adapter-test.js index 06ab28ce357..8bb2bcd604e 100644 --- a/packages/ember-data/tests/integration/adapter/store-adapter-test.js +++ b/packages/ember-data/tests/integration/adapter/store-adapter-test.js @@ -947,3 +947,74 @@ test("findBelongsTo receives a snapshot", function() { person.get('dog'); }); }); + +test("record.save should pass adapterOptions to the updateRecord method", function() { + expect(1); + + env.adapter.updateRecord = async(function(store, type, snapshot) { + deepEqual(snapshot.adapterOptions, { subscribe: true }); + return Ember.RSVP.resolve({ id: 1 }); + }); + + run(function() { + var person = store.push('person', { id: 1, name: 'Tom' }); + person.save({ adapterOptions: { subscribe: true } }); + }); +}); + +test("record.save should pass adapterOptions to the createRecord method", function() { + expect(1); + + env.adapter.createRecord = async(function(store, type, snapshot) { + deepEqual(snapshot.adapterOptions, { subscribe: true }); + return Ember.RSVP.resolve({ id: 1 }); + }); + + run(function() { + var person = store.createRecord('person', { name: 'Tom' }); + person.save({ adapterOptions: { subscribe: true } }); + }); +}); + +test("record.save should pass adapterOptions to the deleteRecord method", function() { + expect(1); + + env.adapter.deleteRecord = async(function(store, type, snapshot) { + deepEqual(snapshot.adapterOptions, { subscribe: true }); + return Ember.RSVP.resolve({ id: 1 }); + }); + + run(function() { + var person = store.push('person', { id: 1, name: 'Tom' }); + person.destroyRecord({ adapterOptions: { subscribe: true } }); + }); +}); + + +test("findRecord should pass adapterOptions to the find method", function() { + expect(1); + + env.adapter.find = async(function(store, type, id, snapshot) { + deepEqual(snapshot.adapterOptions, { query: { embed: true } }); + return Ember.RSVP.resolve({ id: 1 }); + }); + + run(function() { + store.findRecord('person', 1, { adapterOptions: { query: { embed: true } } }); + }); +}); + + +test("findAll should pass adapterOptions to the findAll method", function() { + expect(1); + + env.adapter.findAll = async(function(store, type, sinceToken, arraySnapshot) { + var adapterOptions = arraySnapshot.adapterOptions; + deepEqual(adapterOptions, { query: { embed: true } }); + return Ember.RSVP.resolve([{ id: 1 }]); + }); + + run(function() { + store.findAll('person', { adapterOptions: { query: { embed: true } } }); + }); +});