Skip to content

Commit

Permalink
[BUGFIX release] backport ActiveModelAdapter changes
Browse files Browse the repository at this point in the history
This brings the ActiveModelAdapter in ember data up to date with
the changes in the ActiveModel repository, as of commit
6e846e74f919e8b4c47450a855963f06329a7bdc, about 1.13.4.
  • Loading branch information
Stanley Stuart committed Jul 20, 2015
1 parent 35c7cab commit 95139f8
Show file tree
Hide file tree
Showing 8 changed files with 844 additions and 477 deletions.
17 changes: 10 additions & 7 deletions packages/activemodel-adapter/lib/system/active-model-adapter.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {RESTAdapter} from "ember-data/adapters";
import {pluralize} from "ember-inflector";
import Ember from 'ember';
import {
InvalidError,
errorsHashToArray
} from "ember-data/adapters/errors";
} from 'ember-data/adapters/errors';
import RESTAdapter from 'ember-data/adapters/rest-adapter';

const {
pluralize,
decamelize,
underscore
} = Ember.String;

/**
@module ember-data
*/

var decamelize = Ember.String.decamelize;
var underscore = Ember.String.underscore;

/**
The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
with a JSON API that uses an underscored naming convention instead of camelCasing.
Expand Down Expand Up @@ -103,7 +106,7 @@ var underscore = Ember.String.underscore;
@extends DS.RESTAdapter
**/

var ActiveModelAdapter = RESTAdapter.extend({
const ActiveModelAdapter = RESTAdapter.extend({
defaultSerializer: '-active-model',
/**
The ActiveModelAdapter overrides the `pathForType` method to build
Expand Down
86 changes: 67 additions & 19 deletions packages/activemodel-adapter/lib/system/active-model-serializer.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { singularize } from "ember-inflector";
import RESTSerializer from "ember-data/serializers/rest-serializer";
import normalizeModelName from "ember-data/system/normalize-model-name";
import Ember from 'ember';
import RESTSerializer from 'ember-data/serializers/rest-serializer';
import normalizeModelName from 'ember-data/system/normalize-model-name';

/**
@module ember-data
*/
*/

var forEach = Ember.ArrayPolyfills.forEach;
var camelize = Ember.String.camelize;
var classify = Ember.String.classify;
var decamelize = Ember.String.decamelize;
var underscore = Ember.String.underscore;
const {
singularize,
classify,
decamelize,
camelize,
underscore
} = Ember.String;

/**
The ActiveModelSerializer is a subclass of the RESTSerializer designed to integrate
Expand Down Expand Up @@ -177,9 +180,7 @@ var ActiveModelSerializer = RESTSerializer.extend({
if (Ember.isNone(belongsTo)) {
json[jsonKey] = null;
} else {
json[jsonKey] = classify(belongsTo.modelName).replace(/(\/)([a-z])/g, function(match, separator, chr) {
return match.toUpperCase();
}).replace('/', '::');
json[jsonKey] = classify(belongsTo.modelName).replace('/', '::');
}
},

Expand Down Expand Up @@ -279,10 +280,7 @@ var ActiveModelSerializer = RESTSerializer.extend({
if (payload && payload.type) {
payload.type = this.modelNameFromPayloadKey(payload.type);
} else if (payload && relationship.kind === "hasMany") {
var self = this;
forEach.call(payload, function(single) {
single.type = self.modelNameFromPayloadKey(single.type);
});
payload.forEach((single) => single.type = this.modelNameFromPayloadKey(single.type));
}
} else {
payloadKey = this.keyForRelationship(key, relationship.kind, "deserialize");
Expand All @@ -298,12 +296,62 @@ var ActiveModelSerializer = RESTSerializer.extend({
}, this);
}
},

extractRelationships: function(modelClass, resourceHash) {
modelClass.eachRelationship(function (key, relationshipMeta) {
var relationshipKey = this.keyForRelationship(key, relationshipMeta.kind, "deserialize");

// prefer the format the AMS gem expects, e.g.:
// relationship: {id: id, type: type}
if (relationshipMeta.options.polymorphic) {
extractPolymorphicRelationships(key, relationshipMeta, resourceHash, relationshipKey);
}
// If the preferred format is not found, use {relationship_name_id, relationship_name_type}
if (resourceHash.hasOwnProperty(relationshipKey) && typeof resourceHash[relationshipKey] !== 'object') {
var polymorphicTypeKey = this.keyForRelationship(key) + '_type';
if (resourceHash[polymorphicTypeKey] && relationshipMeta.options.polymorphic) {
let id = resourceHash[relationshipKey];
let type = resourceHash[polymorphicTypeKey];
delete resourceHash[polymorphicTypeKey];
delete resourceHash[relationshipKey];
resourceHash[relationshipKey] = { id: id, type: type };
}
}
}, this);
return this._super.apply(this, arguments);
},

modelNameFromPayloadKey: function(key) {
var convertedFromRubyModule = camelize(singularize(key)).replace(/(^|\:)([A-Z])/g, function(match, separator, chr) {
return match.toLowerCase();
}).replace('::', '/');
var convertedFromRubyModule = singularize(key.replace('::', '/'));
return normalizeModelName(convertedFromRubyModule);
}
});

function extractPolymorphicRelationships(key, relationshipMeta, resourceHash, relationshipKey) {
let polymorphicKey = decamelize(key);
if (polymorphicKey in resourceHash && typeof resourceHash[polymorphicKey] === 'object') {
if (relationshipMeta.kind === 'belongsTo') {
let hash = resourceHash[polymorphicKey];
let {id, type} = hash;
resourceHash[relationshipKey] = { id, type };
// otherwise hasMany
} else {
let hashes = resourceHash[polymorphicKey];

if (!hashes) {
return;
}

// TODO: replace this with map when ActiveModelAdapter branches for Ember Data 2.0
var array = [];
for (let i = 0, length = hashes.length; i < length; i++) {
let hash = hashes[i];
let {id, type} = hash;
array.push({ id, type });
}
resourceHash[relationshipKey] = array;
}
}
}

export default ActiveModelSerializer;
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
var env, store, adapter, User;

var originalAjax;
const {ActiveModelAdapter} = DS;

module("integration/active_model_adapter_serializer - AMS Adapter and Serializer", {
setup: function() {
Expand All @@ -11,7 +13,7 @@ module("integration/active_model_adapter_serializer - AMS Adapter and Serializer

env = setupStore({
user: User,
adapter: DS.ActiveModelAdapter
adapter: ActiveModelAdapter
});

store = env.store;
Expand All @@ -25,7 +27,7 @@ module("integration/active_model_adapter_serializer - AMS Adapter and Serializer
}
});

test('errors are camelCased and are expected under the `errors` property of the payload', function() {
test('errors are camelCased and are expected under the `errors` property of the payload', function(assert) {
var jqXHR = {
status: 422,
getAllResponseHeaders: function() { return ''; },
Expand All @@ -48,8 +50,8 @@ test('errors are camelCased and are expected under the `errors` property of the
Ember.run(function() {
user.save().then(null, function() {
var errors = user.get('errors');
ok(errors.has('firstName'), "there are errors for the firstName attribute");
deepEqual(errors.errorsFor('firstName').getEach('message'), ['firstName error']);
assert.ok(errors.has('firstName'), "there are errors for the firstName attribute");
assert.deepEqual(errors.errorsFor('firstName').getEach('message'), ['firstName error']);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {ActiveModelAdapter} from 'activemodel-adapter';

var env, store, adapter, SuperUser;
var passedUrl, passedVerb, passedHash;
module("integration/active_model_adapter - AMS Adapter", {
Expand All @@ -6,7 +8,7 @@ module("integration/active_model_adapter - AMS Adapter", {

env = setupStore({
superUser: SuperUser,
adapter: DS.ActiveModelAdapter
adapter: ActiveModelAdapter
});

store = env.store;
Expand All @@ -16,11 +18,11 @@ module("integration/active_model_adapter - AMS Adapter", {
}
});

test('buildURL - decamelizes names', function() {
equal(adapter.buildURL('superUser', 1), "/super_users/1");
test('buildURL - decamelizes names', function(assert) {
assert.equal(adapter.buildURL('superUser', 1), "/super_users/1");
});

test('handleResponse - returns invalid error if 422 response', function() {
test('handleResponse - returns invalid error if 422 response', function(assert) {

var jqXHR = {
status: 422,
Expand All @@ -31,17 +33,17 @@ test('handleResponse - returns invalid error if 422 response', function() {

var error = adapter.handleResponse(jqXHR.status, {}, json).errors[0];

equal(error.detail, "can't be blank");
equal(error.source.pointer, "data/attributes/name");
assert.equal(error.detail, "can't be blank");
assert.equal(error.source.pointer, "data/attributes/name");
});

test('handleResponse - returns ajax response if not 422 response', function() {
test('handleResponse - returns ajax response if not 422 response', function(assert) {
var jqXHR = {
status: 500,
responseText: "Something went wrong"
};

var json = adapter.parseErrorResponse(jqXHR.responseText);

ok(adapter.handleResponse(jqXHR.status, {}, json) instanceof DS.AdapterError, 'must be a DS.AdapterError');
assert.ok(adapter.handleResponse(jqXHR.status, {}, json) instanceof DS.AdapterError, 'must be a DS.AdapterError');
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const {ActiveModelSerializer} = DS;

var SuperVillain, EvilMinion, YellowMinion, DoomsdayDevice, MediocreVillain, TestSerializer, env;
var run = Ember.run;

Expand All @@ -22,7 +24,7 @@ module("integration/active_model - AMS-namespaced-model-names (new API)", {
name: DS.attr('string'),
evilMinions: DS.hasMany('evilMinion', { polymorphic: true })
});
TestSerializer = DS.ActiveModelSerializer.extend({
TestSerializer = ActiveModelSerializer.extend({
isNewSerializerAPI: true
});
env = setupStore({
Expand All @@ -49,74 +51,78 @@ module("integration/active_model - AMS-namespaced-model-names (new API)", {
}
});

test("extractPolymorphic hasMany", function() {
var json_hash = {
mediocre_villain: { id: 1, name: "Dr Horrible", evil_minion_ids: [{ type: "EvilMinions::YellowMinion", id: 12 }] },
"evil-minions/yellow-minion": [{ id: 12, name: "Alex", doomsday_device_ids: [1] }]
};
var json;
if (Ember.FEATURES.isEnabled('ds-new-serializer-api')) {

run(function() {
json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json_hash, '1', 'findRecord');
});
test("extractPolymorphic hasMany", function(assert) {
var json_hash = {
mediocre_villain: { id: 1, name: "Dr Horrible", evil_minion_ids: [{ type: "EvilMinions::YellowMinion", id: 12 }] },
"evil-minions/yellow-minion": [{ id: 12, name: "Alex", doomsday_device_ids: [1] }]
};
var json;

deepEqual(json, {
"data": {
"id": "1",
"type": "mediocre-villain",
"attributes": {
"name": "Dr Horrible"
},
"relationships": {
"evilMinions": {
"data": [
{ "id": "12", "type": "evil-minions/yellow-minion" }
]
run(function() {
json = env.amsSerializer.normalizeResponse(env.store, MediocreVillain, json_hash, '1', 'find');
});

assert.deepEqual(json, {
"data": {
"id": "1",
"type": "mediocre-villain",
"attributes": {
"name": "Dr Horrible"
},
"relationships": {
"evilMinions": {
"data": [
{ "id": "12", "type": "evil-minions/yellow-minion" }
]
}
}
}
},
"included": [{
"id": "12",
"type": "evil-minions/yellow-minion",
"attributes": {
"name": "Alex"
},
"relationships": {}
}]
"included": [{
"id": "12",
"type": "evil-minions/yellow-minion",
"attributes": {
"name": "Alex"
},
"relationships": {}
}]
});
});
});

test("extractPolymorphic belongsTo", function() {
var json_hash = {
doomsday_device: { id: 1, name: "DeathRay", evil_minion_id: { type: "EvilMinions::YellowMinion", id: 12 } },
"evil-minions/yellow-minion": [{ id: 12, name: "Alex", doomsday_device_ids: [1] }]
};
var json;
test("extractPolymorphic belongsTo", function(assert) {
var json_hash = {
doomsday_device: { id: 1, name: "DeathRay", evil_minion_id: { type: "EvilMinions::YellowMinion", id: 12 } },
"evil-minions/yellow-minion": [{ id: 12, name: "Alex", doomsday_device_ids: [1] }]
};
var json;

run(function() {
json = env.amsSerializer.normalizeResponse(env.store, DoomsdayDevice, json_hash, '1', 'findRecord');
});
run(function() {
json = env.amsSerializer.normalizeResponse(env.store, DoomsdayDevice, json_hash, '1', 'find');
});

deepEqual(json, {
"data": {
"id": "1",
"type": "doomsday-device",
"attributes": {
"name": "DeathRay"
},
"relationships": {
"evilMinion": {
"data": { "id": "12", "type": "evil-minions/yellow-minion" }
assert.deepEqual(json, {
"data": {
"id": "1",
"type": "doomsday-device",
"attributes": {
"name": "DeathRay"
},
"relationships": {
"evilMinion": {
"data": { "id": "12", "type": "evil-minions/yellow-minion" }
}
}
}
},
"included": [{
"id": "12",
"type": "evil-minions/yellow-minion",
"attributes": {
"name": "Alex"
},
"relationships": {}
}]
"included": [{
"id": "12",
"type": "evil-minions/yellow-minion",
"attributes": {
"name": "Alex"
},
"relationships": {}
}]
});
});
});

}
Loading

0 comments on commit 95139f8

Please sign in to comment.