Skip to content

Instantly share code, notes, and snippets.

@thermokarst
Last active April 23, 2016 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thermokarst/3267b7f064f8174d022a91fe5a681ff9 to your computer and use it in GitHub Desktop.
Save thermokarst/3267b7f064f8174d022a91fe5a681ff9 to your computer and use it in GitHub Desktop.
hyperlinked related fields
import Ember from 'ember';
const ERROR_MESSAGES = {
401: 'Unauthorized',
500: 'Internal Server Error'
};
export default DS.RESTAdapter.extend({
defaultSerializer: "DS/djangoREST",
addTrailingSlashes: true,
nonFieldErrorsKey: 'non_field_errors',
pathForType: function(type) {
var dasherized = Ember.String.dasherize(type);
return Ember.String.pluralize(dasherized);
},
buildURL: function(modelName, id, snapshot, requestType, query) {
var url = this._super(modelName, id, snapshot, requestType, query);
if (this.get('addTrailingSlashes')) {
if (url.charAt(url.length - 1) !== '/') {
url += '/';
}
}
return url;
},
handleResponse: function(status, headers, payload) {
if (this.isSuccess(status, headers, payload)) {
return payload;
} else if (this.isInvalid(status, headers, payload)) {
return new DS.InvalidError(this._drfToJsonAPIValidationErrors(payload));
}
if (Object.getOwnPropertyNames(payload).length === 0) {
payload = '';
} else if (payload.detail) {
payload = payload.detail;
}
let errors = this.normalizeErrorResponse(status, headers, payload);
if (ERROR_MESSAGES[status]) {
return new DS.AdapterError(errors, ERROR_MESSAGES[status]);
}
return new DS.AdapterError(errors);
},
isInvalid: function(status) {
return status === 400;
},
_drfToJsonAPIValidationErrors(payload, keyPrefix='') {
let out = [];
for (let key in payload) {
/*jshint loopfunc: true */
if (payload.hasOwnProperty(key)) {
if (Ember.isArray(payload[key])) {
payload[key].forEach(error => {
if (key === this.get('nonFieldErrorsKey')) {
out.push({
source: { pointer: '/data' },
detail: error,
title: 'Validation Error'
});
} else {
out.push({
source: { pointer: `/data/attributes/${keyPrefix}${key}` },
detail: error,
title: 'Invalid Attribute'
});
}
});
} else {
out = out.concat(
this._drfToJsonAPIValidationErrors(payload[key], `${keyPrefix}${key}/`)
);
}
}
}
return out;
},
_stripIDFromURL: function(store, snapshot) {
return this.buildURL(snapshot.modelName);
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
import DS from 'ember-data';
export default DS.Model.extend({
post: DS.belongsTo('post'),
message: DS.attr('string'),
user: DS.belongsTo('user'),
ready() {
console.log(this.get('id'), this.toJSON());
this.eachRelationship((name, meta) => {
console.log(name, meta);
});
},
});
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('comment'),
});
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
});
import Ember from 'ember';
export default Ember.Route.extend({
init() {
this._super(...arguments);
$.mockjax({
url: '/posts/1/',
responseText: {
id: 1,
title: "lorem ipsum",
comments: '/posts/1/comments/',
}
});
$.mockjax({
url: '/posts/1/comments/',
responseText:
[{
id: 1,
message: "my favorite!",
post: 1,
user: 1,
}]
});
$.mockjax({
url: '/users/1/',
responseText: {
id: 1,
name: "bill",
}
});
},
model() {
return this.store.findRecord('post', 1);
},
});
import DS from 'ember-data';
import Ember from 'ember';
export default DS.RESTSerializer.extend({
isNewSerializerAPI: true,
extractRelationships: function (modelClass, resourceHash) {
if (!resourceHash.hasOwnProperty('links')) {
resourceHash['links'] = {};
}
modelClass.eachRelationship(function(key, relationshipMeta) {
let payloadRelKey = this.keyForRelationship(key);
if (!resourceHash.hasOwnProperty(payloadRelKey)) {
return;
}
if (relationshipMeta.kind === 'hasMany' || relationshipMeta.kind === 'belongsTo') {
// Matches strings starting with: https://, http://, //, /
var payloadRel = resourceHash[payloadRelKey];
if (!Ember.isNone(payloadRel) && !Ember.isNone(payloadRel.match) &&
typeof(payloadRel.match) === 'function' && payloadRel.match(/^((https?:)?\/\/|\/)\w/)) {
resourceHash['links'][key] = resourceHash[payloadRelKey];
delete resourceHash[payloadRelKey];
}
}
}, this);
return this._super(modelClass, resourceHash);
},
extractPageNumber: function(url) {
var match = /.*?[\?&]page=(\d+).*?/.exec(url);
if (match) {
return Number(match[1]).valueOf();
}
return null;
},
normalizeResponse: function (store, primaryModelClass, payload, id, requestType) {
let convertedPayload = {};
if (!Ember.isNone(payload) &&
payload.hasOwnProperty('next') &&
payload.hasOwnProperty('previous') &&
payload.hasOwnProperty('results')) {
// Move DRF metadata to the meta hash.
convertedPayload[primaryModelClass.modelName] = JSON.parse(JSON.stringify(payload.results));
delete payload.results;
convertedPayload['meta'] = JSON.parse(JSON.stringify(payload));
// The next and previous pagination URLs are parsed to make it easier to paginate data in applications.
if (!Ember.isNone(convertedPayload.meta['next'])) {
convertedPayload.meta['next'] = this.extractPageNumber(convertedPayload.meta['next']);
}
if (!Ember.isNone(convertedPayload.meta['previous'])) {
let pageNumber = this.extractPageNumber(convertedPayload.meta['previous']);
// The DRF previous URL doesn't always include the page=1 query param in the results for page 2. We need to
// explicitly set previous to 1 when the previous URL is defined but the page is not set.
if (Ember.isNone(pageNumber)) {
pageNumber = 1;
}
convertedPayload.meta['previous'] = pageNumber;
}
} else {
convertedPayload[primaryModelClass.modelName] = JSON.parse(JSON.stringify(payload));
}
return this._super(store, primaryModelClass, convertedPayload, id, requestType);
},
serializeIntoHash: function(hash, type, snapshot, options) {
Ember.merge(hash, this.serialize(snapshot, options));
},
keyForAttribute: function(key) {
return Ember.String.decamelize(key);
},
keyForRelationship: function(key) {
return Ember.String.decamelize(key);
}
});
<b>{{model.title}}</b>
<ul>
{{#each model.comments as |comment|}}
<li>
{{comment.message}}
(by: {{comment.user.name}})
</li>
{{/each}}
</ul>
{
"version": "0.7.2",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "release",
"ember-data": "release",
"ember-template-compiler": "release",
"jquery-mockjax": "https://cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.6.2/jquery.mockjax.js"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment