Skip to content

Instantly share code, notes, and snippets.

@betocantu93
Last active April 7, 2020 23:40
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 betocantu93/5ff93b996d676a04ca88821155c83c1a to your computer and use it in GitHub Desktop.
Save betocantu93/5ff93b996d676a04ca88821155c83c1a to your computer and use it in GitHub Desktop.
Reopen models in runtime
import DS from "ember-data";
import { computed } from "@ember/object";
import { assert } from "@ember/debug";
const { Model } = DS;
Model.reopenClass({
/**
* The following props are overrided, since per definition
* they are sealed props defined in
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1540
*/
/**
* Copies the relationshipsDescriptor logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/system/relationships/ext.js#L11
* */
_computeRelationships() {
let map = new Map();
let relationshipsByName = get(this, "relationshipsByName");
// Loop through each computed property on the class
relationshipsByName.forEach((desc) => {
let { type } = desc;
if (!map.has(type)) {
map.set(type, []);
}
map.get(type).push(desc);
});
return map;
},
/**
* @override relationships
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1767
*/
relationships: computed(function () {
return this._computeRelationships();
}),
/**
* Copies the relationshipNames logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1803
* */
_computeRelationshipNames() {
let names = {
hasMany: [],
belongsTo: [],
};
this.eachComputedProperty((name, meta) => {
if (meta.isRelationship) {
names[meta.kind].push(name);
}
});
return names;
},
/**
* @override relationshipNames
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1803
*/
relationshipNames: computed(function () {
return this._computeRelationshipNames();
}),
/**
* Copies the relatedTypesDescriptor logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/system/relationships/ext.js#L29
* */
_computeRelatedTypes() {
let parentModelName = this.modelName;
let types = A();
// Loop through each computed property on the class,
// and create an array of the unique types involved
// in relationships
this.eachComputedProperty((name, meta) => {
if (meta.isRelationship) {
meta.key = name;
let modelName = typeForRelationshipMeta(meta);
assert(
`You specified a hasMany (${meta.type}) on ${parentModelName} but ${meta.type} was not found.`,
modelName
);
if (!types.includes(modelName)) {
assert(
`Trying to sideload ${name} on ${this.toString()} but the type doesn't exist.`,
!!modelName
);
types.push(modelName);
}
}
});
return types;
},
/**
* @override relatedTypes
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1851
*/
relatedTypes: computed(function () {
return this._computeRelatedTypes();
}),
/**
* Copies the relationshipsByNameDescriptor logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/system/relationships/ext.js#L67
* */
_computeRelationshipsByName() {
let map = new Map();
let rels = get(this, "relationshipsObject");
let relationships = Object.keys(rels);
for (let i = 0; i < relationships.length; i++) {
let key = relationships[i];
let value = rels[key];
map.set(value.key, value);
}
return map;
},
/**
* @override relationshipsByName
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1889
*/
relationshipsByName: computed(function () {
return this._computeRelationshipsByName();
}),
/**
* Copies the relationshipsObjectDescriptor logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/system/relationships/ext.js#L53
* */
_computeRelationshipsObject() {
let relationships = Object.create(null);
let modelName = this.modelName;
this.eachComputedProperty((name, meta) => {
if (meta.isRelationship) {
meta.key = name;
meta.name = name;
meta.parentModelName = modelName;
relationships[name] = relationshipFromMeta(meta);
}
});
return relationships;
},
/**
* @override relationshipsObject
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1891
*/
relationshipsObject: computed(function () {
return this._computeRelationshipsObject();
}),
/**
* Copies the fields logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1934
* */
_computeFields() {
let map = new Map();
this.eachComputedProperty((name, meta) => {
if (meta.isRelationship) {
map.set(name, meta.kind);
} else if (meta.isAttribute) {
map.set(name, "attribute");
}
});
return map;
},
/**
* @override fields
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L1934
*/
fields: computed(function () {
return this._computeFields();
}),
/**
* Copies the attributes logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L2043
* */
_computeAttributes() {
let map = new Map();
this.eachComputedProperty((name, meta) => {
if (meta.isAttribute) {
assert(
"You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: attr('<type>')` from " +
this.toString(),
name !== "id"
);
meta.name = name;
map.set(name, meta);
}
});
return map;
},
/**
* @override attributes
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L2043
*/
attributes: computed(function () {
return this._computeAttributes();
}),
/**
* Copies the transformedAttributes logic from
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L2100
* */
_computeTransformedAttributes() {
let map = new Map();
this.eachAttribute((key, meta) => {
if (meta.type) {
map.set(key, meta.type);
}
});
return map;
},
/**
* @override transformedAttributes
* https://github.com/emberjs/data/blob/v3.17.0/packages/model/addon/-private/model.js#L2100
*/
transformedAttributes: computed(function () {
return this._computeTransformedAttributes();
}),
/**
* This method is supposed to be called only when updating the schema definition of models in runtime,
* it *should* be called in addition of recalculating the store cache for attributes.
*/
_regenerateSchema() {
this.relationships = this._computeRelationships();
this.relationshipNames = this._computeRelationshipNames();
this.relatedTypes = this._computeRelatedTypes();
this.relationshipsObject = this._computeRelationshipsObject(); //order matters. (before relationshipsByName)
this.relationshipsByName = this._computeRelationshipsByName();
this.fields = this._computeFields();
this.attributes = this._computeAttributes();
this.transformedAttributes = this._computeTransformedAttributes(); //order matters (after attributes computing)
},
});
export function initialize(/* application */) {}
export default {
initialize
};
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { inject } from '@ember/service';
import DS from 'ember-data';
const { attr } = DS;
export default class ApplicationController extends Controller {
@inject store;
@action
reopen(modelName) {
let modelKlass = this.store.modelFor(modelName);
modelKlass.reopen({
someAttr: attr('boolean'),
someOtherAttr: attr('string')
})
modelKlass._regenerateSchema();
this.store._refreshAttributesCacheFor(modelName)
this.store._refreshStoreRelationshipsCacheFor(modelName)
}
}
import DS from 'ember-data';
import { inject as service } from '@ember/service';
import { get } from '@ember/object';
import classic from 'ember-classic-decorator';
@classic
export default class StoreService extends DS.Store {
_refreshAttributesCacheFor(modelName) {
let attributes;
let modelClass = this.modelFor(modelName);
let attributeMap = get(modelClass, 'attributes');
attributes = Object.create(null);
attributeMap.forEach((meta, name) => (attributes[name] = meta));
this._attributesDefCache[modelName] = attributes;
}
_refreshStoreRelationshipsCacheFor(modelName) {
let modelClass = this.modelFor(modelName);
let relationships = get(modelClass, "relationshipsObject") || null;
this._relationshipsDefCache[modelName] = relationships;
return relationships;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment