Skip to content

Commit

Permalink
Merge pull request #260 from firebase/ts-find-query-with-updates
Browse files Browse the repository at this point in the history
Realtime findQuery arrays
  • Loading branch information
tstirrat committed Jun 19, 2015
2 parents 9d71369 + 2274608 commit 95f9d9e
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 3 deletions.
37 changes: 35 additions & 2 deletions addon/adapters/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,45 @@ export default DS.Adapter.extend(Ember.Evented, {
}, fmt('DS: FirebaseAdapter#findAll %@ to %@', [type, ref.toString()]));
},

findQuery: function(store, type, query) {
findQuery: function(store, type, query, recordArray) {
var adapter = this;
var ref = this._getRef(type);

ref = this.applyQueryToRef(ref, query);

ref.on('child_added', function(snapshot) {
var record = store.recordForId(type, snapshot.key());

if (!record || !record.__listening) {
var payload = adapter._assignIdToPayload(snapshot);
var serializer = store.serializerFor(type);
adapter._updateRecordCacheForType(type, payload);
record = store.push(type, serializer.extractSingle(store, type, payload));
}

if (record) {
recordArray.addObject(record);
}
});

// `child_changed` is already handled by the record's
// value listener after a store.push. `child_moved` is
// a much less common case because it relates to priority

ref.on('child_removed', function(snapshot) {
var record = store.recordForId(type, snapshot.key());
if (record) {
recordArray.removeObject(record);
}
});

// clean up event handlers when the array is being destroyed
// so that future firebase events wont keep trying to use a
// destroyed store/serializer
recordArray.__firebaseCleanup = function () {
ref.off('child_added');
ref.off('child_removed');
};

return new Promise(function(resolve, reject) {
// Listen for child events on the type
ref.once('value', function(snapshot) {
Expand Down
12 changes: 12 additions & 0 deletions addon/initializers/emberfire.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ export default {
});
}

if (!DS.AdapterPopulatedRecordArray.prototype._emberfirePatched) {
DS.AdapterPopulatedRecordArray.reopen({
_emberfirePatched: true,
willDestroy: function() {
if (this.__firebaseCleanup) {
this.__firebaseCleanup();
}
return this._super();
}
});
}

DS.FirebaseAdapter = FirebaseAdapter;
DS.FirebaseSerializer = FirebaseSerializer;
}
Expand Down
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
feature - `store.find` with queries now returns a realtime array.
1 change: 1 addition & 0 deletions config/ember-try.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* jshint node: true */
module.exports = {
scenarios: [
{
Expand Down
72 changes: 72 additions & 0 deletions tests/helpers/fixture-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export default {
"post_1": true,
"post_2": true
}
},
"esnowden": {
"firstName": "Edward",
"created": 1395162147634,
"posts": {
"post_3": true
}
}
},
"posts": {
Expand All @@ -31,6 +38,16 @@ export default {
"comment_4": true
},
"title": "Post 2"
},
"post_3": {
"published": 1395162147646,
"user": "esnowden",
"body": "This is the third FireBlog post!",
"comments": {
"comment_5": true,
"comment_6": true
},
"title": "Post 3"
}
},
"comments": {
Expand All @@ -53,9 +70,64 @@ export default {
"published": 1395176007626,
"user": "aputinski",
"body": "This is a fourth comment"
},
"comment_5": {
"published": 1395176007627,
"user": "esnowden",
"body": "This is a fifth comment"
},
"comment_6": {
"published": 1395176007628,
"user": "esnowden",
"body": "This is a sixth comment"
}
}
},
"queries": {
"users": {
"tstirrat": {
"firstName": "Tim",
"created": 1395162147634,
"posts": {
"post_1": true,
"post_2": true,
"post_3": true
}
}
},
"posts": {
"post_1": {
"published": 1395162147646,
"user": "tstirrat",
"body": "This is the first FireBlog post!",
"comments": {
"comment_1": true,
"comment_2": true
},
"title": "Post 1"
},
"post_2": {
"published": 1395162147646,
"user": "tstirrat",
"body": "This is the second FireBlog post!",
"comments": {
"comment_3": true,
"comment_4": true
},
"title": "Post 2"
},
"post_3": {
"published": 1395162147646,
"user": "tstirrat",
"body": "This is the third FireBlog post!",
"comments": {
"comment_3": true,
"comment_4": true
},
"title": "Post 3"
}
},
},
"denormalized": {
"posts": {
"post_1": {
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/finding-records-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe("Integration: FirebaseAdapter - Finding Records", function() {
Ember.run(function() {
findAllPromise.then(function(payload) {
assert(Ember.isArray(payload));
assert.equal(payload.length, 2);
assert.equal(payload.length, 3);
done();
});
});
Expand Down
82 changes: 82 additions & 0 deletions tests/integration/queries-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Ember from 'ember';
import startApp from 'dummy/tests/helpers/start-app';
import { it } from 'ember-mocha';
import stubFirebase from 'dummy/tests/helpers/stub-firebase';
import unstubFirebase from 'dummy/tests/helpers/unstub-firebase';
import createTestRef from 'dummy/tests/helpers/create-test-ref';

describe('Integration: FirebaseAdapter - Queries', function() {
var app, store, adapter, findQueryArray, ref;

beforeEach(function(done) {
stubFirebase();

app = startApp();

ref = createTestRef('blogs/queries');

store = app.__container__.lookup('store:main');
adapter = store.adapterFor('application');
adapter._ref = ref;
adapter._queueFlushDelay = false;

var query = { limitToLast: 3 };

findQueryArray = store.recordArrayManager.createAdapterPopulatedRecordArray(store.modelFor('post'), query);

Ember.run(function () {
adapter.findQuery(store, store.modelFor('post'), query, findQueryArray)
.then(() => {
done();
});
});
});

afterEach(function() {
unstubFirebase();
Ember.run(app, 'destroy');
});

it('creates the correct Firebase reference', function() {
assert(ref.toString().match(/blogs\/queries$/g));
});

it('resolves with the correct initial payload', function() {
assert(Ember.isArray(findQueryArray));
assert.deepEqual(findQueryArray.get('content').mapBy('id'), ['post_1', 'post_2', 'post_3'], 'array should contain post_1, 2, 3');
});

describe('when an item is added to the resultset', function () {
it('populates the item in the array', function(done) {
Ember.run(() => {
ref.child('posts/post_4').set({ title: 'Post 4', body: 'Body', published: 1395162147646, user: 'tstirrat' }, function () {
assert(findQueryArray.get('content').isAny('id', 'post_4'), 'post_4 should exist in the array');
done();
});
});
});
});

describe('when an item is removed from the resultset', function () {
it('removes the item in the array', function(done) {
Ember.run(() => {
ref.child('posts/post_3').remove(function () {
assert(!findQueryArray.get('content').isAny('id', 'post_3'), 'post_3 should not exist in the array');
done();
});
});
});
});

describe('when a resultset changes size', function () {
it('alters the array size', function(done) {
Ember.run(() => {
ref.child('posts/post_3').remove(function () {
assert.deepEqual(findQueryArray.get('content').mapBy('id'), ['post_1', 'post_2']);
done();
});
});
});
});

});

0 comments on commit 95f9d9e

Please sign in to comment.