This repository has been archived by the owner on Aug 23, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat($goQuery): Experimental intergration of Query
$goQuery is a service built on `key.query()`. It accepts a key, room, filter and options. It returns a QueryModel. The Model has the standard `$sync` method which keeps the models value current with that of the query result set. ```js angular.module('ChatApp').controller('ChatCtrl', ['$scope', '$goQuery', function($scope, $query) { var filter = { “sender”: { “$eq”: “GrandMasterLivingston” }}; var sort = { “$name”: “desc”}; var limit = 20; var options = { sort: sort, limit: limit }; $scope.chat = $query('messages', filter, options); $scope.chat.$sync(); }]); ``` Related: #48
- Loading branch information
Matthew Creager
committed
Jan 28, 2014
1 parent
0c4a910
commit eb619b2
Showing
5 changed files
with
625 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* jshint browser:true */ | ||
/* global require, module */ | ||
|
||
/** | ||
* @fileOverview | ||
* | ||
* This file contains the Query Factory & Model, responsible for creating and | ||
* returning instances of the GoAngular Query Model. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var _ = require('lodash'); | ||
var Emitter = require('emitter'); | ||
var Args = require('./util/args'); | ||
|
||
var LOCAL_EVENTS = ['ready', 'error']; | ||
var $key; | ||
|
||
/** | ||
* queryFactory | ||
* @public | ||
* @param {Object} $sync - Responsible for synchronizing query model | ||
* @param {Object} $conn - GoInstant connection | ||
* @param {Function} $goKey - Uses to create GoAngular key model | ||
* @returns {Function} option validation & instance creation | ||
*/ | ||
module.exports = function queryFactory($sync, $conn, $goKey) { | ||
$key = $goKey; | ||
|
||
/** | ||
* @public | ||
* @param {Object} key - GoInstant key | ||
*/ | ||
return function() { | ||
var a = new Args([ | ||
{ key: Args.OBJECT | Args.STRING | Args.Required }, | ||
{ room: Args.STRING | Args.Optional }, | ||
{ filter: Args.OBJECT | Args.Required }, | ||
{ options: Args.OBJECT | Args.Required } | ||
], arguments); | ||
|
||
return new QueryModel($sync, $conn, a.key, a.room, a.filter, a.options); | ||
}; | ||
}; | ||
|
||
function QueryModel($querySync, $connection, key, room, filter, options) { | ||
_.bindAll(this); | ||
|
||
// If a key is provided, use it, otherwise create one | ||
key = (_.isObject(key) ? key : $connection.$key(key, room)); | ||
|
||
_.extend(this, { | ||
$$querySync: $querySync, | ||
$$connection: $connection, | ||
$$key: key, | ||
$$query: key.query(filter, options), | ||
$$emitter: new Emitter(), | ||
$$index: [] | ||
}); | ||
} | ||
|
||
/** | ||
* Primes our model, fetching the current result set and monitoring it for | ||
* changes. | ||
* @public | ||
*/ | ||
QueryModel.prototype.$sync = function() { | ||
var self = this; | ||
|
||
var connected = self.$$connection.$ready(); | ||
|
||
connected.then(function() { | ||
self.$$sync = self.$$querySync(self.$$query, self); | ||
self.$$sync.$initialize(); | ||
}); | ||
|
||
connected.fail(function(err) { | ||
self.$$emitter.emit('error', err); | ||
}); | ||
|
||
return self; | ||
}; | ||
|
||
/** | ||
* Add a generated id with a | ||
* @param {*} value - New value of key | ||
* @returns {Object} promise | ||
*/ | ||
QueryModel.prototype.$add = function(value, opts) { | ||
opts = opts || {}; | ||
|
||
var self = this; | ||
return this.$$connection.$ready().then(function() { | ||
return self.$$key.add(value, opts); | ||
}); | ||
}; | ||
|
||
/** | ||
* Give the current key a new value | ||
* @public | ||
* @param {*} value - New value of key | ||
* @returns {Object} promise | ||
*/ | ||
QueryModel.prototype.$set = function(value, opts) { | ||
opts = opts || {}; | ||
|
||
var self = this; | ||
return this.$$goConnection.$ready().then(function() { | ||
return self.$$key.set(value, opts); | ||
}); | ||
}; | ||
|
||
/** | ||
* Returns a new object that does not contain prefixed methods | ||
* @public | ||
* @returns {Object} model | ||
*/ | ||
QueryModel.prototype.$omit = function() { | ||
return _.omit(this, function(value, key){ | ||
return _.first(key) === '$'; | ||
}); | ||
}; | ||
|
||
/** | ||
* Create and return a new instance of Model, with a relative key. | ||
* @public | ||
* @param {String} keyName - Key name | ||
*/ | ||
QueryModel.prototype.$key = function(keyName) { | ||
var key = this.$$key.key(keyName); | ||
|
||
return $key(key); | ||
}; | ||
|
||
/** | ||
* Remove this key | ||
* @public | ||
* @returns {Object} promise | ||
*/ | ||
QueryModel.prototype.$remove = function(opts) { | ||
opts = opts || {}; | ||
|
||
var self = this; | ||
return this.$$connection.$ready().then(function() { | ||
return self.$$key.remove(opts); | ||
}); | ||
}; | ||
|
||
/** | ||
* Bind a listener to events on this key | ||
* @public | ||
*/ | ||
QueryModel.prototype.$on = function(eventName, listener) { | ||
if (!_.contains(LOCAL_EVENTS, eventName)) { | ||
return this.$$query.on(eventName, listener); | ||
} | ||
|
||
this.$$emitter.on(eventName, listener); | ||
}; | ||
|
||
/** | ||
* Remove a listener on this key | ||
* @public | ||
*/ | ||
QueryModel.prototype.$off = function(eventName, listener) { | ||
if (!_.contains(LOCAL_EVENTS, eventName)) { | ||
return this.$$query.off(eventName, listener); | ||
} | ||
|
||
this.$$emitter.off(eventName, listener); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* jshint browser:true */ | ||
/* global require, module */ | ||
|
||
/** | ||
* @fileOverview | ||
* | ||
* This file contains the QuerySync class, used to create a binding between | ||
* a query model on $scope and a GoInstant query | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var _ = require('lodash'); | ||
|
||
module.exports = function querySync($parse, $timeout) { | ||
|
||
/** | ||
* @public | ||
* @param {Object} key - GoInstant key | ||
*/ | ||
return function(query, qModel) { | ||
return new QuerySync($parse, $timeout, query, qModel); | ||
}; | ||
}; | ||
|
||
/** | ||
* The Sync class is responsible for synchronizing the state of a local model, | ||
* with that of a GoInstant key. | ||
* | ||
* @constructor | ||
* @param {Object} $parse - Angular parse object | ||
* @param {Object} $timeout - Angular timeout object | ||
* @param {Object} query - GoInstant query object | ||
* @param {Object} model - local object | ||
*/ | ||
function QuerySync($parse, $timeout, query, model) { | ||
_.bindAll(this, [ | ||
'$initialize', | ||
'$$handleUpdate', | ||
'$$handleRemove' | ||
]); | ||
|
||
_.extend(this, { | ||
$parse: $parse, | ||
$timeout: $timeout, | ||
$$query: query, | ||
$$model: model, | ||
$$registry: {} | ||
}); | ||
} | ||
|
||
/** | ||
* Creates an association between a local object and a query by | ||
* fetching a result set and monitoring the query. | ||
*/ | ||
QuerySync.prototype.$initialize = function() { | ||
var self = this; | ||
var index = self.$$model.$$index; | ||
|
||
self.$$query.execute(function(err, results) { | ||
if (err) { | ||
return self.$$model.$$emitter.emit('error', err); | ||
} | ||
|
||
self.$$registry = { | ||
update: self.$$handleUpdate, | ||
add: self.$$handleUpdate, | ||
remove: self.$$handleRemove | ||
}; | ||
|
||
_.each(self.$$registry, function(fn, event) { | ||
self.$$query.on(event, fn); | ||
}); | ||
|
||
self.$timeout(function() { | ||
_.map(results, function(result) { | ||
index.push(result.name); | ||
self.$$model[result.name] = result.value; | ||
}); | ||
|
||
self.$$model.$$emitter.emit('ready'); | ||
}); | ||
}); | ||
}; | ||
|
||
/** | ||
* When the query result set changes, update our local model object | ||
* @private | ||
* @param {*} result - new result set | ||
* @param {Object} context - Information related to the key being set | ||
*/ | ||
QuerySync.prototype.$$handleUpdate = function(result, context) { | ||
var self = this; | ||
|
||
var index = self.$$model.$$index; | ||
|
||
// Update the index array with the new position of this key | ||
var currentIndex = index.indexOf(result.name); | ||
|
||
if (currentIndex !== -1) { | ||
index.splice(currentIndex, 1); | ||
} | ||
|
||
index.splice(context.position.current, 0, result.name); | ||
|
||
// Update the value of the model. | ||
// The index update above will NOT trigger any changes on scope. | ||
// Removing this will cause position to not be updated. | ||
self.$timeout(function() { | ||
self.$$model[result.name] = result.value; | ||
}); | ||
}; | ||
|
||
/** | ||
* When an item is removed from the result set, update the model | ||
* @private | ||
* @param {*} result - new result set | ||
* @param {Object} context - information related to the key being set | ||
*/ | ||
QuerySync.prototype.$$handleRemove = function(result, context) { | ||
this.$$model.$$index.splice(context.position.previous, 1); | ||
|
||
var self = this; | ||
this.$timeout(function() { | ||
delete self.$$model[result.name]; | ||
}); | ||
}; |
Oops, something went wrong.