Skip to content
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

[CHORE] Removing detect function (and dependence on model package) in @debug #6549

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/-ember-data/tests/dummy/app/models/foo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Model, { attr, belongsTo, hasMany } from '@ember-data/model';

export default class extends Model {
@attr name;
@belongsTo('foo', { async: false }) parent;
@hasMany('foo', { async: false }) children;
}
15 changes: 14 additions & 1 deletion packages/-ember-data/tests/dummy/app/routes/application/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import Route from '@ember/routing/route';

export default Route.extend({});
export default Route.extend({
model() {
// adding a model to the store to enable manually testing the debug-adapter
return this.store.push({
data: {
id: 1,
type: 'foo',
attributes: {
name: 'taco',
},
},
});
},
});
73 changes: 65 additions & 8 deletions packages/debug/addon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import DataAdapter from '@ember/debug/data-adapter';
import { capitalize, underscore } from '@ember/string';
import { assert } from '@ember/debug';
import { get } from '@ember/object';
import Model from '@ember-data/model';
import { StoreTypesMap } from './setup';

/**
Implements `@ember/debug/data-adapter` with for EmberData
Expand Down Expand Up @@ -38,15 +38,72 @@ export default DataAdapter.extend({
];
},

_nameToClass(type) {
return get(this, 'store').modelFor(type);
},

/**
Detect whether a class is a Model
@private
@method detect
@param {Model} typeClass
@return {Boolean} Whether the typeClass is a Model class or not
Fetch the model types and observe them for changes.
Maintains the list of model types without needing the Model package for detection.
@public
@method watchModelTypes
@param {Function} typesAdded Callback to call to add types.
Takes an array of objects containing wrapped types (returned from `wrapModelType`).
@param {Function} typesUpdated Callback to call when a type has changed.
Takes an array of objects containing wrapped types.
@return {Function} Method to call to remove all observers
*/
detect(typeClass) {
return typeClass !== Model && Model.detect(typeClass);
watchModelTypes(typesAdded, typesUpdated) {
const store = get(this, 'store');
const __createRecordData = store._createRecordData;
const _releaseMethods = [];
const discoveredTypes = StoreTypesMap.get(store);

// Add any models that were added during initialization of the app, before the inspector was opened
discoveredTypes.forEach((_, type) => {
this.watchTypeIfUnseen(store, discoveredTypes, type, typesAdded, typesUpdated, _releaseMethods);
});

// Overwrite _createRecordData so newly added models will get added to the list
store._createRecordData = (identifier) => {
this.watchTypeIfUnseen(store, discoveredTypes, identifier.type, typesAdded, typesUpdated, _releaseMethods);
return __createRecordData.call(store, identifier);
};

let release = () => {
_releaseMethods.forEach(fn => fn());
store._createRecordData = __createRecordData;
// reset the list so the models can be added if the inspector is re-opened
// the entries are set to false instead of removed, since the models still exist in the app
// we just need the inspector to become aware of them
discoveredTypes.forEach((value, key) => {
discoveredTypes.set(key, false);
});
this.releaseMethods.removeObject(release);
};
this.releaseMethods.pushObject(release);
return release;
},

/**
* Loop over the discovered types and use the callbacks from watchModelTypes to notify
* the consumer of this adapter about the mdoels.
*
* @param {store} store
* @param {Map} discoveredTypes
* @param {String} type
* @param {Function} typesAdded
* @param {Function} typesUpdated
* @param {Array} releaseMethods
*/
watchTypeIfUnseen(store, discoveredTypes, type, typesAdded, typesUpdated, releaseMethods) {
if (discoveredTypes.get(type) !== true) {
let klass = store.modelFor(type);
let wrapped = this.wrapModelType(klass, type);
releaseMethods.push(this.observeModelType(type, typesUpdated));
typesAdded([wrapped]);
discoveredTypes.set(type, true);
}
},

/**
Expand Down
22 changes: 22 additions & 0 deletions packages/debug/addon/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const StoreTypesMap = new WeakMap();

function setupDataAdapter(application) {
const store = application.lookup('service:store');
const typesMap = new Map();
// its possible the app has more than one store, so this works on the 'main' store
StoreTypesMap.set(store, typesMap);

const __createRecordData = store._createRecordData;
// override _createRecordData to add the known models to the typesMap
store._createRecordData = function(identifier) {
if (!typesMap.has(identifier.type)) {
typesMap.set(identifier.type, false);
}
return __createRecordData.call(store, identifier);
};
}

export default {
name: '@ember-data/data-adapter',
initialize: setupDataAdapter,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '@ember-data/debug/setup';