[Checkins] SVN: grokcore.view/trunk/ Merged r101414:101487 svn+ssh://reinout at svn.zope.org/repos/main/grokcore.view/branches/reinout-christian-codeview

Reinout van Rees reinout at vanrees.org
Sat Jul 4 06:01:08 EDT 2009


Log message for revision 101488:
  Merged r101414:101487 svn+ssh://reinout@svn.zope.org/repos/main/grokcore.view/branches/reinout-christian-codeview

Changed:
  U   grokcore.view/trunk/CHANGES.txt
  U   grokcore.view/trunk/README.txt
  U   grokcore.view/trunk/src/grokcore/view/__init__.py
  U   grokcore.view/trunk/src/grokcore/view/components.py
  U   grokcore.view/trunk/src/grokcore/view/directive.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/url/redirect.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/url/url.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/url/url_function.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/view/argument.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/view/macros.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/view/require.py
  U   grokcore.view/trunk/src/grokcore/view/ftests/view/skindirective.py
  U   grokcore.view/trunk/src/grokcore/view/meta/views.py
  U   grokcore.view/trunk/src/grokcore/view/templatereg.py
  U   grokcore.view/trunk/src/grokcore/view/tests/skin/conflict.py
  A   grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble.py
  A   grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble_fixture.py
  U   grokcore.view/trunk/src/grokcore/view/tests/template/pluggability.py
  A   grokcore.view/trunk/src/grokcore/view/tests/view/codeview.py
  A   grokcore.view/trunk/src/grokcore/view/tests/view/codeviewnorender.py
  U   grokcore.view/trunk/src/grokcore/view/tests/view/dirtemplateandrender.py
  D   grokcore.view/trunk/src/grokcore/view/tests/view/eithertemplateorrender.py
  A   grokcore.view/trunk/src/grokcore/view/tests/view/norenderinview.py
  A   grokcore.view/trunk/src/grokcore/view/tests/view/notemplate.py
  D   grokcore.view/trunk/src/grokcore/view/tests/view/notemplateorrender.py
  U   grokcore.view/trunk/src/grokcore/view/tests/view/templatenotfound.py
  U   grokcore.view/trunk/src/grokcore/view/tests/view/view.py
  U   grokcore.view/trunk/src/grokcore/view/tests/view/view_decorator.py
  A   grokcore.view/trunk/src/grokcore/view/tests/view/viewwithrender.py
  U   grokcore.view/trunk/versions.cfg

-=-
Modified: grokcore.view/trunk/CHANGES.txt
===================================================================
--- grokcore.view/trunk/CHANGES.txt	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/CHANGES.txt	2009-07-04 10:01:08 UTC (rev 101488)
@@ -6,6 +6,10 @@
 
 - Add validator to templatedir directive to disallow path separator.
 
+- Splitted CodeView out of View.  View only uses templates, CodeView only uses
+  a render() method.  So views that have a render method must subclass from
+  CodeView instead of View (that should be the only change needed).
+
 - Add grok.View permissions to functional tests (requires grokcore.security 1.1)
 
 

Modified: grokcore.view/trunk/README.txt
===================================================================
--- grokcore.view/trunk/README.txt	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/README.txt	2009-07-04 10:01:08 UTC (rev 101488)
@@ -29,20 +29,32 @@
 ``grokcore.view.View`` baseclass.  At a minimum, a browser page must
 have
 
-1. either an associated template or a ``render()`` method
+1. an associated template (for a render() method, use CodeView)
 
 2. a context that it's registered for as a view
 
 3. a name (which is, if not specified explicitly, the class's name in
    lower case characters).
 
+A browser page that does not use a template but just outputs some computed
+data should subclass ``grokcore.view.CodeView``.  At a minimum, such a view
+must have
+
+1. a render() method
+
+2. a context that it's registered for as a view
+
+3. a name (which is, if not specified explicitly, the class's name in
+   lower case characters).
+
+
 For example, the following class defines a view that's registered for
-all objects and simply prints "Hello World!"::
+all objects and simply prints "Hello World!".  This is done with a CodeView::
 
   import grokcore.view
   import zope.interface
 
-  class Hello(grokcore.view.View):
+  class Hello(grokcore.view.CodeView):
       grokcore.view.context(zope.interface.Interface)
 
       def render(self):
@@ -83,6 +95,15 @@
 
 This will greet a logged in user with his or her actual name.
 
+Such a template-using page is a subclass of ``grokcore.view.View``.
+
+  import grokcore.view
+  import zope.interface
+
+  class Hello(grokcore.view.View):
+      grokcore.view.context(zope.interface.Interface)
+
+
 To associate the template with the view, we have to put it in a
 certain place.  Let's say the ``Hello`` view class from above was in
 an ``app.py`` module.  Then we create an ``app_templates`` directory
@@ -174,6 +195,14 @@
     associated with the view as well as the ``layer`` directive to
     specify which layer it should be on if not the default layer.
 
+``CodeView``
+    Base class for browser pages.  Use the ``context`` directive to specify
+    the view's context.  Use the ``name`` directive to set the view's name; if
+    not used, the view's name will be the class's name in lower case
+    characters.  The ``layer`` directive specifies which layer it should be on
+    if not the default layer.
+
+
 View API
 --------
 
@@ -214,7 +243,7 @@
     Optionally, ``data`` can be a dictionary whose contents is added to
     the URL as a query string.
 
-Methods for developers to implement:
+Method for developers to implement:
 
 ``update(**kw)``
     This method will be called before the view's associated template
@@ -224,13 +253,15 @@
     ``view`` variable from the template.  The method can take
     arbitrary keyword parameters which are filled from request values.
 
-``render(**kw)``
-    Implement this method if (and only if) there isn't a template that
-    goes with the view class.  Return either an encoded 8-bit string
-    or a unicode string.  The method can take arbitrary keyword
-    parameters which are filled from request values.
 
+The CodeView class can have abovementioned ``update()`` method and
+additionally *has* to implement ``render()``
 
+``render(**kw)`` 
+    Return either an encoded 8-bit string or a unicode string.  The method can
+    take arbitrary keyword parameters which are filled from request values.
+
+
 Directives
 ----------
 
@@ -244,7 +275,7 @@
     Class-level directive that specifies the name a template file
     that's associated with a view class, *without* the file extension.
     If not used, it defaults to the class's name in lower case
-    characters.
+    characters.  Only used for View, not CodeView.
 
 ``layer(layer_interface)``
     Class-level directive that defines which layer the view is
@@ -288,3 +319,13 @@
 .. _grokcore.component: http://pypi.python.org/pypi/grokcore.component
 .. _grokcore.security: http://pypi.python.org/pypi/grokcore.security
 .. _grokcore.view: http://pypi.python.org/pypi/grokcore.view
+
+
+Upgrade notice
+==============
+
+In grokcore.view 1.8, grokcore.view.View was splitted into View (that only
+works with templates) and CodeView (that only accepts a ``render()`` method).
+
+So views that have a render method must subclass from CodeView instead of
+View.  That should be the only change needed.

Modified: grokcore.view/trunk/src/grokcore/view/__init__.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/__init__.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/__init__.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -19,7 +19,7 @@
 from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 
-from grokcore.view.components import View
+from grokcore.view.components import View, CodeView
 from grokcore.view.components import PageTemplate, PageTemplateFile
 from grokcore.view.components import DirectoryResource
 from grokcore.view.directive import layer, template, templatedir, skin, path

Modified: grokcore.view/trunk/src/grokcore/view/components.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/components.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/components.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -31,13 +31,67 @@
 from grokcore.view import interfaces, util
 
 
-class View(BrowserPage):
+class BaseView(BrowserPage):
     interface.implements(interfaces.IGrokView)
 
     def __init__(self, context, request):
-        super(View, self).__init__(context, request)
+        super(BaseView, self).__init__(context, request)
         self.__name__ = getattr(self, '__view_name__', None)
 
+    def update(self):
+        pass
+
+    def redirect(self, url):
+        return self.request.response.redirect(url)
+
+    def url(self, obj=None, name=None, data=None):
+        """Return string for the URL based on the obj and name. The data
+        argument is used to form a CGI query string.
+        """
+        if isinstance(obj, basestring):
+            if name is not None:
+                raise TypeError(
+                    'url() takes either obj argument, obj, string arguments, '
+                    'or string argument')
+            name = obj
+            obj = None
+
+        if name is None and obj is None:
+            # create URL to view itself
+            obj = self
+        elif name is not None and obj is None:
+            # create URL to view on context
+            obj = self.context
+
+        if data is None:
+            data = {}
+        else:
+            if not isinstance(data, dict):
+                raise TypeError('url() data argument must be a dict.')
+
+        return util.url(self.request, obj, name, data=data)
+
+
+class CodeView(BaseView):
+
+    def __init__(self, context, request):
+        super(CodeView, self).__init__(context, request)
+
+    def __call__(self):
+        mapply(self.update, (), self.request)
+        if self.request.response.getStatus() in (302, 303):
+            # A redirect was triggered somewhere in update().  Don't
+            # continue rendering the template or doing anything else.
+            return
+
+        return mapply(self.render, (), self.request)
+
+
+class View(BaseView):
+
+    def __init__(self, context, request):
+        super(View, self).__init__(context, request)
+
         if getattr(self, 'module_info', None) is not None:
             self.static = component.queryAdapter(
                 self.request,
@@ -47,6 +101,7 @@
         else:
             self.static = None
 
+    # Might be moved to BaseView currently only needed for PageTemplates CHECKTHIS
     @property
     def response(self):
         return self.request.response
@@ -58,12 +113,6 @@
             # continue rendering the template or doing anything else.
             return
 
-        template = getattr(self, 'template', None)
-        if template is not None:
-            return self._render_template()
-        return mapply(self.render, (), self.request)
-
-    def _render_template(self):
         return self.template.render(self)
 
     def default_namespace(self):
@@ -92,40 +141,6 @@
         return value
 
 
-    def url(self, obj=None, name=None, data=None):
-        """Return string for the URL based on the obj and name. The data
-        argument is used to form a CGI query string.
-        """
-        if isinstance(obj, basestring):
-            if name is not None:
-                raise TypeError(
-                    'url() takes either obj argument, obj, string arguments, '
-                    'or string argument')
-            name = obj
-            obj = None
-
-        if name is None and obj is None:
-            # create URL to view itself
-            obj = self
-        elif name is not None and obj is None:
-            # create URL to view on context
-            obj = self.context
-
-        if data is None:
-            data = {}
-        else:
-            if not isinstance(data, dict):
-                raise TypeError('url() data argument must be a dict.')
-
-        return util.url(self.request, obj, name, data=data)
-
-    def redirect(self, url):
-        return self.request.response.redirect(url)
-
-    def update(self):
-        pass
-
-
 class BaseTemplate(object):
     """Any sort of page template"""
 

Modified: grokcore.view/trunk/src/grokcore/view/directive.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/directive.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/directive.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -28,25 +28,30 @@
                                % directive.name)
 
 # Define grok directives
+
 class template(martian.Directive):
     scope = martian.CLASS
     store = martian.ONCE
     validate = martian.validateText
 
+
 class templatedir(martian.Directive):
     scope = martian.MODULE
     store = martian.ONCE
     validate = validateLocalPath
 
+
 class OneInterfaceOrClassOnClassOrModule(martian.Directive):
     """Convenience base class.  Not for public use."""
     scope = martian.CLASS_OR_MODULE
     store = martian.ONCE
     validate = martian.validateInterfaceOrClass
 
+
 class layer(OneInterfaceOrClassOnClassOrModule):
     pass
 
+
 class TaggedValueStoreOnce(StoreOnce):
     """Stores the directive value in a interface tagged value.
     """
@@ -55,7 +60,8 @@
         return component.queryTaggedValue(directive.dotted_name(), default)
 
     def set(self, locals_, directive, value):
-        if directive.dotted_name() in locals_:
+        already_set = locals_.get('__interface_tagged_values__', [])
+        if directive.dotted_name() in already_set:
             raise GrokImportError(
                 "The '%s' directive can only be called once per %s." %
                 (directive.name, directive.scope.description))
@@ -70,6 +76,7 @@
     def setattr(self, context, directive, value):
         context.setTaggedValue(directive.dotted_name(), value)
 
+
 class skin(martian.Directive):
     # We cannot do any better than to check for a class scope. Ideally we
     # would've checked whether the context is indeed an Interface class.
@@ -77,6 +84,7 @@
     store = TaggedValueStoreOnce()
     validate = martian.validateText
 
+
 class path(martian.Directive):
     scope = martian.CLASS
     store = martian.ONCE

Modified: grokcore.view/trunk/src/grokcore/view/ftests/url/redirect.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/url/redirect.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/url/redirect.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -12,18 +12,18 @@
   >>> browser.open('http://localhost/manfred')
   >>> browser.url
   'http://localhost/manfred/another'
-  
+
 """
 import grokcore.view as grok
 
 class Mammoth(grok.Context):
     pass
 
-class Index(grok.View):
+class Index(grok.CodeView):
     def render(self):
         self.redirect(self.url('another'))
 
-class Another(grok.View):
+class Another(grok.CodeView):
     def render(self):
         return "Another view"
 

Modified: grokcore.view/trunk/src/grokcore/view/ftests/url/url.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/url/url.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/url/url.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -180,18 +180,18 @@
 
 grok.context(Mammoth)
 
-class Index(grok.View):
+class Index(grok.CodeView):
     def render(self):
         return self.url()
 
-class Another(grok.View):
+class Another(grok.CodeView):
     def render(self):
         return self.url()
 
 class YetAnother(grok.View):
     pass
 
-class Multiplier(grok.View):
+class Multiplier(grok.CodeView):
     def update(self, age=0):
         self.age = age
 

Modified: grokcore.view/trunk/src/grokcore/view/ftests/url/url_function.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/url/url_function.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/url/url_function.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -61,20 +61,26 @@
   >>> expected = unicode('http://127.0.0.1/herd/árgh', 'UTF-8')
   >>> urllib.unquote(u).decode('utf-8') == expected
   True
+
 """
 import grokcore.view as grok
 from grokcore.view import url
 from zope.app.container.contained import Contained
 
+
 class Mammoth(Contained):
     pass
 
 grok.context(Mammoth)
 
-class Index(grok.View):
+
+class Index(grok.CodeView):
     def render(self):
         return url(self.request, self)
 
+
 class Another(grok.View):
-    def render(self):
-        return url(self.request, self)
+    pass
+
+
+another = grok.PageTemplate('<p tal:replace="view/url" />')

Modified: grokcore.view/trunk/src/grokcore/view/ftests/view/argument.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/view/argument.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/view/argument.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -49,15 +49,18 @@
 """
 import grokcore.view as grok
 
+
 class Mammoth(grok.Context):
     pass
 
-class RenderWithArguments(grok.View):
+
+class RenderWithArguments(grok.CodeView):
     grok.name('render')
 
     def render(self, message, another):
         return "Message: %s\nAnother: %s" % (message, another)
 
+
 class UpdateWithArguments(grok.View):
     grok.name('update')
     grok.template('update')
@@ -66,6 +69,7 @@
         self.message = message
         self.another = another
 
+
 update = grok.PageTemplate("""
 Coming to us from update():
 Message: <span tal:replace="view/message" />

Modified: grokcore.view/trunk/src/grokcore/view/ftests/view/macros.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/view/macros.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/view/macros.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -19,7 +19,8 @@
 
   >>> browser.open("http://localhost/manfred/@@dancing")
   Traceback (most recent call last):
-  AttributeError: 'DancingHall' object has no attribute 'template'
+  ...
+  TraversalError: (<grokcore.view.ftests.view.macros.DancingHall object at ...>, 'macros')
 
 If the view has an attribute with the same name as a macro, the macro
 shadows the view. XXX This should probably generate a warning at runtime.
@@ -71,26 +72,36 @@
   >>> open(template_file, 'w').write(before)
 
 
-
 """
 import grokcore.view as grok
 
+
 class Mammoth(grok.Context):
     pass
 
-class DancingHall(grok.View):
 
+class DancingHall(grok.CodeView):
+
     def render(self):
-        return "A nice large dancing hall for mammoths."
+	return "Bla Bla Dancing Hall"
 
+
 class Grilled(grok.View):
 
     def update(self):
         self.spices = "Pepper and salt"
 
+
+grilled = grok.PageTemplate("""\
+<html metal:define-macro="spices">
+Curry
+</html>""")
+
+
 class Painting(grok.View):
     pass
 
+
 painting = grok.PageTemplate("""\
 <html metal:use-macro="context/@@layout/macros/main">
 <div metal:fill-slot="slot">
@@ -99,38 +110,40 @@
 </html>
 """)
 
+
 class Layout(grok.View):
     # Layout template is in macros_templates/layout.pt for reload test
     # purposes.
     pass
 
+
 class Dancing(grok.View):
     pass
 
+
 dancing = grok.PageTemplate("""\
 <html metal:use-macro="context/@@dancinghall/macros/something">
 </html>
 """)
 
+
 class GrillDish(grok.View):
     pass
 
+
 grilldish = grok.PageTemplate("""
 <html metal:use-macro="context/@@grilled/macros/spices">
 </html>""")
 
+
+
 class Burnt(grok.View):
     pass
 
+
 burnt = grok.PageTemplate("""\
 <html metal:use-macro="context/@@grilled/spices">
 </html>""")
 
-class Grilled(grok.View):
-    pass
 
-grilled = grok.PageTemplate("""\
-<html metal:define-macro="spices">
-Curry
-</html>""")
 

Modified: grokcore.view/trunk/src/grokcore/view/ftests/view/require.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/view/require.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/view/require.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -27,17 +27,20 @@
 import grokcore.view as grok
 import zope.interface
 
+
 class ViewPainting(grok.Permission):
     grok.name('cave.ViewPainting')
 
-class Painting(grok.View):
+
+class Painting(grok.CodeView):
     grok.context(zope.interface.Interface)
     grok.require(ViewPainting)
 
     def render(self):
         return 'What a beautiful painting.'
 
-class PublicNudity(grok.View):
+
+class PublicNudity(grok.CodeView):
     grok.context(zope.interface.Interface)
     grok.require(grok.Public)
 

Modified: grokcore.view/trunk/src/grokcore/view/ftests/view/skindirective.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/ftests/view/skindirective.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/ftests/view/skindirective.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -47,14 +47,14 @@
 </html>
 """)
 
-class MoreDrawings(grok.View):
+class MoreDrawings(grok.CodeView):
     grok.layer(rotterdam)
 
     def render(self):
         return "Pretty"
 
 
-class EvenMoreDrawings(grok.View):
+class EvenMoreDrawings(grok.CodeView):
     grok.layer(MySkinLayer)
 
     def render(self):

Modified: grokcore.view/trunk/src/grokcore/view/meta/views.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/meta/views.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/meta/views.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -46,14 +46,18 @@
         return super(ViewGrokker, self).grok(name, factory, module_info, **kw)
 
     def execute(self, factory, config, context, layer, name, **kw):
+        # Make sure that we have a render Method
+        render = getattr(factory, 'render', None)
+        if render:
+            raise GrokError("View Class '%s' has a render method, use CodeView instead" % factory, factory)
+
         # find templates
         templates = factory.module_info.getAnnotation('grok.templates', None)
-        if templates is not None:
-            config.action(
-                discriminator=None,
-                callable=self.checkTemplates,
-                args=(templates, factory.module_info, factory)
-                )
+        config.action(
+            discriminator=None,
+            callable=self.checkTemplates,
+            args=(templates, factory.module_info, factory)
+            )
 
         # safety belt: make sure that the programmer didn't use
         # @grok.require on any of the view's methods.
@@ -76,21 +80,63 @@
             )
         return True
 
+
+    # CHECK THIS.  There is no template/render checking anymore, so this is a bit
+    # elaborate.
     def checkTemplates(self, templates, module_info, factory):
 
         def has_render(factory):
-            render = getattr(factory, 'render', None)
-            base_method = getattr(render, 'base_method', False)
-            return render and not base_method
+	    return False
 
         def has_no_render(factory):
-            return not getattr(factory, 'render', None)
+	    return True
         templates.checkTemplates(module_info, factory, 'view',
                                  has_render, has_no_render)
 
 
+class CodeViewGrokker(martian.ClassGrokker):
+    martian.component(components.CodeView)
+    martian.directive(grokcore.component.context)
+    martian.directive(grokcore.view.layer, default=IDefaultBrowserLayer)
+    martian.directive(grokcore.component.name, get_default=default_view_name)
+
+    def grok(self, name, factory, module_info, **kw):
+        # Need to store the module info object on the view class so that it
+        # can look up the 'static' resource directory.
+        factory.module_info = module_info
+        return super(CodeViewGrokker, self).grok(name, factory, module_info, **kw)
+
+    def execute(self, factory, config, context, layer, name, **kw):
+
+        # Make sure that we have a render Method
+        render = getattr(factory, 'render', None)
+        if not render:
+            raise GrokError("CodeView Class '%s' without an render method" % factory, factory)
+
+        # safety belt: make sure that the programmer didn't use
+        # @grok.require on any of the view's methods.
+        methods = util.methods_from_class(factory)
+        for method in methods:
+            if grokcore.security.require.bind().get(method) is not None:
+                raise GrokError('The @grok.require decorator is used for '
+                                'method %r in view %r. It may only be used '
+                                'for XML-RPC methods.'
+                                % (method.__name__, factory), factory)
+
+        # __view_name__ is needed to support IAbsoluteURL on views
+        factory.__view_name__ = name
+        adapts = (context, layer)
+
+        config.action(
+            discriminator=('adapter', adapts, interface.Interface, name),
+            callable=component.provideAdapter,
+            args=(factory, adapts, interface.Interface, name),
+            )
+        return True
+
+
 class ViewSecurityGrokker(martian.ClassGrokker):
-    martian.component(components.View)
+    martian.component(components.BaseView)
     martian.directive(grokcore.security.require, name='permission')
 
     def execute(self, factory, config, permission, **kw):

Modified: grokcore.view/trunk/src/grokcore/view/templatereg.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/templatereg.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/templatereg.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -107,6 +107,7 @@
                                 % (component_name, factory, template_name,
                                    factory_name), factory)
         template = self.get(template_name)
+        # TODO: strip render
         if template is not None:
             if has_render(factory):
                 # we do not accept render and template both for a view
@@ -122,8 +123,7 @@
         else:
             if has_no_render(factory):
                 # we do not accept a view without any way to render it
-                raise GrokError("%s %r has no associated template or "
-                                "'render' method." %
+                raise GrokError("%s %r has no associated template." %
                                 (component_name.title(), factory), factory)
 
 class PageTemplateFileFactory(grokcore.component.GlobalUtility):

Modified: grokcore.view/trunk/src/grokcore/view/tests/skin/conflict.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/skin/conflict.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/skin/conflict.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -10,8 +10,10 @@
 
 import grokcore.view as grok
 
+
 class Skin1(grok.IBrowserRequest):
     grok.skin('foo')
 
+
 class Skin2(grok.IBrowserRequest):
     grok.skin('foo')

Copied: grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/skin/nodouble.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,9 @@
+"""
+We cannot register two skins under the same name::
+
+    >>> from grokcore.view.tests.skin import nodouble_fixture
+    Traceback (most recent call last):
+    ...
+    GrokImportError: The 'skin' directive can only be called once per class.
+
+"""

Copied: grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble_fixture.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/skin/nodouble_fixture.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble_fixture.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/skin/nodouble_fixture.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,6 @@
+import grokcore.view as grok
+
+
+class Skin1(grok.IBrowserRequest):
+    grok.skin('foo')
+    grok.skin('bar')

Modified: grokcore.view/trunk/src/grokcore/view/tests/template/pluggability.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/template/pluggability.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/template/pluggability.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -2,12 +2,12 @@
 Testing the plugging in of a template language
 
   >>> grok.testing.grok(__name__)
-  
+
   >>> cave = Cave()
   >>> from zope.publisher.browser import TestRequest
   >>> request = TestRequest()
   >>> from zope import component
-  
+
   # The inline template should work:
   >>> view = component.getMultiAdapter((cave, request), name='sebaayeni')
   >>> print view()
@@ -23,7 +23,7 @@
   >>> print view()
   <html><body>Kakadu is in Australia</body></html>
 
-  # We should be able to extend the namespac in the view and 
+  # We should be able to extend the namespac in the view and
   >>> view = component.getMultiAdapter((cave, request), name='sierra')
   >>> print view()
   <html><body>Sierra de San Fransisco is in Mexico</body></html>
@@ -33,14 +33,15 @@
 
 # Dummy template language:
 class MyTemplate(object):
-    
+
     def __init__(self, text):
         self._text = text
-            
+
     def render(self, **kw):
         # Silliest template language ever:
         return self._text % kw
 
+
 class MyPageTemplate(grok.components.GrokTemplate):
 
     def setFromString(self, string):
@@ -70,19 +71,19 @@
 
 class Sebaayeni(grok.View):
     pass
-    
+
 sebaayeni = MyPageTemplate('<html><body>Sebaayeni is in South Africa</body></html>')
 
 class Lascaux(grok.View):
     pass
-    
+
 lascaux = MyPageTemplate(filename='lascaux.html')
 
 class Kakadu(grok.View):
     pass
 
 class Sierra(grok.View):
-    
+
     def namespace(self):
         return {'cave': 'Sierra de San Fransisco',
                 'country': 'Mexico'}

Copied: grokcore.view/trunk/src/grokcore/view/tests/view/codeview.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/view/codeview.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/codeview.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/codeview.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,24 @@
+"""
+
+  >>> grok.testing.grok(__name__)
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from zope.component import getMultiAdapter
+
+  >>> mammoth = Mammoth()
+
+  >>> code_view = getMultiAdapter((mammoth, TestRequest()), name="cavepainting")
+  >>> print code_view()
+  A cave painting of a mammoth
+
+"""
+
+import grokcore.view as grok
+
+class Mammoth(grok.Context):
+    pass
+
+class CavePainting(grok.CodeView):
+
+    def render(self):
+        return 'A cave painting of a mammoth'

Copied: grokcore.view/trunk/src/grokcore/view/tests/view/codeviewnorender.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/view/codeviewnorender.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/codeviewnorender.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/codeviewnorender.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,16 @@
+"""
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+  ...
+  GrokError: CodeView Class '<class 'grokcore.view.tests.view.codeviewnorender.CaveTiger'>' without an render method 
+
+"""
+
+import grokcore.view as grok
+
+class Mammoth(grok.Context):
+    pass
+
+class CaveTiger(grok.CodeView):
+    pass

Modified: grokcore.view/trunk/src/grokcore/view/tests/view/dirtemplateandrender.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/dirtemplateandrender.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/dirtemplateandrender.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -1,15 +1,13 @@
 """
-A View may either have an associated template or a render-method. Here
-we check that this also works for templates in a template-directory:
+A View may only have an associated template.  Not a render-method, for that
+you must use CodeView.  Here we check that this also works for templates in a
+template-directory:
 
-  >>> grok.testing.grok(__name__)
-  Traceback (most recent call last):
+    >>> grok.testing.grok(__name__)
+    Traceback (most recent call last):
     ...
-  ConfigurationExecutionError: martian.error.GrokError: Multiple possible ways to render view
-  <class 'grokcore.view.tests.view.dirtemplateandrender.CavePainting'>.
-  It has both a 'render' method as well as an associated template.
-  in:
-  
+    GrokError: View Class '<class 'grokcore.view.tests.view.dirtemplateandrender.CavePainting'>' has a render method, use CodeView instead
+
 """
 import grokcore.view as grok
 

Deleted: grokcore.view/trunk/src/grokcore/view/tests/view/eithertemplateorrender.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/eithertemplateorrender.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/eithertemplateorrender.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -1,21 +0,0 @@
-"""
-Only one, either a template, or render() can be specified:
-
-  >>> grok.testing.grok(__name__)
-  Traceback (most recent call last):
-    ...
-  ConfigurationExecutionError: martian.error.GrokError: Multiple possible ways to render view
-  <class 'grokcore.view.tests.view.eithertemplateorrender.CavePainting'>.
-  It has both a 'render' method as well as an associated template.
-  in:
-"""
-import grokcore.view as grok
-
-class Mammoth(grok.Context):
-    pass
-
-class CavePainting(grok.View):
-    def render(self):
-        pass
-
-cavepainting = grok.PageTemplate("nothing")

Copied: grokcore.view/trunk/src/grokcore/view/tests/view/norenderinview.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/view/norenderinview.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/norenderinview.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/norenderinview.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,20 @@
+"""
+Only a template may be specified, not a render() method.  Use CodeView if you
+want a render method.
+
+    >>> grok.testing.grok(__name__)
+    Traceback (most recent call last):
+    ...
+    GrokError: View Class '<class 'grokcore.view.tests.view.norenderinview.CavePainting'>' has a render method, use CodeView instead
+
+"""
+import grokcore.view as grok
+
+class Mammoth(grok.Context):
+    pass
+
+class CavePainting(grok.View):
+    def render(self):
+        pass
+
+cavepainting = grok.PageTemplate("nothing")

Copied: grokcore.view/trunk/src/grokcore/view/tests/view/notemplate.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/view/notemplate.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/notemplate.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/notemplate.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,19 @@
+"""
+Views need an associated template.
+
+    >>> grok.testing.grok(__name__)
+    Traceback (most recent call last):
+    ...
+    ConfigurationExecutionError: <class 'martian.error.GrokError'>: View <class 'grokcore.view.tests.view.notemplate.CavePainting'> has no associated template.
+      in:
+    <BLANKLINE>    
+
+"""
+
+import grokcore.view as grok
+
+class Mammoth(grok.Context):
+    pass
+
+class CavePainting(grok.View):
+    pass

Deleted: grokcore.view/trunk/src/grokcore/view/tests/view/notemplateorrender.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/notemplateorrender.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/notemplateorrender.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -1,19 +0,0 @@
-"""
-Views either need an associated template or a ``render`` method:
-
-  >>> grok.testing.grok(__name__)
-  Traceback (most recent call last):
-    ...
-  ConfigurationExecutionError: martian.error.GrokError: View <class 'grokcore.view.tests.view.notemplateorrender.CavePainting'>
-  has no associated template or 'render' method.
-  in:
-
-"""
-
-import grokcore.view as grok
-
-class Mammoth(grok.Context):
-    pass
-
-class CavePainting(grok.View):
-    pass

Modified: grokcore.view/trunk/src/grokcore/view/tests/view/templatenotfound.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/templatenotfound.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/templatenotfound.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -2,13 +2,15 @@
 This should fail because ``grok.template`` points to a non-existing
 template:
 
-  >>> grok.testing.grok(__name__)
-  Traceback (most recent call last):
+    >>> grok.testing.grok(__name__)
+    Traceback (most recent call last):
     ...
-  ConfigurationExecutionError: martian.error.GrokError: View <class 'grokcore.view.tests.view.templatenotfound.Painting'>
-  has no associated template or 'render' method.
-  in:
+    ConfigurationExecutionError: <class 'martian.error.GrokError'>: View <class 'grokcore.view.tests.view.templatenotfound.Painting'> has no associated template.
+      in:
+      
+
 """
+
 import grokcore.view as grok
 
 class Mammoth(grok.Context):

Modified: grokcore.view/trunk/src/grokcore/view/tests/view/view.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/view.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/view.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -37,12 +37,12 @@
 class Mammoth(grok.Context):
     pass
 
-class CavePainting(grok.View):
+class CavePainting(grok.CodeView):
 
     def render(self):
         return 'A cave painting of a mammoth'
 
-class Food(grok.View):
+class Food(grok.CodeView):
     """Grok says: ME NO SEE MAMMOTH, ME SEE MEAL!"""
     grok.name('meal')
 

Modified: grokcore.view/trunk/src/grokcore/view/tests/view/view_decorator.py
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/view_decorator.py	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/view_decorator.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -12,10 +12,12 @@
 import grokcore.view as grok
 import zope.interface
 
+
 class Bogus(grok.Permission):
     grok.name('bogus.perm')
 
-class BogusView(grok.View):
+
+class BogusView(grok.CodeView):
     grok.context(zope.interface.Interface)
 
     @grok.require(Bogus)

Copied: grokcore.view/trunk/src/grokcore/view/tests/view/viewwithrender.py (from rev 101487, grokcore.view/branches/reinout-christian-codeview/src/grokcore/view/tests/view/viewwithrender.py)
===================================================================
--- grokcore.view/trunk/src/grokcore/view/tests/view/viewwithrender.py	                        (rev 0)
+++ grokcore.view/trunk/src/grokcore/view/tests/view/viewwithrender.py	2009-07-04 10:01:08 UTC (rev 101488)
@@ -0,0 +1,18 @@
+"""
+
+  >>> grok.testing.grok(__name__)
+  Traceback (most recent call last):
+  ...
+  GrokError: View Class '<class 'grokcore.view.tests.view.viewwithrender.CavePainting'>' has a render method, use CodeView instead
+
+"""
+
+import grokcore.view as grok
+
+class Mammoth(grok.Context):
+    pass
+
+class CavePainting(grok.View):
+
+    def render(self):
+        return 'A cave painting of a mammoth'

Modified: grokcore.view/trunk/versions.cfg
===================================================================
--- grokcore.view/trunk/versions.cfg	2009-07-04 09:53:09 UTC (rev 101487)
+++ grokcore.view/trunk/versions.cfg	2009-07-04 10:01:08 UTC (rev 101488)
@@ -102,7 +102,7 @@
 zope.tal = 3.4.1
 zope.tales = 3.4.0
 zope.testbrowser = 3.4.2
-zope.testing = 3.5.6
+zope.testing = 3.7.5
 zope.thread = 3.4
 zope.traversing = 3.4.1
 zope.viewlet = 3.4.2



More information about the Checkins mailing list