-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Refactor]: private model store #7570
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
/** | ||
@module @ember-data/store | ||
*/ | ||
import { getOwner } from '@ember/application'; | ||
import { getOwner, setOwner } from '@ember/application'; | ||
import { A } from '@ember/array'; | ||
import { assert, deprecate, inspect, warn } from '@ember/debug'; | ||
import EmberError from '@ember/error'; | ||
import { computed, defineProperty, get, set } from '@ember/object'; | ||
import { assign } from '@ember/polyfills'; | ||
import { _backburner as emberBackburner } from '@ember/runloop'; | ||
|
@@ -25,6 +26,7 @@ import { | |
import { | ||
HAS_ADAPTER_PACKAGE, | ||
HAS_EMBER_DATA_PACKAGE, | ||
HAS_MODEL_PACKAGE, | ||
HAS_RECORD_DATA_PACKAGE, | ||
HAS_SERIALIZER_PACKAGE, | ||
} from '@ember-data/private-build-infra'; | ||
|
@@ -55,6 +57,7 @@ import { setRecordDataFor } from './record-data-for'; | |
import NotificationManager from './record-notification-manager'; | ||
import { RecordReference } from './references'; | ||
import { RequestPromise } from './request-cache'; | ||
import { DSModelSchemaDefinitionService, getModelFactory } from './schema-definition-service'; | ||
import { _bind, _guard, _objectIsAlive, guardDestroyedStore } from './store/common'; | ||
import { _find, _findAll, _findBelongsTo, _findHasMany, _findMany, _query, _queryRecord } from './store/finders'; | ||
import { internalModelFactoryFor, recordIdentifierFor, setRecordIdentifier } from './store/internal-model-factory'; | ||
|
@@ -79,6 +82,7 @@ type ResourceIdentifierObject = import('../ts-interfaces/ember-data-json-api').R | |
type EmptyResourceDocument = import('../ts-interfaces/ember-data-json-api').EmptyResourceDocument; | ||
type SingleResourceDocument = import('../ts-interfaces/ember-data-json-api').SingleResourceDocument; | ||
type CollectionResourceDocument = import('../ts-interfaces/ember-data-json-api').CollectionResourceDocument; | ||
type DSModelClass = import('@ember-data/model').default; | ||
type JsonApiDocument = import('../ts-interfaces/ember-data-json-api').JsonApiDocument; | ||
type ExistingResourceObject = import('../ts-interfaces/ember-data-json-api').ExistingResourceObject; | ||
type RecordIdentifier = import('../ts-interfaces/identifier').RecordIdentifier; | ||
|
@@ -218,7 +222,7 @@ interface CoreStore { | |
adapter: string; | ||
} | ||
|
||
abstract class CoreStore extends Service { | ||
class CoreStore extends Service { | ||
/** | ||
* EmberData specific backburner instance | ||
* @property _backburner | ||
|
@@ -227,6 +231,10 @@ abstract class CoreStore extends Service { | |
public _backburner: Backburner = edBackburner; | ||
public recordArrayManager: RecordArrayManager = new RecordArrayManager({ store: this }); | ||
|
||
private _attributesDefCache = Object.create(null); | ||
private _relationshipsDefCache = Object.create(null); | ||
public _modelFactoryCache = Object.create(null); | ||
|
||
declare _notificationManager: NotificationManager; | ||
private _adapterCache = Object.create(null); | ||
private _serializerCache = Object.create(null); | ||
|
@@ -495,33 +503,82 @@ abstract class CoreStore extends Service { | |
assert('should not be here, custom model class ff error', false); | ||
} | ||
|
||
abstract instantiateRecord( | ||
instantiateRecord( | ||
identifier: StableRecordIdentifier, | ||
createRecordArgs: { [key: string]: unknown }, // args passed in to store.createRecord() and processed by recordData to be set on creation | ||
createRecordArgs: { [key: string]: unknown }, | ||
recordDataFor: (identifier: RecordIdentifier) => RecordDataRecordWrapper, | ||
notificationManager: NotificationManager | ||
): RecordInstance; | ||
): DSModel | RecordInstance { | ||
let modelName = identifier.type; | ||
|
||
let internalModel = this._internalModelForResource(identifier); | ||
let createOptions: any = { | ||
store: this, | ||
_internalModel: internalModel, | ||
container: null, | ||
}; | ||
assign(createOptions, createRecordArgs); | ||
|
||
// ensure that `getOwner(this)` works inside a model instance | ||
setOwner(createOptions, getOwner(this)); | ||
|
||
delete createOptions.container; | ||
let record = this._modelFactoryFor(modelName).create(createOptions); | ||
return record; | ||
} | ||
|
||
abstract teardownRecord(record: RecordInstance): void; | ||
teardownRecord(record: DSModel | RecordInstance) { | ||
if (HAS_MODEL_PACKAGE) { | ||
if ('destroy' in record) { | ||
record.destroy(); | ||
} | ||
} | ||
} | ||
|
||
_internalDeleteRecord(internalModel: InternalModel) { | ||
internalModel.deleteRecord(); | ||
} | ||
|
||
// FeatureFlagged in the DSModelStore claas | ||
_attributesDefinitionFor(modelName: string, identifier?: StableRecordIdentifier): AttributesSchema { | ||
if (identifier) { | ||
return this.getSchemaDefinitionService().attributesDefinitionFor(identifier); | ||
if (CUSTOM_MODEL_CLASS || !HAS_MODEL_PACKAGE) { | ||
if (identifier) { | ||
return this.getSchemaDefinitionService().attributesDefinitionFor(identifier); | ||
} else { | ||
return this.getSchemaDefinitionService().attributesDefinitionFor(modelName); | ||
} | ||
} else { | ||
return this.getSchemaDefinitionService().attributesDefinitionFor(modelName); | ||
let attributes = this._attributesDefCache[modelName]; | ||
|
||
if (attributes === undefined) { | ||
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; | ||
} | ||
|
||
return attributes; | ||
} | ||
} | ||
|
||
_relationshipsDefinitionFor(modelName: string, identifier?: StableRecordIdentifier) { | ||
if (identifier) { | ||
return this.getSchemaDefinitionService().relationshipsDefinitionFor(identifier); | ||
if (CUSTOM_MODEL_CLASS || !HAS_MODEL_PACKAGE) { | ||
if (identifier) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can similarly collapse this with the above branching |
||
return this.getSchemaDefinitionService().relationshipsDefinitionFor(identifier); | ||
} else { | ||
return this.getSchemaDefinitionService().relationshipsDefinitionFor(modelName); | ||
} | ||
} else { | ||
return this.getSchemaDefinitionService().relationshipsDefinitionFor(modelName); | ||
let relationships = this._relationshipsDefCache[modelName]; | ||
|
||
if (relationships === undefined) { | ||
let modelClass = this.modelFor(modelName); | ||
relationships = get(modelClass, 'relationshipsObject') || null; | ||
this._relationshipsDefCache[modelName] = relationships; | ||
} | ||
|
||
return relationships; | ||
} | ||
} | ||
|
||
|
@@ -530,15 +587,32 @@ abstract class CoreStore extends Service { | |
} | ||
|
||
getSchemaDefinitionService(): SchemaDefinitionService { | ||
if (CUSTOM_MODEL_CLASS) { | ||
return this._schemaDefinitionService; | ||
if (HAS_MODEL_PACKAGE) { | ||
if (CUSTOM_MODEL_CLASS) { | ||
if (!this._schemaDefinitionService) { | ||
this._schemaDefinitionService = new DSModelSchemaDefinitionService(this); | ||
} | ||
return this._schemaDefinitionService; | ||
} else { | ||
throw 'schema service is only available when custom model class feature flag is on'; | ||
} | ||
} else { | ||
if (CUSTOM_MODEL_CLASS) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can collapse this branching with the HAS_MODEL_PACKAGE branch by only instantiating the ds-model schema definition service in the case that the model package is present. |
||
return this._schemaDefinitionService; | ||
} | ||
assert('need to enable CUSTOM_MODEL_CLASS feature flag in order to access SchemaDefinitionService'); | ||
} | ||
assert('need to enable CUSTOM_MODEL_CLASS feature flag in order to access SchemaDefinitionService'); | ||
} | ||
|
||
// TODO Double check this return value is correct | ||
_relationshipMetaFor(modelName: string, id: string | null, key: string) { | ||
return this._relationshipsDefinitionFor(modelName)[key]; | ||
if (CUSTOM_MODEL_CLASS || !HAS_MODEL_PACKAGE) { | ||
return this._relationshipsDefinitionFor(modelName)[key]; | ||
} else { | ||
let modelClass = this.modelFor(modelName); | ||
let relationshipsByName = get(modelClass, 'relationshipsByName'); | ||
return relationshipsByName.get(key); | ||
} | ||
} | ||
|
||
/** | ||
|
@@ -557,15 +631,55 @@ abstract class CoreStore extends Service { | |
@param {String} modelName | ||
@return {subclass of Model | ShimModelClass} | ||
*/ | ||
modelFor(modelName: string): ShimModelClass { | ||
if (DEBUG) { | ||
assertDestroyedStoreOnly(this, 'modelFor'); | ||
modelFor(modelName: string): ShimModelClass | DSModelClass { | ||
if (HAS_MODEL_PACKAGE) { | ||
if (DEBUG) { | ||
assertDestroyedStoreOnly(this, 'modelFor'); | ||
} | ||
assert(`You need to pass a model name to the store's modelFor method`, isPresent(modelName)); | ||
assert( | ||
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, | ||
typeof modelName === 'string' | ||
); | ||
|
||
let maybeFactory = this._modelFactoryFor(modelName); | ||
|
||
// for factorFor factory/class split | ||
let klass = maybeFactory && maybeFactory.class ? maybeFactory.class : maybeFactory; | ||
if (!klass || !klass.isModel) { | ||
if (!CUSTOM_MODEL_CLASS || !this.getSchemaDefinitionService().doesTypeExist(modelName)) { | ||
throw new EmberError(`No model was found for '${modelName}' and no schema handles the type`); | ||
} | ||
return getShimClass(this, modelName); | ||
} else { | ||
return klass; | ||
} | ||
} else { | ||
if (DEBUG) { | ||
assertDestroyedStoreOnly(this, 'modelFor'); | ||
} | ||
|
||
return getShimClass(this, modelName); | ||
} | ||
} | ||
|
||
return getShimClass(this, modelName); | ||
_modelFactoryFor(modelName: string): DSModelClass { | ||
if (HAS_MODEL_PACKAGE) { | ||
if (DEBUG) { | ||
assertDestroyedStoreOnly(this, '_modelFactoryFor'); | ||
} | ||
assert(`You need to pass a model name to the store's _modelFactoryFor method`, isPresent(modelName)); | ||
assert( | ||
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, | ||
typeof modelName === 'string' | ||
); | ||
let normalizedModelName = normalizeModelName(modelName); | ||
let factory = getModelFactory(this, this._modelFactoryCache, normalizedModelName); | ||
|
||
return factory; | ||
} | ||
} | ||
|
||
// Feature Flagged in DSModelStore | ||
/** | ||
Returns whether a ModelClass exists for a given modelName | ||
This exists for legacy support for the RESTSerializer, | ||
|
@@ -579,13 +693,23 @@ abstract class CoreStore extends Service { | |
@private | ||
*/ | ||
_hasModelFor(modelName: string): boolean { | ||
if (DEBUG) { | ||
assertDestroyingStore(this, '_hasModelFor'); | ||
} | ||
assert(`You need to pass a model name to the store's hasModelFor method`, isPresent(modelName)); | ||
assert( | ||
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`, | ||
typeof modelName === 'string' | ||
); | ||
|
||
return this.getSchemaDefinitionService().doesTypeExist(modelName); | ||
if (CUSTOM_MODEL_CLASS || !HAS_MODEL_PACKAGE) { | ||
return this.getSchemaDefinitionService().doesTypeExist(modelName); | ||
} else { | ||
let normalizedModelName = normalizeModelName(modelName); | ||
let factory = getModelFactory(this, this._modelFactoryCache, normalizedModelName); | ||
|
||
return factory !== null; | ||
} | ||
} | ||
|
||
// ..................... | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can collapse this with the above by making the first
if
beif (CUSTOM_MODEL_CLASS || !HAS_MODEL_PACKAGE)