[Zope3-checkins] SVN: Zope3/branches/roger-contentprovider/src/zope/viewlet/ Okay, now all the package tests pass, including the reworked directive

Stephan Richter srichter at cosmos.phy.tufts.edu
Wed Oct 12 21:42:21 EDT 2005


Log message for revision 39110:
  Okay, now all the package tests pass, including the reworked directive 
  tests.
  

Changed:
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py

-=-
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt	2005-10-13 01:42:20 UTC (rev 39110)
@@ -210,6 +210,7 @@
 --------------------
 
 
+
 A Complex Example
 -----------------
 

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt	2005-10-13 01:42:20 UTC (rev 39110)
@@ -1,17 +1,12 @@
-=================================================
-The ``viewletManager`` and ``viewlet`` Directives
-=================================================
+================================
+The ``viewletManager`` Directive
+================================
 
-The viewletManager directive allows you to quickly register a new content
-provider that presents viewlets on a web page. You'll find more information
-about viewlets and viewlet managers in the `README.txt` file.
+The ``viewletManager`` directive allows you to quickly register a new viewlet
+manager without worrying about the details of the ``adapter``
+directive. Before we can use the directives, we have to register their
+handlers by executing the package's meta configuration:
 
-Here is a sample directive::
-
-  >>> from zope.app import zapi
-  >>> import zope.interface
-  
-  >>> from zope.viewlet.tests import ILeftViewlet
   >>> from zope.configuration import xmlconfig
   >>> context = xmlconfig.string('''
   ... <configure i18n_domain="zope">
@@ -19,40 +14,22 @@
   ... </configure>
   ... ''')
 
-  >>> import os, tempfile
-  >>> temp_dir = tempfile.mkdtemp()
+Now we can register a viewlet manager:
 
-  >>> testViewletManager = os.path.join(temp_dir, 'testviewletmanager.pt')
-  >>> open(testViewletManager, 'w').write('''
-  ... <div tal:repeat="viewlet options/viewlets"
-  ...   <div tal:replace="viewlet"></div>
-  ... </div>
-  ... ''')
-
   >>> context = xmlconfig.string('''
-  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-  ...            package="zope.viewlet.tests">
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
   ...   <viewletManager
-  ...       name="testviewlet"
-  ...       for="*"
-  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-  ...       template="%s"
+  ...       name="defaultmanager"
   ...       permission="zope.Public"
   ...       />
   ... </configure>
-  ... ''' % (testViewletManager), context=context)
-  
-As you can see, the directive looks very similar to the page directive and you
-are right. The viewlet manager directive does not permit you to specify a
-`menu` and `title`, since it is not sensible to have a menu item for a viewlet.
-However, it does support two more qualifying attributes, `view` and
-`providerType`. While view is nearly never specified (very common default),
-the `providerType` attribute *must* be specified.
+  ... ''', context=context)
 
-# More (and more precise) information needed here...
+Let's make sure the directive has really issued a sensible adapter
+registration; to do that, we create some dummy content, request and view
+objects:
 
-If we now look into the adapter registry, we will find the viewlet manager:
-
+  >>> import zope.interface
   >>> class Content(object):
   ...     zope.interface.implements(zope.interface.Interface)
   >>> content = Content()
@@ -63,181 +40,347 @@
   >>> from zope.app.publisher.browser import BrowserView
   >>> view = BrowserView(content, request)
 
-  >>> import zope.interface
-  >>> from zope.viewlet.interfaces import IViewletManager
-  
-  >>> manager = zapi.getMultiAdapter(
-  ...     (content, request, view), IViewletManager, name='testviewlet')
+Now let's lookup the manager. This particular registration is pretty boring:
 
-As we did not yet associate viewlets with this viewlet manager, calling it
-gives us an empty string:
+  >>> import zope.component
+  >>> from zope.viewlet import interfaces
+  >>> manager = zope.component.getMultiAdapter(
+  ...     (content, request, view),
+  ...     interfaces.IViewletManager, name='defaultmanager')
 
-  >>> manager().strip()
+  >>> manager
+  <zope.viewlet.manager.<ViewletManager providing IViewletManager> object ...>
+  >>> interfaces.IViewletManager.providedBy(manager)
+  True
+  >>> manager.template is None
+  True
+  >>> manager()
   u''
 
-So let's now create a viewlet for the viewlet manager:
+However, this registration is not very useful, since we did specify a specific
+viewlet manager interface, a specific content interface, specific view or
+specific layer. This means that all viewlets registered will be found.
 
-  >>> testViewlet = os.path.join(temp_dir, 'testviewlet.pt')
-  >>> open(testViewlet, 'w').write('''
-  ... <div>testviewlet content</div>
+The first step to effectively using the viewlet manager directive is to define
+a special viewlet manager interface:
+
+  >>> class ILeftColumn(interfaces.IViewletManager):
+  ...     """Left column of my page."""
+
+Now we can register register a manager providing this interface:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewletManager
+  ...       name="leftcolumn"
+  ...       permission="zope.Public"
+  ...       provides="zope.viewlet.directives.ILeftColumn"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+
+  >>> manager = zope.component.getMultiAdapter(
+  ...     (content, request, view), ILeftColumn, name='leftcolumn')
+
+  >>> manager
+  <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+  >>> ILeftColumn.providedBy(manager)
+  True
+  >>> manager.template is None
+  True
+  >>> manager()
+  u''
+
+Next let's see what happens, if we specify a template for the viewlet manager:
+
+  >>> import os, tempfile
+  >>> temp_dir = tempfile.mkdtemp()
+
+  >>> leftColumnTemplate = os.path.join(temp_dir, 'leftcolumn.pt')
+  >>> open(leftColumnTemplate, 'w').write('''
+  ... <div class="column">
+  ...    <div class="entry"
+  ...         tal:repeat="viewlet options/viewlets"
+  ...         tal:content="structure viewlet" />
+  ... </div>
   ... ''')
-  
+
   >>> context = xmlconfig.string('''
-  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-  ...            package="zope.viewlet.tests">
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewletManager
+  ...       name="leftcolumn"
+  ...       permission="zope.Public"
+  ...       provides="zope.viewlet.directives.ILeftColumn"
+  ...       template="%s"
+  ...       />
+  ... </configure>
+  ... ''' %leftColumnTemplate, context=context)
+
+  >>> manager = zope.component.getMultiAdapter(
+  ...     (content, request, view), ILeftColumn, name='leftcolumn')
+
+  >>> manager
+  <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+  >>> ILeftColumn.providedBy(manager)
+  True
+  >>> manager.template
+  <BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn>  ...>>
+  >>> print manager().strip()
+  <div class="column">
+  </div>
+
+Additionally you can specify a class that will serve as a base to the default
+viewlet manager or be a viewlet manager in its own right. In our case we will
+provide a custom implementation of the ``sort()`` method, which will sort by a
+weight attribute in the viewlet:
+
+  >>> class WeightBasedSorting(object):
+  ...     def sort(self, viewlets):
+  ...         return sorted(viewlets,
+  ...                       lambda x, y: cmp(x[1].weight, y[1].weight))
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewletManager
+  ...       name="leftcolumn"
+  ...       permission="zope.Public"
+  ...       provides="zope.viewlet.directives.ILeftColumn"
+  ...       template="%s"
+  ...       class="zope.viewlet.directives.WeightBasedSorting"
+  ...       />
+  ... </configure>
+  ... ''' %leftColumnTemplate, context=context)
+
+  >>> manager = zope.component.getMultiAdapter(
+  ...     (content, request, view), ILeftColumn, name='leftcolumn')
+
+  >>> manager
+  <zope.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
+  >>> manager.__class__.__bases__
+  (<class 'zope.viewlet.directives.WeightBasedSorting'>,
+   <class 'zope.viewlet.manager.ViewletManagerBase'>)
+  >>> ILeftColumn.providedBy(manager)
+  True
+  >>> manager.template
+  <BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn>  ...>>
+  >>> print manager().strip()
+  <div class="column">
+  </div>
+
+Finally, if a non-existent template is specified, an error is raised:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewletManager
+  ...       name="leftcolumn"
+  ...       permission="zope.Public"
+  ...       template="foo.pt"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 3.2-7.8
+      ConfigurationError: ('No such file', '...foo.pt')
+
+
+=========================
+The ``viewlet`` Directive
+=========================
+
+Now that we have a viewlet manager, we have to register some viewlets for
+it. The ``viewlet`` directive is similar to the ``viewletManager`` directive,
+except that the viewlet is also registered for a particular manager interface,
+as seen below:
+
+  >>> weatherTemplate = os.path.join(temp_dir, 'weather.pt')
+  >>> open(weatherTemplate, 'w').write('''
+  ... <div>sunny</div>
+  ... ''')
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
   ...   <viewlet
-  ...       name="testviewlet"
-  ...       for="*"
-  ...       providerType="zope.viewlet.tests.ILeftViewlet"
+  ...       name="weather"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
   ...       template="%s"
   ...       permission="zope.Public"
   ...       />
   ... </configure>
-  ... ''' % (testViewlet), context=context)
+  ... ''' % weatherTemplate, context=context)
 
-The ``viewlet`` directive is similar to the viewletManager directive.
-
 If we look into the adapter registry, we will find the viewlet:
 
-  >>> from zope.viewlet.interfaces import IViewlet
-  
-  >>> viewlet = zapi.getMultiAdapter(
-  ...     (content, request, view, manager), IViewlet, name='testviewlet')
+  >>> viewlet = zope.component.getMultiAdapter(
+  ...     (content, request, view, manager), interfaces.IViewlet,
+  ...     name='weather')
   >>> viewlet().strip()
-  u'<div>testviewlet content</div>'
+  u'<div>sunny</div>'
 
 The manager now also gives us the output of the one and only viewlet:
 
-  >>> manager().strip()
-  u'<div>testviewlet content</div>'
+  >>> print manager().strip()
+  <div class="column">
+    <div class="entry">
+      <div>sunny</div>
+    </div>
+  </div>
 
 Let's now ensure that we can also specify a viewlet class:
 
+  >>> class Weather(object):
+  ...     weight = 0
+
   >>> context = xmlconfig.string('''
-  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-  ...            package="zope.viewlet.tests">
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
   ...   <viewlet
-  ...       name="testviewlet2"
+  ...       name="weather2"
   ...       for="*"
-  ...       providerType="zope.viewlet.tests.ILeftViewlet"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
   ...       template="%s"
-  ...       class=".TestViewlet"
+  ...       class="zope.viewlet.directives.Weather"
   ...       permission="zope.Public"
   ...       />
   ... </configure>
-  ... ''' % testViewlet, context=context)
+  ... ''' % weatherTemplate, context=context)
 
   >>> viewlet = zope.component.getMultiAdapter(
-  ...     (content, request, view, manager), name='testviewlet2')
+  ...     (content, request, view, manager), interfaces.IViewlet,
+  ...     name='weather2')
   >>> viewlet().strip()
-  u'<div>testviewlet content</div>'
+  u'<div>sunny</div>'
 
-#Okay, so the template-driven cases work. But just specifying a class should
-#also work:
-#
-#  >>> context = xmlconfig.string('''
-#  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-#  ...            package="zope.viewlet.tests">
-#  ...   <viewlet
-#  ...       name="testviewlet3"
-#  ...       for="*"
-#  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-#  ...       class=".tests.TestViewlet2"
-#  ...       permission="zope.Public"
-#  ...       />
-#  ... </configure>
-#  ... ''', context=context)
-#
-#  >>> viewlet = zope.component.getMultiAdapter(
-#  ...     (content, request, view), ILeftViewlet, name='testviewlet3')
-#  >>> viewlet()
-#  u'called'
-#
-#It should also be possible to specify an alternative attribute of the class to
-#be rendered upon calling the viewlet:
-#
-#  >>> context = xmlconfig.string('''
-#  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-#  ...            package="zope.viewlet.tests">
-#  ...   <viewlet
-#  ...       name="testviewlet4"
-#  ...       for="*"
-#  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-#  ...       class=".tests.TestViewlet"
-#  ...       attribute="doSomething"
-#  ...       permission="zope.Public"
-#  ...       />
-#  ... </configure>
-#  ... ''', context=context)
-#
-#  >>> viewlet = zope.component.getMultiAdapter(
-#  ...     (content, request, view), ILeftViewlet, name='testviewlet4')
-#  >>> viewlet()
-#  u'something'
-#
-#
-#Error Scenarios
-#---------------
-#
-#Neither the class or template have been specified:
-#
-#  >>> context = xmlconfig.string('''
-#  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-#  ...            package="zope.viewlet.tests">
-#  ...   <viewlet
-#  ...       name="testviewlet"
-#  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-#  ...       permission="zope.Public"
-#  ...       />
-#  ... </configure>
-#  ... ''', context=context)
-#  Traceback (most recent call last):
-#  ...
-#  ZopeXMLConfigurationError: File "<string>", line 4.2-8.8
-#      ConfigurationError: Must specify a class or template
-#
-#The specified attribute is not ``__call__``, but also a template has been
-#specified:
-#
-#  >>> context = xmlconfig.string('''
-#  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-#  ...            package="zope.viewlet.tests">
-#  ...   <viewlet
-#  ...       name="testviewlet"
-#  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-#  ...       template="test_viewlet.pt"
-#  ...       attribute="faux"
-#  ...       permission="zope.Public"
-#  ...       />
-#  ... </configure>
-#  ... ''', context=context)
-#  Traceback (most recent call last):
-#  ...
-#  ZopeXMLConfigurationError: File "<string>", line 4.2-10.8
-#      ConfigurationError: Attribute and template cannot be used together.
-#
-#Now, we are not specifying a template, but a class that does not have the
-#specified attribute:
-#
-#  >>> context = xmlconfig.string('''
-#  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"
-#  ...            package="zope.viewlet.tests">
-#  ...   <viewlet
-#  ...       name="testviewlet"
-#  ...       providerType="zope.viewlet.tests.ILeftViewlet"
-#  ...       class=".TestViewlet"
-#  ...       attribute="faux"
-#  ...       permission="zope.Public"
-#  ...       />
-#  ... </configure>
-#  ... ''', context=context)
-#  Traceback (most recent call last):
-#  ...
-#  ZopeXMLConfigurationError: File "<string>", line 4.2-10.8
-#    ConfigurationError: The provided class doesn't have the specified attribute
+Okay, so the template-driven cases work. But just specifying a class should
+also work:
 
+  >>> class Sport(object):
+  ...     weight = 0
+  ...     def __call__(self):
+  ...         return u'Red Sox vs. White Sox'
 
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="sport"
+  ...       for="*"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
+  ...       class="zope.viewlet.directives.Sport"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
 
+  >>> viewlet = zope.component.getMultiAdapter(
+  ...     (content, request, view, manager), interfaces.IViewlet, name='sport')
+  >>> viewlet()
+  u'Red Sox vs. White Sox'
+
+It should also be possible to specify an alternative attribute of the class to
+be rendered upon calling the viewlet:
+
+  >>> class Stock(object):
+  ...     weight = 0
+  ...     def getStockTicker(self):
+  ...         return u'SRC $5.19'
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="stock"
+  ...       for="*"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
+  ...       class="zope.viewlet.directives.Stock"
+  ...       attribute="getStockTicker"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+
+  >>> viewlet = zope.component.getMultiAdapter(
+  ...     (content, request, view, manager), interfaces.IViewlet,
+  ...     name='stock')
+  >>> viewlet()
+  u'SRC $5.19'
+
+A final feature the ``viewlet`` directive supports is the additional
+specification of any amount keyword arguments:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="stock2"
+  ...       permission="zope.Public"
+  ...       class="zope.viewlet.directives.Stock"
+  ...       weight="8"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+
+  >>> viewlet = zope.component.getMultiAdapter(
+  ...     (content, request, view, manager), interfaces.IViewlet,
+  ...     name='stock2')
+  >>> viewlet.weight
+  u'8'
+
+
+Error Scenarios
+---------------
+
+Neither the class or template have been specified:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="testviewlet"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 3.2-7.8
+      ConfigurationError: Must specify a class or template
+
+The specified attribute is not ``__call__``, but also a template has been
+specified:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="testviewlet"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
+  ...       template="test_viewlet.pt"
+  ...       attribute="faux"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 3.2-9.8
+      ConfigurationError: Attribute and template cannot be used together.
+
+Now, we are not specifying a template, but a class that does not have the
+specified attribute:
+
+  >>> context = xmlconfig.string('''
+  ... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
+  ...   <viewlet
+  ...       name="testviewlet"
+  ...       manager="zope.viewlet.directives.ILeftColumn"
+  ...       class="zope.viewlet.directives.Sport"
+  ...       attribute="faux"
+  ...       permission="zope.Public"
+  ...       />
+  ... </configure>
+  ... ''', context=context)
+  Traceback (most recent call last):
+  ...
+  ZopeXMLConfigurationError: File "<string>", line 3.2-9.8
+    ConfigurationError: The provided class doesn't have the specified attribute
+
+
 Cleanup
 -------
 

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py	2005-10-13 01:42:20 UTC (rev 39110)
@@ -112,7 +112,11 @@
         template = ViewPageTemplateFile(template)
 
     if ViewletManagerBase not in bases:
-        bases = bases + (ViewletManagerBase,)
+        # Make sure that we do not get a default viewlet manager mixin, if the
+        # provided base is already a full viewlet manager implementation.
+        if not (len(bases) == 1 and
+                interfaces.IViewletManager.implementedBy(bases[0])):
+            bases = bases + (ViewletManagerBase,)
 
     ViewletManager = type(
         '<ViewletManager providing %s>' % interface.getName(),

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py	2005-10-13 01:42:20 UTC (rev 39110)
@@ -30,27 +30,21 @@
 from zope.app.publisher.browser import viewmeta
 from zope.app.publisher.interfaces.browser import IBrowserView
 
-#from zope.contentprovider.interfaces import IWeightSupport
-from zope.viewlet import viewlet
-from zope.viewlet import manager
-from zope.viewlet import interfaces
+from zope.viewlet import viewlet, manager, interfaces
 
+def viewletManagerDirective(
+    _context, name, permission,
+    for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
+    provides=interfaces.IViewletManager, class_=None, template=None,
+    allowed_interface=None, allowed_attributes=None):
 
-# TODO: remove weight out of viewlet manager directive
-# TODO: support default class_
-# TODO: do we need a allowed_interface ?
-# TODO: 
-def viewletManagerDirective(_context, name, permission, providerType,
-                     for_=Interface, layer=IDefaultBrowserLayer,
-                     view=IBrowserView, 
-                     class_=None, template=None, allowed_interface=None):
-
+    # A list of attributes available under the provided permission
     required = {}
 
     # Get the permission; mainly to correctly handle CheckerPublic.
     permission = viewmeta._handle_permission(_context, permission)
 
-    # If class is not given we use the default viewlet manager.
+    # If class is not given we use the basic viewlet manager.
     if class_ is None:
         class_ = manager.ViewletManagerBase
 
@@ -64,50 +58,54 @@
 
         # Create a new class based on the template and class.
         new_class = manager.ViewletManager(
-            providerType, bases=(class_, ))
+            provides, template=template, bases=(class_, ))
+    else:
+        # Create a new class based on the class.
+        new_class = manager.ViewletManager(provides, bases=(class_, ))
 
-    if hasattr(new_class, '__implements__'):
-        classImplements(new_class, IBrowserPublisher)
-
-    # set providerType if the class_ defines the global attribute
-    if hasattr(class_, 'providerType'):
-        classImplements(new_class, providerType)
-
-    for attr_name in ('browserDefault', '__call__',
-                      'publishTraverse'):
+    # Register some generic attributes with the security dictionary
+    for attr_name in ('browserDefault', '__call__', 'publishTraverse'):
         required[attr_name] = permission
 
+    # Register the ``provides`` interface and register fields in the security
+    # dictionary
     viewmeta._handle_allowed_interface(
+        _context, (provides,), permission, required)
+
+    # Register the allowed interface and add the field's security entries
+    viewmeta._handle_allowed_interface(
         _context, allowed_interface, permission, required)
 
+    # Register single allowed attributes in the security dictionary
+    viewmeta._handle_allowed_attributes(
+        _context, allowed_attributes, permission, required)
+
+    # Register interfaces
     viewmeta._handle_for(_context, for_)
     metaconfigure.interface(_context, view)
 
+    # Create a checker for the viewlet manager
     checker.defineChecker(new_class, checker.Checker(required))
 
-    # register viewlet
+    # register a viewlet manager
     _context.action(
         discriminator = ('viewletManager', for_, layer, view, name),
         callable = metaconfigure.handler,
         args = ('provideAdapter',
-                (for_, layer, view), interfaces.IViewletManager, name,
+                (for_, layer, view), provides, name,
                  new_class, _context.info),)
 
 
+def viewletDirective(
+    _context, name, permission,
+    for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
+    manager=interfaces.IViewletManager, class_=None, template=None,
+    attribute='__call__', allowed_interface=None, allowed_attributes=None,
+    **kwargs):
 
-# TODO: support None for weight
-def viewletDirective(_context, name, permission, providerType, for_=Interface, 
-                     layer=IDefaultBrowserLayer, view=IBrowserView, 
-                     class_=None, template=None, attribute='__call__', 
-                     weight=None, allowed_interface=None, 
-                     allowed_attributes=None):
-
+    # Security map dictionary
     required = {}
 
-    #if interfaces.IWeightSupport.implementedBy(class_) and weight == None:
-    #    msg = "Must specify a weight if IWeightSupport is implemented"
-    #    raise ConfigurationError(msg)
-
     # Get the permission; mainly to correctly handle CheckerPublic.
     permission = viewmeta._handle_permission(_context, permission)
 
@@ -144,19 +142,17 @@
         if template:
             # Create a new class for the viewlet template and class.
             new_class = viewlet.SimpleViewletClass(
-                template, bases=(class_, ), weight=weight)
+                template, bases=(class_, ), attributes=kwargs)
         else:
             if not hasattr(class_, 'browserDefault'):
-                cdict = {
-                    'browserDefault':
-                    lambda self, request: (getattr(self, attribute), ())
-                    }
+                cdict = {'browserDefault':
+                         lambda self, request: (getattr(self, attribute), ())}
             else:
                 cdict = {}
 
-            cdict['_weight'] = weight
             cdict['__name__'] = name
             cdict['__page_attribute__'] = attribute
+            cdict.update(kwargs)
             new_class = type(class_.__name__,
                              (class_, viewlet.SimpleAttributeViewlet), cdict)
 
@@ -165,31 +161,31 @@
 
     else:
         # Create a new class for the viewlet template alone.
-        new_class = viewlet.SimpleViewletClass(
-            template, name=name, weight=weight)
+        new_class = viewlet.SimpleViewletClass(template, name=name)
 
-    # set providerType if the class_ defines the global attribute
-    if hasattr(new_class, 'providerType'):
-        classImplements(new_class, providerType)
-
-    for attr_name in (attribute, 'browserDefault', '__call__',
-                      'publishTraverse', 'weight'):
-        required[attr_name] = permission
-
+    # Set up permission mapping for various accessible attributes
     viewmeta._handle_allowed_interface(
         _context, allowed_interface, permission, required)
     viewmeta._handle_allowed_attributes(
-        _context, allowed_interface, permission, required)
+        _context, allowed_attributes, permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context, kwargs.keys(), permission, required)
+    viewmeta._handle_allowed_attributes(
+        _context,
+        (attribute, 'browserDefault', '__call__', 'publishTraverse'),
+        permission, required)
 
+    # Register the interfaces.
     viewmeta._handle_for(_context, for_)
     metaconfigure.interface(_context, view)
 
+    # Create the security checker for the new class
     checker.defineChecker(new_class, checker.Checker(required))
 
     # register viewlet
     _context.action(
-        discriminator = ('viewlet', for_, layer, view, name, providerType),
+        discriminator = ('viewlet', for_, layer, view, manager, name),
         callable = metaconfigure.handler,
         args = ('provideAdapter',
-                (for_, layer, view, providerType), interfaces.IViewlet,
+                (for_, layer, view, manager), interfaces.IViewlet,
                  name, new_class, _context.info),)

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py	2005-10-13 01:42:20 UTC (rev 39110)
@@ -20,59 +20,88 @@
 import zope.configuration.fields
 import zope.schema
 
+from zope.app.i18n import ZopeMessageIDFactory as _
 from zope.app.publisher.browser import metadirectives
+from zope.app.publisher.interfaces import browser
 
+from zope.viewlet import interfaces
 
-class IViewletManagerDirective(metadirectives.IPagesDirective):
-    """A directive to register a new viewlet manager.
 
-    Viewlet manager registrations are very similar to page registrations, 
-    except that they are additionally qualified by a type where is used for
-    lookup viewlets of this type.
+class IContentProvider(metadirectives.IPagesDirective):
+    """A directive to register a simple content provider.
+
+    Content providers are registered by their context (`for` attribute), the
+    request (`layer` attribute) and the view (`view` attribute). They also
+    must provide a name, so that they can be found using the TALES
+    ``provider`` namespace. Other than that, content providers are just like
+    any other views.
     """
 
-    providerType = zope.configuration.fields.GlobalInterface(
-        title=u"Viewlet type",
-        description=u"The type interface for viewlets.",
-        required=True)
+    view = zope.configuration.fields.GlobalObject(
+        title=_("The view the content provider is registered for."),
+        description=_("The view can either be an interface or a class. By "
+                      "default the provider is registered for all views, "
+                      "the most common case."),
+        required=False,
+        default=browser.IBrowserView)
 
     name = zope.schema.TextLine(
-        title=u"The name of the page (view)",
-        description=u"""
-        The name shows up in URLs/paths. For example 'foo' or
-        'foo.html'. This attribute is required unless you use the
-        subdirective 'page' to create sub views. If you do not have
-        sub pages, it is common to use an extension for the view name
-        such as '.html'. If you do have sub pages and you want to
-        provide a view name, you shouldn't use extensions.""",
+        title=_("The name of the content provider."),
+        description=_("The name of the content provider is used in the TALES "
+                      "``provider`` namespace to look up the content "
+                      "provider."),
         required=True)
 
+
+class ITemplatedContentProvider(IContentProvider):
+    """A directive for registering a content provider that uses a page
+    template to provide its content."""
+
     template = zope.configuration.fields.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').""",
+        title=_("Content-generating template."),
+        description=_("Refers to a file containing a page template (should "
+                      "end in extension ``.pt`` or ``.html``)."),
         required=False)
 
 
-class IViewletDirective(metadirectives.IPagesDirective,
-                        metadirectives.IViewPageSubdirective,
-                        IViewletManagerDirective):
+class IViewletManagerDirective(ITemplatedContentProvider):
+    """A directive to register a new viewlet manager.
+
+    Viewlet manager registrations are very similar to content provider
+    registrations, since they are just a simple extension of content
+    providers. However, viewlet managers commonly have a specific provided
+    interface, which is used to discriminate the viewlets they are providing.
+    """
+
+    provides = zope.configuration.fields.GlobalInterface(
+        title=_("The interface this viewlet manager provides."),
+        description=_("A viewlet manager can provide an interface, which "
+                      "is used to lookup its contained viewlets."),
+        required=False,
+        default=interfaces.IViewletManager,
+        )
+
+
+class IViewletDirective(ITemplatedContentProvider):
     """A directive to register a new viewlet.
 
-    Viewlet registrations are very similar to page registrations, except that
-    they are additionally qualified by the region and view they are used for. An
-    additional `weight` attribute is specified that is intended to coarsly
-    control the order of the viewlets.
+    Viewlets are content providers that can only be displayed inside a viewlet
+    manager. Thus they are additionally discriminated by the manager. Viewlets
+    can rely on the specified viewlet manager interface to provide their
+    content.
+
+    The viewlet directive also supports an undefined set of keyword arguments
+    that are set as attributes on the viewlet after creation. Those attributes
+    can then be used to implement sorting and filtering, for example.
     """
 
-    view = zope.configuration.fields.GlobalInterface(
-        title=u"view",
+    manager = zope.configuration.fields.GlobalObject(
+        title=_("view"),
         description=u"The interface of the view this viewlet is for. "
                     u"(default IBrowserView)""",
-        required=False)
+        required=False,
+        default=interfaces.IViewletManager)
 
-    weight = zope.schema.Int(
-        title=u"weight",
-        description=u"Integer key for sorting viewlets in the same region.",
-        required=False)
+
+# Arbitrary keys and values are allowed to be passed to the viewlet.
+IViewletDirective.setTaggedValue('keyword_arguments', True)

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py	2005-10-13 01:42:20 UTC (rev 39110)
@@ -24,30 +24,10 @@
 from zope.testing.doctestunit import DocTestSuite, DocFileSuite
 from zope.app.testing import setup
 
-from zope.viewlet import interfaces
-
-
 class TestParticipation(object):
     principal = 'foobar'
     interaction = None
 
-
-class ILeftViewlet(interfaces.IViewlet):
-    """Test viewlet type."""
-
-
-class TestViewlet(object):
-
-    def doSomething(self):
-        return u'something'
-
-
-class TestViewlet2(object):
-
-    def __call__(self):
-        return u'called'
-
-
 def setUp(test):
     setup.placefulSetUp()
 
@@ -57,11 +37,19 @@
 
     zope.security.management.getInteraction().add(TestParticipation())
 
+def directivesSetUp(test):
+    setUp(test)
+    setup.setUpTestAsModule(test, 'zope.viewlet.directives')
 
+
 def tearDown(test):
     setup.placefulTearDown()
 
+def directivesTearDown(test):
+    tearDown(test)
+    setup.tearDownTestAsModule(test)
 
+
 def test_suite():
     return unittest.TestSuite((
         DocFileSuite('README.txt',
@@ -69,7 +57,7 @@
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
         DocFileSuite('directives.txt',
-                     setUp=setUp, tearDown=tearDown,
+                     setUp=directivesSetUp, tearDown=directivesTearDown,
                      optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
                      ),
         ))

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py	2005-10-13 01:39:47 UTC (rev 39109)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py	2005-10-13 01:42:20 UTC (rev 39110)
@@ -44,20 +44,11 @@
 
     zope.interface.implements(interfaces.IViewlet)
 
-    _weight = 0
-
     def __init__(self, context, request, view, providerType):
         super(SimpleViewlet, self).__init__(context, request)
         self.view = view
 
-    def _getWeight (self):
-        """The weight of the viewlet."""
-        return self._weight
 
-    # See zope.app.viewlet.interfaces.IViewlet
-    weight = property(_getWeight)
-
-
 class SimpleAttributeViewlet(SimpleViewlet):
 
     def publishTraverse(self, request, name):
@@ -75,7 +66,8 @@
         return meth(*args, **kw)
 
 
-def SimpleViewletClass(template, offering=None, bases=(), name=u'', weight=0):
+def SimpleViewletClass(template, offering=None, bases=(), attributes=None,
+                       name=u''):
     # Get the current frame
     if offering is None:
         offering = sys._getframe(1).f_globals
@@ -83,11 +75,13 @@
     # Create the base class hierarchy
     bases += (SimpleViewlet, simple)
 
+    attrs = {'index' : ViewletPageTemplateFile(template, offering),
+             '__name__' : name}
+    if attributes:
+        attrs.update(attributes)
+
     # Generate a derived view class.
-    class_ = type("SimpleViewletClass from %s" % template, bases,
-                  {'index' : ViewletPageTemplateFile(template, offering),
-                   '_weight' : weight,
-                   '__name__' : name})
+    class_ = type("SimpleViewletClass from %s" % template, bases, attrs)
 
     return class_
 



More information about the Zope3-Checkins mailing list