[Repoze-checkins] r1247 - in repoze.bfg/trunk/repoze/bfg: . sampleapp sampleapp/www tests

Chris McDonough chrism at agendaless.com
Tue Jul 8 10:25:47 EDT 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Tue Jul  8 10:25:46 2008
New Revision: 1247

Log:
Redirect on default view if name doesn't end with slash.

Rejigger sample app, adding more templates.


Added:
   repoze.bfg/trunk/repoze/bfg/sampleapp/browser.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/sampleapp/run.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog.pt   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_entry.pt   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/contents.pt   (contents, props changed)
Removed:
   repoze.bfg/trunk/repoze/bfg/sampleapp/app.py
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_view.pt
Modified:
   repoze.bfg/trunk/repoze/bfg/router.py
   repoze.bfg/trunk/repoze/bfg/sampleapp/configure.zcml
   repoze.bfg/trunk/repoze/bfg/sampleapp/models.py
   repoze.bfg/trunk/repoze/bfg/tests/test_router.py

Modified: repoze.bfg/trunk/repoze/bfg/router.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/router.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/router.py	Tue Jul  8 10:25:46 2008
@@ -4,6 +4,7 @@
 
 from webob import Request
 from webob.exc import HTTPNotFound
+from webob.exc import HTTPFound
 
 from repoze.bfg.interfaces import IPublishTraverserFactory
 from repoze.bfg.interfaces import IViewFactory
@@ -24,13 +25,19 @@
         path = environ.get('PATH_INFO', '/')
         traverser = getMultiAdapter((root, request), IPublishTraverserFactory)
         context, name, subpath = traverser(path)
-        request.subpath = subpath
-        app = queryMultiAdapter((context, request), IViewFactory, name=name,
-                                default=_marker)
-        if app is _marker:
-            app = HTTPNotFound(request.url)
+        if (not name) and (not path.endswith('/')):
+            # if this is the default view of the context, and the URL
+            # doesn't end in a slash, redirect to the url + '/' (so we
+            # don't have to play base tag games)
+            app = HTTPFound(add_slash=True)
         else:
-            app = getMultiAdapter((app, request), IWSGIApplicationFactory)
+            request.subpath = subpath
+            app = queryMultiAdapter((context, request), IViewFactory, name=name,
+                                    default=_marker)
+            if app is _marker:
+                app = HTTPNotFound(request.url)
+            else:
+                app = getMultiAdapter((app, request), IWSGIApplicationFactory)
         return app(environ, start_response)
 
 def make_app(root_policy, package=None, filename='configure.zcml'):

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/browser.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/browser.py	Tue Jul  8 10:25:46 2008
@@ -0,0 +1,31 @@
+from webob import Response
+
+from repoze.bfg.template import TemplateView
+
+def datestring(dt):
+    return dt.strftime('%Y-%m-%dT%H:%M:%S')
+
+class BlogDefaultView(TemplateView):
+    def getInfo(self):
+        entrydata = []
+        for name, entry in self.context.items():
+            entrydata.append(
+                {
+                'name':name,
+                'title':entry.title,
+                'author':entry.author,
+                'created':datestring(entry.created),
+                }
+                )
+        return {'name':self.context.__name__, 'entries':entrydata}
+
+class BlogEntryDefaultView(TemplateView):
+    def getInfo(self):
+        return {
+            'name':self.context.__name__,
+            'title':self.context.title,
+            'body':self.context.body,
+            'author':self.context.author,
+            'created':datestring(self.context.created),
+            }
+

Modified: repoze.bfg/trunk/repoze/bfg/sampleapp/configure.zcml
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/sampleapp/configure.zcml	(original)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/configure.zcml	Tue Jul  8 10:25:46 2008
@@ -4,24 +4,27 @@
 
   <include package="repoze.bfg" />
 
+  <!-- the default view for a Blog -->
   <browser:page
-      for=".models.IBlogModel"
-      class=".app.BlogWooHooView"
+      for=".models.IBlog"
+      class=".browser.BlogDefaultView"
+      template="www/blog.pt"
       permission="repoze.view"
-      name="woohoo.html"
-      template="www/blog_view.pt"
       />
 
+  <!-- the default view for a BlogEntry -->
   <browser:page
-      for=".models.IBlogModel"
-      class=".app.BlogDefaultView"
+      for=".models.IBlogEntry"
+      class=".browser.BlogEntryDefaultView"
+      template="www/blog_entry.pt"
       permission="repoze.view"
-      template="www/blog_view.pt"
       />
 
+  <!-- the contents view for any mapping (shows dict members) -->
   <browser:page
-      for="*"
-      class=".app.DefaultView"
+      for=".models.IMapping"
+      template="www/contents.pt"
+      name="contents.html"
       permission="repoze.view"
       />
 

Modified: repoze.bfg/trunk/repoze/bfg/sampleapp/models.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/sampleapp/models.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/models.py	Tue Jul  8 10:25:46 2008
@@ -1,12 +1,28 @@
 from zope.interface import Interface
-from zope.interface import Attribute
 from zope.interface import implements
 
-class IBlogModel(Interface):
-    id = Attribute('id')
+import datetime
 
-class BlogModel(object):
-    implements(IBlogModel)
-    def __init__(self, id):
-        self.id = id
+class IMapping(Interface):
+    pass
 
+class IBlog(Interface):
+    pass
+
+class Blog(dict):
+    implements(IBlog, IMapping)
+    def __init__(self, name):
+        self.__name__ = name
+        dict.__init__(self)
+
+class IBlogEntry(Interface):
+    pass
+
+class BlogEntry(object):
+    implements(IBlogEntry)
+    def __init__(self, name, title, body, author):
+        self.__name__ = name
+        self.title = title
+        self.body =  body
+        self.author = author
+        self.created = datetime.datetime.now()

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/run.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/run.py	Tue Jul  8 10:25:46 2008
@@ -0,0 +1,15 @@
+from repoze.bfg import sampleapp
+from repoze.bfg.sampleapp.models import Blog
+from repoze.bfg.sampleapp.models import BlogEntry
+from repoze.bfg.router import make_app
+
+if __name__ == '__main__':
+    blog = Blog('Sample blog')
+    blog['sample'] = BlogEntry('sample', 'Sample Blog Entry',
+                               '<p>This is a sample blog entry</p>',
+                               'chrism')
+    def get_root(environ):
+        return blog
+    app = make_app(get_root, sampleapp)
+    from paste import httpserver
+    httpserver.serve(app, host='0.0.0.0', port='5432')

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog.pt
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog.pt	Tue Jul  8 10:25:46 2008
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head></head>
+<body tal:define="info view.getInfo()">
+  <h1 tal:content="info.name">Blog Name</h1>
+  <table border="0">
+    <thead>
+      <th>Title</th>
+      <th>Author</th>
+      <th>Created</th>
+    </thead>
+    <tr tal:repeat="entry info.entries">
+      <td><a href="${entry.name}/">${entry.title}</a></td>
+      <td>${entry.author}</td>
+      <td>${entry.created}</td>
+    </tr>
+  </table>
+</body>
+</html>

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_entry.pt
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_entry.pt	Tue Jul  8 10:25:46 2008
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head></head>
+<body>
+  <div tal:define="info view.getInfo()">
+    <p><a href="..">Up</a></p>
+    <h1>${info.title}</h1>
+    <p>by ${info.author}</p>
+    <div tal:content="structure info.body"></div>
+  </div>
+</body>
+</html>

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/www/contents.pt
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/www/contents.pt	Tue Jul  8 10:25:46 2008
@@ -0,0 +1,6 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <div tal:repeat="name context">
+     <a href="${name}/">${name}</a>
+  </div>
+</div>

Modified: repoze.bfg/trunk/repoze/bfg/tests/test_router.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/tests/test_router.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/tests/test_router.py	Tue Jul  8 10:25:46 2008
@@ -60,6 +60,30 @@
         self.assertEqual(status, '404 Not Found')
         self.failUnless('http://localhost:8080' in result[0], result)
 
+    def test_call_default_view_redirect(self):
+        rootpolicy = make_rootpolicy(None)
+        context = DummyContext()
+        traversalfactory = make_traversal_factory(context, '', [])
+        response = DummyResponse()
+        viewfactory = make_view_factory(response)
+        wsgifactory = make_wsgi_factory('200 OK', (), ['Hello world'])
+        environ = self._makeEnviron(PATH_INFO='/doesnt/end/in/slash')
+        self._registerTraverserFactory(traversalfactory, '', None, None)
+        self._registerViewFactory(viewfactory, '', None, None)
+        self._registerWSGIFactory(wsgifactory, '', None, None)
+        router = self._makeOne(rootpolicy)
+        start_response = DummyStartResponse()
+        result = router(environ, start_response)
+        headers = start_response.headers
+        self.assertEqual(len(headers), 3)
+        self.assertEqual(
+            headers[0],
+            ('content-type', 'text/html; charset=UTF-8'))
+        self.assertEqual(
+            headers[1],
+            ('location', 'http://localhost:8080/doesnt/end/in/slash/'))
+        self.assertEqual(start_response.status, '302 Found')
+
     def test_call_view_registered_nonspecific_default_path(self):
         rootpolicy = make_rootpolicy(None)
         context = DummyContext()


More information about the Repoze-checkins mailing list