Skip to content

Instantly share code, notes, and snippets.

@FND
Last active September 16, 2019 06:32
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 FND/9c9357a20c6ef7f9db752ce4a940f1cb to your computer and use it in GitHub Desktop.
Save FND/9c9357a20c6ef7f9db752ce4a940f1cb to your computer and use it in GitHub Desktop.
(De-)Serializing JavaScript Models With Metaprogramming

compare before.js with after.js

import Record from "./record.js";
export default class Store extends Record {
static get slots() {
return {
id: null,
lists: collection(List)
};
}
}
class List extends Record {
static get slots() {
return {
title: null,
items: collection(Item)
};
}
}
class Item extends Record {
static get slots() {
return {
caption: null,
done: value => !!value
};
}
}
function collection(model) {
return items => items.map(item => new model(item));
}
export default class Store {
constructor(id, lists) {
this.id = id;
this.lists = lists;
}
toJSON() {
let { id, lists } = this;
return { id, lists };
}
static fromJSON({ id, lists }) {
return new this(id, lists.map(list => List.fromJSON(list)));
}
}
class List {
constructor(title, items) {
this.title = title;
this.items = items;
}
toJSON() {
let { title, items } = this;
return { title, items };
}
static fromJSON({ title, items }) {
return new this(title, items.map(item => Item.fromJSON(item)));
}
}
class Item {
constructor(caption, done = false) {
this.caption = caption;
this.done = done;
}
toJSON() {
let { caption, done } = this;
return { caption, done };
}
static fromJSON({ caption, done }) {
return new this(caption, !!done);
}
}
export default class Record {
constructor(data) {
let ctor = this.constructor;
if(!ctor._initialized) { // generate accessors
Object.entries(ctor.slots).forEach(([slot, handler]) => {
makeGetter(ctor.prototype, slot, handler);
});
ctor._initialized = true;
}
this._data = data;
}
toJSON() {
return this._data;
}
}
function makeGetter(proto, slot, handler) {
Object.defineProperty(proto, slot, {
get() {
let value = this._data[slot];
return handler ? handler(value) : value;
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment