diff options
| author | Chris McDonough <chrism@agendaless.com> | 2008-07-14 07:31:31 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2008-07-14 07:31:31 +0000 |
| commit | 85427fa0479aefd59bd55dca397b9a36277edade (patch) | |
| tree | cafad1ccb7d3f4c9a0b953adba78cd8caae70e93 | |
| parent | 9fcb68e1563e22eed1fd7cbbd10993cff30242f8 (diff) | |
| download | pyramid-85427fa0479aefd59bd55dca397b9a36277edade.tar.gz pyramid-85427fa0479aefd59bd55dca397b9a36277edade.tar.bz2 pyramid-85427fa0479aefd59bd55dca397b9a36277edade.zip | |
Less magical templated view model: make custom template views responsible for
knowing their template's name.
| -rw-r--r-- | README.txt | 23 | ||||
| -rw-r--r-- | repoze/bfg/__init__.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/interfaces.py | 4 | ||||
| -rw-r--r-- | repoze/bfg/meta.zcml | 8 | ||||
| -rw-r--r-- | repoze/bfg/sampleapp/configure.zcml | 20 | ||||
| -rw-r--r-- | repoze/bfg/sampleapp/views.py | 17 | ||||
| -rw-r--r-- | repoze/bfg/template.py | 74 | ||||
| -rw-r--r-- | repoze/bfg/tests/fixtureapp/configure.zcml | 12 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_template.py | 117 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_view.py | 64 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 126 | ||||
| -rw-r--r-- | repoze/bfg/view.py | 27 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 89 |
13 files changed, 393 insertions, 190 deletions
diff --git a/README.txt b/README.txt index 73548ee9d..104f321cb 100644 --- a/README.txt +++ b/README.txt @@ -266,7 +266,7 @@ views.py A views.py module might look like so:: from webob import Response - from repoze.bfg.template import TemplateView + from repoze.bfg.view import TemplateView class MyHelloView(object): def __init__(self, context, request): @@ -274,11 +274,15 @@ A views.py module might look like so:: self.request = request def __call__(self): - response = Response('Hello from %s @ %s' % (self.context.__name__, - self.request['PATH_INFO']) + response = Response('Hello from %s @ %s' % ( + self.context.__name__, + self.request.environ['PATH_INFO'])) return response class MyTemplateView(TemplateView): + + template = 'templates/my.pt' + def getInfo(self): return {'name':self.context.__name__} @@ -318,24 +322,23 @@ configure.zcml A view registry might look like so:: <configure xmlns="http://namespaces.zope.org/zope" - xmlns:browser="http://namespaces.repoze.org/browser" - i18n_domain="repoze.bfg"> + xmlns:bfg="http://namespaces.repoze.org/bfg" + i18n_domain="repoze.bfg"> <!-- this must be included for the view declarations to work --> <include package="repoze.bfg" /> <!-- the default view for a MyModel --> - <browser:page + <bfg:view for=".models.IMyModel" - class=".views.MyHelloView" + factory=".views.MyHelloView" permission="repoze.view" /> <!-- the templated view for a MyModel --> - <browser:page + <bfg:view for=".models.IMyModel" - class=".views.MyTemplateView" - template="templates/my.pt" + factory=".views.MyTemplateView" name="templated.html" permission="repoze.view" /> diff --git a/repoze/bfg/__init__.py b/repoze/bfg/__init__.py index f89ca36f1..76bdd005d 100644 --- a/repoze/bfg/__init__.py +++ b/repoze/bfg/__init__.py @@ -1,2 +1,2 @@ from repoze.bfg.router import make_app # for import elsewhere - +from repoze.bfg.view import View diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index eac3ecdae..005d30027 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -39,3 +39,7 @@ class IWSGIApplicationFactory(Interface): def __call__(context, request, view): """ Return an object that implements IWSGIApplication """ +class ITemplateFactory(Interface): + def __call__(template_path): + """ Return an IView given a template path """ + diff --git a/repoze/bfg/meta.zcml b/repoze/bfg/meta.zcml index c27123485..72e963ae6 100644 --- a/repoze/bfg/meta.zcml +++ b/repoze/bfg/meta.zcml @@ -2,12 +2,12 @@ xmlns="http://namespaces.zope.org/zope" xmlns:meta="http://namespaces.zope.org/meta"> - <meta:directives namespace="http://namespaces.repoze.org/browser"> + <meta:directives namespace="http://namespaces.repoze.org/bfg"> <meta:directive - name="page" - schema=".zcml.IPageDirective" - handler=".zcml.page" + name="view" + schema=".zcml.IViewDirective" + handler=".zcml.view" /> </meta:directives> diff --git a/repoze/bfg/sampleapp/configure.zcml b/repoze/bfg/sampleapp/configure.zcml index dfe30d56b..d5cba4d33 100644 --- a/repoze/bfg/sampleapp/configure.zcml +++ b/repoze/bfg/sampleapp/configure.zcml @@ -1,27 +1,25 @@ <configure xmlns="http://namespaces.zope.org/zope" - xmlns:browser="http://namespaces.repoze.org/browser" + xmlns:bfg="http://namespaces.repoze.org/bfg" i18n_domain="repoze.bfg"> <include package="repoze.bfg" /> <!-- the default view for a Blog --> - <browser:page + <bfg:view for=".models.IBlog" - class=".views.BlogDefaultView" - template="templates/blog.pt" + factory=".views.BlogDefaultView" permission="repoze.view" /> <!-- the default view for a BlogEntry --> - <browser:page + <bfg:view for=".models.IBlogEntry" - class=".views.BlogEntryDefaultView" - template="templates/blog_entry.pt" + factory=".views.BlogEntryDefaultView" permission="repoze.view" /> <!-- the add template for a BlogEntry --> - <browser:page + <bfg:view for=".models.IBlog" template="templates/blog_entry_add.pt" name="add_entry.html" @@ -29,15 +27,15 @@ /> <!-- the add handler for a BlogEntry --> - <browser:page + <bfg:view for=".models.IBlog" - class=".views.BlogEntryAddView" + factory=".views.BlogEntryAddView" permission="repoze.view" name="add_entry_handler" /> <!-- the contents view for any mapping (shows dict members) --> - <browser:page + <bfg:view for=".models.IMapping" template="templates/contents.pt" name="contents.html" diff --git a/repoze/bfg/sampleapp/views.py b/repoze/bfg/sampleapp/views.py index 3d61802bb..98b285b67 100644 --- a/repoze/bfg/sampleapp/views.py +++ b/repoze/bfg/sampleapp/views.py @@ -2,13 +2,18 @@ import time from webob.exc import HTTPFound -from repoze.bfg.template import TemplateView +from repoze.bfg.view import TemplateView +from repoze.bfg.view import View + from repoze.bfg.sampleapp.models import BlogEntry def datestring(dt): - return dt.strftime('%Y-%m-%dT%H:%M:%S') + return dt.strftime('%Y-%m-%d %H:%M:%S') class BlogDefaultView(TemplateView): + + template = 'templates/blog.pt' + def getInfo(self): entrydata = [] for name, entry in self.context.items(): @@ -23,6 +28,9 @@ class BlogDefaultView(TemplateView): return {'name':self.context.__name__, 'entries':entrydata} class BlogEntryDefaultView(TemplateView): + + template = 'templates/blog_entry.pt' + def getInfo(self): return { 'name':self.context.__name__, @@ -32,10 +40,7 @@ class BlogEntryDefaultView(TemplateView): 'created':datestring(self.context.created), } -class BlogEntryAddView(object): - def __init__(self, context, request): - self.context = context - self.request = request +class BlogEntryAddView(View): def __call__(self): author = self.request.params['author'] diff --git a/repoze/bfg/template.py b/repoze/bfg/template.py index b73a095f3..c3e34c99a 100644 --- a/repoze/bfg/template.py +++ b/repoze/bfg/template.py @@ -1,40 +1,56 @@ +import os +import sys + +from zope.component import queryUtility +from zope.component.interfaces import ComponentLookupError +from zope.component import getSiteManager + from zope.interface import classProvides from zope.interface import implements -from z3c.pt import PageTemplateFile as PageTemplateFileBase from webob import Response -from repoze.bfg.interfaces import IViewFactory from repoze.bfg.interfaces import IView +from repoze.bfg.interfaces import ITemplateFactory - -class PageTemplateFile(PageTemplateFileBase): - def render(self, *arg, **kw): - result = PageTemplateFileBase.render(self, *arg, **kw) - return Response(result) - -class ViewPageTemplateFile(property): - def __init__(self, template): - self.template = template - property.__init__(self, self.render) - - def render(self, view): - def template(**kwargs): - return self.template.render(view=view, - context=view.context, - request=view.request, - options=kwargs) - return template - -class TemplateView(object): - classProvides(IViewFactory) +class Z3CPTTemplateFactory(object): + classProvides(ITemplateFactory) implements(IView) - def __init__(self, context, request): - self.context = context - self.request = request + def __init__(self, path): + from z3c.pt import PageTemplateFile + self.template = PageTemplateFile(path) def __call__(self, *arg, **kw): - """ See metaconfigure.py to see where 'index' comes from """ - return self.index(*arg, **kw) - + result = self.template.render(**kw) + response = Response(result) + return response + +def package_path(package): + return os.path.abspath(os.path.dirname(package.__file__)) + +def render_template(view, template_path, **kw): + # XXX use pkg_resources + + if not os.path.isabs(template_path): + package_globals = sys._getframe(1).f_globals + package_name = package_globals['__name__'] + package = sys.modules[package_name] + prefix = package_path(package) + template_path = os.path.join(prefix, template_path) + + template = queryUtility(IView, template_path) + + if template is None: + if not os.path.exists(template_path): + raise ValueError('Missing template file: %s' % template_path) + template = Z3CPTTemplateFactory(template_path) + try: + sm = getSiteManager() + except ComponentLookupError: + pass + else: + sm.registerUtility(template, IView, name=template_path) + + return template(view=view, context=view.context, request=view.request, + options=kw) diff --git a/repoze/bfg/tests/fixtureapp/configure.zcml b/repoze/bfg/tests/fixtureapp/configure.zcml index 0e91afbc0..f08cd58ad 100644 --- a/repoze/bfg/tests/fixtureapp/configure.zcml +++ b/repoze/bfg/tests/fixtureapp/configure.zcml @@ -1,14 +1,20 @@ <configure xmlns="http://namespaces.zope.org/zope" - xmlns:browser="http://namespaces.repoze.org/browser" + xmlns:bfg="http://namespaces.repoze.org/bfg" i18n_domain="repoze.bfg"> <include package="repoze.bfg" /> - <browser:page + <bfg:view + for=".models.IFixture" + factory=".views.FixtureView" + permission="repoze.view" + /> + + <bfg:view for=".models.IFixture" - class=".views.FixtureView" template="templates/fixture.pt" permission="repoze.view" + name="fixture.html" /> </configure> diff --git a/repoze/bfg/tests/test_template.py b/repoze/bfg/tests/test_template.py index 322810fb4..59dce3efa 100644 --- a/repoze/bfg/tests/test_template.py +++ b/repoze/bfg/tests/test_template.py @@ -19,8 +19,7 @@ class Base(PlacelessSetup): here = os.path.abspath(os.path.dirname(__file__)) return os.path.join(here, 'fixtures', name) - -class PageTemplateFileTests(unittest.TestCase, Base): +class Z3CPTTemplateFactoryTests(unittest.TestCase, Base): def setUp(self): Base.setUp(self) @@ -28,76 +27,90 @@ class PageTemplateFileTests(unittest.TestCase, Base): Base.tearDown(self) def _getTargetClass(self): - from repoze.bfg.template import PageTemplateFile - return PageTemplateFile + from repoze.bfg.template import Z3CPTTemplateFactory + return Z3CPTTemplateFactory def _makeOne(self, *arg, **kw): klass = self._getTargetClass() return klass(*arg, **kw) - def test_render(self): + def test_instance_conforms_to_IView(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import IView + path = self._getTemplatePath('minimal.pt') + verifyObject(IView, self._makeOne(path)) + + def test_class_conforms_to_IView(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import IView + verifyClass(IView, self._getTargetClass()) + + def test_class_conforms_to_ITemplateFactory(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import ITemplateFactory + verifyObject(ITemplateFactory, self._getTargetClass()) + + def test_call(self): self._zcmlConfigure() minimal = self._getTemplatePath('minimal.pt') instance = self._makeOne(minimal) - result = instance.render() + result = instance() from webob import Response self.failUnless(isinstance(result, Response)) self.assertEqual(result.app_iter, ['<div>\n</div>']) self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) - -class ViewPageTemplateFileTests(unittest.TestCase, Base): - def setUp(self): - Base.setUp(self) - def tearDown(self): - Base.tearDown(self) - - def _getTargetClass(self): - from repoze.bfg.template import ViewPageTemplateFile - return ViewPageTemplateFile - - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) - - def test_render(self): - self._zcmlConfigure() - f = DummyPageTemplateFile() - instance = self._makeOne(f) - class View: - context = 'context' - request = 'request' - view = View() - template = instance.render(view) - args, kw = template(foo='bar') - self.assertEqual(kw['request'], 'request') - self.assertEqual(kw['options'], {'foo':'bar'}) - self.assertEqual(kw['context'], 'context') - self.assertEqual(kw['view'], view) - -class TemplateViewTests(unittest.TestCase, Base): +class RenderTemplateTests(unittest.TestCase, Base): def setUp(self): Base.setUp(self) def tearDown(self): Base.tearDown(self) - def _getTargetClass(self): - from repoze.bfg.template import TemplateView - return TemplateView + def _getFUT(self): + from repoze.bfg.template import render_template + return render_template - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) + def test_nonabs_unregistered(self): + self._zcmlConfigure() + from zope.component import queryUtility + from repoze.bfg.interfaces import IView + minimal = self._getTemplatePath('minimal.pt') + self.assertEqual(queryUtility(IView, minimal), None) + view = DummyView() + render = self._getFUT() + result = render(view, minimal) + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, ['<div>\n</div>']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + from repoze.bfg.template import Z3CPTTemplateFactory + self.failUnless(isinstance(queryUtility(IView, minimal), + Z3CPTTemplateFactory)) - def test_call(self): - view = self._makeOne(None, None) - _marker = () - view.index = lambda *arg, **kw: _marker - result = view('foo') - self.assertEqual(result, _marker) + def test_nonabs_registered(self): + self._zcmlConfigure() + from zope.component import getGlobalSiteManager + from zope.component import queryUtility + from repoze.bfg.template import Z3CPTTemplateFactory + from repoze.bfg.interfaces import IView + minimal = self._getTemplatePath('minimal.pt') + utility = Z3CPTTemplateFactory(minimal) + gsm = getGlobalSiteManager() + gsm.registerUtility(utility, IView, name=minimal) + view = DummyView() + render = self._getFUT() + result = render(view, minimal) + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, ['<div>\n</div>']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + self.assertEqual(queryUtility(IView, minimal), utility) + +class DummyView: + context = 'context' + request = 'request' -class DummyPageTemplateFile: - def render(self, *arg, **kw): - return arg, kw diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py new file mode 100644 index 000000000..7d87b9c83 --- /dev/null +++ b/repoze/bfg/tests/test_view.py @@ -0,0 +1,64 @@ +import unittest + +from zope.component.testing import PlacelessSetup + +class Base(PlacelessSetup): + def setUp(self): + PlacelessSetup.setUp(self) + + def tearDown(self): + PlacelessSetup.tearDown(self) + + def _zcmlConfigure(self): + import repoze.bfg + import zope.configuration.xmlconfig + zope.configuration.xmlconfig.file('configure.zcml', package=repoze.bfg) + + def _getTemplatePath(self, name): + import os + here = os.path.abspath(os.path.dirname(__file__)) + return os.path.join(here, 'fixtures', name) + +class ViewTests(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.view import View + return View + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_call(self): + view = self._makeOne(None, None) + self.assertRaises(NotImplementedError, view) + +class TemplateViewTests(unittest.TestCase, Base): + def setUp(self): + Base.setUp(self) + + def tearDown(self): + Base.tearDown(self) + + def _getTargetClass(self): + from repoze.bfg.view import TemplateView + return TemplateView + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_call(self): + self._zcmlConfigure() + view = self._makeOne(None, None) + view.template = self._getTemplatePath('minimal.pt') + result = view(foo='foo') + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, ['<div>\n</div>']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + + def test_call_no_template(self): + self._zcmlConfigure() + view = self._makeOne(None, None) + self.assertRaises(ValueError, view) diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index bcb065d9e..11f392f78 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -2,7 +2,7 @@ import unittest from zope.component.testing import PlacelessSetup -class TestPageDirective(unittest.TestCase, PlacelessSetup): +class TestViewDirective(unittest.TestCase, PlacelessSetup): def setUp(self): PlacelessSetup.setUp(self) @@ -10,8 +10,8 @@ class TestPageDirective(unittest.TestCase, PlacelessSetup): PlacelessSetup.tearDown(self) def _getFUT(self): - from repoze.bfg.zcml import page - return page + from repoze.bfg.zcml import view + return view def test_no_class_or_template(self): f = self._getFUT() @@ -29,45 +29,107 @@ class TestPageDirective(unittest.TestCase, PlacelessSetup): def test_only_template(self): f = self._getFUT() context = DummyContext() - f(context, 'repoze.view', None, template='minimal.pt') + class IFoo: + pass + f(context, 'repoze.view', IFoo, template='minimal.pt') actions = context.actions + from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewFactory from zope.component.zcml import handler - expected0 = ('view', None, '', IRequest, IViewFactory) - expected1 = handler - self.assertEqual(actions[0]['discriminator'], expected0) - self.assertEqual(actions[0]['callable'], expected1) - self.assertEqual(actions[0]['args'][0], 'registerAdapter') - import types - self.failUnless(isinstance(actions[0]['args'][1], types.FunctionType)) - self.assertEqual(actions[0]['args'][2], (None, IRequest)) - self.assertEqual(actions[0]['args'][3], IViewFactory) - self.assertEqual(actions[0]['args'][4], '') - self.assertEqual(actions[0]['args'][5], None) - - def test_template_and_class(self): + from zope.component.interface import provideInterface + + self.assertEqual(len(actions), 3) + + regutil_discriminator = ('utility', IView, context.path('minimal.pt')) + regutil = actions[0] + self.assertEqual(regutil['discriminator'], regutil_discriminator) + self.assertEqual(regutil['callable'], handler) + self.assertEqual(regutil['args'][0], 'registerUtility') + self.assertEqual(regutil['args'][1].template.filename, + context.path('minimal.pt')) + self.assertEqual(regutil['args'][2], IView) + self.assertEqual(regutil['args'][3], context.path('minimal.pt')) + + provide = actions[1] + self.assertEqual(provide['discriminator'], None) + self.assertEqual(provide['callable'], provideInterface) + self.assertEqual(provide['args'][0], '') + self.assertEqual(provide['args'][1], IFoo) + + regadapt = actions[2] + regadapt_discriminator = ('view', IFoo, '', IRequest, IViewFactory) + self.assertEqual(regadapt['discriminator'], regadapt_discriminator) + self.assertEqual(regadapt['callable'], handler) + self.assertEqual(regadapt['args'][0], 'registerAdapter') + self.assertEqual(regadapt['args'][1].template, + context.path('minimal.pt')) + self.assertEqual(regadapt['args'][2], (IFoo, IRequest)) + self.assertEqual(regadapt['args'][3], IViewFactory) + self.assertEqual(regadapt['args'][4], '') + self.assertEqual(regadapt['args'][5], None) + + def test_only_factory(self): f = self._getFUT() context = DummyContext() - f(context, 'repoze.view', None, template='minimal.pt', - class_=DummyViewClass) + class IFoo: + pass + f(context, 'repoze.view', IFoo, factory=Dummy) actions = context.actions from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewFactory from zope.component.zcml import handler - expected0 = ('view', None, '', IRequest, IViewFactory) - expected1 = handler - self.assertEqual(actions[0]['discriminator'], expected0) - self.assertEqual(actions[0]['callable'], expected1) - self.assertEqual(actions[0]['args'][0], 'registerAdapter') - import types - self.failUnless(isinstance(actions[0]['args'][1], types.FunctionType)) - self.assertEqual(actions[0]['args'][2], (None, IRequest)) - self.assertEqual(actions[0]['args'][3], IViewFactory) - self.assertEqual(actions[0]['args'][4], '') - self.assertEqual(actions[0]['args'][5], None) - -class DummyViewClass: + from zope.component.interface import provideInterface + + self.assertEqual(len(actions), 2) + + provide = actions[0] + self.assertEqual(provide['discriminator'], None) + self.assertEqual(provide['callable'], provideInterface) + self.assertEqual(provide['args'][0], '') + self.assertEqual(provide['args'][1], IFoo) + + regadapt = actions[1] + regadapt_discriminator = ('view', IFoo, '', IRequest, IViewFactory) + self.assertEqual(regadapt['discriminator'], regadapt_discriminator) + self.assertEqual(regadapt['callable'], handler) + self.assertEqual(regadapt['args'][0], 'registerAdapter') + self.assertEqual(regadapt['args'][1], Dummy) + self.assertEqual(regadapt['args'][2], (IFoo, IRequest)) + self.assertEqual(regadapt['args'][3], IViewFactory) + self.assertEqual(regadapt['args'][4], '') + self.assertEqual(regadapt['args'][5], None) + + def test_template_and_factory_raises(self): + f = self._getFUT() + context = DummyContext() + from zope.configuration.exceptions import ConfigurationError + self.assertRaises(ConfigurationError, f, context, 'repoze.view', None, + Dummy, 'minimal.html', 'minimal.pt') + +class TestTemplateViewFactory(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.zcml import TemplateViewFactory + return TemplateViewFactory + + def _makeOne(self, template): + return self._getTargetClass()(template) + + def test_instance_conforms_to_IViewFactory(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import IViewFactory + verifyObject(IViewFactory, self._makeOne('a')) + + def test_call(self): + context = DummyContext() + template = context.path('minimal.pt') + factory = self._makeOne(template) + view = factory(None, None) + from repoze.bfg.view import TemplateView + self.failUnless(isinstance(view, TemplateView)) + + +class Dummy: pass class DummyContext: diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py new file mode 100644 index 000000000..07c3d183d --- /dev/null +++ b/repoze/bfg/view.py @@ -0,0 +1,27 @@ +from repoze.bfg.template import render_template + +class View(object): + """ Convenience base class for user-defined views """ + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self, **kw): + raise NotImplementedError + +class TemplateView(View): + template = None + def __call__(self, **kw): + if self.template is None: + raise ValueError('a "template" attribute must be attached to ' + 'a TemplateView') + return render_template(self, self.template, **kw) + + def __repr__(self): + klass = self.__class__ + return '<%s.%s object at %s for %s>' % (klass.__module__, + klass.__mame__, + id(self), + self.template) + + diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index bc136055a..a1a2497b5 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -1,62 +1,68 @@ import os -from zope.schema import TextLine -from zope.configuration.fields import Path -from zope.interface import Interface from zope.component.zcml import handler from zope.component.interface import provideInterface from zope.configuration.exceptions import ConfigurationError from zope.configuration.fields import GlobalObject +from zope.configuration.fields import Path + +from zope.schema import TextLine +from zope.interface import Interface +from zope.interface import implements + from zope.security.zcml import Permission from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IViewFactory +from repoze.bfg.interfaces import IView + +from repoze.bfg.template import Z3CPTTemplateFactory +from repoze.bfg.view import TemplateView -from repoze.bfg.template import ViewPageTemplateFile -from repoze.bfg.template import PageTemplateFile +class TemplateViewFactory(object): + """ Pickleable template view factory """ -class ViewBase: - def __init__(self, context, request): - self.context = context - self.request = request + implements(IViewFactory) - def __call__(self, *arg, **kw): - return self.index(*arg, **kw) + def __init__(self, template): + self.template = template -def page(_context, + def __call__(self, context, request): + factory = TemplateView(context, request) + factory.template = self.template + return factory + +def view(_context, permission, - for_, + for_=None, + factory=None, name="", template=None, - class_=None, ): # XXX we do nothing yet with permission - if not (class_ or template): - raise ConfigurationError("Must specify a class or a template") + if template and factory: + raise ConfigurationError('A template must not be specified if a ' + 'factory is also specified') - if template: - template = os.path.abspath(str(_context.path(template))) - if not os.path.isfile(template): - raise ConfigurationError("No such file", template) - - template_inst = PageTemplateFile(template) - - def view_factory(context, request): - if template: - if class_ is None: - base = ViewBase - else: - base = class_ - class ViewClass(base): - __name__ = name - index = ViewPageTemplateFile(template_inst) - return ViewClass(context, request) - - else: - return class_(context, request) + if not (template or factory): + raise ConfigurationError( + 'One of template or factory must be specified') + + if template: + template_abs = os.path.abspath(str(_context.path(template))) + if not os.path.exists(template_abs): + raise ConfigurationError('No template file named %s' % template_abs) + utility = Z3CPTTemplateFactory(template_abs) + _context.action( + discriminator = ('utility', IView, template_abs), + callable = handler, + args = ('registerUtility', utility, IView, template_abs), + ) + factory = TemplateViewFactory(template_abs) + if for_ is not None: _context.action( discriminator = None, @@ -68,11 +74,11 @@ def page(_context, discriminator = ('view', for_, name, IRequest, IViewFactory), callable = handler, args = ('registerAdapter', - view_factory, (for_, IRequest), IViewFactory, name, + factory, (for_, IRequest), IViewFactory, name, _context.info), ) -class IPageDirective(Interface): +class IViewDirective(Interface): """ The page directive is used to create views that provide a single url or page. @@ -92,7 +98,7 @@ class IPageDirective(Interface): required=True ) - class_ = GlobalObject( + factory = GlobalObject( title=u"Class", description=u"A class that provides a __call__ used by the view.", required=False, @@ -108,9 +114,8 @@ class IPageDirective(Interface): template = Path( title=u"The name of a template that implements the page.", - description=u""" - Refers to a file containing a page template (should end in - extension '.pt' or '.html').""", + description=u"""Refers to a file containing a z3c.pt page template""", required=False ) + |
