You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2013/05/15 10:29:07 UTC

[07/13] git commit: updated refs/heads/master to 0cd82b9

Experimental Route Objects to reduce full page rendering


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/61419957
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/61419957
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/61419957

Branch: refs/heads/master
Commit: 61419957cfb00dbf83f49458ed611bc75d2611fb
Parents: 3fd0931
Author: Russell Branca <ch...@gmail.com>
Authored: Thu Apr 4 16:18:47 2013 -0700
Committer: Garren Smith <ga...@gmail.com>
Committed: Wed May 15 10:26:26 2013 +0200

----------------------------------------------------------------------
 src/fauxton/app/api.js                             |  128 +++++++++
 src/fauxton/app/modules/databases/routes.js        |   68 +++++-
 src/fauxton/app/modules/databases/views.js         |    4 +
 src/fauxton/app/modules/documents/resources.js     |    4 +-
 src/fauxton/app/modules/documents/routes.js        |  202 +++++----------
 src/fauxton/app/router.js                          |   11 +
 .../templates/documents/doc_field_editor_tabs.html |    2 +-
 7 files changed, 272 insertions(+), 147 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/api.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/api.js b/src/fauxton/app/api.js
index 52e8611..2c68086 100644
--- a/src/fauxton/app/api.js
+++ b/src/fauxton/app/api.js
@@ -96,6 +96,134 @@ function(app, Fauxton) {
     }
   });
 
+  FauxtonAPI.RouteObject = function(options) {
+    this._options = options;
+
+    this._configure(options || {});
+    this.initialize.apply(this, arguments);
+    this.addEvents();
+  };
+
+  // Piggy-back on Backbone's self-propagating extend function
+  FauxtonAPI.RouteObject.extend = Backbone.Model.extend;
+
+  var routeObjectOptions = ["views", "routes", "events", "data", "crumbs", "layout", "apiUrl", "establish"];
+
+  _.extend(FauxtonAPI.RouteObject.prototype, Backbone.Events, {
+    // Should these be default vals or empty funcs?
+    views: {},
+    routes: {},
+    events: {},
+    data: {},
+    crumbs: [],
+    layout: "with_sidebar",
+    apiUrl: null,
+    renderedState: false,
+    currTab: "databases",
+    establish: function() {},
+    route: function() {},
+    initialize: function() {}
+  }, {
+    // By default, rerender is a full rerender
+    rerender: function() {
+      this.renderWith.apply(this, arguments);
+    },
+
+    // TODO:: combine this and the renderWith function
+    // All the things should go through establish, as it will resolve
+    // immediately if its already done, but this way the RouteObject.route
+    // function can rebuild the deferred as needed
+    render: function(route, masterLayout, args) {
+      this.route.call(this, route, args);
+
+      if (this.renderedState === true) {
+        this.rerender.apply(this, arguments);
+      } else {
+        this.renderWith.apply(this, arguments);
+      }
+    },
+
+    renderWith: function(route, masterLayout, args) {
+      var routeObject = this;
+      //this.route.apply(this, args);
+
+      masterLayout.setTemplate(this.layout);
+      masterLayout.clearBreadcrumbs();
+
+      if (this.crumbs.length) {
+        masterLayout.setBreadcrumbs(new Fauxton.Breadcrumbs({
+          crumbs: this.crumbs
+        }));
+      }
+
+      $.when.apply(this, this.establish()).done(function(resp) {
+        _.each(routeObject.views, function(view, selector) {
+          masterLayout.setView(selector, view);
+
+          $.when.apply(null, view.establish()).then(function(resp) {
+            masterLayout.renderView(selector);
+          }, function(resp) {
+            view.establishError = {
+              error: true,
+              reason: resp
+            };
+            masterLayout.renderView(selector);
+          });
+
+          var hooks = masterLayout.hooks[selector];
+
+          if(hooks){
+            _.each(hooks, function(hook){
+              if (_.any(hook.routes, function(route){return route == boundRoute;})){
+                hook.callback(view);
+              }
+            });
+          }
+        });
+      });
+
+      if (this.get('apiUrl')) masterLayout.apiBar.update(this.get('apiUrl'));
+
+      // Track that we've done a full initial render
+      this.renderedState = true;
+    },
+
+    get: function(key) {
+      return _.isFunction(this[key]) ? this[key]() : this[key];
+    },
+
+    addEvents: function(events) {
+      events = events || this.get('events');
+      _.each(events, function(method, event) {
+        if (!_.isFunction(method) && !_.isFunction(this[method])) {
+          throw new Error("Invalid method: "+method);
+        }
+        method = _.isFunction(method) ? method : this[method];
+
+        this.on(event, method);
+      }, this);
+    },
+
+    _configure: function(options) {
+      _.each(_.intersection(_.keys(options), routeObjectOptions), function(key) {
+        this[key] = options[key];
+      }, this);
+    },
+
+    getView: function(selector) {
+      return this.views[selector];
+    },
+
+    setView: function(selector, view) {
+      this.views[selector] = view;
+      return view;
+    },
+
+    getViews: function() {
+      return this.views;
+    }
+  });
+
   app.fauxtonAPI = FauxtonAPI;
   return app.fauxtonAPI;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/modules/databases/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/routes.js b/src/fauxton/app/modules/databases/routes.js
index 2ba59d7..b7f1dbb 100644
--- a/src/fauxton/app/modules/databases/routes.js
+++ b/src/fauxton/app/modules/databases/routes.js
@@ -16,10 +16,70 @@ define([
   "api",
 
   // Modules
-  "modules/databases/resources"
+  "modules/databases/resources",
+  // TODO:: fix the include flow modules so we don't have to require views here
+  "modules/databases/views"
 ],
 
-function(app, FauxtonAPI, Databases) {
+function(app, FauxtonAPI, Databases, Views) {
+  var AllDbsRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "with_sidebar",
+
+    crumbs: [
+      {"name": "Databases", "link": "/_all_dbs"}
+    ],
+
+    routes: ["", "index.html", "_all_dbs(:params)"],
+
+    apiUrl: function() {
+      return this.databases.url();
+    },
+
+    initialize: function() {
+      this.databases = new Databases.List();
+      this.deferred = FauxtonAPI.Deferred();
+
+      this.databasesView = this.setView("#dashboard-content", new Views.List({
+          collection: this.databases
+      }));
+      this.sidebarView = this.setView("#sidebar-content", new Views.Sidebar({
+          collection: this.databases
+      }));
+    },
+
+    route: function() {
+      var params = app.getParams();
+      this.databasesView.setPage(params.page);
+    },
+
+    rerender: function() {
+      this.databasesView.render();
+    },
+
+    establish: function() {
+      var databases = this.databases;
+      var deferred = this.deferred;
+
+      databases.fetch().done(function(resp) {
+        $.when.apply(null, databases.map(function(database) {
+          return database.status.fetch();
+        })).done(function(resp) {
+          deferred.resolve();
+        });
+      });
+
+      return [deferred];
+    },
+
+    mrEvent: function() {
+      console.log("Triggering a most excellent event!!!!");
+    },
+
+    events: {
+      "myrandom_event": "mrEvent"
+    }
+  });
+
   var allDbsCallback = function() {
     var data = {
       databases: new Databases.List()
@@ -60,11 +120,15 @@ function(app, FauxtonAPI, Databases) {
     };
   };
 
+  /*
   Databases.Routes = {
     "": allDbsCallback,
     "index.html": allDbsCallback,
     "_all_dbs(:params)": allDbsCallback
   };
+  */
+
+  Databases.RouteObjects = [new AllDbsRouteObject()];
 
   return Databases;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/modules/databases/views.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/databases/views.js b/src/fauxton/app/modules/databases/views.js
index 1a02979..6b9cfea 100644
--- a/src/fauxton/app/modules/databases/views.js
+++ b/src/fauxton/app/modules/databases/views.js
@@ -87,6 +87,10 @@ function(app, Fauxton, FauxtonAPI) {
       }));
     },
 
+    setPage: function(page) {
+      this.page = page || 1;
+    },
+
     afterRender: function() {
       var dbLimit = this.dbLimit;
       var ajaxReq;

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/modules/documents/resources.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/resources.js b/src/fauxton/app/modules/documents/resources.js
index 3f07238..f1a07c3 100644
--- a/src/fauxton/app/modules/documents/resources.js
+++ b/src/fauxton/app/modules/documents/resources.js
@@ -35,9 +35,11 @@ function(app, FauxtonAPI, Views) {
       }
     },
 
-    initialize: function() {
+    initialize: function(_attrs, options) {
       if (this.collection && this.collection.database) {
         this.database = this.collection.database;
+      } else if (options.database) {
+        this.database = options.database;
       }
     },
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/modules/documents/routes.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/modules/documents/routes.js b/src/fauxton/app/modules/documents/routes.js
index 34b00d8..ffed186 100644
--- a/src/fauxton/app/modules/documents/routes.js
+++ b/src/fauxton/app/modules/documents/routes.js
@@ -25,51 +25,69 @@ function(app, FauxtonAPI, Documents, Databases) {
   // var Documents = require("modules/documents/models_collections");
   // var Databases = require("modules/databases/module");
 
-  var codeEditorCallback = function(databaseName, docID) {
-    var data = {
-      database: new Databases.Model({id:databaseName}),
-      doc: new Documents.Doc({
-        "_id": docID
-      }),
-      selected: "code_editor"
-    };
-    data.doc.database = data.database;
-    data.designDocs = new Documents.AllDocs(null, {
-      database: data.database,
-      params: {startkey: '"_design"',
-        endkey: '"_design1"',
-        include_docs: true}
-    });
+  // TODO:: expand this for new docs and design docs
+  var DocEditorRouteObject = FauxtonAPI.RouteObject.extend({
+    layout: "one_pane",
 
-    var options = app.getParams();
-    options.include_docs = true;
-    data.database.buildAllDocs(options);
+    initialize: function() {
+      this.selected = false;
+    },
 
-    return {
-      layout: "one_pane",
+    routes: function() {
+      return _.keys(this.selectedRoutes);
+    },
 
-      data: data,
+    selectedRoutes: {
+      "database/:database/:doc/field_editor": "field_editor",
+      "database/:database/:doc/code_editor": "code_editor",
+      "database/:database/:doc": "code_editor"
+    },
 
-      crumbs: [
+    crumbs: function() {
+      return [
         {"name": "Databases", "link": "/_all_dbs"},
-        {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-        {"name": docID, "link": "#"}
-      ],
+        {"name": this.database.id, "link": Databases.databaseUrl(this.database)},
+        {"name": this.docID, "link": "#"}
+      ];
+    },
 
-      views: {
-        "#dashboard-content": new Documents.Views.Doc({
-          model: data.doc
-        }),
+    setEditorView: function() {
+      if (this.selected === "field_editor") {
+        this.docView = this.setView("#dashboard-content", new Documents.Views.DocFieldEditor({
+          model: this.doc
+        }));
+      } else {
+        this.docView = this.setView("#dashboard-content", new Documents.Views.Doc({
+          model: this.doc
+        }));
+      }
+    },
 
-        "#tabs": new Documents.Views.FieldEditorTabs({
-          selected: data.selected,
-          model: data.doc
-        })
-      },
+    route: function(route, args) {
+      var databaseName = args[0], docID = args[1];
 
-      apiUrl: data.doc.url()
-    };
-  };
+      this.database = this.database || new Databases.Model({id: databaseName});
+      this.doc = this.doc || new Documents.Doc({
+        _id: docID
+      }, {
+        database: this.database
+      });
+
+      if (this.selected !== this.selectedRoutes[route]) {
+        this.selected = this.selectedRoutes[route];
+        this.setEditorView();
+      }
+
+      this.tabsView = this.setView("#tabs", new Documents.Views.FieldEditorTabs({
+        selected: this.selected,
+        model: this.doc
+      }));
+    },
+
+    apiUrl: function() {
+      return this.doc.url();
+    }
+  });
 
   var newViewEditorCallback = function(databaseName) {
     var data = {
@@ -161,117 +179,13 @@ function(app, FauxtonAPI, Documents, Databases) {
   };
 
   Documents.Routes = {
-    "database/:database/:doc/field_editor": function(databaseName, docID) {
-      var data = {
-        database: new Databases.Model({id:databaseName}),
-        doc: new Documents.Doc({
-          "_id": docID
-        }),
-        selected: "field_editor"
-      };
-      data.doc.database = data.database;
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
-        params: {startkey: '"_design"',
-          endkey: '"_design1"',
-          include_docs: true}
-      });
-
-      var options = app.getParams();
-      options.include_docs = true;
-      data.database.buildAllDocs(options);
-
-      return {
-        layout: "one_pane",
-
-        data: data,
-
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": docID, "link": "#"}
-        ],
-
-        views: {
-          "#dashboard-content": new Documents.Views.DocFieldEditor({
-            model: data.doc
-          }),
-
-          "#tabs": new Documents.Views.FieldEditorTabs({
-            selected: data.selected,
-            model: data.doc
-          })
-        },
-
-        apiUrl: data.doc.url()
-      };
-    },
-
-    "database/:database/:doc/code_editor": codeEditorCallback,
-    "database/:database/:doc": codeEditorCallback,
+    //"database/:database/:doc/code_editor": codeEditorCallback,
+    //"database/:database/:doc": codeEditorCallback,
     "database/:database/_design%2F:doc": function(database, doc) {
       var docID = "_design/"+doc;
       return codeEditorCallback(database, docID);
     },
 
-    // HACK
-    // The ordering of routes is different in this object that the
-    // routes object in the Backbone.Router. As a result, the
-    // declaration order of show doc and _handler methods has been
-    // switched. This is a brittle solution that needs to be fixed.
-    // Conflicts with route: "database/:database/_:handler"
-    //
-    // TODO: add support for regex based rotues
-    // Javascript does not handle a regex as an object key very well,
-    // and it turns it into its string representation when you use in
-    // non object literal form, which does get recast back as a regex
-    // when we need it.
-    // The inability to use regex based routes here is a design flaw
-    // and should be rectified.
-    "old_database/:database/:doc": function(databaseName, docID) {
-      var data = {
-        database: new Databases.Model({id:databaseName}),
-        doc: new Documents.Doc({
-          "_id": docID
-        })
-      };
-      data.doc.database = data.database;
-      data.designDocs = new Documents.AllDocs(null, {
-        database: data.database,
-        params: {startkey: '"_design"',
-          endkey: '"_design1"',
-          include_docs: true}
-      });
-
-      var options = app.getParams();
-      options.include_docs = true;
-      data.database.buildAllDocs(options);
-
-      return {
-        layout: "with_sidebar",
-
-        data: data,
-
-        crumbs: [
-          {"name": "Databases", "link": "/_all_dbs"},
-          {"name": data.database.id, "link": Databases.databaseUrl(data.database)},
-          {"name": docID, "link": "#"}
-        ],
-
-        views: {
-          "#dashboard-content": new Documents.Views.Doc({
-            model: data.doc
-          }),
-
-          "#sidebar-content": new Documents.Views.Sidebar({
-            collection: data.designDocs
-          })
-        },
-
-        apiUrl: data.doc.url()
-      };
-    },
-
     "database/:database/_all_docs(:extra)": function(databaseName, page) {
       var data = {
         database: new Databases.Model({id:databaseName})
@@ -422,5 +336,7 @@ function(app, FauxtonAPI, Documents, Databases) {
     }
   };
 
+  Documents.RouteObjects = [new DocEditorRouteObject()];
+
   return Documents;
 });

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/router.js
----------------------------------------------------------------------
diff --git a/src/fauxton/app/router.js b/src/fauxton/app/router.js
index 77cc36b..45fd282 100644
--- a/src/fauxton/app/router.js
+++ b/src/fauxton/app/router.js
@@ -107,10 +107,21 @@ function(req, app, Initialize, FauxtonAPI, Fauxton, Layout, Databases, Documents
       this.route(route, route.toString(), generateRoute(generator, route));
     },
 
+    addModuleRouteObject: function(routeObject) {
+      var masterLayout = this.masterLayout;
+      _.each(routeObject.get('routes'), function(route) {
+        //this.route(route, route.toString(), _.partial(routeObject.renderWith, route, this.masterLayout));
+        this.route(route, route.toString(), function() {
+          routeObject.render(route, masterLayout, Array.prototype.slice.call(arguments));
+        });
+      }, this);
+    },
+
     setModuleRoutes: function() {
       _.each(modules, function(module) {
         if (module){
           _.each(module.Routes, this.addModuleRoute, this);
+          _.each(module.RouteObjects, this.addModuleRouteObject, this);
         }
       }, this);
       _.each(LoadAddons.addons, function(module) {

http://git-wip-us.apache.org/repos/asf/couchdb/blob/61419957/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
----------------------------------------------------------------------
diff --git a/src/fauxton/app/templates/documents/doc_field_editor_tabs.html b/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
index ecb0e48..bb16d73 100644
--- a/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
+++ b/src/fauxton/app/templates/documents/doc_field_editor_tabs.html
@@ -13,7 +13,7 @@ the License.
 -->
 
 <ul class="nav nav-tabs">
-  <!--<li class="<%= isSelectedClass('field_editor') %>"><a href="#<%= doc.url('app') %>/field_editor">Doc fields</a></li>-->
+  <li class="<%= isSelectedClass('field_editor') %>"><a href="#<%= doc.url('app') %>/field_editor">Doc fields</a></li>
   <li class="<%= isSelectedClass('code_editor') %>"><a href="#<%= doc.url('app') %>/code_editor"><i class="icon-pencil"></i> Code editor</a></li>
   <ul class="nav pull-right" style="margin:5px 10px 0px 10px;">
     <li>