The unified diff between revisions [79b896cf..] and [000645e7..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'viewmtn.py'

#
#
# patch "viewmtn.py"
#  from [657d61af6a6a853bd10a1e38aaf5eb7f53588f9f]
#    to [e76f6d2b58d5b7cda4c2ddbb92dfaeee73e05702]
#
============================================================
--- viewmtn.py	657d61af6a6a853bd10a1e38aaf5eb7f53588f9f
+++ viewmtn.py	e76f6d2b58d5b7cda4c2ddbb92dfaeee73e05702
@@ -34,10 +34,16 @@ import binascii
 hq = cgi.escape
 import heapq
 import binascii
+from itertools import izip, chain, repeat

 import web
 debug = web.debug

+# purloined from: http://docs.python.org/lib/itertools-recipes.html
+def grouper(n, iterable, padvalue=None):
+    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
+    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
+
 # /about.psp -> /about

 # /branch.psp -> /branch/{branch}/
@@ -359,7 +365,31 @@ class Renderer:
         terms.update(kwargs)
         web.render(template, terms)

+class OperationsFactory:
+    def __init__ (self):
+        # has the user specified a dbfiles hash? if so, use it
+        self.ops_instances = {}
+        self.default = None
+        if hasattr (config, "dbfiles"):
+            for name, dbfile in config.dbfiles:
+                self.ops_instances[name] = mtn.Operations([config.monotone, dbfile])
+            if hasattr (config, "defaultdb"):
+                self.default = config.defaultdb
+        else:
+            self.ops_instances["legacy"] = mtn.Operations([config.monotone, config.dbfile])
+            self.default = "legacy"
+
+    def get_ops(self, name):
+        if not (name is None):
+            return self.ops.get (name, None)
+        else:
+            return self.ops.get (self.default, None)
+
+#
+# TODO: try and wrap these globals up in a single global object
+#
 renderer = Renderer()
+op_fact = OperationsFactory()
 ops = mtn.Operations([config.monotone, config.dbfile])
 mimehelp = sharedmimeinfo.LookupHelper(getattr(config, "mime_map", None))
 if config.icon_theme:
@@ -1266,43 +1296,44 @@ class RobotsTxt:
             for access_method in ['/revision/', '/branch/head/', '/branch/anyhead/', '/branch/changes/from/', '/json/', '/mimeicon/']:
                 print "Disallow:", access_method + revision_page

-urls = (
-    r'/', 'Index', #done
-    r'/about', 'About', #done
-    r'/tags', 'Tags', #done
-    r'/help', 'Help', #done
-    r'/json/([A-Za-z]+)/(.*)', 'Json',
+common_urls = (
+# these don't care about multiple databases specified via the URL
+    r'', 'Index',
+    r'about', 'About',
+    r'help', 'Help',
+    r'robots.txt', 'RobotsTxt',  ## FIXME needs o exclude per-db paths
+    r'mimeicon/([A-Za-z0-9][a-z0-9\-\+\.]*)/([A-Za-z0-9][a-z0-9\-\+\.]*)', 'MimeIcon',
+)

-    r'/revision/browse/('+mtn.revision_re+')/(.*)', 'RevisionBrowse',
-    r'/revision/browse/('+mtn.revision_re+')()', 'RevisionBrowse',
-    r'/revision/diff/('+mtn.revision_re+')/with/('+mtn.revision_re+')', 'RevisionDiff',
-    r'/revision/rawdiff/('+mtn.revision_re+')/with/('+mtn.revision_re+')', 'RevisionRawDiff',
-    r'/revision/diff/('+mtn.revision_re+')/with/('+mtn.revision_re+')'+'/(.*)', 'RevisionDiff',
-    r'/revision/rawdiff/('+mtn.revision_re+')/with/('+mtn.revision_re+')'+'/(.*)', 'RevisionRawDiff',
-    r'/revision/file/('+mtn.revision_re+')/(.*)', 'RevisionFile',
-    r'/revision/filechanges/()()('+mtn.revision_re+')/(.*)', 'RevisionFileChanges',
-    r'/revision/filechanges/from/(\d+)/to/(\d+)/('+mtn.revision_re+')/(.*)', 'RevisionFileChanges',
-    r'/revision/filechanges/rss/()()('+mtn.revision_re+')/(.*)', 'RevisionFileChangesRSS',
-    r'/revision/filechanges/rss/from/(\d+)/to/(\d+)/('+mtn.revision_re+')/(.*)', 'RevisionFileChangesRSS',
-    r'/revision/downloadfile/('+mtn.revision_re+')/(.*)', 'RevisionDownloadFile',
-    r'/revision/info/('+mtn.revision_re+')', 'RevisionInfo',
-    r'/revision/tar/('+mtn.revision_re+')', 'RevisionTar',
-    r'/revision/graph/('+mtn.revision_re+')', 'RevisionGraph',
+perdb_urls = (
+    r'tags', 'Tags',
+    r'json/([A-Za-z]+)/(.*)', 'Json',

-    r'/branch/changes/(.*)/from/(\d+)/to/(\d+)', 'HTMLBranchChanges',
-    r'/branch/changes/([^/]+)()()', 'HTMLBranchChanges',
-    r'/branch/changes/(.*)/from/(\d+)/to/(\d+)/rss', 'RSSBranchChanges',
-    r'/branch/changes/([^/]+)()()/rss', 'RSSBranchChanges',
-    r'/branch/tags/([^/]+)', 'Tags',
+    r'([a-zA-Z]/)?revision/browse/('+mtn.revision_re+')/(.*)', 'RevisionBrowse',
+    r'revision/browse/('+mtn.revision_re+')()', 'RevisionBrowse',
+    r'revision/diff/('+mtn.revision_re+')/with/('+mtn.revision_re+')', 'RevisionDiff',
+    r'revision/diff/('+mtn.revision_re+')/with/('+mtn.revision_re+')'+'/(.*)', 'RevisionDiff',
+    r'revision/file/('+mtn.revision_re+')/(.*)', 'RevisionFile',
+    r'revision/filechanges/()()('+mtn.revision_re+')/(.*)', 'RevisionFileChanges',
+    r'revision/filechanges/from/(\d+)/to/(\d+)/('+mtn.revision_re+')/(.*)', 'RevisionFileChanges',
+    r'revision/filechanges/rss/()()('+mtn.revision_re+')/(.*)', 'RevisionFileChangesRSS',
+    r'revision/filechanges/rss/from/(\d+)/to/(\d+)/('+mtn.revision_re+')/(.*)', 'RevisionFileChangesRSS',
+    r'revision/downloadfile/('+mtn.revision_re+')/(.*)', 'RevisionDownloadFile',
+    r'revision/info/('+mtn.revision_re+')', 'RevisionInfo',
+    r'revision/tar/('+mtn.revision_re+')', 'RevisionTar',
+    r'revision/graph/('+mtn.revision_re+')', 'RevisionGraph',

+    r'branch/changes/(.*)/from/(\d+)/to/(\d+)', 'HTMLBranchChanges',
+    r'branch/changes/([^/]+)()()', 'HTMLBranchChanges',
+    r'branch/changes/(.*)/from/(\d+)/to/(\d+)/rss', 'RSSBranchChanges',
+    r'branch/changes/([^/]+)()()/rss', 'RSSBranchChanges',
+    r'branch/tags/([^/]+)', 'Tags',
+
     # let's make it possible to access any function on the head revision
     # through this proxy method; it'll return a redirect to the head revision
     # with the specified function
-    r'/branch/(head)/([A-Za-z]+)/([^/]+)(.*)', 'BranchHead',
-    r'/branch/(anyhead)/([A-Za-z]+)/([^/]+)(.*)', 'BranchHead',
-
-    r'/robots.txt', 'RobotsTxt',
-    r'/mimeicon/([A-Za-z0-9][a-z0-9\-\+\.]*)/([A-Za-z0-9][a-z0-9\-\+\.]*)', 'MimeIcon',
+    r'branch/(head)/([A-Za-z]+)/([^/]+)(.*)', 'BranchHead',
+    r'branch/(anyhead)/([A-Za-z]+)/([^/]+)(.*)', 'BranchHead',
 )

 def runfcgi_apache(func):
@@ -1317,7 +1348,32 @@ if __name__ == '__main__':
         web.wsgi.runwsgi = runfcgi_apache
     if hasattr(config, "debug") and config.debug:
         web.webapi.internalerror = web.debugerror
-    func = lambda : per_request_wrapper(web.webpyfunc(urls, fvars=globals()))
+
+    def assemble_urls():
+        fvars = {}
+        urls = ()
+        for url, fn in grouper (2, common_urls):
+            url = r'^/' + url
+            urls += (url, fn)
+            fvars[fn] = globals()[fn]
+
+        def get_db_closure (fn):
+            the_cls = globals()[fn]()
+            class PerDBClosure:
+                def GET (self, *args, **kwargs):
+                    db, other_args = args[0], args[1:]
+                    the_cls.GET (*other_args, **kwargs)
+            return PerDBClosure
+
+        for url, fn in grouper (2, perdb_urls):
+            url = r'^/([A-Za-z]+/)?' + url
+            urls += (url, fn)
+            fvars[fn] = get_db_closure (fn)
+        print "fvars::", fvars['Index']
+        print "globals:", globals()['Index']
+        return urls, fvars
+    urls, fvars = assemble_urls()
+    func = lambda : per_request_wrapper(web.webpyfunc(urls, fvars=fvars))
     web.run(func, globals(), web.reloader)

 ###