diff --git a/docs/backend.html b/docs/backend.html index 26bd5b61..e49f5bbe 100644 --- a/docs/backend.html +++ b/docs/backend.html @@ -7,106 +7,132 @@ convenience (they do not save or load themselves from any remote source)
this.recline = this.recline || {};
this.recline.Model = this.recline.Model || {};
-(function($, my) {
- my.backends = {};
+(function($, my) {Override Backbone.sync to hand off to sync function in relevant backend
Backbone.sync = function(method, model, options) {
+ return model.backend.sync(method, model, options);
+ }To use you should:
+Crude way to catch backend errors +Many of backends use JSONP and so will not get error messages and this is +a crude way to catch those errors.
function wrapInTimeout(ourFunction) {
+ var dfd = $.Deferred();
+ var timeout = 5000;
+ var timer = setTimeout(function() {
+ dfd.reject({
+ message: 'Request Error: Backend did not respond after ' + (timeout / 1000) + ' seconds'
+ });
+ }, timeout);
+ ourFunction.done(function(arguments) {
+ clearTimeout(timer);
+ dfd.resolve(arguments);
+ })
+ .fail(function(arguments) {
+ clearTimeout(timer);
+ dfd.reject(arguments);
+ })
+ ;
+ return dfd.promise();
+ }A. provide metadata as model data to the Dataset
+This is very artificial and is really only designed for testing +purposes.
-B. Set backendConfig on your dataset with attributes:
+To use it you should provide in your constructor data:
data: hash with 2 keys:
+documents: list of hashes, each hash being one doc. A doc must have an id attribute which is unique.
-Example of data:
+Example:
- {
- headers: ['x', 'y', 'z']
- , rows: [
- {id: 0, x: 1, y: 2, z: 3}
- , {id: 1, x: 2, y: 4, z: 6}
- ]
- };
+// Backend setup
+var backend = Backend();
+backend.addDataset({
+metadata: {
+ id: 'my-id',
+ title: 'My Title',
+ headers: ['x', 'y', 'z'],
+},
+documents: [
+ {id: 0, x: 1, y: 2, z: 3},
+ {id: 1, x: 2, y: 4, z: 6}
+ ]
+});
+// later ...
+var dataset = Dataset({id: 'my-id'});
+dataset.fetch();
+etc ...
my.BackendMemory = Backbone.Model.extend({
- sync: function(method, model, options) {
- var self = this;
- if (method === "read") {
- var dfd = $.Deferred();
- if (model.__type__ == 'Dataset') {
- var dataset = model;
- dataset.set({
- headers: dataset.backendConfig.data.headers
- });
- dataset.docCount = dataset.backendConfig.data.rows.length;
- dfd.resolve(dataset);
- }
- return dfd.promise();
- } else if (method === 'update') {
- var dfd = $.Deferred();
- if (model.__type__ == 'Document') {
- _.each(model.backendConfig.data.rows, function(row, idx) {
- if(row.id === model.id) {
- model.backendConfig.data.rows[idx] = model.toJSON();
- }
- });
- dfd.resolve(model);
- }
- return dfd.promise();
- } else if (method === 'delete') {
- var dfd = $.Deferred();
- if (model.__type__ == 'Document') {
- model.backendConfig.data.rows = _.reject(model.backendConfig.data.rows, function(row) {
- return (row.id === model.id);
- });
- dfd.resolve(model);
- }
- return dfd.promise();
- } else {
- alert('Not supported: sync on BackendMemory with method ' + method + ' and model ' + model);
- }
- },
- getDocuments: function(model, numRows, start) {
- if (start === undefined) {
- start = 0;
- }
- if (numRows === undefined) {
- numRows = 10;
- }
+ initialize: function() {
+ this.datasets = {};
+ },
+ addDataset: function(data) {
+ this.datasets[data.metadata.id] = $.extend(true, {}, data);
+ },
+ sync: function(method, model, options) {
+ var self = this;
+ if (method === "read") {
var dfd = $.Deferred();
- rows = model.backendConfig.data.rows;
- var results = rows.slice(start, start+numRows);
- dfd.resolve(results);
+ if (model.__type__ == 'Dataset') {
+ var rawDataset = this.datasets[model.id];
+ model.set(rawDataset.metadata);
+ model.docCount = rawDataset.documents.length;
+ dfd.resolve(model);
+ }
return dfd.promise();
+ } else if (method === 'update') {
+ var dfd = $.Deferred();
+ if (model.__type__ == 'Document') {
+ _.each(self.datasets[model.dataset.id].documents, function(doc, idx) {
+ if(doc.id === model.id) {
+ self.datasets[model.dataset.id].documents[idx] = model.toJSON();
+ }
+ });
+ dfd.resolve(model);
+ }
+ return dfd.promise();
+ } else if (method === 'delete') {
+ var dfd = $.Deferred();
+ if (model.__type__ == 'Document') {
+ var rawDataset = self.datasets[model.dataset.id];
+ var newdocs = _.reject(rawDataset.documents, function(doc) {
+ return (doc.id === model.id);
+ });
+ rawDataset.documents = newdocs;
+ dfd.resolve(model);
+ }
+ return dfd.promise();
+ } else {
+ alert('Not supported: sync on BackendMemory with method ' + method + ' and model ' + model);
}
+ },
+ query: function(model, queryObj) {
+ var numRows = queryObj.size;
+ var start = queryObj.offset;
+ var dfd = $.Deferred();
+ results = this.datasets[model.id].documents;not complete sorting!
_.each(queryObj.sort, function(item) {
+ results = _.sortBy(results, function(doc) {
+ var _out = doc[item[0]];
+ return (item[1] == 'asc') ? _out : -1*_out;
+ });
+ });
+ var results = results.slice(start, start+numRows);
+ dfd.resolve(results);
+ return dfd.promise();
+ }
});
- my.backends['memory'] = new my.BackendMemory();Connecting to Webstores
-To use this backend set backendConfig on your Dataset as:
- -
-{
- 'type': 'webstore',
- 'url': url to relevant Webstore table
-}
- my.BackendWebstore = Backbone.Model.extend({
+To use this backend ensure your Dataset has a webstore_url in its attributes.
my.BackendWebstore = Backbone.Model.extend({
sync: function(method, model, options) {
if (method === "read") {
if (model.__type__ == 'Dataset') {
- var dataset = model;
- var base = dataset.backendConfig.url;
+ var base = model.get('webstore_url');
var schemaUrl = base + '/schema.json';
var jqxhr = $.ajax({
url: schemaUrl,
@@ -114,72 +140,72 @@ source) For connecting to DataProxy-s.
-Set a Dataset to use this backend:
- -dataset.backendConfig = {
- // required
- url: {url-of-data-to-proxy},
- format: csv | xls,
-}
-
-
When initializing the DataProxy backend you can set the following attributes:
Datasets using using this backend should set the following attributes:
+ +Note that this is a read-only backend.
my.BackendDataProxy = Backbone.Model.extend({
defaults: {
- dataproxy: 'http://jsonpdataproxy.appspot.com'
+ dataproxy_url: 'http://jsonpdataproxy.appspot.com'
},
sync: function(method, model, options) {
+ var self = this;
if (method === "read") {
if (model.__type__ == 'Dataset') {
- var dataset = model;
- var base = my.backends['dataproxy'].get('dataproxy');TODO: should we cache for extra efficiency
var data = {
- url: dataset.backendConfig.url
+ var base = self.get('dataproxy_url');TODO: should we cache for extra efficiency
var data = {
+ url: model.get('url')
, 'max-results': 1
- , type: dataset.backendConfig.format
+ , type: model.get('format') || 'csv'
};
var jqxhr = $.ajax({
url: base
@@ -187,11 +213,14 @@ source) Connect to Google Docs spreadsheet. For write operations
my.BackendGDoc = Backbone.Model.extend({
+Connect to Google Docs spreadsheet.
+
+Dataset must have a url attribute pointing to the Gdocs
+spreadsheet's JSON feed e.g.
+
+
+var dataset = new recline.Model.Dataset({
+ url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json'
+ },
+ 'gdocs'
+);
+ my.BackendGDoc = Backbone.Model.extend({
sync: function(method, model, options) {
+ var self = this;
if (method === "read") {
var dfd = $.Deferred();
var dataset = model;
- $.getJSON(model.backendConfig.url, function(d) {
- result = my.backends['gdocs'].gdocsToJavascript(d);
- model.set({'headers': result.header});cache data onto dataset (we have loaded whole gdoc it seems!)
model._dataCache = result.data;
+ $.getJSON(model.get('url'), function(d) {
+ result = self.gdocsToJavascript(d);
+ model.set({'headers': result.header});cache data onto dataset (we have loaded whole gdoc it seems!)
model._dataCache = result.data;
dfd.resolve(model);
})
return dfd.promise(); }
},
- getDocuments: function(dataset, start, numRows) {
+ query: function(dataset, queryObj) {
var dfd = $.Deferred();
- var fields = dataset.get('headers');zip the field headers with the data rows to produce js objs + var fields = dataset.get('headers');
zip the field headers with the data rows to produce js objs TODO: factor this out as a common method with other backends
var objs = _.map(dataset._dataCache, function (d) {
var obj = {};
_.each(_.zip(fields, d), function (x) { obj[x[0]] = x[1]; })
@@ -274,11 +309,11 @@ TODO: factor this out as a common method with other backends var results = {
'header': [],
'data': []
- };default is no special info on type of columns
var colTypes = {};
+ };default is no special info on type of columns
var colTypes = {};
if (options.colTypes) {
colTypes = options.colTypes;
- }either extract column headings from spreadsheet directly, or used supplied ones
if (options.columnsToUse) {columns set to subset supplied
results.header = options.columnsToUse;
- } else {set columns to use to be all available
if (gdocsSpreadsheet.feed.entry.length > 0) {
+ }either extract column headings from spreadsheet directly, or used supplied ones
if (options.columnsToUse) {columns set to subset supplied
results.header = options.columnsToUse;
+ } else {set columns to use to be all available
if (gdocsSpreadsheet.feed.entry.length > 0) {
for (var k in gdocsSpreadsheet.feed.entry[0]) {
if (k.substr(0, 3) == 'gsx') {
var col = k.substr(4)
@@ -286,13 +321,13 @@ TODO: factor this out as a common method with other backends }
}
}
- }converts non numberical values that should be numerical (22.3%[string] -> 0.223[float])
var rep = /^([\d\.\-]+)\%$/;
+ }converts non numberical values that should be numerical (22.3%[string] -> 0.223[float])
var rep = /^([\d\.\-]+)\%$/;
$.each(gdocsSpreadsheet.feed.entry, function (i, entry) {
var row = [];
for (var k in results.header) {
var col = results.header[k];
var _keyname = 'gsx$' + col;
- var value = entry[_keyname]['$t'];if labelled as % and value contains %, convert
if (colTypes[col] == 'percent') {
+ var value = entry[_keyname]['$t'];if labelled as % and value contains %, convert
if (colTypes[col] == 'percent') {
if (rep.test(value)) {
var value2 = rep.exec(value);
var value3 = parseFloat(value2);
diff --git a/docs/model.html b/docs/model.html
index 74e34674..f6fee7df 100644
--- a/docs/model.html
+++ b/docs/model.html
@@ -10,11 +10,17 @@
my.Dataset = Backbone.Model.extend({
__type__: 'Dataset',
- initialize: function(options) {
+ initialize: function(model, backend) {
+ this.backend = backend;
+ if (backend && backend.constructor == String) {
+ this.backend = my.backends[backend];
+ }
this.currentDocuments = new my.DocumentList();
this.docCount = null;
- this.backend = null;
- },this.queryState = {};
},AJAX method with promise API to get rows (documents) from the backend.
@@ -25,19 +31,23 @@ also returned. :param start: passed onto backend getDocuments.this does not fit very well with Backbone setup. Backbone really expects you to know the ids of objects your are fetching (which you do in classic RESTful ajax-y world). But this paradigm does not fill well with data set up we have here. -This also illustrates the limitations of separating the Dataset and the Backend
getDocuments: function(numRows, start) {
+This also illustrates the limitations of separating the Dataset and the Backend query: function(queryObj) {
var self = this;
- var backend = my.backends[this.backendConfig.type];
+ this.queryState = queryObj || this.defaultQuery;
+ this.queryState = _.extend({size: 100, offset: 0}, this.queryState);
var dfd = $.Deferred();
- backend.getDocuments(this, numRows, start).then(function(rows) {
+ this.backend.query(this, this.queryState).done(function(rows) {
var docs = _.map(rows, function(row) {
var _doc = new my.Document(row);
- _doc.backendConfig = self.backendConfig;
- _doc.backend = backend;
+ _doc.backend = self.backend;
+ _doc.dataset = self;
return _doc;
});
self.currentDocuments.reset(docs);
dfd.resolve(self.currentDocuments);
+ })
+ .fail(function(arguments) {
+ dfd.reject(arguments);
});
return dfd.promise();
},
@@ -47,14 +57,17 @@ This also illustrates the limitations of separating the Dataset and the Backend<
data.docCount = this.docCount;
return data;
}
- });A single entry or row in the dataset
my.Document = Backbone.Model.extend({
__type__: 'Document'
- }); my.DocumentList = Backbone.Collection.extend({
+ }); my.DocumentList = Backbone.Collection.extend({
__type__: 'DocumentList',
model: my.Document
- });
+ });Backends will register themselves by id into this registry
my.backends = {};
+
}(jQuery, this.recline.Model));