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

Modernizes relationship containers #4684

Merged
merged 1 commit into from
Nov 30, 2016
Merged
Show file tree
Hide file tree
Changes from all 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
251 changes: 122 additions & 129 deletions addon/-private/system/relationships/state/belongs-to.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,163 +5,156 @@ import {
} from "ember-data/-private/system/promise-proxies";

import { assertPolymorphicType } from "ember-data/-private/debug";

import Relationship from "ember-data/-private/system/relationships/state/relationship";

export default function BelongsToRelationship(store, record, inverseKey, relationshipMeta) {
this._super$constructor(store, record, inverseKey, relationshipMeta);
this.record = record;
this.key = relationshipMeta.key;
this.inverseRecord = null;
this.canonicalState = null;
}
export default class BelongsToRelationship extends Relationship {
constructor(store, internalModel, inverseKey, relationshipMeta) {
super(store, internalModel, inverseKey, relationshipMeta);
this.internalModel = internalModel;
this.key = relationshipMeta.key;
this.inverseRecord = null;
this.canonicalState = null;
}

BelongsToRelationship.prototype = Object.create(Relationship.prototype);
BelongsToRelationship.prototype.constructor = BelongsToRelationship;
BelongsToRelationship.prototype._super$constructor = Relationship;
setRecord(newRecord) {
if (newRecord) {
this.addRecord(newRecord);
} else if (this.inverseRecord) {
this.removeRecord(this.inverseRecord);
}
this.setHasData(true);
this.setHasLoaded(true);
}

BelongsToRelationship.prototype.setRecord = function(newRecord) {
if (newRecord) {
this.addRecord(newRecord);
} else if (this.inverseRecord) {
this.removeRecord(this.inverseRecord);
setCanonicalRecord(newRecord) {
if (newRecord) {
this.addCanonicalRecord(newRecord);
} else if (this.canonicalState) {
this.removeCanonicalRecord(this.canonicalState);
}
this.flushCanonicalLater();
}
this.setHasData(true);
this.setHasLoaded(true);
};

BelongsToRelationship.prototype.setCanonicalRecord = function(newRecord) {
if (newRecord) {
this.addCanonicalRecord(newRecord);
} else if (this.canonicalState) {
this.removeCanonicalRecord(this.canonicalState);

addCanonicalRecord(newRecord) {
if (this.canonicalMembers.has(newRecord)) { return;}

if (this.canonicalState) {
this.removeCanonicalRecord(this.canonicalState);
}

this.canonicalState = newRecord;
super.addCanonicalRecord(newRecord);
}
this.flushCanonicalLater();
};

BelongsToRelationship.prototype._super$addCanonicalRecord = Relationship.prototype.addCanonicalRecord;
BelongsToRelationship.prototype.addCanonicalRecord = function(newRecord) {
if (this.canonicalMembers.has(newRecord)) { return;}
flushCanonical() {
//temporary fix to not remove newly created records if server returned null.
//TODO remove once we have proper diffing
if (this.inverseRecord && this.inverseRecord.isNew() && !this.canonicalState) {
return;
}
if (this.inverseRecord !== this.canonicalState) {
this.inverseRecord = this.canonicalState;
this.internalModel.notifyBelongsToChanged(this.key);
}

if (this.canonicalState) {
this.removeCanonicalRecord(this.canonicalState);
super.flushCanonical();
}

this.canonicalState = newRecord;
this._super$addCanonicalRecord(newRecord);
};
addRecord(newRecord) {
if (this.members.has(newRecord)) { return; }

assertPolymorphicType(this.internalModel, this.relationshipMeta, newRecord);

if (this.inverseRecord) {
this.removeRecord(this.inverseRecord);
}

BelongsToRelationship.prototype._super$flushCanonical = Relationship.prototype.flushCanonical;
BelongsToRelationship.prototype.flushCanonical = function() {
//temporary fix to not remove newly created records if server returned null.
//TODO remove once we have proper diffing
if (this.inverseRecord && this.inverseRecord.isNew() && !this.canonicalState) {
return;
this.inverseRecord = newRecord;
super.addRecord(newRecord);
this.internalModel.notifyBelongsToChanged(this.key);
}
if (this.inverseRecord !== this.canonicalState) {
this.inverseRecord = this.canonicalState;
this.record.notifyBelongsToChanged(this.key);

setRecordPromise(newPromise) {
var content = newPromise.get && newPromise.get('content');
assert("You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.", content !== undefined);
this.setRecord(content ? content._internalModel : content);
}
this._super$flushCanonical();
};

BelongsToRelationship.prototype._super$addRecord = Relationship.prototype.addRecord;
BelongsToRelationship.prototype.addRecord = function(newRecord) {
if (this.members.has(newRecord)) { return;}
removeRecordFromOwn(record) {
if (!this.members.has(record)) { return;}
this.inverseRecord = null;
super.removeRecordFromOwn(record);
this.internalModel.notifyBelongsToChanged(this.key);
}

assertPolymorphicType(this.record, this.relationshipMeta, newRecord);
removeCanonicalRecordFromOwn(record) {
if (!this.canonicalMembers.has(record)) { return;}
this.canonicalState = null;
super.removeCanonicalRecordFromOwn(record);
}

if (this.inverseRecord) {
this.removeRecord(this.inverseRecord);
findRecord() {
if (this.inverseRecord) {
return this.store._findByInternalModel(this.inverseRecord);
} else {
return Ember.RSVP.Promise.resolve(null);
}
}

this.inverseRecord = newRecord;
this._super$addRecord(newRecord);
this.record.notifyBelongsToChanged(this.key);
};

BelongsToRelationship.prototype.setRecordPromise = function(newPromise) {
var content = newPromise.get && newPromise.get('content');
assert("You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.", content !== undefined);
this.setRecord(content ? content._internalModel : content);
};

BelongsToRelationship.prototype._super$removeRecordFromOwn = Relationship.prototype.removeRecordFromOwn;
BelongsToRelationship.prototype.removeRecordFromOwn = function(record) {
if (!this.members.has(record)) { return;}
this.inverseRecord = null;
this._super$removeRecordFromOwn(record);
this.record.notifyBelongsToChanged(this.key);
};

BelongsToRelationship.prototype._super$removeCanonicalRecordFromOwn = Relationship.prototype.removeCanonicalRecordFromOwn;
BelongsToRelationship.prototype.removeCanonicalRecordFromOwn = function(record) {
if (!this.canonicalMembers.has(record)) { return;}
this.canonicalState = null;
this._super$removeCanonicalRecordFromOwn(record);
};

BelongsToRelationship.prototype.findRecord = function() {
if (this.inverseRecord) {
return this.store._findByInternalModel(this.inverseRecord);
} else {
return Ember.RSVP.Promise.resolve(null);
fetchLink() {
return this.store.findBelongsTo(this.internalModel, this.link, this.relationshipMeta).then((record) => {
if (record) {
this.addRecord(record);
}
return record;
});
}
};

BelongsToRelationship.prototype.fetchLink = function() {
return this.store.findBelongsTo(this.record, this.link, this.relationshipMeta).then((record) => {
if (record) {
this.addRecord(record);
}
return record;
});
};

BelongsToRelationship.prototype.getRecord = function() {
//TODO(Igor) flushCanonical here once our syncing is not stupid
if (this.isAsync) {
var promise;
if (this.link) {
if (this.hasLoaded) {
promise = this.findRecord();
getRecord() {
//TODO(Igor) flushCanonical here once our syncing is not stupid
if (this.isAsync) {
var promise;
if (this.link) {
if (this.hasLoaded) {
promise = this.findRecord();
} else {
promise = this.findLink().then(() => this.findRecord());
}
} else {
promise = this.findLink().then(() => this.findRecord());
promise = this.findRecord();
}

return PromiseObject.create({
promise: promise,
content: this.inverseRecord ? this.inverseRecord.getRecord() : null
});
} else {
promise = this.findRecord();
if (this.inverseRecord === null) {
return null;
}
var toReturn = this.inverseRecord.getRecord();
assert("You looked up the '" + this.key + "' relationship on a '" + this.internalModel.modelName + "' with id " + this.internalModel.id + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", toReturn === null || !toReturn.get('isEmpty'));
return toReturn;
}
}

return PromiseObject.create({
promise: promise,
content: this.inverseRecord ? this.inverseRecord.getRecord() : null
});
} else {
if (this.inverseRecord === null) {
return null;
reload() {
// TODO handle case when reload() is triggered multiple times

if (this.link) {
return this.fetchLink();
}
var toReturn = this.inverseRecord.getRecord();
assert("You looked up the '" + this.key + "' relationship on a '" + this.record.modelName + "' with id " + this.record.id + " but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)", toReturn === null || !toReturn.get('isEmpty'));
return toReturn;
}
};

BelongsToRelationship.prototype.reload = function() {
// TODO handle case when reload() is triggered multiple times
// reload record, if it is already loaded
if (this.inverseRecord && this.inverseRecord.hasRecord) {
return this.inverseRecord.record.reload();
}

if (this.link) {
return this.fetchLink();
return this.findRecord();
}

// reload record, if it is already loaded
if (this.inverseRecord && this.inverseRecord.hasRecord) {
return this.inverseRecord.record.reload();
updateData(data) {
let internalModel = this.store._pushResourceIdentifier(this, data);
this.setCanonicalRecord(internalModel);
}

return this.findRecord();
};

BelongsToRelationship.prototype.updateData = function(data) {
let internalModel = this.store._pushResourceIdentifier(this, data);
this.setCanonicalRecord(internalModel);
};
}
48 changes: 29 additions & 19 deletions addon/-private/system/relationships/state/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,55 @@ import ManyRelationship from "ember-data/-private/system/relationships/state/has
import BelongsToRelationship from "ember-data/-private/system/relationships/state/belongs-to";
import EmptyObject from "ember-data/-private/system/empty-object";

var get = Ember.get;
const { get } = Ember;

function shouldFindInverse(relationshipMeta) {
let options = relationshipMeta.options;
return !(options && options.inverse === null);
}

function createRelationshipFor(record, relationshipMeta, store) {
function createRelationshipFor(internalModel, relationshipMeta, store) {
let inverseKey;
let inverse = null;
if (shouldFindInverse(relationshipMeta)) {
inverse = record.type.inverseFor(relationshipMeta.key, store);
inverse = internalModel.type.inverseFor(relationshipMeta.key, store);
}

if (inverse) {
inverseKey = inverse.name;
}

if (relationshipMeta.kind === 'hasMany') {
return new ManyRelationship(store, record, inverseKey, relationshipMeta);
return new ManyRelationship(store, internalModel, inverseKey, relationshipMeta);
} else {
return new BelongsToRelationship(store, record, inverseKey, relationshipMeta);
return new BelongsToRelationship(store, internalModel, inverseKey, relationshipMeta);
}
}

export default function Relationships(record) {
this.record = record;
this.initializedRelationships = new EmptyObject();
}
export default class Relationships {
constructor(internalModel) {
this.internalModel = internalModel;
this.initializedRelationships = new EmptyObject();
}

// TODO @runspired deprecate this as it was never truly a record instance
get record() {
return this.internalModel;
}

Relationships.prototype.has = function(key) {
return !!this.initializedRelationships[key];
};
has(key) {
return !!this.initializedRelationships[key];
}

get(key) {
let relationships = this.initializedRelationships;
let internalModel = this.internalModel;
let relationshipsByName = get(internalModel.type, 'relationshipsByName');

Relationships.prototype.get = function(key) {
var relationships = this.initializedRelationships;
var relationshipsByName = get(this.record.type, 'relationshipsByName');
if (!relationships[key] && relationshipsByName.get(key)) {
relationships[key] = createRelationshipFor(this.record, relationshipsByName.get(key), this.record.store);
if (!relationships[key] && relationshipsByName.get(key)) {
relationships[key] = createRelationshipFor(internalModel, relationshipsByName.get(key), internalModel.store);
}

return relationships[key];
}
return relationships[key];
};
}
Loading