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

Chris McDonough chrism at agendaless.com
Tue Jul 8 00:37:08 EDT 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Tue Jul  8 00:37:07 2008
New Revision: 1243

Log:
Add a browser:page directive type and make our sample app use it.


Added:
   repoze.bfg/trunk/repoze/bfg/meta.zcml   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/metaconfigure.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/
   repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_view.pt   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/template.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/tests/fixtures/
   repoze.bfg/trunk/repoze/bfg/tests/fixtures/minimal.pt   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/tests/test_metaconfigure.py   (contents, props changed)
   repoze.bfg/trunk/repoze/bfg/tests/test_template.py   (contents, props changed)
Modified:
   repoze.bfg/trunk/repoze/bfg/configure.zcml
   repoze.bfg/trunk/repoze/bfg/sampleapp/app.py
   repoze.bfg/trunk/repoze/bfg/sampleapp/configure.zcml

Modified: repoze.bfg/trunk/repoze/bfg/configure.zcml
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/configure.zcml	(original)
+++ repoze.bfg/trunk/repoze/bfg/configure.zcml	Tue Jul  8 00:37:07 2008
@@ -2,6 +2,12 @@
 	           i18n_domain="repoze.bfg">
 
   <include package="z3c.pt" />
+  <include package="zope.security" file="meta.zcml"/>
+
+  <permission
+      id="repoze.view"
+      title="View"
+      />
 
   <adapter
       factory=".traversal.NaivePublishTraverser"
@@ -15,4 +21,6 @@
       for="* *"
    />
 
+  <include file="meta.zcml" />
+
 </configure>

Added: repoze.bfg/trunk/repoze/bfg/meta.zcml
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/meta.zcml	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.repoze.org/browser">
+
+    <meta:directive
+        name="page"
+        schema=".metaconfigure.IPageDirective"
+        handler=".metaconfigure.page"
+        />
+
+  </meta:directives>
+
+</configure>

Added: repoze.bfg/trunk/repoze/bfg/metaconfigure.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/metaconfigure.py	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,113 @@
+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.security.zcml import Permission
+
+from repoze.bfg.interfaces import IRequest
+from repoze.bfg.interfaces import IViewFactory
+
+from repoze.bfg.template import ViewPageTemplateFile
+
+class ViewBase:
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def __call__(self, *arg, **kw):
+        return self.index(*arg, **kw)
+
+def page(_context,
+         permission,
+         for_,
+         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:
+        template = os.path.abspath(str(_context.path(template)))
+        if not os.path.isfile(template):
+            raise ConfigurationError("No such file", 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)
+            return ViewClass(context, request)
+                    
+        else:
+            return class_(context, request)
+        
+    if for_ is not None:
+        _context.action(
+            discriminator = None,
+            callable = provideInterface,
+            args = ('', for_)
+            )
+
+    _context.action(
+        discriminator = ('view', for_, name, IRequest, IViewFactory),
+        callable = handler,
+        args = ('registerAdapter',
+                view_factory, (for_, IRequest), IViewFactory, name,
+                _context.info),
+        )
+
+class IPageDirective(Interface):
+    """
+    The page directive is used to create views that provide a single
+    url or page.
+
+    The page directive creates a new view class from a given template
+    and/or class and registers it.
+    """
+
+    for_ = GlobalObject(
+        title=u"The interface or class this view is for.",
+        required=False
+        )
+
+    permission = Permission(
+        title=u"Permission",
+        description=u"The permission needed to use the view.",
+        required=True
+        )
+
+    class_ = GlobalObject(
+        title=u"Class",
+        description=u"A class that provides a __call__ used by the view.",
+        required=False,
+        )
+
+    name = TextLine(
+        title=u"The name of the page (view)",
+        description=u"""
+        The name shows up in URLs/paths. For example 'foo' or
+        'foo.html'.""",
+        required=False,
+        )
+
+    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').""",
+        required=False
+        )
+

Modified: repoze.bfg/trunk/repoze/bfg/sampleapp/app.py
==============================================================================
--- repoze.bfg/trunk/repoze/bfg/sampleapp/app.py	(original)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/app.py	Tue Jul  8 00:37:07 2008
@@ -1,32 +1,21 @@
-from zope.interface import classProvides
-from zope.interface import implements
-
-from repoze.bfg.interfaces import IViewFactory
-from repoze.bfg.interfaces import IView
+from repoze.bfg.template import View
 
 from webob import Response
 
-class View(object):
-    classProvides(IViewFactory)
-    implements(IView)
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
 class BlogDefaultView(View):
-    def __call__(self):
-        return Response('Hello world from the blog %s!' % self.context.id)
+    def getInfo(self):
+        return {'greeting':'Hello, I\'m the default view',
+                'id':self.context.id}
 
 class BlogWooHooView(View):
-    def __call__(self):
-        return Response('Woo hoo from the blog named %s!' % self.context.id)
+    def getInfo(self):
+        return {'greeting':'Woo hoo, I\'m another view' ,
+                'id':self.context.id}
 
 class DefaultView(View):
     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

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 00:37:07 2008
@@ -1,25 +1,28 @@
 <configure xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.repoze.org/browser"
 	           i18n_domain="repoze.bfg">
 
   <include package="repoze.bfg" />
 
-  <adapter
-        factory=".app.BlogDefaultView"
-        provides="repoze.bfg.interfaces.IViewFactory"
-        for=".models.IBlogModel repoze.bfg.interfaces.IRequest"
-   />
+  <browser:page
+      for=".models.IBlogModel"
+      class=".app.BlogWooHooView"
+      permission="repoze.view"
+      name="woohoo.html"
+      template="www/blog_view.pt"
+      />
 
-  <adapter
-        factory=".app.BlogWooHooView"
-        provides="repoze.bfg.interfaces.IViewFactory"
-        for=".models.IBlogModel repoze.bfg.interfaces.IRequest"
-        name="woohoo.html"
-   />
+  <browser:page
+      for=".models.IBlogModel"
+      class=".app.BlogDefaultView"
+      permission="repoze.view"
+      template="www/blog_view.pt"
+      />
 
-  <adapter
-        factory=".app.DefaultView"
-        provides="repoze.bfg.interfaces.IViewFactory"
-        for="* repoze.bfg.interfaces.IRequest"
-   />
+  <browser:page
+      for="*"
+      class=".app.DefaultView"
+      permission="repoze.view"
+      />
 
 </configure>

Added: repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_view.pt
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/sampleapp/www/blog_view.pt	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,7 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <div tal:define="info view.getInfo()">
+    <span tal:content="info.greeting"/> from
+    <span tal:content="info.id"/>
+  </div>
+</div>

Added: repoze.bfg/trunk/repoze/bfg/template.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/template.py	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,40 @@
+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
+
+
+class PageTemplateFile(PageTemplateFileBase):
+    def render(self, *arg, **kw):
+        result = PageTemplateFileBase.render(self, *arg, **kw)
+        return Response(result)
+
+class ViewPageTemplateFile(property):
+    def __init__(self, filename, **kwargs):
+        self.template = PageTemplateFile(filename, **kwargs)
+        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 BrowserView(object):
+    classProvides(IViewFactory)
+    implements(IView)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def __call__(self, *arg, **kw):
+        """ See metaconfigure.py to see where 'index' comes from """
+        return self.index(*arg, **kw)
+

Added: repoze.bfg/trunk/repoze/bfg/tests/fixtures/minimal.pt
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/tests/fixtures/minimal.pt	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,3 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+</div>

Added: repoze.bfg/trunk/repoze/bfg/tests/test_metaconfigure.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/tests/test_metaconfigure.py	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,92 @@
+import unittest
+
+from zope.component.testing import PlacelessSetup
+
+class TestPageDirective(unittest.TestCase, PlacelessSetup):
+    def setUp(self):
+        PlacelessSetup.setUp(self)
+
+    def tearDown(self):
+        PlacelessSetup.tearDown(self)
+
+    def _getFUT(self):
+        from repoze.bfg.metaconfigure import page
+        return page
+
+    def test_no_class_or_template(self):
+        f = self._getFUT()
+        from zope.configuration.exceptions import ConfigurationError
+        context = DummyContext()
+        self.assertRaises(ConfigurationError, f, context, 'repoze.view', None)
+
+    def test_no_such_file(self):
+        f = self._getFUT()
+        from zope.configuration.exceptions import ConfigurationError
+        context = DummyContext()
+        self.assertRaises(ConfigurationError, f, context, 'repoze.view', None,
+                          template='notthere.pt')
+
+    def test_only_template(self):
+        f = self._getFUT()
+        context = DummyContext()
+        f(context, 'repoze.view', None, template='minimal.pt')
+        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)
+
+    def test_template_and_class(self):
+        f = self._getFUT()
+        context = DummyContext()
+        f(context, 'repoze.view', None, template='minimal.pt',
+          class_=DummyViewClass)
+        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:
+    pass
+
+class DummyContext:
+    def __init__(self):
+        self.actions = []
+        self.info = None
+
+    def path(self, name):
+        import os
+        here = os.path.dirname(__file__)
+        fixtures = os.path.join(here, 'fixtures')
+        return os.path.join(fixtures, name)
+
+    def action(self, discriminator, callable, args):
+        self.actions.append(
+            {'discriminator':discriminator,
+             'callable':callable,
+             'args':args}
+            )
+
+
+    

Added: repoze.bfg/trunk/repoze/bfg/tests/test_template.py
==============================================================================
--- (empty file)
+++ repoze.bfg/trunk/repoze/bfg/tests/test_template.py	Tue Jul  8 00:37:07 2008
@@ -0,0 +1,103 @@
+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 PageTemplateFileTests(unittest.TestCase, Base):
+    def setUp(self):
+        Base.setUp(self)
+
+    def tearDown(self):
+        Base.tearDown(self)
+
+    def _getTargetClass(self):
+        from repoze.bfg.template import PageTemplateFile
+        return PageTemplateFile
+
+    def _makeOne(self, *arg, **kw):
+        klass = self._getTargetClass()
+        return klass(*arg, **kw)
+
+    def test_render(self):
+        self._zcmlConfigure()
+        minimal = self._getTemplatePath('minimal.pt')
+        instance = self._makeOne(minimal)
+        result = instance.render()
+        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()
+        minimal = self._getTemplatePath('minimal.pt')
+        instance = self._makeOne(minimal)
+        class View:
+            context = 'context'
+            request = 'request'
+        view = View()
+        template = instance.render(view)
+        result = template()
+        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 BrowserViewTests(unittest.TestCase, Base):
+    def setUp(self):
+        Base.setUp(self)
+
+    def tearDown(self):
+        Base.tearDown(self)
+
+    def _getTargetClass(self):
+        from repoze.bfg.template import BrowserView
+        return BrowserView
+
+    def _makeOne(self, *arg, **kw):
+        klass = self._getTargetClass()
+        return klass(*arg, **kw)
+
+    def test_call(self):
+        view = self._makeOne(None, None)
+        _marker = ()
+        view.index = lambda *arg, **kw: _marker
+        result = view('foo')
+        self.assertEqual(result, _marker)
+        
+        
+        


More information about the Repoze-checkins mailing list