diff --git a/bower.json b/bower.json index 2cc6a21..87a1def 100644 --- a/bower.json +++ b/bower.json @@ -20,7 +20,7 @@ "vendor" ], "dependencies": { - "ember-data": ">= 1.0.0-beta.16.1", - "ember": ">= 1.9.0" + "ember-data": "1.13.5", + "ember": ">= 1.12.0" } } diff --git a/localstorage_adapter.js b/localstorage_adapter.js index 41ce2e4..3f20041 100644 --- a/localstorage_adapter.js +++ b/localstorage_adapter.js @@ -4,11 +4,17 @@ 'use strict'; DS.LSSerializer = DS.JSONSerializer.extend({ + + /** + * Invokes the new serializer API. + * This should be removed in 2.0 + */ + isNewSerializerAPI: true, serializeHasMany: function(snapshot, json, relationship) { var key = relationship.key; var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key; - var relationshipType = snapshot.type.determineRelationshipType(relationship); + var relationshipType = snapshot.type.determineRelationshipType(relationship, this.store); if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany' || @@ -17,8 +23,8 @@ // TODO support for polymorphic manyToNone and manyToMany relationships } }, - - /** + + /** * Extracts whatever was returned from the adapter. * * If the adapter returns relationships in an embedded way, such as follows: @@ -39,13 +45,9 @@ * } * } * - * this method will create separated JSON for each resource and then push - * them individually to the Store. - * - * In the end, only the main resource will remain, containing the ids of its - * relationships. Given the relations are already in the Store, we will - * return a JSON with the main resource alone. The Store will sort out the - * associations by itself. + * this method will create separated JSON for each resource and then combine + * the data and the embed payload into the JSON.Api spec for included objects + * returning a single object. * * @method extractSingle * @private @@ -53,7 +55,9 @@ * @param {DS.Model} type the type/model * @param {Object} payload returned JSON */ - extractSingle: function(store, type, payload) { + normalizeSingleResponse: function(store, type, payload) { + var included = []; + if (payload && payload._embedded) { for (var relation in payload._embedded) { var relType = type.typeForRelationship(relation,store); @@ -62,9 +66,11 @@ if (embeddedPayload) { if (Ember.isArray(embeddedPayload)) { - store.pushMany(typeName, embeddedPayload); + embeddedPayload.forEach(function(record) { + included.pushObject(this.normalize(relType,record).data); + }.bind(this)); } else { - store.push(typeName, embeddedPayload); + included.pushObject(this.normalize(relType, embeddedPayload).data); } } } @@ -72,10 +78,14 @@ delete payload._embedded; } - return this.normalize(type, payload); + var normalPayload = this.normalize(type, payload); + if(included.length > 0){ + normalPayload['included'] = included; + } + return normalPayload; }, - - /** + + /** * This is exactly the same as extractSingle, but used in an array. * * @method extractSingle @@ -84,10 +94,23 @@ * @param {DS.Model} type the type/model * @param {Array} payload returned JSONs */ - extractArray: function(store, type, payload) { - return payload.map(function(json) { - return this.extractSingle(store, type, json); - }, this); + normalizeArrayResponse: function(store, type, payload) { + var response = { data: [], included: [] }; + + payload.forEach(function(json){ + var normalized = this.normalizeSingleResponse(store, type, json); + response.data.pushObject(normalized.data); + + if(normalized.included){ + normalized.included.forEach(function(included){ + if(!response.included.contains(included.id)){ + response.included.addObject(included); + } + }); + } + }.bind(this)); + + return response; } }); @@ -101,7 +124,7 @@ @param {DS.Model} type @param {Object|String|Integer|null} id */ - find: function(store, type, id, opts) { + findRecord: function(store, type, id, opts) { var allowRecursive = true; var namespace = this._namespaceForType(type); var record = Ember.A(namespace.records[id]); @@ -179,9 +202,9 @@ // match records with "complete: true" and the name "foo" or "bar" // // { complete: true, name: /foo|bar/ } - findQuery: function (store, type, query, recordArray) { + query: function (store, type, query, recordArray) { var namespace = this._namespaceForType(type); - var results = this.query(namespace.records, query); + var results = this._query(namespace.records, query); if (results.get('length')) { return this.loadRelationshipsForMany(store, type, results); @@ -190,11 +213,11 @@ } }, - query: function (records, query) { + _query: function (records, query) { var results = [], record; function recordMatchesQuery(record) { - return Ember.keys(query).every(function(property) { + return Object.keys(query).every(function(property) { var test = query[property]; if (Object.prototype.toString.call(test) === '[object RegExp]') { return test.test(record[property]); @@ -387,7 +410,7 @@ var relationEmbeddedId = record[relationName]; var relationProp = adapter.relationshipProperties(type, relationName); var relationType = relationProp.kind; - var foreignAdapter = store.adapterFor(relationName); + var foreignAdapter = store.adapterFor(relationModel.modelName); var opts = {allowRecursive: false}; @@ -405,12 +428,12 @@ * In this case, cart belongsTo customer and its id is present in the * main payload. We find each of these records and add them to _embedded. */ - if (relationEmbeddedId && foreignAdapter === adapter) + if (relationEmbeddedId && DS.LSAdapter.prototype.isPrototypeOf(adapter)) { recordPromise = recordPromise.then(function(recordPayload) { var promise; if (relationType === 'belongsTo' || relationType === 'hasOne') { - promise = adapter.find(null, relationModel, relationEmbeddedId, opts); + promise = adapter.findRecord(null, relationModel, relationEmbeddedId, opts); } else if (relationType == 'hasMany') { promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts); } @@ -465,7 +488,7 @@ */ addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) { var objectHasId = (relationshipRecord && relationshipRecord.id); - var arrayHasIds = (relationshipRecord.length && relationshipRecord.everyBy("id")); + var arrayHasIds = (relationshipRecord.length && relationshipRecord.isEvery("id")); var isValidRelationship = (objectHasId || arrayHasIds); if (isValidRelationship) { diff --git a/test/tests.js b/test/tests.js index eaf954f..11da33d 100644 --- a/test/tests.js +++ b/test/tests.js @@ -63,7 +63,7 @@ test('find with id', function() { expect(3); stop(); - store.find('list', 'l1').then(function(list) { + store.findRecord('list', 'l1').then(function(list) { equal(get(list, 'id'), 'l1', 'id is loaded correctly'); equal(get(list, 'name'), 'one', 'name is loaded correctly'); equal(get(list, 'b'), true, 'b is loaded correctly'); @@ -75,42 +75,42 @@ test('#find - rejects promise when non-existing record', function () { expect(1); stop(); - store.find("list", "unknown").catch(function () { + store.findRecord("list", "unknown").catch(function () { ok(true); start(); }); }); -test('findQuery', function() { +test('query', function() { stop(); - store.findQuery('list', {name: /one|two/}).then(function(records) { + store.query('list', {name: /one|two/}).then(function(records) { equal(get(records, 'length'), 2, 'found results for /one|two/'); start(); }); stop(); - store.findQuery('list', {name: /.+/, id: /l1/}).then(function(records) { + store.query('list', {name: /.+/, id: /l1/}).then(function(records) { equal(get(records, 'length'), 1, 'found results for {name: /.+/, id: /l1/}'); start(); }); stop(); - store.findQuery('list', {name: 'one'}).then(function(records) { + store.query('list', {name: 'one'}).then(function(records) { equal(get(records, 'length'), 1, 'found results for name "one"'); start(); }); stop(); - store.findQuery('list', {b: true}).then(function(records) { + store.query('list', {b: true}).then(function(records) { equal(get(records, 'length'), 1, 'found results for {b: true}'); start(); }); }); -test('#findQuery - rejects promise when there are no records', function() { +test('#query - rejects promise when there are no records', function() { stop(); - store.findQuery('list', {name: /unknown/}).catch(function() { + store.query('list', {name: /unknown/}).catch(function() { ok(true); equal(store.hasRecordForId("list", "unknown"), false); start(); @@ -136,10 +136,10 @@ test('findAll', function() { }); }); -test('findQueryMany', function() { +test('queryMany', function() { expect(11); stop(); - store.find('order', { b: true }).then(function(records) { + store.query('order', { b: true }).then(function(records) { var firstRecord = records.objectAt(0), secondRecord = records.objectAt(1), thirdRecord = records.objectAt(2); @@ -175,7 +175,7 @@ test('createRecord', function() { list = store.createRecord('list', { name: 'Rambo' }); list.save().then(function() { - store.findQuery('list', { name: 'Rambo' }).then(function(records) { + store.query('list', { name: 'Rambo' }).then(function(records) { var record = records.objectAt(0); equal(get(records, 'length'), 1, "Only Rambo was found"); @@ -184,7 +184,7 @@ test('createRecord', function() { }); list.save().then(function() { - store.find('list', list.id).then(function(record) { + store.findRecord('list', list.id).then(function(record) { equal(get(record, 'name'), "Rambo", "Correct name"); equal(get(record, 'id'), list.id, "Correct, original id"); @@ -200,7 +200,7 @@ test('updateRecords', function() { list = store.createRecord('list', { name: 'Rambo' }); var UpdateList = function(list) { - return store.findQuery('list', { name: 'Rambo' }).then(function(records) { + return store.query('list', { name: 'Rambo' }).then(function(records) { var record = records.objectAt(0); record.set('name', 'Macgyver'); return record.save(); @@ -208,7 +208,7 @@ test('updateRecords', function() { } var AssertListIsUpdated = function() { - return store.findQuery('list', { name: 'Macgyver' }).then(function(records) { + return store.query('list', { name: 'Macgyver' }).then(function(records) { var record = records.objectAt(0); equal(get(records, 'length'), 1, "Only one record was found"); @@ -227,13 +227,13 @@ test('deleteRecord', function() { expect(2); stop(); var AssertListIsDeleted = function() { - return store.findQuery('list', { name: 'one' }).catch(function() { + return store.query('list', { name: 'one' }).catch(function() { ok(true, "List was deleted"); start(); }); } - store.findQuery('list', { name: 'one' }).then(function(lists) { + store.query('list', { name: 'one' }).then(function(lists) { var list = lists.objectAt(0); equal(get(list, "id"), "l1", "Item exists"); @@ -247,8 +247,8 @@ test('deleteRecord', function() { test('changes in bulk', function() { stop(); var promises, - listToUpdate = store.find('list', 'l1'), - listToDelete = store.find('list', 'l2'), + listToUpdate = store.findRecord('list', 'l1'), + listToDelete = store.findRecord('list', 'l2'), listToCreate = store.createRecord('list', { name: 'Rambo' }); var UpdateList = function(list) { @@ -275,8 +275,8 @@ test('changes in bulk', function() { return promises; }).then(function() { - var updatedList = store.find('list', 'l1'), - createdList = store.findQuery('list', {name: 'Rambo'}), + var updatedList = store.findRecord('list', 'l1'), + createdList = store.query('list', {name: 'Rambo'}), promises = Ember.A(); createdList.then(function(lists) { @@ -284,7 +284,7 @@ test('changes in bulk', function() { promises.push(new Ember.RSVP.Promise(function(){})); }); - store.find('list', 'l2').then(function(list) { + store.findRecord('list', 'l2').then(function(list) { equal(get(list, 'length'), undefined, "Record was deleted successfully"); promises.push(new Ember.RSVP.Promise(function(){})); }); @@ -310,7 +310,7 @@ test('load hasMany association', function() { expect(4); stop(); - store.find('list', 'l1').then(function(list) { + store.findRecord('list', 'l1').then(function(list) { var items = list.get('items'); var item1 = items.get('firstObject'), @@ -328,7 +328,7 @@ test('load hasMany association', function() { test('load belongsTo association', function() { stop(); - store.find('item', 'i1').then(function(item) { + store.findRecord('item', 'i1').then(function(item) { return new Ember.RSVP.Promise(function(resolve) { resolve(get(item, 'list')); }); }).then(function(list) { equal(get(list, 'id'), 'l1', "id is loaded correctly"); @@ -344,14 +344,14 @@ test('saves belongsTo', function() { stop(); - store.find('list', listId).then(function(list) { + store.findRecord('list', listId).then(function(list) { item = store.createRecord('item', { name: 'three thousand' }); item.set('list', list); return item.save(); }).then(function(item) { store.unloadAll('item'); - return store.find('item', item.get('id')); + return store.findRecord('item', item.get('id')); }).then(function(item) { var list = item.get('list'); ok(item.get('list'), 'list is present'); @@ -366,7 +366,7 @@ test('saves hasMany', function() { stop(); - store.find('list', listId).then(function(list) { + store.findRecord('list', listId).then(function(list) { item = store.createRecord('item', { name: 'three thousand' }); list.get('items').pushObject(item); @@ -375,7 +375,7 @@ test('saves hasMany', function() { return item.save(); }).then(function(item) { store.unloadAll('list'); - return store.find('list', listId); + return store.findRecord('list', listId); }).then(function(list) { var items = list.get('items'), item1 = items.objectAt(0); @@ -419,7 +419,7 @@ test("extractArray calls extractSingle", function() { expect(1); stop(); - store.find('list').then(function(lists) { + store.findRecord('list').then(function(lists) { equal(callback.callCount, 3); start(); @@ -436,7 +436,7 @@ test('date is loaded correctly', function() { var person = store.createRecord('person', { name: 'Tom', birthdate: date }); person.save().then(function() { - store.find('person', { name: 'Tom' }).then(function(records) { + store.query('person', { name: 'Tom' }).then(function(records) { var loadedPerson = records.get('firstObject'); var birthdate = get(loadedPerson, 'birthdate'); ok((birthdate instanceof Date), 'Date should be loaded as an instance of Date'); @@ -465,7 +465,7 @@ test('handles localStorage being unavailable', function() { person.save().then(function() { ok(handler.calledWith(exception), 'Saving a record without local storage should trigger `persistenceUnavailable`'); store.unloadRecord(person); - return store.find('person', 'tom'); + return store.findRecord('person', 'tom'); }).then(function(reloadedPerson) { equal(reloadedPerson.get('name'), 'Tom', 'Records should still persist in-memory without local storage'); start();