Switched from RESTResource to PrestHandler
authorAaron Bentley <abentley@panoramicfeedback.com>
Wed, 21 Dec 2005 19:26:15 +0000 (14:26 -0500)
committerAaron Bentley <abentley@panoramicfeedback.com>
Wed, 21 Dec 2005 19:26:15 +0000 (14:26 -0500)
beweb/beweb/controllers.py
beweb/beweb/prest.py
beweb/beweb/restresource.py [deleted file]

index 168d61025262e6b92d99f370e86f19a85cff43be..6fc927753b20ace25949a5de2f409fe2f7be0e99 100644 (file)
@@ -4,7 +4,7 @@ import cherrypy
 from libbe.bugdir import tree_root, cmp_severity, new_bug
 from libbe import names
 from config import projects
-from restresource import RESTResource
+from prest import PrestHandler, provide_action
 
 def project_tree(project):
     try:
@@ -19,48 +19,53 @@ def expose_resource(html=None):
         return func
     return exposer 
 
-class Bug(RESTResource):
-    @expose_resource(html="beweb.templates.edit_bug")
-    def index(self, bug):
-        return {"bug": bug, "project_id": self.parent}
+class Bug(PrestHandler):
+    @turbogears.expose(html="beweb.templates.edit_bug")
+    def index(self, project, bug):
+        return {"bug": bug, "project_id": project}
     
+    def dispatch(self, bug_data, bug, *args, **kwargs):
+        if bug is None:
+            return self.list(bug_data['project'], **kwargs)
+        else:
+            return self.index(bug_data['project'], bug)
+
     @turbogears.expose(html="beweb.templates.bugs")
-    def list(self, sort_by=None, show_closed=False, action=None):
+    def list(self, project, sort_by=None, show_closed=False, action=None):
         if action == "New bug":
             self.new_bug()
         if show_closed == "False":
             show_closed = False
-        bug_tree = project_tree(self.parent)
+        bug_tree = project_tree(project)
         bugs = list(bug_tree.list())
         if sort_by is None:
             def cmp_date(bug1, bug2):
                 return -cmp(bug1.time, bug2.time)
             bugs.sort(cmp_date)
             bugs.sort(cmp_severity)
-        return {"project_id"      : self.parent,
-                "project_name"    : projects[self.parent][0],
+        return {"project_id"      : project,
+                "project_name"    : projects[project][0],
                 "bugs"            : bugs,
                 "show_closed"     : show_closed,
                }
 
-    def new_bug(self):
-        bug = new_bug(self.bug_tree())
+    @provide_action("action", "New bug")
+    def new_bug(self, bug_data, bug, **kwargs):
+        bug = new_bug(project_tree(bug_data['project']))
         bug.save()
-        raise cherrypy.HTTPRedirect(bug_url(self.parent, bug.uuid))
+        raise cherrypy.HTTPRedirect(bug_url(bug_data['project'], bug.uuid))
 
-    @expose_resource()
-    def update(self, bug, status, severity, summary, action):
+    @provide_action("action", "Update")
+    def update(self, bug_data, bug, status, severity, summary, action):
         bug.status = status
         bug.severity = severity
         bug.summary = summary
         bug.save()
-        raise cherrypy.HTTPRedirect(bug_list_url(self.parent))
+        raise cherrypy.HTTPRedirect(bug_list_url(bug_data["project"]))
 
-    def REST_instantiate(self, bug_uuid):
-        return self.bug_tree().get_bug(bug_uuid)
+    def instantiate(self, project, bug):
+        return project_tree(project).get_bug(bug)
 
-    def bug_tree(self):
-        return project_tree(self.parent)
 
 def project_url(project_id=None):
     project_url = "/project/"
@@ -80,21 +85,25 @@ def bug_list_url(project_id, show_closed=False):
     return turbogears.url(bug_url)
 
 
-class Project(RESTResource):
-    REST_children = {"bug": Bug()}
-    @expose_resource(html="beweb.templates.projects")
-    def index(self, project_id=None):
-        if project_id is not None:
-            raise cherrypy.HTTPRedirect(bug_url(project_id)) 
+class Project(PrestHandler):
+    bug = Bug()
+    @turbogears.expose(html="beweb.templates.projects")
+    def dispatch(self, project_data, project, *args, **kwargs):
+        if project is not None:
+            raise cherrypy.HTTPRedirect(bug_url(project)) 
         else:
             return {"projects": projects}
 
-    def REST_instantiate(self, project_id):
-        return project_id
+    def instantiate(self, project):
+        return project
 
 
 class Root(controllers.Root):
-    project = Project()
+    prest = PrestHandler()
+    prest.project = Project()
     @turbogears.expose()
     def index(self):
         raise cherrypy.HTTPRedirect(project_url()) 
+    @turbogears.expose()
+    def default(self, *args, **kwargs):
+        return self.prest.default(*args, **kwargs)
index 97ab1d970e677e9f558bbb002d083be0230a7219..7de1290a518bfb5fe09297169be66c0e30d51b14 100644 (file)
@@ -39,7 +39,7 @@ class PrestHandler(object):
         """Convert the path into a handler, a resource, data, and extra_path"""
         if data is None:
             data = {}
-        if len(path) < 2 or not (path[0] is None or hasattr(self, path[0])):
+        if len(path) < 2 or not (hasattr(self, path[1])):
             if len(path) == 0:
                 resource = None
             else:
@@ -54,9 +54,10 @@ class PrestHandler(object):
         action = child.get_action(**kwargs)
         new_args = ([data, resource]+extra)
         if action is not None:
-            action(*new_args, **kwargs)
+            return action(*new_args, **kwargs)
         else:
-            child.dispatch(*new_args, **kwargs)
+            print child.__class__.__name__
+            return child.dispatch(*new_args, **kwargs)
 
     def get_action(self, **kwargs):
         """Return the action requested by kwargs, if any.
@@ -119,6 +120,36 @@ class PrestTester(TestCase):
                           *['project', '27', 'extra'], 
                           **{'behavior':'Update', 'action':'Save', 'b':'97'})
                 
+        class BugHandler(PrestHandler):
+            actions = {}
+            def dispatch(self, bug_data, bug, *args, **kwargs):
+                self.project_id = project_data['project']
+                self.project_data = project_data
+                self.resource = project
+                self.args = args
+                self.kwargs = kwargs
+
+            def instantiate(self, project, bug):
+                return [project, bug]
+
+            @provide_action('action', 'Save')
+            def save(self, project_data, project, *args, **kwargs):
+                self.action = "save"
+
+            @provide_action('behavior', 'Update')
+            def update(self, project_data, project, *args, **kwargs):
+                self.action = "update"
+
+        foo.project.bug = BugHandler()
+        handler, resource, data, extra = foo.decode([None, 'project', '83', 
+                                                     'bug', '92'])
+        assert handler is foo.project.bug
+        self.assertEqual(resource[0], '83')
+        self.assertEqual(resource[1], '92')
+        self.assertEqual([], extra)
+        self.assertEqual(data['project'], '83')
+        self.assertEqual(data['bug'], '92')
+
 def test():
     patchesTestSuite = unittest.makeSuite(PrestTester,'test')
     runner = unittest.TextTestRunner(verbosity=0)
diff --git a/beweb/beweb/restresource.py b/beweb/beweb/restresource.py
deleted file mode 100644 (file)
index 47db637..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-"""
-REST Resource
-
-cherrypy controller mixin to make it easy to build REST applications.
-
-handles nested resources and method-based dispatching.
-
-here's a rough sample of what a controller would look like using this:
-
-cherrypy.root = MainController()
-cherrypy.root.user = UserController()
-
-class PostController(RESTResource):
-    def index(self,post):
-        return post.as_html()
-    index.expose_resource = True
-
-    def delete(self,post):
-        post.destroySelf()
-        return "ok"
-    delete.expose_resource = True
-
-    def update(self,post,title="",body=""):
-        post.title = title
-        post.body = body
-        return "ok"
-    update.expose_resource = True
-
-    def add(self, post, title="", body="")
-        post.title = title
-        post.body = body
-        return "ok"
-    update.expose_resource = True
-
-    def REST_instantiate(self, slug):
-        try:
-            return Post.select(Post.q.slug == slug, Post.q.userID = self.parent.id)[0]
-        except:
-            return None
-
-    def REST_create(self, slug):
-        return Post(slug=slug,user=self.parent)
-
-class UserController(RESTResource):
-    REST_children = {'posts' : PostController()}
-
-    def index(self,user):
-        return user.as_html()
-    index.expose_resource = True
-
-    def delete(self,user):
-        user.destroySelf()
-        return "ok"
-    delete.expose_resource = True
-
-    def update(self,user,fullname="",email=""):
-        user.fullname = fullname
-        user.email = email
-        return "ok"
-    update.expose_resource = True
-
-    def add(self, user, fullname="", email=""):
-        user.fullname = fullname
-        user.email = email
-        return "ok"
-    add.expose_resource = True
-
-    def extra_action(self,user):
-        # do something else
-    extra_action.expose_resource = True
-
-    def REST_instantiate(self, username):
-        try:
-            return User.byUsername(username)
-        except:
-            return None
-
-    def REST_create(self, username):
-        return User(username=username)
-
-then, the site would have urls like:
-
-    /user/bob
-    /user/bob/posts/my-first-post
-    /user/bob/posts/my-second-post
-
-which represent REST resources. calling 'GET /usr/bob' would call the index() method on UserController
-for the user bob. 'PUT /usr/joe' would create a new user with username 'joe'. 'DELETE /usr/joe'
-would delete that user. 'GET /usr/bob/posts/my-first-post' would call index() on the Post Controller
-with the post with the slug 'my-first-post' that is owned by bob.
-
-
-"""
-
-
-import cherrypy
-class RESTResource:
-    # default method mapping. ie, if a GET request is made for
-    # the resource's url, it will try to call an index() method (if it exists);
-    # if a PUT request is made, it will try to call an add() method.
-    # if you prefer other method names, just override these values in your
-    # controller with REST_map
-    REST_defaults = {'DELETE' : 'delete',
-                     'GET' : 'index',
-                     'POST' : 'update',
-                     'PUT' : 'add'}
-    REST_map = {}
-    # if the resource has children resources, list them here. format is
-    # a dictionary of name -> resource mappings. ie,
-    #
-    # REST_children = {'posts' : PostController()}
-
-    REST_children = {}
-
-    def REST_dispatch(self, resource, **params):
-        # if this gets called, we assume that default has already
-        # traversed down the tree to the right location and this is
-        # being called for a raw resource
-        method = cherrypy.request.method
-        if self.REST_map.has_key(method):
-            m = getattr(self,self.REST_map[method])
-            if m and getattr(m, "expose_resource"):
-                return m(resource,**params)
-        else:
-            if self.REST_defaults.has_key(method):
-                m = getattr(self,self.REST_defaults[method])
-                try:
-                    if m and getattr(m, "expose_resource"):
-                        return m(resource,**params)
-                except:
-                    raise
-                    raise Exception("can't find expose_resource on %r", m)
-
-        raise cherrypy.NotFound
-
-    @cherrypy.expose
-    def default(self, *vpath, **params):
-        if not vpath:
-            return self.list(**params)
-        # Make a copy of vpath in a list
-        vpath = list(vpath)
-        atom = vpath.pop(0)
-
-        # Coerce the ID to the correct db type
-        resource = self.REST_instantiate(atom)
-        if resource is None:
-            if cherrypy.request.method == "PUT":
-                # PUT is special since it can be used to create
-                # a resource
-                resource = self.REST_create(atom)
-            else:
-                raise cherrypy.NotFound
-
-        # There may be further virtual path components.
-        # Try to map them to methods in children or this class.
-        if vpath:
-            a = vpath.pop(0)
-            if self.REST_children.has_key(a):
-                c = self.REST_children[a]
-                c.parent = resource
-                return c.default(*vpath, **params)
-            method = getattr(self, a, None)
-            if method and getattr(method, "expose_resource"):
-                return method(resource, *vpath, **params)
-            else:
-                # path component was specified but doesn't
-                # map to anything exposed and callable
-                raise cherrypy.NotFound
-
-        # No further known vpath components. Call a default handler
-        # based on the method
-        return self.REST_dispatch(resource,**params)
-
-    def REST_instantiate(self,id):
-        """ instantiate a REST resource based on the id
-
-        this method MUST be overridden in your class. it will be passed
-        the id (from the url fragment) and should return a model object
-        corresponding to the resource.
-
-        if the object doesn't exist, it should return None rather than throwing
-        an error. if this method returns None and it is a PUT request,
-        REST_create() will be called so you can actually create the resource.
-        """
-        raise cherrypy.NotFound
-
-    def REST_create(self,id):
-        """ create a REST resource with the specified id
-
-        this method should be overridden in your class.
-        this method will be called when a PUT request is made for a resource
-        that doesn't already exist. you should create the resource in this method
-        and return it.
-        """
-        raise cherrypy.NotFound