Last active
April 7, 2020 23:40
-
-
Save betocantu93/5ff93b996d676a04ca88821155c83c1a to your computer and use it in GitHub Desktop.
Reopen models in runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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