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:
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/"
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)
"""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:
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.
*['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)
+++ /dev/null
-"""
-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