Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active July 29, 2019 19:01
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 sukima/4af97997693f9082f4c3d56ab480d638 to your computer and use it in GitHub Desktop.
Save sukima/4af97997693f9082f4c3d56ab480d638 to your computer and use it in GitHub Desktop.
Relationships in ember-data serialization
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import { inject as service } from '@ember/service';
const LAST_ID_TAG = '_lastId';
export default JSONAPIAdapter.extend({
storage: service('fake-chrome-store'),
debug: service(),
generateIdForRecord(store, type) {
let lastId = this.storage.get(type, LAST_ID_TAG) || 0;
lastId += 1;
this.storage.set(type, LAST_ID_TAG, lastId);
return `${lastId}`;
},
shouldReloadAll() {
return false;
},
shouldReloadRecord() {
return false;
},
shouldBackgroundReloadAll() {
return false;
},
shouldBackgroundReloadRecord() {
return false;
},
findAll(store, model) {
let bucket = this.storage.getBucket(model.modelName);
let data = Object.keys(bucket)
.filter(key => key !== LAST_ID_TAG)
.map(key => bucket[key]);
this.debug.log('findAll', model.modelName, JSON.stringify(data, null, 2));
return { data };
},
findRecord(store, type, id) {
let data = this.storage.get(type.modelName, id);
this.debug.log('findRecord', type.modelName, id, JSON.stringify(data, null, 2));
if (!data) {
let error = new Error(`${type.modelName}[${id}] not found`);
error.code = 'ENOTFOUND';
throw error;
}
return { data };
},
createRecord(store, type, snapshot) {
let serializedData = this.serialize(snapshot, { includeId: true });
serializedData.data.attributes['created-at'] = new Date().toISOString();
this.debug.log('create', JSON.stringify(serializedData.data, null, 2));
this.storage.set(type.modelName, snapshot.id, serializedData.data);
return JSON.parse(JSON.stringify(serializedData));
},
updateRecord(store, type, snapshot) {
let serializedData = this.serialize(snapshot, { includeId: true });
this.debug.log('update', JSON.stringify(serializedData.data, null, 2));
this.storage.set(type.modelName, snapshot.id, serializedData.data);
return JSON.parse(JSON.stringify(serializedData));
},
deleteRecord(store, type, snapshot) {
this.storage.remove(type.modelName, snapshot.id);
this.debug.log('delete', type.modelName, snapshot.id);
}
});
import Ember from 'ember';
import { inject as service } from '@ember/service';
import { sort } from '@ember/object/computed';
export default Ember.Controller.extend({
debug: service(),
sortBy: ['createdAt:desc'],
projects: sort('model', 'sortBy')
});
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo, hasMany } from 'ember-data/relationships';
export default Model.extend({
name: attr('string'),
createdAt: attr('date'),
pullRequests: hasMany('pull-request', { polymorphic: true })
});
import PullRequestModel from './pull-request';
export default PullRequestModel.extend({
polyType: 'Addon'
});
import PullRequestModel from './pull-request';
export default PullRequestModel.extend({
polyType: 'Application'
});
import PullRequestModel from './pull-request';
export default PullRequestModel.extend({
polyType: 'Misc'
});
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { belongsTo, hasMany } from 'ember-data/relationships';
export default Model.extend({
name: attr('string'),
createdAt: attr('date'),
project: belongsTo('project', { inverse: 'pullRequests' })
});
import Ember from 'ember';
export default Ember.Route.extend({
async beforeModel() {
let p = this.store.createRecord('project', { name: 'p2' });
let pr1 = this.store.createRecord('pull-request-misc', { name: 'p2-pr1', project: p });
let pr2 = this.store.createRecord('pull-request-addon', { name: 'p2-pr2', project: p });
let pr3 = this.store.createRecord('pull-request-app', { name: 'p2-pr3', project: p });
await pr1.save();
await pr2.save();
await pr3.save();
await p.save();
await pr1.save();
await pr2.save();
await pr3.save();
},
model() {
return this.store.findAll('project', { reload: true });
}
});
import JSONAPISerializer from 'ember-data/serializers/json-api';
export default JSONAPISerializer.extend({
shouldSerializeHasMany() {
return true;
}
});
import Ember from 'ember';
export default Ember.Service.extend({
init() {
this._super(...arguments);
this.set('logs', []);
},
log() {
console.log(...arguments);
this.logs.pushObject([...arguments].join(' '));
}
});
import Service from '@ember/service';
import { loadFixtureData } from '../utils/fixture-data';
// This is a fake wrapper around what would be a wrapper for Chrome's extension
// storage. An real wrapper would be used but have the same API as here. This
// one simply allows an adapter to use this API and think it saved to Chome but
// really it is just an in-memory version for
// development/debugging/demonstration.
export default Service.extend({
init() {
this._super(...arguments);
this._storage = new Map();
loadFixtureData(this._storage);
},
getBucket(type) {
return this._storage.get(type) || {};
},
get(type, id) {
return this.getBucket(type)[id];
},
set(type, id, data) {
let bucket = this.getBucket(type);
bucket[id] = data;
this._storage.set(type, bucket);
},
remove(type, id) {
let bucket = this.getBucket(type);
delete bucket[id];
this._storage.set(type, bucket);
}
});
.logs > .log-entry {
border-top: thin solid black;
padding: 10px;
}
.logs > .log-entry:first-child {
border-top: none;
}
<h2>Data</h2>
<ul>
{{#each this.projects as |project|}}
<li>
Project: {{project.name}}
<ul>
{{#each project.pullRequests as |pr|}}
<li>Pull Request ({{pr.polyType}}): {{pr.name}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
<h2>Logs</h2>
<div class="logs">
{{#each this.debug.logs as |item|}}
<pre class="log-entry">{{item}}</pre>
{{/each}}
</div>
{
"version": "0.15.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.4.3",
"ember-template-compiler": "3.4.3",
"ember-testing": "3.4.3"
},
"addons": {
"ember-data": "3.4.2"
}
}
export function loadFixtureData(storage) {
storage.set('pull-request-misc', {
_lastId: 1,
'1': {
id: '1',
type: 'pull-request-miscs',
attributes: {
name: 'p1-pr1',
'created-at': '2019-07-29T18:22:40.489Z'
}
}
});
storage.set('pull-request-addon', {
_lastId: 1,
'1': {
id: '1',
type: 'pull-request-addons',
attributes: {
name: 'p1-pr2',
'created-at': '2019-07-29T18:22:40.489Z'
}
}
});
storage.set('pull-request-app', {
_lastId: 1,
'1': {
id: '1',
type: 'pull-request-apps',
attributes: {
name: 'p1-pr3',
'created-at': '2019-07-29T18:22:40.489Z'
}
}
});
storage.set('project', {
_lastId: 1,
'1': {
id: '1',
type: 'projects',
attributes: {
name: 'p1',
'created-at': '2019-07-29T18:22:40.489Z'
},
relationships: {
'pull-requests': {
data: [
{ type: 'pull-request-miscs', id: '1' },
{ type: 'pull-request-addons', id: '1' },
{ type: 'pull-request-apps', id: '1' }
]
}
}
}
});
console.log(storage);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment