Skip to content

Instantly share code, notes, and snippets.

@sfoster
Created October 13, 2010 16:11
Show Gist options
  • Save sfoster/624360 to your computer and use it in GitHub Desktop.
Save sfoster/624360 to your computer and use it in GitHub Desktop.
dojo.provide("myns.data.FeedStore");
dojo.require("dojox.data.ServiceStore");
dojo.require("dojox.rpc.Service");
dojo.declare("myns.data.FeedStore", dojox.data.ServiceStore, {
// summary:
// Flexible, extensible store for normalizing read-only JSON feed data
service: null,
// baseParams: Object
// Any properties which should always be included with store queries
baseParams: null,
feedId: "",
// idAttribute: String?
// the item property which is a unique identifier for each item
idAttribute: "id",
// itemsProperty: String
// the property in the result object which contains all the result items
itemsProperty: "items",
// totalProperty: String?
// the property in the result object which indicates the total number of items,
// used when result is a page or slice of a large dataset
totalProperty: 'totalCount',
// labelAttribute: String?
// the item property which should be used when an item label is called for
labelAttribute: "title",
// itemPropertyMapping: Object
// A key:dotpath mapping used by getValue to associate a item attribute with the values' location in the item data structure
itemPropertyMapping: {},
// estimateCountFactor: Number
// This parameter is used by the ServiceStore to estimate the total count. See dojox.data.ServiceStore for more details
estimateCountFactor: 2,
// knownTotal: Boolean
// Flag that indicates if the total number of records is known (e.g. by the service)
knownTotal: false,
constructor: function() {
this.baseParams = { feedId: this.feedId };
// look to a static defaultService property if no service param was provided
if(!this.service) {
var service = dojo.getObject(this.declaredClass + ".defaultService");
if(typeof service == "function") {
this.service = service;
}
}
},
fetch: function(/* object */ request){
// mix in some of the request properties into our query
// TODO: is there a mechanism for this in ServiceStore?
var query = {};
if("count" in request) {
query.count = request.count;
}
if("start" in request) {
query.start = request.start;
}
request.query = dojo.delegate(query, request.query || {});
return this.inherited(arguments);
},
preprocessResult: function(result) {
// summary:
// Optionally manipulate service response before passing on to fetch handlers
// default is to pass thru
return result;
},
_processResults : function(results, deferred, depth){
// summary:
// Override ServiceStore's _processResults to pick up resultCount from results
// and to make recursion into item properties conditional
// this should return an object with the items as an array and the total count of
// items (maybe more than currently in the result set).
// for example:
// | {totalCount:10, items: [{id:1},{id:2}]}
depth = depth || 0;
// index the results, assigning ids as necessary
if(results && typeof results == 'object'){
var id = results.__id;
if(!id){// if it hasn't been assigned yet
if(this.idAttribute){
// use the defined id if available
id = results[this.idAttribute];
}else{
id = this._currentId++;
}
if(id !== undefined){
var existingObj = this._index[id];
if(existingObj){
for(var j in existingObj){
delete existingObj[j]; // clear it so we can mixin
}
results = dojo.mixin(existingObj,results);
}
results.__id = id;
this._index[id] = results;
}
}
if(this.isHeirarchical) {
// console.log("_processResults recursing into heirarchical result data");
// only recurse if necessary
for(var i in results){
results[i] = this._processResults(results[i], deferred, depth+1).items;
}
}
var count = results[this.itemsProperty].length;
}
var response = {
totalCount: (this.totalProperty in results) ?
// use the provided total when there is one
results[this.totalProperty] :
// does this look like a paged result?
deferred.request.count == count ?
// use a guestimated count as we don't know the total
(deferred.request.start || 0) + count * this.estimateCountFactor
:
// ..or just use the item count
count,
items: depth? results : results[this.itemsProperty]
};
// set a flag to indicate if the total number of results (of
// which this result may be a subset) is known
// this heuristic will be mostly correct most times,
// but to be sure, set a totalProperty in the response
console.log("_processResults for: ", deferred.request.query.feedId);
this.knownTotal = (this.totalProperty in results) || (deferred.request.count && deferred.request.count !== count);
console.log("setting knownTotal: ", this.knownTotal, (this.totalProperty in results), count);
return response;
},
_doQuery: function(args){
var query= typeof args.queryStr == 'string' ? args.queryStr : args.query;
var serviceDfd = this.service(query);
var dfd = new dojo.Deferred();
serviceDfd.addErrback(function(){
dfd.errback.apply(dfd, arguments);
});
serviceDfd.addCallback(this, function(result){
arguments[0] = this.preprocessResult(result);
dfd.callback.apply(dfd, arguments);
});
return dfd;
},
getValue: function(/*Object*/ item, /*String*/property, /*value?*/defaultValue){
// summary:
// Gets the value of an item's 'property'
//
// item:
// The item to get the value from
// property:
// property to look up value for
// defaultValue:
// the default value
var value = void(0), // undefined
attrData = this.itemPropertyMapping,
dotpath = property in attrData ? attrData[property].mapping : null;
if(dotpath) {
// use the mapping to lookup the property value in the item
value = dojo.getObject(dotpath, false, item);
} else {
// fallback to a plain property look up
value = item[property];
}
if(attrData.format) {
value = attrData.format(value, property, attrData);
}
// TODO: lazy-loaded value access not tested
return typeof value !== "undefined" ?
value : // return the plain value since it was found;
(item._loadObject ? // property was not found, maybe because the item is not loaded, we will try to load it synchronously so we can get the property
(dojox.rpc._sync = true) && arguments.callee.call(this,dojox.data.ServiceStore.prototype.loadItem({item:item}) || {}, property, defaultValue) : // load the item and run getValue again
defaultValue);// not in item -> return default value
}
});
myns.data.FeedStore.defaultService = null;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment