[Checkins] SVN: grok/trunk/ REST now works properly with custom traversal behavior. XML-RPC works

Martijn Faassen faassen at infrae.com
Wed Apr 16 10:08:38 EDT 2008


Log message for revision 85438:
  REST now works properly with custom traversal behavior. XML-RPC works
  too.
  

Changed:
  U   grok/trunk/CHANGES.txt
  U   grok/trunk/src/grok/components.py
  U   grok/trunk/src/grok/ftests/rest/rest.py
  A   grok/trunk/src/grok/ftests/rest/rest_traverse.py
  U   grok/trunk/src/grok/ftests/xmlrpc/xmlrpc.py
  U   grok/trunk/src/grok/meta.py

-=-
Modified: grok/trunk/CHANGES.txt
===================================================================
--- grok/trunk/CHANGES.txt	2008-04-16 13:06:40 UTC (rev 85437)
+++ grok/trunk/CHANGES.txt	2008-04-16 14:08:38 UTC (rev 85438)
@@ -82,6 +82,10 @@
   name, but this is actually a conflict. Now give configuration
   conflict error when someone tries this.
 
+* Overriding traversal behavior using the ``traverse()`` method or
+  ``grok.Traverser`` failed in the face of (REST) ``PUT`` and
+  ``DELETE``. XML-RPC also failed when custom traversal was in use.
+
 Restructuring
 -------------
 

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2008-04-16 13:06:40 UTC (rev 85437)
+++ grok/trunk/src/grok/components.py	2008-04-16 14:08:38 UTC (rev 85438)
@@ -28,8 +28,8 @@
 from zope.securitypolicy.role import Role
 from zope.publisher.browser import BrowserPage
 from zope.publisher.interfaces import NotFound
-from zope.publisher.interfaces.browser import (IBrowserPublisher,
-                                               IBrowserRequest)
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.publisher.interfaces.http import IHTTPRequest
 from zope.publisher.publish import mapply
 from zope.pagetemplate import pagetemplate, pagetemplatefile
 from zope.formlib import form
@@ -418,7 +418,7 @@
 
 
 class ModelTraverser(Traverser):
-    component.adapts(Model, IBrowserRequest)
+    component.adapts(Model, IHTTPRequest)
 
     def traverse(self, name):
         traverse = getattr(self.context, 'traverse', None)
@@ -427,7 +427,7 @@
 
 
 class ContainerTraverser(Traverser):
-    component.adapts(Container, IBrowserRequest)
+    component.adapts(Container, IHTTPRequest)
 
     def traverse(self, name):
         traverse = getattr(self.context, 'traverse', None)

Modified: grok/trunk/src/grok/ftests/rest/rest.py
===================================================================
--- grok/trunk/src/grok/ftests/rest/rest.py	2008-04-16 13:06:40 UTC (rev 85437)
+++ grok/trunk/src/grok/ftests/rest/rest.py	2008-04-16 14:08:38 UTC (rev 85438)
@@ -235,15 +235,8 @@
  We shouldn't be allowed to PUT either::
  
   >>> print http('PUT /app/beta HTTP/1.1')
-  HTTP/1. 500 Internal Server Error
-  Content-Length: 127
-  Content-Type: text/html;charset=utf-8
-  <BLANKLINE>
-  <html><head><title>ForbiddenAttribute</title></head>
-  <body><h2>ForbiddenAttribute</h2>
-  A server error occurred.
-  </body></html>
-  <BLANKLINE>
+  HTTP/1. 404 Not Found
+  Content-Length: 0
 
 XXX shouldn't this really give a FORBIDDEN response?
 

Added: grok/trunk/src/grok/ftests/rest/rest_traverse.py
===================================================================
--- grok/trunk/src/grok/ftests/rest/rest_traverse.py	                        (rev 0)
+++ grok/trunk/src/grok/ftests/rest/rest_traverse.py	2008-04-16 14:08:38 UTC (rev 85438)
@@ -0,0 +1,100 @@
+"""
+Let's examine Grok's REST support in the context of custom traversal to
+verify that works. We set the REST protocol in the code instead of specifying
+it in the URL, just in time (when we traverse to the content object that
+supports REST).
+
+Let's create a simple application with REST support::
+
+  >>> from grok.ftests.rest.rest_traverse import MyApp
+  >>> root = getRootFolder()
+  >>> root['app'] = MyApp()
+
+Let's first look at the index view of the application object::
+
+  >>> response = http_call('GET', 'http://localhost/app')
+  >>> print response.getBody()
+  The index view
+
+Now let's look at the content subobject, which should use REST by default
+because we make sure we set the skin::
+
+  >>> response = http_call('GET', 'http://localhost/app/content')
+  >>> print response.getBody()
+  GET content
+
+The other methods should also work::
+
+  >>> response = http_call('POST', 'http://localhost/app/content')
+  >>> print response.getBody()
+  POST content
+
+  >>> response = http_call('PUT', 'http://localhost/app/content')
+  >>> print response.getBody()
+  PUT content
+
+  >>> response = http_call('DELETE', 'http://localhost/app/content')
+  >>> print response.getBody()
+  DELETE content
+
+Besides the Traverser, we also want to make sure our ``traverse`` method
+on content objects works::
+
+  >>> response = http_call('GET', 'http://localhost/app/content/sub')
+  >>> print response.getBody()
+  GET content
+  >>> response = http_call('PUT', 'http://localhost/app/content/sub')
+  >>> print response.getBody()
+  PUT content
+
+"""
+
+import grok
+from zope.interface import Interface
+from zope.publisher.http import applySkin
+
+class IFoo(Interface):
+    pass
+
+class MyApp(grok.Model, grok.Application):
+    pass
+
+class Traverser(grok.Traverser):
+    grok.context(MyApp)
+
+    def traverse(self, name):
+        if name == 'content':
+            applySkin(self.request, LayerZ, grok.IRESTSkinType)
+            return MyContent()
+
+class Index(grok.View):
+    grok.context(MyApp)
+    def render(self):
+        return "The index view"
+
+class MyContent(grok.Model):
+    def traverse(self, name):
+        if 'sub':
+            return MyContent()
+
+class LayerZ(grok.IRESTLayer):
+    pass
+
+class Z(grok.RESTProtocol):
+    grok.layer(LayerZ)
+
+class ZContentRest(grok.REST):
+    grok.layer(LayerZ)
+    grok.context(MyContent)
+    
+    def GET(self):
+        return "GET content"
+
+    def POST(self):
+        return "POST content"
+
+    def PUT(self):
+        return "PUT content"
+
+    def DELETE(self):
+        return "DELETE content"

Modified: grok/trunk/src/grok/ftests/xmlrpc/xmlrpc.py
===================================================================
--- grok/trunk/src/grok/ftests/xmlrpc/xmlrpc.py	2008-04-16 13:06:40 UTC (rev 85437)
+++ grok/trunk/src/grok/ftests/xmlrpc/xmlrpc.py	2008-04-16 14:08:38 UTC (rev 85438)
@@ -9,18 +9,36 @@
   >>> server.Manfred.dance()
   "Manfred doesn't like to dance."
 
+Let's also check whether we can use XML-RPC with subobjects we
+traverse to::
+
+  >>> server.Manfred.baby.pounce()
+  'baby pounced.'
+  
 """
 import grok
 
 
 class Mammoth(grok.Model):
+    def traverse(self, name):
+        if name == 'baby':
+            return MammothBaby()
+
+class MammothBaby(grok.Model):
     pass
 
-
 class MammothRPC(grok.XMLRPC):
-
+    grok.context(Mammoth)
+    
     def stomp(self):
         return '%s stomped.' % self.context.__name__
 
     def dance(self):
         return '%s doesn\'t like to dance.' % self.context.__name__
+
+class BabyRPC(grok.XMLRPC):
+    grok.context(MammothBaby)
+
+    def pounce(self):
+        return '%s pounced.' % self.context.__name__
+

Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py	2008-04-16 13:06:40 UTC (rev 85437)
+++ grok/trunk/src/grok/meta.py	2008-04-16 14:08:38 UTC (rev 85438)
@@ -22,6 +22,8 @@
                                                IBrowserRequest,
                                                IBrowserPublisher,
                                                IBrowserSkinType)
+from zope.publisher.interfaces.http import IHTTPRequest
+
 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
 from zope.viewlet.interfaces import IViewletManager, IViewlet
 from zope.security.interfaces import IPermission
@@ -365,7 +367,7 @@
 
     def grok(self, name, factory, module_info, config, **kw):
         factory_context = get_context(module_info, factory)
-        adapts = (factory_context, IBrowserRequest)
+        adapts = (factory_context, IHTTPRequest)
 
         config.action(
             discriminator=('adapter', adapts, IBrowserPublisher, ''),



More information about the Checkins mailing list