summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--repoze/bfg/router.py19
-rw-r--r--repoze/bfg/sampleapp/app.py34
-rw-r--r--repoze/bfg/sampleapp/browser.py31
-rw-r--r--repoze/bfg/sampleapp/configure.zcml21
-rw-r--r--repoze/bfg/sampleapp/models.py30
-rw-r--r--repoze/bfg/sampleapp/run.py15
-rw-r--r--repoze/bfg/sampleapp/www/blog.pt19
-rw-r--r--repoze/bfg/sampleapp/www/blog_entry.pt12
-rw-r--r--repoze/bfg/sampleapp/www/blog_view.pt7
-rw-r--r--repoze/bfg/sampleapp/www/contents.pt6
-rw-r--r--repoze/bfg/tests/test_router.py24
11 files changed, 155 insertions, 63 deletions
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index a15ac0e62..efc7406b6 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -4,6 +4,7 @@ from zope.interface import directlyProvides
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 @@ class Router:
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'):
diff --git a/repoze/bfg/sampleapp/app.py b/repoze/bfg/sampleapp/app.py
deleted file mode 100644
index 777feb5ca..000000000
--- a/repoze/bfg/sampleapp/app.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from repoze.bfg.template import TemplateView
-
-from webob import Response
-
-class BlogDefaultView(TemplateView):
- def getInfo(self):
- return {'greeting':'Hello, I\'m the default view',
- 'id':self.context.id}
-
-class BlogWooHooView(TemplateView):
- def getInfo(self):
- return {'greeting':'Woo hoo, I\'m another view' ,
- 'id':self.context.id}
-
-class DefaultView(object):
- def __init__(self, context, request):
- self.context = context
- self.request = request
-
- def __call__(self):
- return Response('Default page, context is %s' % self.context)
-
-if __name__ == '__main__':
- from repoze.bfg import sampleapp
- from repoze.bfg.sampleapp.models import BlogModel
- from repoze.bfg.router import make_app
- blog = BlogModel('myblog')
- root = {'blog':blog}
- def get_root(environ):
- return root
- app = make_app(get_root, sampleapp)
- from paste import httpserver
- httpserver.serve(app, host='0.0.0.0', port='5432')
-
diff --git a/repoze/bfg/sampleapp/browser.py b/repoze/bfg/sampleapp/browser.py
new file mode 100644
index 000000000..5dc354736
--- /dev/null
+++ b/repoze/bfg/sampleapp/browser.py
@@ -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),
+ }
+
diff --git a/repoze/bfg/sampleapp/configure.zcml b/repoze/bfg/sampleapp/configure.zcml
index cddefc172..0e58126a7 100644
--- a/repoze/bfg/sampleapp/configure.zcml
+++ b/repoze/bfg/sampleapp/configure.zcml
@@ -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"
/>
diff --git a/repoze/bfg/sampleapp/models.py b/repoze/bfg/sampleapp/models.py
index 19ef03c8c..975d79142 100644
--- a/repoze/bfg/sampleapp/models.py
+++ b/repoze/bfg/sampleapp/models.py
@@ -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()
diff --git a/repoze/bfg/sampleapp/run.py b/repoze/bfg/sampleapp/run.py
new file mode 100644
index 000000000..d0eb53a51
--- /dev/null
+++ b/repoze/bfg/sampleapp/run.py
@@ -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')
diff --git a/repoze/bfg/sampleapp/www/blog.pt b/repoze/bfg/sampleapp/www/blog.pt
new file mode 100644
index 000000000..ffe8659df
--- /dev/null
+++ b/repoze/bfg/sampleapp/www/blog.pt
@@ -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>
diff --git a/repoze/bfg/sampleapp/www/blog_entry.pt b/repoze/bfg/sampleapp/www/blog_entry.pt
new file mode 100644
index 000000000..20e1b4409
--- /dev/null
+++ b/repoze/bfg/sampleapp/www/blog_entry.pt
@@ -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>
diff --git a/repoze/bfg/sampleapp/www/blog_view.pt b/repoze/bfg/sampleapp/www/blog_view.pt
deleted file mode 100644
index ceedf0cc1..000000000
--- a/repoze/bfg/sampleapp/www/blog_view.pt
+++ /dev/null
@@ -1,7 +0,0 @@
-<div xmlns="http://www.w3.org/1999/xhtml"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
- <div tal:define="info path: view/getInfo">
- <span tal:content="path: info/greeting"/> from
- <span tal:content="path: info/id"/>
- </div>
-</div>
diff --git a/repoze/bfg/sampleapp/www/contents.pt b/repoze/bfg/sampleapp/www/contents.pt
new file mode 100644
index 000000000..eaae20ed5
--- /dev/null
+++ b/repoze/bfg/sampleapp/www/contents.pt
@@ -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>
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index bfa72fe41..25d3bfdcf 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -60,6 +60,30 @@ class RouterTests(unittest.TestCase, PlacelessSetup):
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()