[#88,backend][s]: add __type__ attribute to all backends to identify them and provide a more robust and generic way to load backends from a string identifier such as that __type__ field.
* Also remove recline.Model.backends registry as can be replaced with this more generic solution. * This refactoring is necessitated by our need to serialize backend info for save/reload of a dataset and explorer state in #88.
This commit is contained in:
parent
002308f78f
commit
7743534eac
10
index.html
10
index.html
@ -182,13 +182,9 @@ Backbone.history.start();
|
||||
|
||||
<h4>Creating a Dataset Explicitly with a Backend</h4>
|
||||
<pre>
|
||||
// Backend can be an instance or string id for a backend in the
|
||||
// recline.Model.backends registry
|
||||
var backend = 'elasticsearch'
|
||||
// alternatively you can create explicitly
|
||||
// var backend = new recline.Backend.ElasticSearch();
|
||||
// or even from your own backend ...
|
||||
// var backend = new myModule.Backend();
|
||||
// Connect to ElasticSearch index/type as our data source
|
||||
// There are many other backends you can use (and you can write your own)
|
||||
var backend = new recline.Backend.ElasticSearch();
|
||||
|
||||
// Dataset is a Backbone model so the first hash become model attributes
|
||||
var dataset = recline.Model.Dataset({
|
||||
|
||||
@ -17,10 +17,20 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
// ## recline.Backend.Base
|
||||
//
|
||||
// Base class for backends providing a template and convenience functions.
|
||||
// You do not have to inherit from this class but even when not it does provide guidance on the functions you must implement.
|
||||
// You do not have to inherit from this class but even when not it does
|
||||
// provide guidance on the functions you must implement.
|
||||
//
|
||||
// Note also that while this (and other Backends) are implemented as Backbone models this is just a convenience.
|
||||
my.Base = Backbone.Model.extend({
|
||||
// ### __type__
|
||||
//
|
||||
// 'type' of this backend. This should be either the class path for this
|
||||
// object as a string (e.g. recline.Backend.Memory) or for Backends within
|
||||
// recline.Backend module it may be their class name.
|
||||
//
|
||||
// This value is used as an identifier for this backend when initializing
|
||||
// backends (see recline.Model.Dataset.initialize).
|
||||
__type__: 'base',
|
||||
|
||||
// ### sync
|
||||
//
|
||||
|
||||
@ -17,6 +17,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
//
|
||||
// Note that this is a **read-only** backend.
|
||||
my.DataProxy = my.Base.extend({
|
||||
__type__: 'dataproxy',
|
||||
defaults: {
|
||||
dataproxy_url: 'http://jsonpdataproxy.appspot.com'
|
||||
},
|
||||
@ -71,7 +72,5 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return dfd.promise();
|
||||
}
|
||||
});
|
||||
recline.Model.backends['dataproxy'] = new my.DataProxy();
|
||||
|
||||
|
||||
}(jQuery, this.recline.Backend));
|
||||
|
||||
@ -20,6 +20,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
//
|
||||
// <pre>http://localhost:9200/twitter/tweet</pre>
|
||||
my.ElasticSearch = my.Base.extend({
|
||||
__type__: 'elasticsearch',
|
||||
_getESUrl: function(dataset) {
|
||||
var out = dataset.get('elasticsearch_url');
|
||||
if (out) return out;
|
||||
@ -115,7 +116,6 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return dfd.promise();
|
||||
}
|
||||
});
|
||||
recline.Model.backends['elasticsearch'] = new my.ElasticSearch();
|
||||
|
||||
}(jQuery, this.recline.Backend));
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
// );
|
||||
// </pre>
|
||||
my.GDoc = my.Base.extend({
|
||||
__type__: 'gdoc',
|
||||
getUrl: function(dataset) {
|
||||
var url = dataset.get('url');
|
||||
if (url.indexOf('feeds/list') != -1) {
|
||||
@ -134,7 +135,6 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return results;
|
||||
}
|
||||
});
|
||||
recline.Model.backends['gdocs'] = new my.GDoc();
|
||||
|
||||
}(jQuery, this.recline.Backend));
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
if (!metadata.id) {
|
||||
metadata.id = String(Math.floor(Math.random() * 100000000) + 1);
|
||||
}
|
||||
var backend = recline.Model.backends['memory'];
|
||||
var backend = new recline.Backend.Memory();
|
||||
var datasetInfo = {
|
||||
documents: data,
|
||||
metadata: metadata
|
||||
@ -35,7 +35,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
}
|
||||
}
|
||||
backend.addDataset(datasetInfo);
|
||||
var dataset = new recline.Model.Dataset({id: metadata.id}, 'memory');
|
||||
var dataset = new recline.Model.Dataset({id: metadata.id}, backend);
|
||||
dataset.fetch();
|
||||
return dataset;
|
||||
};
|
||||
@ -70,6 +70,7 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
// etc ...
|
||||
// </pre>
|
||||
my.Memory = my.Base.extend({
|
||||
__type__: 'memory',
|
||||
initialize: function() {
|
||||
this.datasets = {};
|
||||
},
|
||||
@ -209,6 +210,5 @@ this.recline.Backend = this.recline.Backend || {};
|
||||
return facetResults;
|
||||
}
|
||||
});
|
||||
recline.Model.backends['memory'] = new my.Memory();
|
||||
|
||||
}(jQuery, this.recline.Backend));
|
||||
|
||||
45
src/model.js
45
src/model.js
@ -18,7 +18,7 @@ this.recline.Model = this.recline.Model || {};
|
||||
//
|
||||
// @property {number} docCount: total number of documents in this dataset
|
||||
//
|
||||
// @property {Backend} backend: the Backend (instance) for this Dataset
|
||||
// @property {Backend} backend: the Backend (instance) for this Dataset.
|
||||
//
|
||||
// @property {Query} queryState: `Query` object which stores current
|
||||
// queryState. queryState may be edited by other components (e.g. a query
|
||||
@ -28,14 +28,24 @@ this.recline.Model = this.recline.Model || {};
|
||||
// Facets.
|
||||
my.Dataset = Backbone.Model.extend({
|
||||
__type__: 'Dataset',
|
||||
|
||||
// ### initialize
|
||||
//
|
||||
// Sets up instance properties (see above)
|
||||
//
|
||||
// @param {Object} model: standard set of model attributes passed to Backbone models
|
||||
//
|
||||
// @param {Object or String} backend: Backend instance (see
|
||||
// `recline.Backend.Base`) or a string specifying that instance. The
|
||||
// string specifying may be a full class path e.g.
|
||||
// 'recline.Backend.ElasticSearch' or a simple name e.g.
|
||||
// 'elasticsearch' or 'ElasticSearch' (in this case must be a Backend in
|
||||
// recline.Backend module)
|
||||
initialize: function(model, backend) {
|
||||
_.bindAll(this, 'query');
|
||||
this.backend = backend;
|
||||
if (backend && backend.constructor == String) {
|
||||
this.backend = my.backends[backend];
|
||||
if (typeof(backend) === 'string') {
|
||||
this.backend = this._backendFromString(backend);
|
||||
}
|
||||
this.fields = new my.FieldList();
|
||||
this.currentDocuments = new my.DocumentList();
|
||||
@ -99,6 +109,35 @@ my.Dataset = Backbone.Model.extend({
|
||||
data.docCount = this.docCount;
|
||||
data.fields = this.fields.toJSON();
|
||||
return data;
|
||||
},
|
||||
|
||||
// ### _backendFromString(backendString)
|
||||
//
|
||||
// See backend argument to initialize for details
|
||||
_backendFromString: function(backendString) {
|
||||
var parts = backendString.split('.');
|
||||
// walk through the specified path xxx.yyy.zzz to get the final object which should be backend class
|
||||
var current = window;
|
||||
for(ii=0;ii<parts.length;ii++) {
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
current = current[parts[ii]];
|
||||
}
|
||||
if (current) {
|
||||
return new current();
|
||||
}
|
||||
|
||||
// alternatively we just had a simple string
|
||||
var backend = null;
|
||||
if (recline && recline.Backend) {
|
||||
_.each(_.keys(recline.Backend), function(name) {
|
||||
if (name.toLowerCase() === backendString.toLowerCase()) {
|
||||
backend = new recline.Backend[name]();
|
||||
}
|
||||
});
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -108,10 +108,11 @@ var sample_data = {
|
||||
};
|
||||
|
||||
test("ElasticSearch", function() {
|
||||
var backend = new recline.Backend.ElasticSearch();
|
||||
var dataset = new recline.Model.Dataset({
|
||||
url: 'https://localhost:9200/my-es-db/my-es-type'
|
||||
},
|
||||
'elasticsearch'
|
||||
backend
|
||||
);
|
||||
|
||||
var stub = sinon.stub($, 'ajax', function(options) {
|
||||
|
||||
@ -217,10 +217,11 @@ var dataProxyData = {
|
||||
test('DataProxy Backend', function() {
|
||||
// needed only if not stubbing
|
||||
// stop();
|
||||
var backend = new recline.Backend.DataProxy();
|
||||
var dataset = new recline.Model.Dataset({
|
||||
url: 'http://webstore.thedatahub.org/rufuspollock/gold_prices/data.csv'
|
||||
},
|
||||
'dataproxy'
|
||||
backend
|
||||
);
|
||||
|
||||
var stub = sinon.stub($, 'ajax', function(options) {
|
||||
@ -419,10 +420,11 @@ var sample_gdocs_spreadsheet_data = {
|
||||
}
|
||||
|
||||
test("GDoc Backend", function() {
|
||||
var backend = new recline.Backend.GDoc();
|
||||
var dataset = new recline.Model.Dataset({
|
||||
url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json'
|
||||
},
|
||||
'gdocs'
|
||||
backend
|
||||
);
|
||||
|
||||
var stub = sinon.stub($, 'getJSON', function(options, cb) {
|
||||
@ -450,7 +452,7 @@ test("GDoc Backend.getUrl", function() {
|
||||
var dataset = new recline.Model.Dataset({
|
||||
url: 'https://docs.google.com/spreadsheet/ccc?key=' + key + '#gid=0'
|
||||
});
|
||||
var backend = recline.Model.backends['gdocs'];
|
||||
var backend = new recline.Backend.GDoc();
|
||||
var out = backend.getUrl(dataset);
|
||||
var exp = 'https://spreadsheets.google.com/feeds/list/' + key + '/1/public/values?alt=json'
|
||||
equal(exp, out);
|
||||
|
||||
@ -19,13 +19,13 @@
|
||||
<script type="text/javascript" src="base.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/model.js"></script>
|
||||
<script type="text/javascript" src="model.test.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/base.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/memory.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/dataproxy.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/gdocs.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/elasticsearch.js"></script>
|
||||
<script type="text/javascript" src="../src/backend/localcsv.js"></script>
|
||||
<script type="text/javascript" src="model.test.js"></script>
|
||||
<script type="text/javascript" src="backend.test.js"></script>
|
||||
<script type="text/javascript" src="backend.elasticsearch.test.js"></script>
|
||||
<script type="text/javascript" src="backend.localcsv.test.js"></script>
|
||||
|
||||
@ -103,6 +103,16 @@ test('Dataset _prepareQuery', function () {
|
||||
deepEqual(out, exp);
|
||||
});
|
||||
|
||||
test('Dataset _backendFromString', function () {
|
||||
var dataset = new recline.Model.Dataset();
|
||||
|
||||
var out = dataset._backendFromString('recline.Backend.Memory');
|
||||
equal(out.__type__, 'memory');
|
||||
|
||||
var out = dataset._backendFromString('dataproxy');
|
||||
equal(out.__type__, 'dataproxy');
|
||||
});
|
||||
|
||||
|
||||
// =================================
|
||||
// Query
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user