Skip to content

Commit

Permalink
Merge pull request #815 from everwakeful/master
Browse files Browse the repository at this point in the history
Extend existing "meta" solution for JSON REST responses
  • Loading branch information
igorT committed Apr 11, 2013
2 parents 5268133 + 0bce730 commit 44f8de1
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 32 deletions.
25 changes: 20 additions & 5 deletions packages/ember-data/lib/serializers/json_serializer.js
Expand Up @@ -13,14 +13,23 @@ DS.JSONSerializer = DS.Serializer.extend({
this.set('transforms', DS.JSONTransforms);
}

this.sideloadMapping = Ember.Map.create();
this.metadataMapping = Ember.Map.create();

this.configure({
meta: 'meta',
since: 'since'
});
},

configure: function(type, configuration) {
var key;

if (type && !configuration) {
for(key in type){
this.metadataMapping.set(get(type, key), key);
}

return this._super(type);
}

Expand Down Expand Up @@ -220,12 +229,18 @@ DS.JSONSerializer = DS.Serializer.extend({
},

extractMeta: function(loader, type, json) {
var meta = json[this.configOption(type, 'meta')], since;
if (!meta) { return; }
var meta = this.configOption(type, 'meta'),
data = json, key, property, value;

if (since = meta[this.configOption(type, 'since')]) {
loader.sinceForType(type, since);
if(meta && json[meta]){
data = json[meta];
}

this.metadataMapping.forEach(function(property, key){
if(value = data[property]){
loader.metaForType(type, key, value);
}
});
},

extractEmbeddedType: function(relationship, data) {
Expand Down Expand Up @@ -265,7 +280,7 @@ DS.JSONSerializer = DS.Serializer.extend({
for (var prop in json) {
if (!json.hasOwnProperty(prop) ||
prop === root ||
prop === this.configOption(type, 'meta')) {
!!this.metadataMapping.get(prop)) {
continue;
}

Expand Down
42 changes: 21 additions & 21 deletions packages/ember-data/lib/system/adapter.js
Expand Up @@ -28,8 +28,8 @@ function loaderFor(store) {
store.prematerialize(reference, prematerialized);
},

sinceForType: function(type, since) {
store.sinceForType(type, since);
metaForType: function(type, property, data) {
store.metaForType(type, property, data);
}
};
}
Expand Down Expand Up @@ -121,7 +121,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
occupations: [{
id: 345,
title: "Tricycle Mechanic"
}]
}]
});
```
Expand Down Expand Up @@ -151,12 +151,12 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
For example, the `RESTAdapter` saves newly created records by
making an Ajax request. When the server returns, the adapter
calls didCreateRecord. If the server returns a response body,
it is passed as the payload.
it is passed as the payload.
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {any} payload
@param {any} payload
*/
didCreateRecord: function(store, type, record, payload) {
store.didSaveRecord(record);
Expand All @@ -177,7 +177,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
Acknowledges that the adapter has finished creating several records.
Your adapter should call this method from `createRecords` when it
has saved multiple created records to its persistent storage
has saved multiple created records to its persistent storage
received an acknowledgement.
If the persistent storage returns a new payload in response to the
Expand All @@ -187,7 +187,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {any} payload
@param {any} payload
*/
didCreateRecords: function(store, type, records, payload) {
records.forEach(function(record) {
Expand Down Expand Up @@ -216,7 +216,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {any} payload
@param {any} payload
*/
didSaveRecord: function(store, type, record, payload) {
store.didSaveRecord(record);
Expand Down Expand Up @@ -249,7 +249,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {any} payload
@param {any} payload
*/
didUpdateRecord: function() {
this.didSaveRecord.apply(this, arguments);
Expand All @@ -268,14 +268,14 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} record
@param {any} payload
@param {any} payload
*/
didDeleteRecord: function() {
this.didSaveRecord.apply(this, arguments);
},

/**
Acknowledges that the adapter has finished updating or deleting
Acknowledges that the adapter has finished updating or deleting
multiple records.
Your adapter should call this method from its `updateRecords` or
Expand All @@ -288,7 +288,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} records
@param {any} payload
@param {any} payload
*/
didSaveRecords: function(store, type, records, payload) {
records.forEach(function(record) {
Expand All @@ -314,7 +314,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} records
@param {any} payload
@param {any} payload
*/
didUpdateRecords: function() {
this.didSaveRecords.apply(this, arguments);
Expand All @@ -333,7 +333,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Model} records
@param {any} payload
@param {any} payload
*/
didDeleteRecords: function() {
this.didSaveRecords.apply(this, arguments);
Expand All @@ -351,7 +351,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {any} payload
@param {any} payload
@param {String} id
*/
didFindRecord: function(store, type, payload, id) {
Expand All @@ -375,7 +375,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {any} payload
@param {any} payload
*/
didFindAll: function(store, type, payload) {
var loader = DS.loaderFor(store),
Expand All @@ -394,7 +394,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {any} payload
@param {any} payload
@param {DS.AdapterPopulatedRecordArray} recordArray
*/
didFindQuery: function(store, type, payload, recordArray) {
Expand All @@ -415,7 +415,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {any} payload
@param {any} payload
*/
didFindMany: function(store, type, payload) {
var loader = DS.loaderFor(store);
Expand Down Expand Up @@ -539,7 +539,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
},

/**
A public method that allows you to register an enumerated
A public method that allows you to register an enumerated
type on your adapter. This is useful if you want to utilize
a text representation of an integer value.
Expand All @@ -551,7 +551,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
You would then refer to the 'priority' DS.attr in your model:
App.Task = DS.Model.extend({
priority: DS.attr('priority')
priority: DS.attr('priority')
});
And lastly, you would set/get the text representation on your model instance,
Expand All @@ -561,7 +561,7 @@ DS.Adapter = Ember.Object.extend(DS._Mappable, {
Server Response / Load: { myTask: {priority: 0} }
@param {String} type of the transform
@param {Array} array of String objects to use for the enumerated values.
@param {Array} array of String objects to use for the enumerated values.
This is an ordered list and the index values will be used for the transform.
*/
registerEnumTransform: function(attributeType, objects) {
Expand Down
12 changes: 7 additions & 5 deletions packages/ember-data/lib/system/store.js
Expand Up @@ -798,8 +798,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
@private
*/
fetchAll: function(type, array) {
var sinceToken = this.typeMapFor(type).sinceToken,
adapter = this.adapterForType(type);
var adapter = this.adapterForType(type),
sinceToken = this.typeMapFor(type).metadata.since;

set(array, 'isUpdating', true);

Expand All @@ -813,8 +813,9 @@ DS.Store = Ember.Object.extend(DS._Mappable, {

/**
*/
sinceForType: function(type, sinceToken) {
this.typeMapFor(type).sinceToken = sinceToken;
metaForType: function(type, property, data) {
var target = this.typeMapFor(type).metadata;
set(target, property, data);
},

/**
Expand Down Expand Up @@ -1533,7 +1534,8 @@ DS.Store = Ember.Object.extend(DS._Mappable, {
{
idToCid: {},
clientIds: [],
recordArrays: []
recordArrays: [],
metadata: {}
});
}
},
Expand Down
111 changes: 110 additions & 1 deletion packages/ember-data/tests/integration/json_serialization_test.js
Expand Up @@ -217,7 +217,7 @@ test("loadValue should be called once per sideloaded type", function() {
}
};

loader = { load: K, loadMany: K, prematerialize: K, sinceForType: K };
loader = { load: K, loadMany: K, prematerialize: K, metaForType: K };

serializer.loadValue = function(store, type, value) {
loadedTypes.push(type);
Expand All @@ -227,3 +227,112 @@ test("loadValue should be called once per sideloaded type", function() {

equal(loadedTypes.length, 3, "Loaded: " + loadedTypes.join(", "));
});

module("Adapter serialization with metadata", {
setup: function() {
store = DS.Store.create();
serializer = DS.JSONSerializer.create();
},

teardown: function() {
serializer.destroy();
store.destroy();
}
});

test("metadata that has been configured is passed to the store", function() {
var payload, typeMap, loader = DS.loaderFor(store), K = Ember.K, App = Ember.Namespace.create({
toString: function() { return "App"; }
});

payload = {
meta: {
pages: { total_pages: '2', per_page: '1' }, since: '123'
},
posts: [
{ id: 1, name: "My First Post" },
{ id: 2, name: "The Second Post" }
]
};

App.Post = DS.Model.extend({
name: DS.attr('string', {defaultValue: 'A Post'})
});

serializer.configure({
meta: 'meta',
since: 'since',
pagination: 'pages'
});

loader = { load: K, loadMany: K, prematerialize: K, populateArray: K, metaForType: loader.metaForType };
typeMap = store.typeMapFor(App.Post);

serializer.extractMany(loader, payload, App.Post);

equal(typeMap.metadata.pagination, payload.meta[serializer.configOption(App.Post, 'pagination')], "Loaded meta property: pagination");
equal(typeMap.metadata.since, payload.meta[serializer.configOption(App.Post, 'since')], "Loaded meta property: since");
});

test("metadata that has not been configured is not passed to the store", function() {
var payload, typeMap, loader = DS.loaderFor(store), K = Ember.K, App = Ember.Namespace.create({
toString: function() { return "App"; }
});

payload = {
meta: {
pages: { total_pages: '2', per_page: '1' }, since: '123'
},
posts: [
{ id: 1, name: "My First Post" },
{ id: 2, name: "The Second Post" }
]
};

App.Post = DS.Model.extend({
name: DS.attr('string', {defaultValue: 'A Post'})
});

loader = { load: K, loadMany: K, prematerialize: K, populateArray: K, metaForType: loader.metaForType };
typeMap = store.typeMapFor(App.Post);

serializer.extractMany(loader, payload, App.Post);

equal(typeMap.metadata.pagination, null, "Did not load meta property: pagination");
equal(typeMap.metadata.since, payload.meta[serializer.configOption(App.Post, 'since')], "Loaded meta property: since");
});

test("metadata that is not nested under 'meta' is passed to store", function() {
var payload, typeMap, loader = DS.loaderFor(store), K = Ember.K, App = Ember.Namespace.create({
toString: function() { return "App"; }
});

payload = {
total_pages: '2',
per_page: '1',
since: '123',
posts: [
{ id: 1, name: "My First Post" },
{ id: 2, name: "The Second Post" }
]
};

App.Post = DS.Model.extend({
name: DS.attr('string', {defaultValue: 'A Post'})
});

serializer.configure({
since: 'since',
pages: 'total_pages',
pageLimit: 'per_page'
});

loader = { load: K, loadMany: K, prematerialize: K, populateArray: K, metaForType: loader.metaForType };
typeMap = store.typeMapFor(App.Post);

serializer.extractMany(loader, payload, App.Post);

equal(typeMap.metadata.pages, payload[serializer.configOption(App.Post, 'pages')], "Loaded meta property: pages");
equal(typeMap.metadata.pageLimit, payload[serializer.configOption(App.Post, 'pageLimit')], "Loaded meta property: pageLimit");
equal(typeMap.metadata.since, payload[serializer.configOption(App.Post, 'since')], "Loaded meta property: since");
});

0 comments on commit 44f8de1

Please sign in to comment.