[Checkins] SVN: grok/trunk/ Change grok.traverse decorator to simply looking for 'traverse' method

Martijn Faassen faassen at infrae.com
Tue Oct 31 13:22:02 EST 2006


Log message for revision 71015:
  Change grok.traverse decorator to simply looking for 'traverse' method
  on the model. Make 'traverse' method work for containers so that
  it's not necessary anymore to always define a traverse method on it,
  simplifying grokwiki.
  

Changed:
  U   grok/trunk/grokwiki/src/grokwiki/wiki.py
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/_grok.py
  U   grok/trunk/src/grok/components.py
  A   grok/trunk/src/grok/ftests/traversal/containertraverse.py
  U   grok/trunk/src/grok/ftests/traversal/modeltraverse.py
  U   grok/trunk/src/grok/ftests/traversal/traverser.py
  D   grok/trunk/src/grok/tests/traversal/bogustraverse.py

-=-
Modified: grok/trunk/grokwiki/src/grokwiki/wiki.py
===================================================================
--- grok/trunk/grokwiki/src/grokwiki/wiki.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/grokwiki/src/grokwiki/wiki.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -20,11 +20,6 @@
 class Wiki(grok.Container):
     """This is our wiki application wich contains all wiki pages."""
 
-    @grok.traverse
-    def getWikiPage(self, name):
-        # XXX This should be the default of grok.Container
-        return self[name]
-
 class WikiIndex(grok.View):
     grok.name('index')
 

Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/__init__.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -35,7 +35,6 @@
 from grok.directive import context, name, template, templatedir
 from grok._grok import do_grok as grok  # Avoid name clash within _grok
 from grok._grok import SubscribeDecorator as subscribe
-from grok._grok import traverseDecorator as traverse
 from grok.error import GrokError, GrokImportError
 
 # Our __init__ provides the grok API directly so using 'import grok' is enough.

Modified: grok/trunk/src/grok/_grok.py
===================================================================
--- grok/trunk/src/grok/_grok.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/_grok.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -43,6 +43,8 @@
 _bootstrapped = False
 def bootstrap():
     component.provideAdapter(components.ModelTraverser)
+    component.provideAdapter(components.ContainerTraverser)
+    
     # register the name 'index' as the default view name
     component.provideAdapter('index',
                              adapts=(grok.Model, IBrowserRequest),
@@ -369,14 +371,3 @@
         if subscribers is None:
             frame.f_locals['__grok_subscribers__'] = subscribers = []
         subscribers.append((function, self.subscribed))
-
-def traverseDecorator(function):
-    frame = sys._getframe(1)
-    if not frame_is_class(frame):
-        raise GrokImportError("@grok.traverse can only be used on class "
-                              "level.")
-
-    if '__grok_traverse__' in frame.f_locals:
-        raise GrokImportError("@grok.traverse can only be used once per class.")
-
-    frame.f_locals['__grok_traverse__'] = function

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/components.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -228,11 +228,22 @@
     component.adapts(Model, IBrowserRequest)
 
     def traverse(self, name):
-        traverser = util.class_annotation(self.context, 'grok.traverse', None)
-        if traverser:
-            return traverser(name)
+        traverse = getattr(self.context, 'traverse', None)
+        if traverse:
+            return traverse(name)
 
+class ContainerTraverser(Traverser):
+    component.adapts(Container, IBrowserRequest)
 
+    def traverse(self, name):
+        traverse = getattr(self.context, 'traverse', None)
+        if traverse:
+            result = traverse(name)
+            if result is not None:
+                return result
+        # try to get the item from the container
+        return self.context.get(name)
+        
 class Form(View):
     def _init(self):
         fields = schema_fields(self.context)

Added: grok/trunk/src/grok/ftests/traversal/containertraverse.py
===================================================================
--- grok/trunk/src/grok/ftests/traversal/containertraverse.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/ftests/traversal/containertraverse.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -0,0 +1,86 @@
+"""
+Containers can determine how they want to be traversed by
+implementing a 'traverse' method, but the behavior falls back to
+basic container traversal if the 'traverse' method returns None:
+
+  >>> import grok
+  >>> from grok.ftests.traversal.containertraverse import Herd, Mammoth
+  >>> grok.grok('grok.ftests.traversal.containertraverse')
+  >>> getRootFolder()["herd"] = herd = Herd()
+  >>> herd['manfred'] = Mammoth('Manfred')
+  >>> herd['ellie'] = Mammoth('Ellie')
+
+Let's first try to look up the special traversed item:
+
+  >>> from zope.testbrowser.testing import Browser
+  >>> browser = Browser()
+  >>> browser.handleErrors = False
+  >>> browser.open("http://localhost/herd/special")
+  >>> print browser.contents
+  special view
+  >>> browser.open("http://localhost/herd/special/index")
+  >>> print browser.contents
+  special view
+
+Even if we have a container item called 'special', we should still
+get our special object:
+
+  >>> herd['special'] = Mammoth('Special invisible mammoth')
+  >>> browser.open("http://localhost/herd/special")
+  >>> print browser.contents
+  special view
+  >>> browser.open("http://localhost/herd/special/index")
+  >>> print browser.contents
+  special view
+  
+The fall-back behavior should work for items that aren't traversed:
+
+  >>> browser.open("http://localhost/herd/manfred")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Manfred!</h1>
+  </body>
+  </html>
+
+  >>> browser.open("http://localhost/herd/ellie")
+  >>> print browser.contents
+  <html>
+  <body>
+  <h1>Hello, Ellie!</h1>
+  </body>
+  </html>
+
+"""
+import grok
+
+class Herd(grok.Container):
+
+    def traverse(self, name):
+        if name == 'special':
+            return Special()
+        return None
+    
+class Mammoth(grok.Model):
+
+    def __init__(self, name):
+        self.name = name
+
+class Special(grok.Model):
+    pass
+
+class SpecialIndex(grok.View):
+    grok.context(Special)
+    grok.name('index')
+    
+    def render(self):
+        return "special view"
+
+grok.context(Mammoth)
+index = grok.PageTemplate("""\
+<html>
+<body>
+<h1>Hello, <span tal:replace="context/name/title" />!</h1>
+</body>
+</html>
+""")

Modified: grok/trunk/src/grok/ftests/traversal/modeltraverse.py
===================================================================
--- grok/trunk/src/grok/ftests/traversal/modeltraverse.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/ftests/traversal/modeltraverse.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -1,6 +1,6 @@
 """
-Models can determine how they want to be traversed by using the
-``@grok.traverse`` decorator:
+Models can determine how they want to be traversed by
+implementing a 'traverse' method:
 
   >>> import grok
   >>> from grok.ftests.traversal.modeltraverse import Herd
@@ -31,10 +31,12 @@
 
 class Herd(grok.Model):
 
-    @grok.traverse
     def getMammoth(self, name):
         return Mammoth(name)
 
+    def traverse(self, name):
+        return self.getMammoth(name)
+    
 class Mammoth(grok.Model):
 
     def __init__(self, name):

Modified: grok/trunk/src/grok/ftests/traversal/traverser.py
===================================================================
--- grok/trunk/src/grok/ftests/traversal/traverser.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/ftests/traversal/traverser.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -1,5 +1,5 @@
 """
-Apart from using the ``@grok.traverse`` decorator on a model, you can
+Apart from using the ``traverse`` method on a model, you can
 also create a separate traverser component:
 
   >>> import grok

Deleted: grok/trunk/src/grok/tests/traversal/bogustraverse.py
===================================================================
--- grok/trunk/src/grok/tests/traversal/bogustraverse.py	2006-10-31 17:26:07 UTC (rev 71014)
+++ grok/trunk/src/grok/tests/traversal/bogustraverse.py	2006-10-31 18:22:01 UTC (rev 71015)
@@ -1,35 +0,0 @@
-"""
-You can't use @grok.traverse more than once on a class:
-
-  >>> using_traverse_twice()
-  Traceback (most recent call last):
-   ...
-  GrokImportError: @grok.traverse can only be used once per class.
-
-You can't use @grok.traverse outside a class definition:
-
-  >>> outside_class()
-  Traceback (most recent call last):
-   ...
-  GrokImportError: @grok.traverse can only be used on class level.
-
-
-"""
-import grok
-
-def using_traverse_twice():
-    class Herd(grok.Model):
-
-        @grok.traverse
-        def getMammoth(self, name):
-            pass
-
-        @grok.traverse
-        def getAntilope(self, name):
-            pass
-
-def outside_class():
-
-    @grok.traverse
-    def getMammoth(self, name):
-        pass



More information about the Checkins mailing list