Skip to content

Commit

Permalink
Merge pull request #4684 from runspired/cleanup/es6-relationships
Browse files Browse the repository at this point in the history
Modernizes relationship containers
  • Loading branch information
pangratz authored Nov 30, 2016
2 parents f76a961 + a946dce commit 91d8977
Show file tree
Hide file tree
Showing 4 changed files with 417 additions and 413 deletions.
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

0 comments on commit 91d8977

Please sign in to comment.