Permalink
Browse files

Make Ember Data objects promises

  • Loading branch information...
1 parent 7da1b29 commit ca9968a2da1952fb7e89e0c66ff486edcec13ae9 tomhuda committed Dec 30, 2012
@@ -0,0 +1,14 @@
+var DeferredMixin = Ember.DeferredMixin, // ember-runtime/mixins/deferred
+ Evented = Ember.Evented, // ember-runtime/mixins/evented
+ run = Ember.run; // ember-metal/run-loop
+
+var LoadPromise = Ember.Mixin.create(Evented, DeferredMixin, {
+ init: function() {
+ this._super.apply(this, arguments);
+ this.one('didLoad', function() {
+ run(this, 'resolve', this);
@krisselden

krisselden Jun 14, 2013

Owner

Ember.run should be in the adapter in the ajax event handler, not here, this causes a lot of nested run loops. cc @stefanpenner

@stefanpenner

stefanpenner Jun 14, 2013

Owner

Good catch. The current rest adapter does catch this case.

I think we need to raise an exception (during testing) when nested run loops occur.

I will make this fix shortly.

+ });
+ }
+});
+
+DS.LoadPromise = LoadPromise;
@@ -1,12 +1,15 @@
require("ember-data/system/model/states");
+require("ember-data/system/mixins/load_promise");
+
+var LoadPromise = DS.LoadPromise; // system/mixins/load_promise
var get = Ember.get, set = Ember.set, none = Ember.isNone, map = Ember.EnumerableUtils.map;
var retrieveFromCurrentState = Ember.computed(function(key) {
return get(get(this, 'stateManager.currentState'), key);
}).property('stateManager.currentState');
-DS.Model = Ember.Object.extend(Ember.Evented, {
+DS.Model = Ember.Object.extend(Ember.Evented, LoadPromise, {
isLoaded: retrieveFromCurrentState,
isDirty: retrieveFromCurrentState,
isSaving: retrieveFromCurrentState,
@@ -66,6 +69,8 @@ DS.Model = Ember.Object.extend(Ember.Evented, {
_data: null,
init: function() {
+ this._super();
+
var stateManager = DS.StateManager.create({ record: this });
set(this, 'stateManager', stateManager);
@@ -1,5 +1,9 @@
+require("ember-data/system/mixins/load_promise");
+
var get = Ember.get, set = Ember.set;
+var LoadPromise = DS.LoadPromise; // system/mixins/load_promise
+
/**
A record array is an array that contains records of a certain type. The record
array materializes records as needed when they are retrieved for the first
@@ -8,7 +12,7 @@ var get = Ember.get, set = Ember.set;
in response to queries.
*/
-DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
+DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, LoadPromise, {
/**
The model type contained by this record array.
@@ -35,7 +35,7 @@ module("Has-Many Relationships", {
});
test("A hasMany relationship has an isLoaded flag that indicates whether the ManyArray has finished loaded", function() {
- expect(8);
+ expect(9);
var array, hasLoaded;
@@ -58,6 +58,10 @@ test("A hasMany relationship has an isLoaded flag that indicates whether the Man
ok(true, "didLoad was triggered");
});
+ array.then(function(resolvedValue) {
+ equal(resolvedValue, array, "the promise was resolved with itself");
+ });
+
equal(get(array, 'isLoaded'), false, "isLoaded should not be true when first created");
});
@@ -21,7 +21,7 @@ module("Queries", {
});
test("When a query is made, the adapter should receive a record array it can populate with the results of the query.", function() {
- expect(7);
+ expect(8);
adapter.findQuery = function(store, type, query, recordArray) {
equal(type, Person, "the find method is called with the correct type");
@@ -40,12 +40,16 @@ test("When a query is made, the adapter should receive a record array it can pop
equal(get(queryResults, 'isLoaded'), false, "the record array's `isLoaded` property is false");
queryResults.one('didLoad', function() {
- start();
-
equal(get(queryResults, 'length'), 2, "the record array has a length of 2 after the results are loaded");
equal(get(queryResults, 'isLoaded'), true, "the record array's `isLoaded` property should be true");
equal(queryResults.objectAt(0).get('name'), "Peter Wagenet", "the first record is 'Peter Wagenet'");
equal(queryResults.objectAt(1).get('name'), "Brohuda Katz", "the second record is 'Brohuda Katz'");
});
+
+ queryResults.then(function(resolvedValue) {
+ start();
+
+ equal(resolvedValue, queryResults, "The promise was resolved with the query results");
+ });
});
@@ -46,7 +46,7 @@ test("should load data for a type asynchronously when it is requested", function
equal(get(ebryn, 'isLoaded'), false, "record from fixtures is returned in the loading state");
- ebryn.on('didLoad', function() {
+ ebryn.then(function() {
clearTimeout(timer);
start();
@@ -56,7 +56,7 @@ test("should load data for a type asynchronously when it is requested", function
stop();
var wycats = store.find(Person, 'wycats');
- wycats.on('didLoad', function() {
+ wycats.then(function() {
clearTimeout(timer);
start();
@@ -892,7 +892,7 @@ test("sideloaded data is loaded prior to primary data (to ensure relationship co
expect(1);
group = store.find(Group, 1);
- group.on("didLoad", function() {
+ group.then(function(group) {
equal(group.get('people.firstObject').get('name'), "Tom Dale", "sideloaded data are already loaded");
});
@@ -266,7 +266,7 @@ test("DS.Store loads individual records without explicit IDs with a custom prima
*/
test("DS.Store passes only needed guids to findMany", function() {
- expect(11);
+ expect(13);
var adapter = TestAdapter.create({
findMany: function(store, type, ids) {
@@ -286,6 +286,11 @@ test("DS.Store passes only needed guids to findMany", function() {
equal(get(objects, 'length'), 6, "the RecordArray returned from findMany has all the objects");
equal(get(objects, 'isLoaded'), false, "the RecordArrays' isLoaded flag is false");
+ objects.then(function(resolvedObjects) {
+ strictEqual(resolvedObjects, objects, "The promise is resolved with the RecordArray");
+ equal(get(objects, 'isLoaded'), true, "The objects are loaded");
+ });
+
var i, object, hash;
for (i=0; i<3; i++) {
object = objects.objectAt(i);
@@ -306,7 +311,7 @@ test("DS.Store passes only needed guids to findMany", function() {
});
test("a findManys' isLoaded is true when all objects are loaded", function() {
- expect(2);
+ expect(4);
var adapter = TestAdapter.create({
findMany: function(store, type, ids) {
@@ -323,6 +328,11 @@ test("a findManys' isLoaded is true when all objects are loaded", function() {
var objects = currentStore.findMany(currentType, [1,2,3]);
+ objects.then(function(resolvedObjects) {
+ strictEqual(resolvedObjects, objects, "The resolved RecordArray is correct");
+ equal(get(objects, 'isLoaded'), true, "The RecordArray is loaded by the time the promise is resolved");
+ });
+
equal(get(objects, 'length'), 3, "the RecordArray returned from findMany has all the objects");
equal(get(objects, 'isLoaded'), true, "the RecordArrays' isLoaded flag is true");
});
@@ -384,6 +394,11 @@ test("a new record of a particular type is created via store.createRecord(type)"
var person = store.createRecord(Person);
+ person.then(function(resolvedPerson) {
+ strictEqual(resolvedPerson, person, "The promise is resolved with the record");
+ equal(get(person, 'isLoaded'), true, "The record is loaded");
+ });
+
equal(get(person, 'isLoaded'), true, "A newly created record is loaded");
equal(get(person, 'isNew'), true, "A newly created record is new");
equal(get(person, 'isDirty'), true, "A newly created record is dirty");
@@ -401,6 +416,11 @@ test("an initial data hash can be provided via store.createRecord(type, hash)",
var person = store.createRecord(Person, { name: "Brohuda Katz" });
+ person.then(function(resolvedPerson) {
+ strictEqual(resolvedPerson, person, "The promise is resolved with the record");
+ equal(get(person, 'isLoaded'), true, "The record is loaded");
+ });
+
equal(get(person, 'isLoaded'), true, "A newly created record is loaded");
equal(get(person, 'isNew'), true, "A newly created record is new");
equal(get(person, 'isDirty'), true, "A newly created record is dirty");
@@ -453,12 +473,10 @@ test("records inside a collection view should have their ids updated", function(
module("DS.State - Lifecycle Callbacks");
-test("a record receives a didLoad callback when it has finished loading", function() {
- var callCount = 0;
-
+asyncTest("a record receives a didLoad callback when it has finished loading", function() {
var Person = DS.Model.extend({
didLoad: function() {
- callCount++;
+ ok("The didLoad callback was called");
}
});
@@ -471,9 +489,12 @@ test("a record receives a didLoad callback when it has finished loading", functi
var store = DS.Store.create({
adapter: adapter
});
- store.find(Person, 1);
+ var person = store.find(Person, 1);
- equal(callCount, 1, "didLoad callback was called once");
+ person.then(function(resolvedPerson) {
+ equal(resolvedPerson, person, "The resolved value is correct");
+ start();
+ });
});
test("a record receives a didUpdate callback when it has finished updating", function() {
Oops, something went wrong.

1 comment on commit ca9968a

Member

lukemelia commented on ca9968a Dec 31, 2012

DS.LoadPromise includes Ember.Evented so DS.Model and DS.RecordArray don't need to include it again.

Please sign in to comment.