Skip to content

Commit

Permalink
Merge pull request #2939 from wecc/snapshots-unknown-relationships
Browse files Browse the repository at this point in the history
Snapshots unknown relationships
  • Loading branch information
igorT committed Apr 12, 2015
2 parents 1d17291 + 94dfd68 commit 7e4f137
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ BelongsToRelationship.prototype.setRecord = function(newRecord) {
} else if (this.inverseRecord) {
this.removeRecord(this.inverseRecord);
}
this.setHasData(true);
};

BelongsToRelationship.prototype.setCanonicalRecord = function(newRecord) {
Expand All @@ -30,6 +31,7 @@ BelongsToRelationship.prototype.setCanonicalRecord = function(newRecord) {
} else if (this.inverseRecord) {
this.removeCanonicalRecord(this.inverseRecord);
}
this.setHasData(true);
};

BelongsToRelationship.prototype._super$addCanonicalRecord = Relationship.prototype.addCanonicalRecord;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Relationship.prototype = {
}
}
this.flushCanonicalLater();
this.setHasData(true);
},

removeCanonicalRecords: function(records, idx) {
Expand Down Expand Up @@ -125,6 +126,7 @@ Relationship.prototype = {
}
this.record.updateRecordArraysLater();
}
this.setHasData(true);
},

removeRecord: function(record) {
Expand Down Expand Up @@ -228,13 +230,14 @@ Relationship.prototype = {
var self = this;
//TODO Once we have adapter support, we need to handle updated and canonical changes
self.computeChanges(records);
self.setHasData(true);
},

notifyRecordRelationshipAdded: Ember.K,
notifyRecordRelationshipRemoved: Ember.K,

setHasData: function() {
this.hasData = true;
setHasData: function(value) {
this.hasData = value;
}
};

Expand Down
66 changes: 43 additions & 23 deletions packages/ember-data/lib/system/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ Snapshot.prototype = {
```javascript
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
postSnapshot.id; // => '1'
```
Expand Down Expand Up @@ -113,7 +112,6 @@ Snapshot.prototype = {
```javascript
// store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' });
postSnapshot.attr('author'); // => 'Tomster'
postSnapshot.attr('title'); // => 'Ember.js rocks'
```
Expand All @@ -138,7 +136,6 @@ Snapshot.prototype = {
```javascript
// store.push('post', { id: 1, author: 'Tomster', title: 'Hello World' });
postSnapshot.attributes(); // => { author: 'Tomster', title: 'Ember.js rocks' }
```
Expand All @@ -163,26 +160,31 @@ Snapshot.prototype = {
```javascript
// store.push('post', { id: 1, title: 'Hello World' });
// store.createRecord('comment', { body: 'Lorem ipsum', post: post });
commentSnapshot.belongsTo('post'); // => DS.Snapshot
commentSnapshot.belongsTo('post', { id: true }); // => '1'
// store.push('comment', { id: 1, body: 'Lorem ipsum' });
commentSnapshot.belongsTo('post'); // => undefined
```
Calling `belongsTo` will return a new Snapshot as long as there's any
data available, such as an ID. If there's no data available `belongsTo` will
return undefined.
Calling `belongsTo` will return a new Snapshot as long as there's any known
data for the relationship available, such as an ID. If the relationship is
known but unset, `belongsTo` will return `null`. If the contents of the
relationship is unknown `belongsTo` will return `undefined`.
Note: Relationships are loaded lazily and cached upon first access.
@method belongsTo
@param {String} keyName
@param {Object} [options]
@return {DS.Snapshot|String|undefined} A snapshot or ID of a belongsTo relationship, or undefined
@return {DS.Snapshot|String|null|undefined} A snapshot or ID of a known
relationship or null if the relationship is known but unset. undefined
will be returned if the contents of the relationship is unknown.
*/
belongsTo: function(keyName, options) {
var id = options && options.id;
var relationship, inverseRecord, hasData;
var result;
var relationship, inverseRecord;

if (id && keyName in this._belongsToIds) {
return this._belongsToIds[keyName];
Expand All @@ -197,16 +199,24 @@ Snapshot.prototype = {
throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no belongsTo relationship named '" + keyName + "' defined.");
}

hasData = get(relationship, 'hasData');
inverseRecord = get(relationship, 'inverseRecord');
if (id) {

if (hasData) {
if (inverseRecord) {
result = get(inverseRecord, 'id');
if (id) {
result = get(inverseRecord, 'id');
} else {
result = inverseRecord._createSnapshot();
}
} else {
result = null;
}
}

if (id) {
this._belongsToIds[keyName] = result;
} else {
if (inverseRecord) {
result = inverseRecord._createSnapshot();
}
this._belongsToRelationships[keyName] = result;
}

Expand All @@ -226,22 +236,26 @@ Snapshot.prototype = {
```javascript
// store.push('post', { id: 1, title: 'Hello World', comments: [2, 3] });
postSnapshot.hasMany('comments'); // => [DS.Snapshot, DS.Snapshot]
postSnapshot.hasMany('comments', { ids: true }); // => ['2', '3']
// store.push('post', { id: 1, title: 'Hello World' });
postSnapshot.hasMany('comments'); // => undefined
```
Note: Relationships are loaded lazily and cached upon first access.
@method hasMany
@param {String} keyName
@param {Object} [options]
@return {Array} An array of snapshots or IDs of a hasMany relationship
@return {Array|undefined} An array of snapshots or IDs of a known
relationship or an empty array if the relationship is known but unset.
undefined will be returned if the contents of the relationship is unknown.
*/
hasMany: function(keyName, options) {
var ids = options && options.ids;
var results = [];
var relationship, members;
var relationship, members, hasData;
var results;

if (ids && keyName in this._hasManyIds) {
return this._hasManyIds[keyName];
Expand All @@ -256,17 +270,23 @@ Snapshot.prototype = {
throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no hasMany relationship named '" + keyName + "' defined.");
}

hasData = get(relationship, 'hasData');
members = get(relationship, 'members');

if (ids) {
if (hasData) {
results = [];
members.forEach(function(member) {
results.push(get(member, 'id'));
if (ids) {
results.push(get(member, 'id'));
} else {
results.push(member._createSnapshot());
}
});
}

if (ids) {
this._hasManyIds[keyName] = results;
} else {
members.forEach(function(member) {
results.push(member._createSnapshot());
});
this._hasManyRelationships[keyName] = results;
}

Expand Down
1 change: 0 additions & 1 deletion packages/ember-data/lib/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -2070,7 +2070,6 @@ function setupRelationships(store, record, data) {
} else if (kind === 'hasMany') {
relationship.updateRecordsFromAdapter(value);
}
relationship.setHasData(true);
}
});
}
Expand Down
141 changes: 139 additions & 2 deletions packages/ember-data/tests/integration/snapshot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ test("snapshot.belongsTo() returns undefined if relationship is undefined", func
});
});

test("snapshot.belongsTo() returns null if relationship is unset", function() {
expect(1);

run(function() {
env.store.push('post', { id: 1, title: 'Hello World' });
var comment = env.store.push('comment', { id: 2, body: 'This is comment', post: null });
var snapshot = comment._createSnapshot();
var relationship = snapshot.belongsTo('post');

equal(relationship, null, 'relationship is unset');
});
});

test("snapshot.belongsTo() returns a snapshot if relationship is set", function() {
expect(3);

Expand All @@ -139,6 +152,86 @@ test("snapshot.belongsTo() returns a snapshot if relationship is set", function(
});
});

test("snapshot.belongsTo() returns undefined if relationship is a link", function() {
expect(1);

run(function() {
var comment = env.store.push('comment', { id: 2, body: 'This is comment', links: { post: 'post' } });
var snapshot = comment._createSnapshot();
var relationship = snapshot.belongsTo('post');

equal(relationship, undefined, 'relationship is undefined');
});
});

test("snapshot.belongsTo() returns a snapshot if relationship link has been fetched", function() {
expect(2);

env.adapter.findBelongsTo = function(store, snapshot, link, relationship) {
return Ember.RSVP.resolve({ id: 1, title: 'Hello World' });
};

run(function() {
var comment = env.store.push('comment', { id: 2, body: 'This is comment', links: { post: 'post' } });

comment.get('post').then(function(post) {
var snapshot = comment._createSnapshot();
var relationship = snapshot.belongsTo('post');

ok(relationship instanceof DS.Snapshot, 'snapshot is an instance of DS.Snapshot');
equal(relationship.id, '1', 'post id is correct');
});
});
});

test("snapshot.belongsTo() and snapshot.hasMany() returns correctly when adding an object to a hasMany relationship", function() {
expect(4);

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World' });
var comment = env.store.push('comment', { id: 2, body: 'blabla' });

post.get('comments').then(function(comments) {
comments.addObject(comment);

var postSnapshot = post._createSnapshot();
var commentSnapshot = comment._createSnapshot();

var hasManyRelationship = postSnapshot.hasMany('comments');
var belongsToRelationship = commentSnapshot.belongsTo('post');

ok(hasManyRelationship instanceof Array, 'hasMany relationship is an instance of Array');
equal(hasManyRelationship.length, 1, 'hasMany relationship contains related object');

ok(belongsToRelationship instanceof DS.Snapshot, 'belongsTo relationship is an instance of DS.Snapshot');
equal(belongsToRelationship.attr('title'), 'Hello World', 'belongsTo relationship contains related object');
});
});
});

test("snapshot.belongsTo() and snapshot.hasMany() returns correctly when setting an object to a belongsTo relationship", function() {
expect(4);

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World' });
var comment = env.store.push('comment', { id: 2, body: 'blabla' });

comment.set('post', post);

var postSnapshot = post._createSnapshot();
var commentSnapshot = comment._createSnapshot();

var hasManyRelationship = postSnapshot.hasMany('comments');
var belongsToRelationship = commentSnapshot.belongsTo('post');

ok(hasManyRelationship instanceof Array, 'hasMany relationship is an instance of Array');
equal(hasManyRelationship.length, 1, 'hasMany relationship contains related object');

ok(belongsToRelationship instanceof DS.Snapshot, 'belongsTo relationship is an instance of DS.Snapshot');
equal(belongsToRelationship.attr('title'), 'Hello World', 'belongsTo relationship contains related object');
});
});

test("snapshot.hasMany() returns ID if option.id is set", function() {
expect(1);

Expand All @@ -152,14 +245,26 @@ test("snapshot.hasMany() returns ID if option.id is set", function() {
});
});

test("snapshot.hasMany() returns empty array if relationship is undefined", function() {
expect(2);
test("snapshot.hasMany() returns undefined if relationship is undefined", function() {
expect(1);

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World' });
var snapshot = post._createSnapshot();
var relationship = snapshot.hasMany('comments');

equal(relationship, undefined, 'relationship is undefined');
});
});

test("snapshot.hasMany() returns empty array if relationship is unset", function() {
expect(2);

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World', comments: null });
var snapshot = post._createSnapshot();
var relationship = snapshot.hasMany('comments');

ok(relationship instanceof Array, 'relationship is an instance of Array');
equal(relationship.length, 0, 'relationship is empty');
});
Expand Down Expand Up @@ -199,6 +304,38 @@ test("snapshot.hasMany() returns array of IDs if option.ids is set", function()
});
});

test("snapshot.hasMany() returns undefined if relationship is a link", function() {
expect(1);

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World', links: { comments: 'comments' } });
var snapshot = post._createSnapshot();
var relationship = snapshot.hasMany('comments');

equal(relationship, undefined, 'relationship is undefined');
});
});

test("snapshot.hasMany() returns array of snapshots if relationship link has been fetched", function() {
expect(2);

env.adapter.findHasMany = function(store, snapshot, link, relationship) {
return Ember.RSVP.resolve([{ id: 2, body: 'This is comment' }]);
};

run(function() {
var post = env.store.push('post', { id: 1, title: 'Hello World', links: { comments: 'comments' } });

post.get('comments').then(function(comments) {
var snapshot = post._createSnapshot();
var relationship = snapshot.hasMany('comments');

ok(relationship instanceof Array, 'relationship is an instance of Array');
equal(relationship.length, 1, 'relationship has one item');
});
});
});

test("snapshot.hasMany() respects the order of items in the relationship", function() {
expect(3);

Expand Down

0 comments on commit 7e4f137

Please sign in to comment.