[Checkins] SVN: bobo/trunk/bobo Merged: branches/patricks-backtrack

jim cvs-admin at zope.org
Sun Apr 29 16:10:24 UTC 2012


Log message for revision 125405:
  Merged: branches/patricks-backtrack
  
  - Added backtracking when searching for resources to deal with a case
    when a route doesn't handle a request method, but a later-matching
    route does.
  

Changed:
  U   bobo/trunk/bobo/README.txt
  U   bobo/trunk/bobo/src/bobo.py
  U   bobo/trunk/bobodoctestumentation/src/bobodoctestumentation/more.txt

-=-
Modified: bobo/trunk/bobo/README.txt
===================================================================
--- bobo/trunk/bobo/README.txt	2012-04-29 16:08:53 UTC (rev 125404)
+++ bobo/trunk/bobo/README.txt	2012-04-29 16:10:20 UTC (rev 125405)
@@ -29,6 +29,10 @@
 
 - Updated to work with webob 1.2
 
+- Added backtracking when searching for resources to deal with a case
+  when a route doesn't handle a request method, but a later-matching
+  route does.
+
 0.2.3 2012-03-12
 ----------------
 

Modified: bobo/trunk/bobo/src/bobo.py
===================================================================
--- bobo/trunk/bobo/src/bobo.py	2012-04-29 16:08:53 UTC (rev 125404)
+++ bobo/trunk/bobo/src/bobo.py	2012-04-29 16:10:20 UTC (rev 125405)
@@ -183,18 +183,23 @@
 
     def bobo_response(self, request, path, method):
         try:
+            allowed = set()
             for handler in self.handlers:
-                response = handler(request, path, method)
+                try:
+                    response = handler(request, path, method)
+                except MethodNotAllowed, exc:
+                    allowed.update(exc.allowed)
+                    continue
                 if response is not None:
                     return response
+            if allowed:
+                return self.method_not_allowed(request, method, allowed)
             return self.not_found(request, method)
         except BoboException, exc:
             return self.build_response(request, method, exc)
-        except MethodNotAllowed, v:
-            return self.method_not_allowed(request, method, v.allowed)
         except MissingFormVariable, v:
             return self.missing_form_variable(request, method, v.name)
-        except NotFound, v:
+        except NotFound:
             return self.not_found(request, method)
         except bbbbad_errors:
             raise
@@ -1232,10 +1237,17 @@
         handlers.append(_make_br_method_for_name(name))
 
     def bobo_response(self, request, path, method):
+        allowed = set()
         for handler in handlers:
-            found = handler(self, request, path, method)
+            try:
+                found = handler(self, request, path, method)
+            except MethodNotAllowed, exc:
+                allowed.update(exc.allowed)
+                continue
             if found is not None:
                 return found
+        if allowed:
+            raise MethodNotAllowed(allowed)
 
     old = class_.__dict__.get('bobo_response')
     if isinstance(old, _subroute_class_method):

Modified: bobo/trunk/bobodoctestumentation/src/bobodoctestumentation/more.txt
===================================================================
--- bobo/trunk/bobodoctestumentation/src/bobodoctestumentation/more.txt	2012-04-29 16:08:53 UTC (rev 125404)
+++ bobo/trunk/bobodoctestumentation/src/bobodoctestumentation/more.txt	2012-04-29 16:10:20 UTC (rev 125405)
@@ -549,7 +549,7 @@
    method, bobo generates a "405 Method Not Allowed" response.
 3. When a ``query`` or ``post`` decorated function requires a
    parameter and the parameter is isn't in the given form data, bobo
-   generates a "405 Forbidden" response with a body that indicates the
+   generates a "403 Forbidden" response with a body that indicates the
    missing parameter.
 
 For each of these responses, bobo generates a small HTML body.
@@ -810,3 +810,105 @@
    :term:`resource` implementations.  Custom resource implementations
    must implement the resource interface and will provide an order
    using the ``bobo_order`` attribute.  See :ref:`resourceinterface`.
+
+
+Backtracking
+------------
+
+When handling a request, if bobo finds a resource that matches
+the route but does not accept the request method, it will continue
+looking for matching resources; if it eventually finds none, it will
+then generate a "405 Method Not Allowed" response.
+
+::
+
+   import bobo
+
+   @bobo.post("/event/create")
+   def create(bobo_request):
+       return "created event"
+
+   @bobo.resource("/event/:action?", method=("GET",))
+   def catch_all(bobo_request, action=None):
+       return "get request for %r" % (action,)
+
+   class User(object):
+
+       @bobo.resource("/:userid", method=("POST",))
+       def create(self, bobo_request, userid):
+           return "created user with id %r" % (userid,)
+
+       @bobo.resource("/:identifier", method=("HEAD",))
+       def head(self, bobo_request, identifier):
+           return ""
+
+       @bobo.resource("/:id", method=("GET",))
+       def catch_all(self, bobo_request, id):
+           return "get user with id %r" % (id,)
+
+   bobo.scan_class(User)
+
+   class Thing(object):
+
+       @bobo.resource("/:id", method=("PUT",))
+       def put(self, bobo_request, id):
+           return "put thing with id %r" % (id,)
+   bobo.scan_class(Thing)
+
+   @bobo.subroute("/users")
+   def users(bobo_request):
+       return User()
+
+   @bobo.subroute("/:thing")
+   def thing(bobo_request, thing):
+       return Thing()
+
+
+.. -> src
+
+    >>> update_module('backtrack', src)
+    >>> app = webtest.TestApp(bobo.Application(
+    ...    bobo_resources='backtrack'))
+
+We have a resource that matches the route "/event/create", but it is
+for POST requests.  If we make a GET request, the second resource
+with the matching route that can handle GET requests gets called.
+
+    >>> app.get('/event/create').body
+    "get request for u'create'"
+
+Of course POST requests go to the appropriate resource.
+
+    >>> app.post('/event/create').body
+    'created event'
+
+If we perform a HEAD request for "/event/create", we get a 405 response,
+as no resource is able to handle the method.  The "Allow" header indicates
+all of the request methods that are valid for the particular path.
+
+    >>> app.head('/event/create', status=405).headers["Allow"]
+    'GET, POST, PUT'
+
+The backtracking behavior works with subroutes.
+
+    >>> app.get('/users/1234').body
+    "get user with id u'1234'"
+
+    >>> app.head('/users/1234').status
+    '200 OK'
+
+    >>> app.post('/users/1234').body
+    "created user with id u'1234'"
+
+If the first matching subroute returns a resource with no handlers for
+the request method, the next matching subroute is tried.
+
+    >>> app.put('/users/54321').body
+    "put thing with id u'54321'"
+
+If no resource is able to handle the request method, we get a 405 response
+with an Allow header.
+
+    >>> app.request('/users/54321',
+    ...     method="OPTIONS", status=405).headers["Allow"]
+    'GET, HEAD, POST, PUT'



More information about the checkins mailing list