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

Support embedded relations (embed: always-style) #24

Merged
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
34 changes: 25 additions & 9 deletions addon/adapters/localforage.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default DS.Adapter.extend(Ember.Evented, {
}

if (allowRecursive) {
adapter.loadRelationships(type, record).then(function(finalRecord) {
adapter.loadRelationships(store, type, record).then(function(finalRecord) {
resolve(finalRecord);
});
} else {
Expand All @@ -67,7 +67,7 @@ export default DS.Adapter.extend(Ember.Evented, {
});
}).then(function(records) {
if (records.get('length')) {
return adapter.loadRelationshipsForMany(type, records);
return adapter.loadRelationshipsForMany(store, type, records);
} else {
return records;
}
Expand Down Expand Up @@ -95,7 +95,7 @@ export default DS.Adapter.extend(Ember.Evented, {
var results = adapter.query(namespace.records, query);

if (results.get('length')) {
results = adapter.loadRelationshipsForMany(type, results);
results = adapter.loadRelationshipsForMany(store, type, results);
}

resolve(results);
Expand Down Expand Up @@ -272,10 +272,11 @@ export default DS.Adapter.extend(Ember.Evented, {
*
* @method loadRelationships
* @private
* @param {DS.Store} store
* @param {DS.Model} type
* @param {Object} record
*/
loadRelationships: function(type, record) {
loadRelationships: function(store, type, record) {
var adapter = this;

return new Ember.RSVP.Promise(function(resolve, reject) {
Expand Down Expand Up @@ -314,11 +315,14 @@ export default DS.Adapter.extend(Ember.Evented, {
* In this case, cart belongsTo customer and its id is present in the
* main payload. We find each of these records and add them to _embedded.
*/
if (relationEmbeddedId) {
var embeddedAlways = adapter.isEmbeddedAlways(store, type.typeKey, relationProp.key);

// For embeddedAlways-style data, we assume the data to be present already, so no further loading is needed.
if (relationEmbeddedId && !embeddedAlways) {
if (relationType === 'belongsTo' || relationType === 'hasOne') {
promise = adapter.find(null, relationModel, relationEmbeddedId, opts);
promise = adapter.find(store, relationModel, relationEmbeddedId, opts);
} else if (relationType === 'hasMany') {
promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts);
promise = adapter.findMany(store, relationModel, relationEmbeddedId, opts);
}

embedPromise = new Ember.RSVP.Promise(function(resolve, reject) {
Expand Down Expand Up @@ -413,10 +417,11 @@ export default DS.Adapter.extend(Ember.Evented, {
*
* @method loadRelationshipsForMany
* @private
* @param {DS.Store} store
* @param {DS.Model} type
* @param {Object} recordsArray
*/
loadRelationshipsForMany: function(type, recordsArray) {
loadRelationshipsForMany: function(store, type, recordsArray) {
var adapter = this;

return new Ember.RSVP.Promise(function(resolve, reject) {
Expand All @@ -440,7 +445,7 @@ export default DS.Adapter.extend(Ember.Evented, {
*/
recordsToBeLoaded = recordsToBeLoaded.slice(1);

var promise = adapter.loadRelationships(type, record);
var promise = adapter.loadRelationships(store, type, record);

promise.then(function(recordWithRelationships) {
recordsWithRelationships.push(recordWithRelationships);
Expand Down Expand Up @@ -475,6 +480,17 @@ export default DS.Adapter.extend(Ember.Evented, {
} else {
return relationships;
}
},

isEmbeddedAlways: function(store, typeKey, relationKey) {
if (store === undefined || store === null) {
return false;
}

var serializer = store.serializerFor(typeKey);
var embeddedAlways = typeof(serializer.hasEmbeddedAlwaysOption) === 'function' &&
serializer.hasEmbeddedAlwaysOption(relationKey);
return embeddedAlways;
}
});

Expand Down
11 changes: 11 additions & 0 deletions app/serializers/customer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import DS from 'ember-data';
import LFSerializer from 'ember-localforage-adapter/serializers/localforage';

export default LFSerializer.extend(
DS.EmbeddedRecordsMixin, {
attrs: {
addresses: { embedded: 'always' },
hour: { embedded: 'always' }
}
}
);
8 changes: 8 additions & 0 deletions tests/dummy/app/models/address.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import DS from 'ember-data';

var attr = DS.attr;
var hasMany = DS.hasMany;

export default DS.Model.extend({
addressNumber: DS.attr()
});
7 changes: 7 additions & 0 deletions tests/dummy/app/models/customer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import DS from 'ember-data';

export default DS.Model.extend({
customerNumber: DS.Model(),
hour: DS.belongsTo('hour'),
addresses: DS.hasMany('address')
});
120 changes: 120 additions & 0 deletions tests/integration/crud-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ var FIXTURES = {
'h3': { id: 'h3', name: 'three', amount: 2, order: 'o3' },
'h4': { id: 'h4', name: 'four', amount: 1, order: 'o3' }
}
},

'customer': {
records: {
'1': {
id: '1',
customerNumber: '123',
addresses: [
{ id: '1', addressNumber: '12345' },
{ id: '2', addressNumber: '54321' }
],
hour: {
id: 'h5',
name: 'five'
}
}
}
}
};

Expand Down Expand Up @@ -429,5 +446,108 @@ test('saves hasMany', function() {
});
});

test("loads embedded hasMany in a 'find with id' operation", function() {
expect(5);

stop();

run(function() {
store.find('customer', '1').then(function(customer) {
var addresses = customer.get('addresses');

equal(addresses.length, 2);
var address1 = addresses.get('firstObject'),
address2 = addresses.get('lastObject');

equal(get(address1, 'id'), '1',
'first address id is loaded correctly');
equal(get(address1, 'addressNumber'), '12345',
'first address number is loaded correctly');
equal(get(address2, 'id'), '2',
'first address id is loaded correctly');
equal(get(address2, 'addressNumber'), '54321',
'first address number is loaded correctly');

start();
});
});
});

test("loads embedded hasMany in a 'find all' operation", function() {
expect(6);

stop();

run(function() {
store.find('customer').then(function(customers) {
equal(get(customers, 'length'), 1, 'one customer was retrieved');

var customer = customers.objectAt(0);
var addresses = customer.get('addresses');

equal(addresses.length, 2);
var address1 = addresses.get('firstObject'),
address2 = addresses.get('lastObject');

equal(get(address1, 'id'), '1',
'first address id is loaded correctly');
equal(get(address1, 'addressNumber'), '12345',
'first address number is loaded correctly');
equal(get(address2, 'id'), '2',
'first address id is loaded correctly');
equal(get(address2, 'addressNumber'), '54321',
'first address number is loaded correctly');

start();
});
});
});

test("loads embedded hasMany in a 'find many' operation", function() {
expect(6);

stop();

run(function() {
store.find('customer', { customerNumber: '123' }).then(function(customers) {
equal(get(customers, 'length'), 1);

var customer = customers.objectAt(0);
var addresses = customer.get('addresses');

equal(addresses.length, 2);
var address1 = addresses.get('firstObject'),
address2 = addresses.get('lastObject');

equal(get(address1, 'id'), '1',
'first address id is loaded correctly');
equal(get(address1, 'addressNumber'), '12345',
'first address number is loaded correctly');
equal(get(address2, 'id'), '2',
'first address id is loaded correctly');
equal(get(address2, 'addressNumber'), '54321',
'first address number is loaded correctly');

start();
});
});
});

test("loads embedded belongsTo in a 'find with id' operation", function() {
expect(2);

stop();

run(function() {
store.find('customer', '1').then(function(customer) {
var hour = customer.get('hour');

equal(get(hour, 'id'), 'h5',
'hour id is loaded correctly');
equal(get(hour, 'name'), 'five',
'hour name is loaded correctly');

start();
});
});
});
18 changes: 18 additions & 0 deletions tests/unit/serializers/customer-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
moduleFor,
test
} from 'ember-qunit';

moduleFor('serializer:customer', {
});

test("it is set up with embedded:always style for the 'addresses' relation", function(assert) {
var serializer = this.subject();
assert.ok(serializer.hasEmbeddedAlwaysOption('addresses'), 'addresses relation is not set up with embedded:always style');
});

test("it is not set up with embedded:always style for the 'foo' relation", function(assert) {
var serializer = this.subject();
assert.ok(!serializer.hasEmbeddedAlwaysOption('foo'), 'foo relation is unexpectedly set up with embedded:always style');
});