From 8529a6d8278fb0e3a98a41aa8224c8ea8e7f8acf Mon Sep 17 00:00:00 2001 From: Aaron Bentley Date: Wed, 21 Dec 2005 14:26:15 -0500 Subject: [PATCH] Switched from RESTResource to PrestHandler --- beweb/beweb/controllers.py | 65 ++++++------ beweb/beweb/prest.py | 37 ++++++- beweb/beweb/restresource.py | 195 ------------------------------------ 3 files changed, 71 insertions(+), 226 deletions(-) delete mode 100644 beweb/beweb/restresource.py diff --git a/beweb/beweb/controllers.py b/beweb/beweb/controllers.py index 168d610..6fc9277 100644 --- a/beweb/beweb/controllers.py +++ b/beweb/beweb/controllers.py @@ -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) diff --git a/beweb/beweb/prest.py b/beweb/beweb/prest.py index 97ab1d9..7de1290 100644 --- a/beweb/beweb/prest.py +++ b/beweb/beweb/prest.py @@ -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 index 47db637..0000000 --- a/beweb/beweb/restresource.py +++ /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 -- 2.26.2