Commits

Walt Woods committed 3e15b69

Added cherrypy.SubHandler for conveniently binding dynamic handlers

  • Participants
  • Parent commits f5466ee

Comments (0)

Files changed (2)

File cherrypy/__init__.py

         
     return decorated
 
+class SubHandler(object):
+    """This is a utility class designed to be mounted on a cherrypy object
+    to serve up a different handler based on a parameter in the url.  For
+    instance, if your application has multiple databases, each of which
+    have requests that are handled by a common DatabaseHandler class, but you
+    want the database ID to appear as the first part of the URL, you would
+    use SubHandler as follows:
+
+    class DatabaseHandler:
+        def __init__(self, myDbName):
+            ...
+
+        @cherrypy.expose
+        def index(self):
+            ...
+
+    class Root:
+        db = cherrypy.SubHandler(DatabaseHandler, 'myDbName')
+
+    cherrypy.tree.mount(Root())
+
+    This will make the URL '/db/myDb/klar' first create a DatabaseHandler
+    object initialized with the kwarg myDbName='myDb'.  Then the cherrypy
+    default dispatcher would look for the property arg.klar in that 
+    DatabaseHandler object to handle the request.
+
+    Note that if vpath elements end up in a path that does not exist, 
+    SubHandler will still create the handler, but will not pass those kwargs
+    not existing in the path.  The caveat to this is that if no argument is 
+    passed, SubHandler() will not be executed, and any parent object's default()
+    method will be used instead.  That is, at least one of the arguments
+    specified in SubHandler() must be passed in the URL for SubHandler
+    to do anything.
+    """
+    def __init__(self, handler_type, *args):
+        self._handler_type = handler_type
+        self._args = args
+
+    def _cp_dispatch(self, vpath):
+        parms = {}
+        for arg in self._args:
+            if len(vpath) == 0:
+                break
+            parms[arg] = vpath.pop(0)
+
+        return self._handler_type(**parms)
+        request.params.update(parms)
+
 def url(path="", qs="", script_name=None, base=None, relative=None):
     """Create an absolute URL for the given path.
     

File cherrypy/test/test_dynamicobjectmapping.py

     class ParameterizedPopArgs:
         """Test cherrypy.popargs() with a function call handler"""
     ParameterizedPopArgs = cherrypy.popargs('a', handler=ParameterizedHandler)(ParameterizedPopArgs)
-            
+
+    class SubHandler:
+        """Test cherrypy.SubHandler()"""
+        def __init__(self, **kwargs):
+            self.kwargs = kwargs
+        def index(self):
+            return str(self.kwargs)
+        index.exposed = True
+        def method(self):
+            return "SubHandler.method()"
+        method.exposed = True
+
+    class SubHandler2:
+        """Second test handler; ensures that nested class doesn't cause
+        issues.
+        """
+        def __init__(self, a):
+            self.a = a
+        def index(self):
+            return "second handler"
+        index.exposed = True
+
     Root.decorated = DecoratedPopArgs()
     Root.undecorated = NonDecoratedPopArgs()
     Root.index_only = IndexOnly()
     Root.parameter_test = ParameterizedPopArgs()
+    Root.subhandler = cherrypy.SubHandler(SubHandler, 'a', 'b')
+    Root.subhandler2 = cherrypy.SubHandler(SubHandler2, 'a')
 
     Root.users = UserContainerNode()
 
         self.getPage("/parameter_test/argument2/")
         self.assertBody("argument2")
 
+        # No params falls back to default
+        self.getPage("/subhandler/")
+        self.assertBody("default ('subhandler',)")
+
+        self.getPage("/subhandler/342/")
+        self.assertBody("{'a': '342'}")
+
+        self.getPage("/subhandler/344/245/")
+        self.assertBody("{'a': '344', 'b': '245'}")
+
+        self.getPage("/subhandler/344/245/method/")
+        self.assertBody("SubHandler.method()")
+
+        self.getPage("/subhandler2/a/")
+        self.assertBody("second handler")
+