From 7743534eac6673c9dc942f0912d194e6cdc64215 Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Sun, 15 Apr 2012 22:19:43 +0100 Subject: [PATCH] [#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. --- index.html | 10 ++----- src/backend/base.js | 12 +++++++- src/backend/dataproxy.js | 3 +- src/backend/elasticsearch.js | 2 +- src/backend/gdocs.js | 2 +- src/backend/memory.js | 6 ++-- src/model.js | 45 ++++++++++++++++++++++++++++-- test/backend.elasticsearch.test.js | 3 +- test/backend.test.js | 8 ++++-- test/index.html | 2 +- test/model.test.js | 10 +++++++ 11 files changed, 80 insertions(+), 23 deletions(-) diff --git a/index.html b/index.html index 30c80bf8..db4c4b93 100644 --- a/index.html +++ b/index.html @@ -182,13 +182,9 @@ Backbone.history.start();

Creating a Dataset Explicitly with a Backend

-// 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({
diff --git a/src/backend/base.js b/src/backend/base.js
index ec4d8412..e9c4eea7 100644
--- a/src/backend/base.js
+++ b/src/backend/base.js
@@ -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
     //
diff --git a/src/backend/dataproxy.js b/src/backend/dataproxy.js
index 794b8e79..8f2b7496 100644
--- a/src/backend/dataproxy.js
+++ b/src/backend/dataproxy.js
@@ -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));
diff --git a/src/backend/elasticsearch.js b/src/backend/elasticsearch.js
index 6944a2f4..164393a8 100644
--- a/src/backend/elasticsearch.js
+++ b/src/backend/elasticsearch.js
@@ -20,6 +20,7 @@ this.recline.Backend = this.recline.Backend || {};
   //
   // 
http://localhost:9200/twitter/tweet
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)); diff --git a/src/backend/gdocs.js b/src/backend/gdocs.js index 8cf0407c..e6f29b55 100644 --- a/src/backend/gdocs.js +++ b/src/backend/gdocs.js @@ -17,6 +17,7 @@ this.recline.Backend = this.recline.Backend || {}; // ); //
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)); diff --git a/src/backend/memory.js b/src/backend/memory.js index 49f03087..f79991e8 100644 --- a/src/backend/memory.js +++ b/src/backend/memory.js @@ -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 ... // 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)); diff --git a/src/model.js b/src/model.js index 5f834cad..137244d7 100644 --- a/src/model.js +++ b/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 - + diff --git a/test/model.test.js b/test/model.test.js index 2625a7a4..2f0c4e6a 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -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