Skip to content

Commit

Permalink
Relationship Refactor (part 2): The graph should coordinate state upd…
Browse files Browse the repository at this point in the history
…ates (#7493)

* perf: the graph should coordinate state updates

* fixes for m3

* fix ff on state

* prevent infinite recursion during teardown of hasMany

* remove unused feature flag

* fix ie11
  • Loading branch information
runspired authored May 4, 2021
1 parent e9eafd7 commit ea7dbbb
Show file tree
Hide file tree
Showing 49 changed files with 2,017 additions and 1,835 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ module('async belongs-to rendering tests', function (hooks) {
assert.ok(pirate.get('bestHuman') === null, 'precond - pirate has no best human');
assert.ok(bestDog === null, 'precond - Chris has no best dog');

// locally update
chris.set('bestDog', shen);
bestDog = await chris.get('bestDog');
await settled();
Expand All @@ -385,6 +386,7 @@ module('async belongs-to rendering tests', function (hooks) {
assert.ok(pirate.get('bestHuman') === null, 'scene 1 - pirate has no best human');
assert.ok(bestDog === shen, "scene 1 - Shen is Chris's best dog");

// locally update to a different value
chris.set('bestDog', pirate);
bestDog = await chris.get('bestDog');
await settled();
Expand All @@ -394,6 +396,7 @@ module('async belongs-to rendering tests', function (hooks) {
assert.ok(pirate.get('bestHuman') === chris, 'scene 2 - pirate now has Chris as best human');
assert.ok(bestDog === pirate, "scene 2 - Pirate is now Chris's best dog");

// locally clear the relationship
chris.set('bestDog', null);
bestDog = await chris.get('bestDog');
await settled();
Expand Down
6 changes: 3 additions & 3 deletions packages/-ember-data/tests/helpers/accessors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { recordIdentifierFor } from '@ember-data/store';
type CoreStore = import('@ember-data/store/-private/system/core-store').default;
type BelongsToRelationship = import('@ember-data/record-data/-private').BelongsToRelationship;
type ManyRelationship = import('@ember-data/record-data/-private').ManyRelationship;
type Relationship = import('@ember-data/record-data/-private').Relationship;
type ImplicitRelationship = import('@ember-data/record-data/-private').Relationship;
type RecordDataStoreWrapper = import('@ember-data/store/-private').RecordDataStoreWrapper;
type StableRecordIdentifier = import('@ember-data/store/-private/ts-interfaces/identifier').StableRecordIdentifier;
type RelationshipDict = import('@ember-data/store/-private/ts-interfaces/utils').ConfidentDict<Relationship>;
type RelationshipDict = import('@ember-data/store/-private/ts-interfaces/utils').ConfidentDict<ImplicitRelationship>;

export function getRelationshipStateForRecord(
record: { store: CoreStore },
propertyName: string
): BelongsToRelationship | ManyRelationship | Relationship {
): BelongsToRelationship | ManyRelationship | ImplicitRelationship {
const identifier = recordIdentifierFor(record);
return graphFor(record.store._storeWrapper).get(identifier, propertyName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ module('Store.createRecord() coverage', function (hooks) {
assert.deepEqual(pets, ['Shen'], 'Precondition: Chris has Shen as a pet');

pet.unloadRecord();

assert.ok(pet.get('owner') === null, 'Shen no longer has an owner');

// check that the relationship has been dissolved
pets = chris
.get('pets')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,21 +266,21 @@ module('Editing a Record', function (hooks) {
assert.deepEqual(pets, ['Shen', 'Rocky'], 'Precondition: Chris has Shen and Rocky as pets');

shen.set('owner', john);
assert.ok(shen.get('owner') === john, 'Precondition: John is the new owner of Shen');
assert.ok(shen.get('owner') === john, 'After Update: John is the new owner of Shen');

pets = chris.pets.toArray().map((pet) => pet.name);
assert.deepEqual(pets, ['Rocky'], 'Precondition: Chris has Rocky as a pet');
assert.deepEqual(pets, ['Rocky'], 'After Update: Chris has Rocky as a pet');

pets = john.pets.toArray().map((pet) => pet.name);
assert.deepEqual(pets, ['Shen'], 'Precondition: John has Shen as a pet');
assert.deepEqual(pets, ['Shen'], 'After Update: John has Shen as a pet');

chris.unloadRecord();

assert.ok(rocky.get('owner') === null, 'Rocky has no owner');
assert.ok(shen.get('owner') === john, 'John should still be the owner of Shen');
assert.ok(rocky.get('owner') === null, 'After Unload: Rocky has no owner');
assert.ok(shen.get('owner') === john, 'After Unload: John should still be the owner of Shen');

pets = john.pets.toArray().map((pet) => pet.name);
assert.deepEqual(pets, ['Shen'], 'John still has Shen as a pet');
assert.deepEqual(pets, ['Shen'], 'After Unload: John still has Shen as a pet');
});
});

Expand Down
140 changes: 66 additions & 74 deletions packages/-ember-data/tests/integration/records/unload-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import JSONAPIAdapter from '@ember-data/adapter/json-api';
import JSONAPISerializer from '@ember-data/serializer/json-api';
import { recordDataFor } from '@ember-data/store/-private';

function idsFromOrderedSet(set) {
return set.list.map((i) => i.id);
function idsFromArr(arr) {
return arr.map((i) => i.id);
}

const { attr, belongsTo, hasMany, Model } = DS;
Expand Down Expand Up @@ -429,26 +429,24 @@ module('integration/unload - Unloading Records', function (hooks) {
};
}

test('unloadAll(type) does not leave stranded internalModels in relationships (rediscover via store.push)', function (assert) {
test('unloadAll(type) does not leave stranded internalModels in relationships (rediscover via store.push)', async function (assert) {
assert.expect(15);

let person = run(() =>
store.push({
data: {
type: 'person',
id: '1',
attributes: {
name: 'Could be Anybody',
},
relationships: {
boats: {
data: [{ type: 'boat', id: '1' }],
},
let person = store.push({
data: {
type: 'person',
id: '1',
attributes: {
name: 'Could be Anybody',
},
relationships: {
boats: {
data: [{ type: 'boat', id: '1' }],
},
},
included: [makeBoatOneForPersonOne()],
})
);
},
included: [makeBoatOneForPersonOne()],
});

let boat = store.peekRecord('boat', '1');
let initialBoatInternalModel = boat._internalModel;
Expand All @@ -463,10 +461,10 @@ module('integration/unload - Unloading Records', function (hooks) {
assert.true(store.hasRecordForId('boat', '1'));

// ensure the relationship was established (we reach through the async proxy here)
let peopleBoats = run(() => person.get('boats.content'));
let boatPerson = run(() => boat.get('person.content'));
let peopleBoats = await person.get('boats');
let boatPerson = await boat.get('person');

assert.equal(relationshipState.canonicalMembers.size, 1, 'canonical member size should be 1');
assert.equal(relationshipState.canonicalState.length, 1, 'canonical member size should be 1');
assert.equal(relationshipState.members.size, 1, 'members size should be 1');
assert.ok(get(peopleBoats, 'length') === 1, 'Our person has a boat');
assert.ok(peopleBoats.objectAt(0) === boat, 'Our person has the right boat');
Expand All @@ -479,7 +477,7 @@ module('integration/unload - Unloading Records', function (hooks) {
// ensure that our new state is correct
assert.equal(knownPeople.models.length, 1, 'one person record is loaded');
assert.equal(knownBoats.models.length, 0, 'no boat records are loaded');
assert.equal(relationshipState.canonicalMembers.size, 1, 'canonical member size should still be 1');
assert.equal(relationshipState.canonicalState.length, 1, 'canonical member size should still be 1');
assert.equal(relationshipState.members.size, 1, 'members size should still be 1');
assert.ok(get(peopleBoats, 'length') === 0, 'Our person thinks they have no boats');

Expand Down Expand Up @@ -543,7 +541,7 @@ module('integration/unload - Unloading Records', function (hooks) {
let peopleBoats = run(() => person.get('boats.content'));
let boatPerson = run(() => boat.get('person.content'));

assert.equal(relationshipState.canonicalMembers.size, 1, 'canonical member size should be 1');
assert.equal(relationshipState.canonicalState.length, 1, 'canonical member size should be 1');
assert.equal(relationshipState.members.size, 1, 'members size should be 1');
assert.ok(get(peopleBoats, 'length') === 1, 'Our person has a boat');
assert.ok(peopleBoats.objectAt(0) === boat, 'Our person has the right boat');
Expand All @@ -556,7 +554,7 @@ module('integration/unload - Unloading Records', function (hooks) {
// ensure that our new state is correct
assert.equal(knownPeople.models.length, 1, 'one person record is loaded');
assert.equal(knownBoats.models.length, 0, 'no boat records are loaded');
assert.equal(relationshipState.canonicalMembers.size, 1, 'canonical member size should still be 1');
assert.equal(relationshipState.canonicalState.length, 1, 'canonical member size should still be 1');
assert.equal(relationshipState.members.size, 1, 'members size should still be 1');
assert.ok(get(peopleBoats, 'length') === 0, 'Our person thinks they have no boats');

Expand Down Expand Up @@ -614,8 +612,8 @@ module('integration/unload - Unloading Records', function (hooks) {
let peopleBoats = run(() => person.get('boats.content'));
let boatPerson = run(() => boat.get('person.content'));

assert.deepEqual(idsFromOrderedSet(relationshipState.canonicalMembers), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromOrderedSet(relationshipState.members), ['1'], 'members size should be 1');
assert.deepEqual(idsFromArr(relationshipState.canonicalState), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromArr(relationshipState.currentState), ['1'], 'members size should be 1');
assert.ok(get(peopleBoats, 'length') === 1, 'Our person has a boat');
assert.ok(peopleBoats.objectAt(0) === boat, 'Our person has the right boat');
assert.ok(boatPerson === person, 'Our boat has the right person');
Expand All @@ -635,12 +633,8 @@ module('integration/unload - Unloading Records', function (hooks) {
);
assert.ok(knownBoats.models[0] === initialBoatInternalModel, 'We still have our boat');
assert.true(initialBoatInternalModel.currentState.isEmpty, 'Model is in the empty state');
assert.deepEqual(
idsFromOrderedSet(relationshipState.canonicalMembers),
['1'],
'canonical member size should still be 1'
);
assert.deepEqual(idsFromOrderedSet(relationshipState.members), ['1'], 'members size should still be 1');
assert.deepEqual(idsFromArr(relationshipState.canonicalState), ['1'], 'canonical member size should still be 1');
assert.deepEqual(idsFromArr(relationshipState.currentState), ['1'], 'members size should still be 1');
assert.ok(get(peopleBoats, 'length') === 0, 'Our person thinks they have no boats');

run(() =>
Expand All @@ -652,8 +646,8 @@ module('integration/unload - Unloading Records', function (hooks) {
let reloadedBoat = store.peekRecord('boat', '1');
let reloadedBoatInternalModel = reloadedBoat._internalModel;

assert.deepEqual(idsFromOrderedSet(relationshipState.canonicalMembers), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromOrderedSet(relationshipState.members), ['1'], 'members size should be 1');
assert.deepEqual(idsFromArr(relationshipState.canonicalState), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromArr(relationshipState.currentState), ['1'], 'members size should be 1');
assert.ok(
reloadedBoatInternalModel === initialBoatInternalModel,
'after an unloadRecord, subsequent fetch results in the same InternalModel'
Expand All @@ -673,8 +667,8 @@ module('integration/unload - Unloading Records', function (hooks) {
let yaBoat = store.peekRecord('boat', '1');
let yaBoatInternalModel = yaBoat._internalModel;

assert.deepEqual(idsFromOrderedSet(relationshipState.canonicalMembers), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromOrderedSet(relationshipState.members), ['1'], 'members size should be 1');
assert.deepEqual(idsFromArr(relationshipState.canonicalState), ['1'], 'canonical member size should be 1');
assert.deepEqual(idsFromArr(relationshipState.currentState), ['1'], 'members size should be 1');
assert.ok(
yaBoatInternalModel === initialBoatInternalModel,
'after an unloadRecord, subsequent same-loop push results in the same InternalModel'
Expand Down Expand Up @@ -1837,48 +1831,46 @@ module('integration/unload - Unloading Records', function (hooks) {
};
};

let [person1, person2] = run(() =>
store.push({
data: [
{
id: 1,
type: 'person',
relationships: {
friends: {
data: [
{
id: 3,
type: 'person',
},
{
id: 4,
type: 'person',
},
],
},
let [person1, person2] = store.push({
data: [
{
id: 1,
type: 'person',
relationships: {
friends: {
data: [
{
id: 3,
type: 'person',
},
{
id: 4,
type: 'person',
},
],
},
},
{
id: 2,
type: 'person',
relationships: {
friends: {
data: [
{
id: 3,
type: 'person',
},
{
id: 4,
type: 'person',
},
],
},
},
{
id: 2,
type: 'person',
relationships: {
friends: {
data: [
{
id: 3,
type: 'person',
},
{
id: 4,
type: 'person',
},
],
},
},
],
})
);
},
],
});

let person1Friends, person3, person4;

Expand Down
Loading

0 comments on commit ea7dbbb

Please sign in to comment.