[Checkins] SVN: grok/trunk/ Merge snowsprint-viewlets2 branch. We now have support for

Martijn Faassen faassen at infrae.com
Mon Mar 3 09:23:03 EST 2008


Log message for revision 84437:
  Merge snowsprint-viewlets2 branch. We now have support for
  grok.Viewlet and grok.ViewletManager.
  

Changed:
  U   grok/trunk/CHANGES.txt
  U   grok/trunk/setup.py
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/components.py
  U   grok/trunk/src/grok/configure.zcml
  U   grok/trunk/src/grok/directive.py
  U   grok/trunk/src/grok/ftests/test_grok_functional.py
  A   grok/trunk/src/grok/ftests/viewlet/
  U   grok/trunk/src/grok/interfaces.py
  U   grok/trunk/src/grok/meta.py
  U   grok/trunk/src/grok/templatereg.py
  U   grok/trunk/src/grok/tests/test_grok.py
  A   grok/trunk/src/grok/tests/viewlet/
  U   grok/trunk/src/grok/util.py
  U   grok/trunk/versions.cfg

-=-
Modified: grok/trunk/CHANGES.txt
===================================================================
--- grok/trunk/CHANGES.txt	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/CHANGES.txt	2008-03-03 14:23:03 UTC (rev 84437)
@@ -10,6 +10,9 @@
 * Added testsetup classes in grok.testing to improve easy setup of
   unit- and functional tests.
 
+* Add support for viewlets and viewlet managers, ``grok.Viewlet``
+  and ``grok.ViewletManager``.
+
 * Add a new directive, ``grok.order()``, which can be used to help
   sort components. At the time it is not used yet, but we intend to
   use it for the viewlets support. Note that this means Grok now

Modified: grok/trunk/setup.py
===================================================================
--- grok/trunk/setup.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/setup.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -77,6 +77,7 @@
                       'zope.testing',
                       'zope.traversing',
                       'zope.testbrowser',
+                      'zope.viewlet',
                       'zc.catalog',
                       'z3c.flashmessage',
                       'z3c.autoinclude',

Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/__init__.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -40,9 +40,12 @@
 from grok.components import Skin, IGrokLayer
 from grok.components import RESTProtocol, IRESTLayer
 from grok.interfaces import IRESTSkinType
+from grok.components import ViewletManager, Viewlet
+
 from grok.directive import (context, name, title, template, templatedir,
                             provides, baseclass, global_utility, local_utility,
-                            permissions, require, site, layer, direct, order)
+                            permissions, require, site, layer, direct, viewletmanager,
+                            view, order)
 from grok.decorators import subscribe, adapter, implementer
 from martian.error import GrokError, GrokImportError
 

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/components.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -16,7 +16,6 @@
 import sys
 import os
 import persistent
-import urllib
 import datetime
 import warnings
 import pytz
@@ -47,6 +46,9 @@
 from zope.app.component.site import SiteManagerContainer
 from zope.app.component.site import LocalSiteManager
 
+from zope.viewlet.manager import ViewletManagerBase
+from zope.viewlet.viewlet import ViewletBase
+
 import z3c.flashmessage.interfaces
 
 import martian.util
@@ -608,3 +610,114 @@
 
 class RESTProtocol(object):
     pass
+
+class ViewletManager(ViewletManagerBase):
+    template = None
+
+    def __init__(self, context, request, view):
+        super(ViewletManager, self).__init__(context, request, view)
+        self.__name__ = util.class_annotation(self.__class__,
+                                              'grok.name',
+                                              self.__class__.__name__.lower())
+        self.static = component.queryAdapter(
+            self.request,
+            interface.Interface,
+            name=self.module_info.package_dotted_name
+            )
+
+
+    def render(self):
+        """See zope.contentprovider.interfaces.IContentProvider"""
+        # Now render the view
+        if self.template:
+            #return self.template(viewlets=self.viewlets)
+            return self._render_template()
+        else:
+            viewlets = util.sort_components(self.viewlets)
+            return u'\n'.join([viewlet.render() for viewlet in viewlets])
+
+
+    @property
+    def response(self):
+        return self.request.response
+
+    def _render_template(self):
+        namespace = self.template.pt_getContext()
+        namespace['request'] = self.request
+        namespace['view'] = self
+        namespace['viewlets'] = self.viewlets
+        namespace['static'] = self.static
+        namespace['context'] = self.context
+        # XXX need to check whether we really want to put None here if missing
+        return self.template.pt_render(namespace)
+
+    def url(self, obj=None, name=None):
+        # if the first argument is a string, that's the name. There should
+        # be no second argument
+        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
+        return util.url(self.request, obj, name)
+
+    def redirect(self, url):
+        return self.request.response.redirect(url)
+
+class Viewlet(ViewletBase):
+    """ Batteries included viewlet """
+
+
+    def __init__(self, context, request, view, manager):
+        super(Viewlet, self).__init__(context, request, view, manager)
+        # would be nice to move this to the ViewletGrokker but
+        # new objects don't have __name__ of their class
+        self.__name__ = util.class_annotation(self.__class__,
+                                             'grok.name',
+                                              self.__class__.__name__.lower())
+        self.static = component.queryAdapter(
+            self.request,
+            interface.Interface,
+            name=self.module_info.package_dotted_name
+            )
+
+    @property
+    def response(self):
+        return self.request.response
+
+    def render(self):
+        return self.template.render(self)
+
+    def namespace(self):
+        return {}
+
+    def url(self, obj=None, name=None):
+        # if the first argument is a string, that's the name. There should
+        # be no second argument
+        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
+        return util.url(self.request, obj, name)
+
+    def update(self):
+        pass

Modified: grok/trunk/src/grok/configure.zcml
===================================================================
--- grok/trunk/src/grok/configure.zcml	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/configure.zcml	2008-03-03 14:23:03 UTC (rev 84437)
@@ -11,6 +11,7 @@
 
   <include package="zope.annotation" />
   <include package="zope.copypastemove" />
+  <include package="zope.contentprovider" />
   <include package="zope.formlib" />
   <include package="zope.i18n.locales" />
   <include package="zope.publisher" />

Modified: grok/trunk/src/grok/directive.py
===================================================================
--- grok/trunk/src/grok/directive.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/directive.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -138,5 +138,9 @@
     'grok.permissions', ClassDirectiveContext())
 layer = InterfaceOrClassDirective('grok.layer',
                            ClassOrModuleDirectiveContext())
+order = OrderDirective('grok.order', ClassDirectiveContext())
 direct = MarkerDirective('grok.direct', ClassDirectiveContext())
-order = OrderDirective('grok.order', ClassDirectiveContext())
+viewletmanager = InterfaceOrClassDirective('grok.viewletmanager',
+                                           ClassOrModuleDirectiveContext())
+view = InterfaceOrClassDirective('grok.view',
+                                 ClassOrModuleDirectiveContext())

Modified: grok/trunk/src/grok/ftests/test_grok_functional.py
===================================================================
--- grok/trunk/src/grok/ftests/test_grok_functional.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/ftests/test_grok_functional.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -70,7 +70,8 @@
 def test_suite():
     suite = unittest.TestSuite()
     for name in ['view', 'staticdir', 'xmlrpc', 'traversal', 'form', 'url',
-                 'security', 'utility', 'catalog', 'admin', 'site', 'rest']:
+                 'security', 'utility', 'catalog', 'admin', 'site', 'rest',
+                 'viewlet']:
         suite.addTest(suiteFromPackage(name))
 
     # this test cannot follow the normal testing pattern, as the

Copied: grok/trunk/src/grok/ftests/viewlet (from rev 84436, grok/branches/snowsprint-viewlets2/src/grok/ftests/viewlet)

Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/interfaces.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -47,6 +47,8 @@
     Indexes = interface.Attribute("Base class for catalog index definitions.")
     Layer = interface.Attribute("Base interface for layers.")
     Skin = interface.Attribute("Base class for skin.")
+    ViewletManager = interface.Attribute("Base class for viewletmanager.")
+    Viewlet = interface.Attribute("Base class for viewlet.")
 
 
 class IGrokErrors(interface.Interface):

Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/meta.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -17,11 +17,13 @@
 
 import zope.component.interface
 from zope import interface, component
+from zope.publisher.browser import IBrowserView
 from zope.publisher.interfaces.browser import (IDefaultBrowserLayer,
                                                IBrowserRequest,
                                                IBrowserPublisher,
                                                IBrowserSkinType)
 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
+from zope.viewlet.interfaces import IViewletManager, IViewlet
 from zope.security.interfaces import IPermission
 from zope.securitypolicy.interfaces import IRole
 from zope.securitypolicy.rolepermission import rolePermissionManager
@@ -47,16 +49,20 @@
 import grok
 from grok import components, formlib, templatereg
 from grok.util import check_adapts, get_default_permission, make_checker
+from grok.util import check_module_component, determine_module_component
+from grok.util import determine_class_component
 from grok.util import determine_class_directive, public_methods_from_class
-from grok.util import determine_module_context, determine_class_context
-from grok.util import check_context
 from grok.rest import RestPublisher
 from grok.interfaces import IRESTSkinType
 
 def get_context(module_info, factory):
-    context = module_info.getAnnotation('grok.context', None)
-    return determine_class_context(factory, context)
+    return determine_class_component(module_info, factory,
+                                     'context', 'grok.context')
 
+def get_viewletmanager(module_info, factory):
+    return determine_class_component(module_info, factory,
+                                     'viewletmanager', 'grok.viewletmanager')
+
 def get_name_classname(factory):
     return get_name(factory, factory.__name__.lower())
 
@@ -74,13 +80,23 @@
     priority = 1001
 
     def grok(self, name, module, module_info, config, **kw):
-        possible_contexts = martian.scan_for_classes(module, [grok.Model,
-                                                              grok.Container])
-        context = determine_module_context(module_info, possible_contexts)
+        context = determine_module_component(module_info, 'grok.context',
+                                             [grok.Model, grok.Container])
         module.__grok_context__ = context
         return True
 
 
+class ViewletManagerContextGrokker(martian.GlobalGrokker):
+
+    priority = 1001
+
+    def grok(self, name, module, module_info, config, **kw):
+        viewletmanager = determine_module_component(module_info,
+                                                    'grok.viewletmanager',
+                                                    [grok.ViewletManager])
+        module.__grok_viewletmanager__ = viewletmanager
+        return True
+    
 class AdapterGrokker(martian.ClassGrokker):
     component_class = grok.Adapter
 
@@ -247,8 +263,8 @@
         if templates is not None:
             config.action(
                 discriminator=None,
-                callable=templates.checkTemplates,
-                args=(module_info, factory, factory.__name__.lower())
+                callable=templates.checkTemplatesView,
+                args=(module_info, factory)
             )
 
         # safety belt: make sure that the programmer didn't use
@@ -452,7 +468,8 @@
             if interfaces is None:
                 # There's no explicit interfaces defined, so we assume the
                 # module context to be the thing adapted.
-                check_context(module_info.getModule(), context)
+                check_module_component(module_info.getModule(), context,
+                                       'context', 'grok.context')
                 interfaces = (context, )
 
             config.action(
@@ -873,3 +890,69 @@
             args=(name, layer, IRESTSkinType)
             )
         return True
+
+class ViewletManagerGrokker(martian.ClassGrokker):
+    component_class = grok.ViewletManager
+
+    def grok(self, name, factory, module_info, config, **kw):
+        factory.module_info = module_info
+        
+        name = get_name(factory)
+        view_context = get_context(module_info, factory)
+
+        view = determine_class_directive('grok.view', factory,
+                                         module_info, default=IBrowserView)
+        viewlet_layer = determine_class_directive('grok.layer', factory,
+                                                  module_info,
+                                                  default=IDefaultBrowserLayer)
+
+        config.action(
+            discriminator = ('viewletManager', view_context, viewlet_layer,
+                             view, name),
+            callable = component.provideAdapter,
+            args = (factory, (view_context, viewlet_layer, view),
+                    IViewletManager, name)
+            )
+
+        return True
+
+class ViewletGrokker(martian.ClassGrokker):
+    component_class = grok.Viewlet
+
+    def grok(self, name, factory, module_info, config, **kw):
+        viewlet_name = get_name_classname(factory)
+        viewlet_context = get_context(module_info, factory)
+
+        factory.module_info = module_info # to make /static available
+
+        # find templates
+        templates = module_info.getAnnotation('grok.templates', None)
+        if templates is not None:
+            config.action(
+                discriminator=None,
+                callable=templates.checkTemplatesViewlet,
+                args=(module_info, factory))
+    
+        view = determine_class_directive('grok.view', factory,
+                                         module_info, default=IBrowserView)
+        viewlet_layer = determine_class_directive('grok.layer', factory,
+                                                  module_info,
+                                                  default=IDefaultBrowserLayer)
+        viewletmanager = get_viewletmanager(module_info, factory)
+    
+        config.action(
+            discriminator = ('viewlet', viewlet_context, viewlet_layer,
+                             view, viewletmanager, viewlet_name),
+            callable = component.provideAdapter,
+            args = (factory, (viewlet_context, viewlet_layer, view,
+                    viewletmanager), IViewlet, viewlet_name)
+            )
+
+        permission = get_default_permission(factory)
+        config.action(
+            discriminator=('protectName', factory, '__call__'),
+            callable=make_checker,
+            args=(factory, factory, permission, ['update', 'render']),
+            )
+        
+        return True

Modified: grok/trunk/src/grok/templatereg.py
===================================================================
--- grok/trunk/src/grok/templatereg.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/templatereg.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -81,7 +81,9 @@
                 module_info.dotted_name, ', '.join(unassociated)))
             warnings.warn(msg, UserWarning, 2)
 
-    def checkTemplates(self, module_info, factory, factory_name):
+    def _checkTemplates(self, module_info, factory, component_name,
+                        has_render, has_no_render):
+        factory_name = factory.__name__.lower()
         template_name = util.class_annotation(factory, 'grok.template',
                                               factory_name)
 
@@ -89,30 +91,48 @@
             # grok.template is being used
 
             if self.get(factory_name):
-                raise GrokError("Multiple possible templates for view %r. It "
+                raise GrokError("Multiple possible templates for %s %r. It "
                                 "uses grok.template('%s'), but there is also "
                                 "a template called '%s'."
-                                % (factory, template_name, factory_name),
-                                factory)
+                                % (component_name, factory, template_name,
+                                   factory_name), factory)
         template = self.get(template_name)
-        if template:
-            if (getattr(factory, 'render', None) and not
-                util.check_subclass(factory, grok.components.GrokForm)):
+        if template is not None:
+            if has_render(factory):
                 # we do not accept render and template both for a view
                 # (unless it's a form, they happen to have render.
                 raise GrokError(
-                    "Multiple possible ways to render view %r. "
+                    "Multiple possible ways to render %s %r. "
                     "It has both a 'render' method as well as "
-                    "an associated template." % factory, factory)
+                    "an associated template." %
+                    (component_name, factory), factory)
             self.markAssociated(template_name)
             factory.template = template
             template._initFactory(factory)
         else:
-            if not getattr(factory, 'render', None):
+            if has_no_render(factory):
                 # we do not accept a view without any way to render it
-                raise GrokError("View %r has no associated template or "
-                                "'render' method." % factory, factory)
+                raise GrokError("%s %r has no associated template or "
+                                "'render' method." %
+                                (component_name.title(), factory), factory)
 
+    def checkTemplatesView(self, module_info, factory):
+        def has_render(factory):
+            return (getattr(factory, 'render', None) and
+                    not util.check_subclass(factory, grok.components.GrokForm))
+        def has_no_render(factory):
+            return not getattr(factory, 'render', None)
+        self._checkTemplates(module_info, factory, 'view',
+                             has_render, has_no_render)
+
+    def checkTemplatesViewlet(self, module_info, factory):
+        def has_render(factory):
+            return factory.render != grok.components.Viewlet.render
+        def has_no_render(factory):
+            return not has_render(factory)
+        self._checkTemplates(module_info, factory, 'viewlet',
+                             has_render, has_no_render)
+
 class PageTemplateFileFactory(grok.GlobalUtility):
 
     grok.implements(grok.interfaces.ITemplateFileFactory)

Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/tests/test_grok.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -46,7 +46,7 @@
                  'zcml', 'static', 'utility', 'xmlrpc', 'json', 'container',
                  'traversal', 'form', 'grokker', 'directive', 'util',
                  'baseclass', 'annotation', 'application', 'template', 'order',
-                 'testsetup']:
+                 'viewlet', 'testsetup']:
         suite.addTest(suiteFromPackage(name))
     return suite
 

Copied: grok/trunk/src/grok/tests/viewlet (from rev 84436, grok/branches/snowsprint-viewlets2/src/grok/tests/viewlet)

Modified: grok/trunk/src/grok/util.py
===================================================================
--- grok/trunk/src/grok/util.py	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/src/grok/util.py	2008-03-03 14:23:03 UTC (rev 84437)
@@ -25,7 +25,7 @@
 from zope.security.interfaces import IPermission
 
 from martian.error import GrokError, GrokImportError
-from martian.util import class_annotation, methods_from_class
+from martian.util import class_annotation, methods_from_class, scan_for_classes
 
 def check_adapts(class_):
     if component.adaptedBy(class_) is None:
@@ -33,18 +33,20 @@
                         "(use grok.adapts to specify)."
                         % class_, class_)
 
-def make_checker(factory, view_factory, permission):
+def make_checker(factory, view_factory, permission, method_names=None):
     """Make a checker for a view_factory associated with factory.
 
     These could be one and the same for normal views, or different
     in case we make method-based views such as for JSON and XMLRPC.
     """
+    if method_names is None:
+        method_names = ['__call__']
     if permission is not None:
         check_permission(factory, permission)
     if permission is None or permission == 'zope.Public':
-        checker = NamesChecker(['__call__'])
+        checker = NamesChecker(method_names)
     else:
-        checker = NamesChecker(['__call__'], permission)
+        checker = NamesChecker(method_names, permission)
     defineChecker(view_factory, checker)
 
 def check_permission(factory, permission):
@@ -123,31 +125,66 @@
     # if components have a grok.order directive, sort by that
     return sorted(components, key=_sort_key)
 
-AMBIGUOUS_CONTEXT = object()
-def check_context(component, context):
-    if context is None:
-        raise GrokError("No module-level context for %r, please use "
-                        "grok.context." % component, component)
-    elif context is AMBIGUOUS_CONTEXT:
-        raise GrokError("Multiple possible contexts for %r, please use "
-                        "grok.context." % component, component)
+AMBIGUOUS_COMPONENT = object()
+def check_module_component(factory, component,
+                           component_name, component_directive):
+    """Raise error if module-level component cannot be determined.
 
-def determine_module_context(module_info, models):
-    if len(models) == 0:
-        context = None
-    elif len(models) == 1:
-        context = models[0]
+    If the module-level component is None, it's never been specified;
+    raise error telling developer to specify.
+
+    if the module-level component is AMBIGUOUS_COMPONENT, raise
+    an error telling developer to specify which one to use.
+    """
+    if component is None:
+        raise GrokError("No module-level %s for %r, please use "
+                        "%s." % (component_name, factory, component_directive),
+                        factory)
+    elif component is AMBIGUOUS_COMPONENT:
+        raise GrokError("Multiple possible %ss for %r, please use "
+                        "%s." % (component_name, factory, component_directive),
+                        factory)
+    
+def determine_module_component(module_info, annotation, classes):
+    """Determine module-level component.
+
+    The module-level component can be set explicitly using the
+    annotation (such as grok.context).
+
+    If there is no annotation, the module-level component is determined
+    by scanning for subclasses of any in the list of classes.
+
+    If there is no module-level component, the module-level component is
+    None.
+
+    If there is one module-level component, it is returned.
+
+    If there are more than one module-level component, AMBIGUOUS_COMPONENT
+    is returned.
+    """
+    components = scan_for_classes(module_info.getModule(), classes)
+    if len(components) == 0:
+        component = None
+    elif len(components) == 1:
+        component = components[0]
     else:
-        context = AMBIGUOUS_CONTEXT
+        component= AMBIGUOUS_COMPONENT
+        
+    module_component = module_info.getAnnotation(annotation, None)
+    if module_component:
+        component = module_component
+    return component
 
-    module_context = module_info.getAnnotation('grok.context', None)
-    if module_context:
-        context = module_context
 
-    return context
+def determine_class_component(module_info, class_,
+                              component_name, component_directive):
+    """Determine component for a class.
 
-
-def determine_class_context(class_, module_context):
-    context = class_annotation(class_, 'grok.context', module_context)
-    check_context(class_, context)
-    return context
+    Determine a component for a class. If no class-specific component exists,
+    try falling back on module-level component.
+    """
+    module_component = module_info.getAnnotation(component_directive, None)
+    component = class_annotation(class_, component_directive, module_component)
+    check_module_component(class_, component,
+                           component_name, component_directive)
+    return component

Modified: grok/trunk/versions.cfg
===================================================================
--- grok/trunk/versions.cfg	2008-03-03 12:39:06 UTC (rev 84436)
+++ grok/trunk/versions.cfg	2008-03-03 14:23:03 UTC (rev 84437)
@@ -62,6 +62,7 @@
 zope.cachedescriptors = 3.4.0
 zope.component = 3.4.0
 zope.configuration = 3.4.0
+zope.contentprovider = 3.4.0
 zope.contenttype = 3.4.0
 zope.copypastemove = 3.4.0
 zope.datetime = 3.4.0
@@ -99,3 +100,4 @@
 zope.testing = 3.5.1
 zope.thread = 3.4
 zope.traversing = 3.5.0a1.dev-r78730
+zope.viewlet = 3.4.1



More information about the Checkins mailing list