================================ The ``viewletManager`` Directive ================================ 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: >>> from zope.configuration import xmlconfig >>> context = xmlconfig.string(''' ... ... ... ... ''') Now we can register a viewlet manager: >>> context = xmlconfig.string(''' ... ... ... ... ''', context=context) 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: >>> import zope.interface >>> class Content(object): ... zope.interface.implements(zope.interface.Interface) >>> content = Content() >>> from zope.publisher.browser import TestRequest >>> request = TestRequest() >>> from zope.publisher.browser import BrowserView >>> view = BrowserView(content, request) Now let's lookup the manager. This particular registration is pretty boring: >>> import zope.component >>> from zope.viewlet import interfaces >>> manager = zope.component.getMultiAdapter( ... (content, request, view), ... interfaces.IViewletManager, name='defaultmanager') >>> manager object ...> >>> interfaces.IViewletManager.providedBy(manager) True >>> manager.template is None True >>> manager.update() >>> manager.render() u'' 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. 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(''' ... ... ... ... ''', context=context) >>> manager = zope.component.getMultiAdapter( ... (content, request, view), ILeftColumn, name='leftcolumn') >>> manager object ...> >>> ILeftColumn.providedBy(manager) True >>> manager.template is None True >>> manager.update() >>> manager.render() 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(''' ...
...
...
... ''') >>> context = xmlconfig.string(''' ... ... ... ... ''' %leftColumnTemplate, context=context) >>> manager = zope.component.getMultiAdapter( ... (content, request, view), ILeftColumn, name='leftcolumn') >>> manager object ...> >>> ILeftColumn.providedBy(manager) True >>> manager.template ...>> >>> manager.update() >>> print manager.render().strip()
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(''' ... ... ... ... ''' %leftColumnTemplate, context=context) >>> manager = zope.component.getMultiAdapter( ... (content, request, view), ILeftColumn, name='leftcolumn') >>> manager object ...> >>> manager.__class__.__bases__ (, ) >>> ILeftColumn.providedBy(manager) True >>> manager.template ...>> >>> manager.update() >>> print manager.render().strip()
Finally, if a non-existent template is specified, an error is raised: >>> context = xmlconfig.string(''' ... ... ... ... ''', context=context) Traceback (most recent call last): ... ZopeXMLConfigurationError: File "", 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(''' ...
sunny
... ''') >>> context = xmlconfig.string(''' ... ... ... ... ''' % weatherTemplate, context=context) If we look into the adapter registry, we will find the viewlet: >>> viewlet = zope.component.getMultiAdapter( ... (content, request, view, manager), interfaces.IViewlet, ... name='weather') >>> viewlet.render().strip() u'
sunny
' >>> viewlet.extra_string_attributes u'can be specified' The manager now also gives us the output of the one and only viewlet: >>> manager.update() >>> print manager.render().strip()
sunny
Let's now ensure that we can also specify a viewlet class: >>> class Weather(object): ... weight = 0 >>> context = xmlconfig.string(''' ... ... ... ... ''' % weatherTemplate, context=context) >>> viewlet = zope.component.getMultiAdapter( ... (content, request, view, manager), interfaces.IViewlet, ... name='weather2') >>> viewlet().strip() u'
sunny
' 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(''' ... ... ... ... ''', 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(''' ... ... ... ... ''', context=context) >>> viewlet = zope.component.getMultiAdapter( ... (content, request, view, manager), interfaces.IViewlet, ... name='stock') >>> viewlet.render() u'SRC $5.19' A final feature the ``viewlet`` directive is that it supports the specification of any number of keyword arguments: >>> context = xmlconfig.string(''' ... ... ... ... ''', 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(''' ... ... ... ... ''', context=context) Traceback (most recent call last): ... ZopeXMLConfigurationError: File "", 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(''' ... ... ... ... ''', context=context) Traceback (most recent call last): ... ZopeXMLConfigurationError: File "", 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(''' ... ... ... ... ''', context=context) Traceback (most recent call last): ... ZopeXMLConfigurationError: File "", line 3.2-9.8 ConfigurationError: The provided class doesn't have the specified attribute Cleanup ------- >>> import shutil >>> shutil.rmtree(temp_dir)