miragejs
Version:
A client-side server to help you build, test and demo your JavaScript app
146 lines (120 loc) • 3.51 kB
JavaScript
import isPlainObject from "lodash/isPlainObject";
import isFunction from "lodash/isFunction";
import mapValues from "lodash/mapValues";
import referenceSort from "./utils/reference-sort";
let Factory = function () {
this.build = function (sequence) {
let object = {};
let topLevelAttrs = Object.assign({}, this.attrs);
delete topLevelAttrs.afterCreate;
Object.keys(topLevelAttrs).forEach((attr) => {
if (Factory.isTrait.call(this, attr)) {
delete topLevelAttrs[attr];
}
});
let keys = sortAttrs(topLevelAttrs, sequence);
keys.forEach(function (key) {
let buildAttrs, buildSingleValue;
buildAttrs = function (attrs) {
return mapValues(attrs, buildSingleValue);
};
buildSingleValue = (value) => {
if (Array.isArray(value)) {
return value.map(buildSingleValue);
} else if (isPlainObject(value)) {
return buildAttrs(value);
} else if (isFunction(value)) {
return value.call(topLevelAttrs, sequence);
} else {
return value;
}
};
let value = topLevelAttrs[key];
if (isFunction(value)) {
object[key] = value.call(object, sequence);
} else {
object[key] = buildSingleValue(value);
}
});
return object;
};
};
Factory.extend = function (attrs) {
// Merge the new attributes with existing ones. If conflict, new ones win.
let newAttrs = Object.assign({}, this.attrs, attrs);
let Subclass = function () {
this.attrs = newAttrs;
Factory.call(this);
};
// Copy extend
Subclass.extend = Factory.extend;
Subclass.extractAfterCreateCallbacks = Factory.extractAfterCreateCallbacks;
Subclass.isTrait = Factory.isTrait;
// Store a reference on the class for future subclasses
Subclass.attrs = newAttrs;
return Subclass;
};
Factory.extractAfterCreateCallbacks = function ({ traits } = {}) {
let afterCreateCallbacks = [];
let attrs = this.attrs || {};
let traitCandidates;
if (attrs.afterCreate) {
afterCreateCallbacks.push(attrs.afterCreate);
}
if (Array.isArray(traits)) {
traitCandidates = traits;
} else {
traitCandidates = Object.keys(attrs);
}
traitCandidates
.filter((attr) => {
return this.isTrait(attr) && attrs[attr].extension.afterCreate;
})
.forEach((attr) => {
afterCreateCallbacks.push(attrs[attr].extension.afterCreate);
});
return afterCreateCallbacks;
};
Factory.isTrait = function (attrName) {
let { attrs } = this;
return isPlainObject(attrs[attrName]) && attrs[attrName].__isTrait__ === true;
};
function sortAttrs(attrs, sequence) {
let Temp = function () {};
let obj = new Temp();
let refs = [];
let property;
Object.keys(attrs).forEach(function (key) {
let value;
Object.defineProperty(obj.constructor.prototype, key, {
get() {
refs.push([property, key]);
return value;
},
set(newValue) {
value = newValue;
},
enumerable: false,
configurable: true,
});
});
Object.keys(attrs).forEach(function (key) {
let value = attrs[key];
if (typeof value !== "function") {
obj[key] = value;
}
});
Object.keys(attrs).forEach(function (key) {
let value = attrs[key];
property = key;
if (typeof value === "function") {
obj[key] = value.call(obj, sequence);
}
refs.push([key]);
});
return referenceSort(refs);
}
/**
* @hide
*/
export default Factory;