[Zope3-checkins]
SVN: Zope3/branches/roger-contentprovider/src/zope/viewlet/
Fixed the API and tests to the new content provider API.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Tue Oct 25 00:01:56 EDT 2005
Log message for revision 39607:
Fixed the API and tests to the new content provider API.
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/interfaces.py
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/viewlet.py
-=-
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt 2005-10-25 04:01:56 UTC (rev 39607)
@@ -17,13 +17,13 @@
frequently want to define a region in our page and allow specialized content
providers to be inserted based on configuration. Those specialized content
providers are known viewlets and are only available inside viewlet managers,
-which are just a more complex example of a content provider.
+which are just a more complex example of content providers.
Unfortunately, the Java world does not implement this layer separately. The
viewlet manager is most similar to a Java "channel", but we decided against
using this name, since it is very generic and not very meaningful. The viewlet
has no Java counterpart, since Java does not implement content providers using
-a component archicture and thus does not register content providers
+a component architecture and thus does not register content providers
specifically for viewlet managers, which I believe makes the Java
implementation less usefull as a generic concept. In fact, the main design
goal in the Java world is the implementation of reusable and sharable
@@ -67,7 +67,8 @@
So initially nothing gets rendered:
- >>> leftColumn()
+ >>> leftColumn.update()
+ >>> leftColumn.render()
u''
But now we register some viewlets for the manager
@@ -79,14 +80,17 @@
... zope.interface.implements(interfaces.IViewlet)
...
... def __init__(self, context, request, view, manager):
+ ... self.__parent__ = view
+ ...
+ ... def update(self):
... pass
...
- ... def __call__(self):
+ ... def render(self):
... return u'<div class="box">It is sunny today!</div>'
# Create a security checker for viewlets.
>>> from zope.security.checker import NamesChecker, defineChecker
- >>> viewletChecker = NamesChecker(('__call__', 'weight'))
+ >>> viewletChecker = NamesChecker(('update', 'render'))
>>> defineChecker(WeatherBox, viewletChecker)
>>> zope.component.provideAdapter(
@@ -99,9 +103,12 @@
... zope.interface.implements(interfaces.IViewlet)
...
... def __init__(self, context, request, view, manager):
+ ... self.__parent__ = view
+ ...
+ ... def update(self):
... pass
...
- ... def __call__(self):
+ ... def render(self):
... return u'<div class="box">Patriots (23) : Steelers (7)</div>'
>>> defineChecker(SportBox, viewletChecker)
@@ -114,7 +121,8 @@
and thus the left column is filled:
- >>> print leftColumn()
+ >>> leftColumn.update()
+ >>> print leftColumn.render()
<div class="box">Patriots (23) : Steelers (7)</div>
<div class="box">It is sunny today!</div>
@@ -129,7 +137,7 @@
>>> open(leftColTemplate, 'w').write('''
... <div class="left-column">
... <tal:block repeat="viewlet options/viewlets"
- ... replace="structure viewlet" />
+ ... replace="structure viewlet/render" />
... </div>
... ''')
@@ -142,7 +150,8 @@
variable that is an iterable of all the avialable viewlets in the correct
order:
- >>> print leftColumn().strip()
+ >>> leftColumn.update()
+ >>> print leftColumn.render().strip()
<div class="left-column">
<div class="box">Patriots (23) : Steelers (7)</div>
<div class="box">It is sunny today!</div>
@@ -200,7 +209,8 @@
So we get the weather box first and the sport box second:
- >>> print leftColumn().strip()
+ >>> leftColumn.update()
+ >>> print leftColumn.render().strip()
<div class="left-column">
<div class="box">It is sunny today!</div>
<div class="box">Patriots (23) : Steelers (7)</div>
@@ -212,7 +222,8 @@
and the order should switch as well:
- >>> print leftColumn().strip()
+ >>> leftColumn.update()
+ >>> print leftColumn.render().strip()
<div class="left-column">
<div class="box">Patriots (23) : Steelers (7)</div>
<div class="box">It is sunny today!</div>
@@ -221,7 +232,8 @@
Of course, we also can remove a shown viewlet:
>>> weather = shown.pop()
- >>> print leftColumn().strip()
+ >>> leftColumn.update()
+ >>> print leftColumn.render().strip()
<div class="left-column">
<div class="box">Patriots (23) : Steelers (7)</div>
</div>
@@ -242,17 +254,17 @@
'context'
>>> base.request
'request'
- >>> base.view
+ >>> base.__parent__
'view'
>>> base.manager
'manager'
-But a default `__call__()` method implementation is not provided:
+But a default ``render()`` method implementation is not provided:
- >>> base()
+ >>> base.render()
Traceback (most recent call last):
...
- NotImplementedError: `__call__` method must be implemented by subclass.
+ NotImplementedError: `render` method must be implemented by subclass.
If you have already an existing class that produces the HTML content in some
method, then the ``SimpleAttributeViewlet`` might be for you, since it can be
@@ -270,22 +282,22 @@
>>> foo = FooViewlet('context', 'request', 'view', 'manager')
>>> foo.foo()
'output'
- >>> foo()
+ >>> foo.render()
'output'
-If you specify `__call__` as the attribute an error is raised to prevent
+If you specify `render` as the attribute an error is raised to prevent
infinite recursion:
- >>> foo.__page_attribute__ = '__call__'
- >>> foo()
+ >>> foo.__page_attribute__ = 'render'
+ >>> foo.render()
Traceback (most recent call last):
...
- AttributeError: __call__
+ AttributeError: render
The same is true if the specified attribute does not exist:
>>> foo.__page_attribute__ = 'bar'
- >>> foo()
+ >>> foo.render()
Traceback (most recent call last):
...
AttributeError: 'FooViewlet' object has no attribute 'bar'
@@ -300,7 +312,7 @@
>>> open(template, 'w').write('''<div>contents</div>''')
>>> Demo = viewlet.SimpleViewletClass(template)
- >>> print Demo(content, request, view, manager)()
+ >>> print Demo(content, request, view, manager).render()
<div>contents</div>
Now let's additionally specify a class that can provide additional features:
@@ -353,7 +365,7 @@
>>> ztapi.browserResource('resource.js', JSResource)
>>> JSViewlet = viewlet.JavaScriptViewlet('resource.js')
- >>> print JSViewlet(content, request, view, manager)().strip()
+ >>> print JSViewlet(content, request, view, manager).render().strip()
<script type="text/javascript" src="/@@/resource.js">
</script>
@@ -369,14 +381,14 @@
>>> ztapi.browserResource('resource.css', CSSResource)
>>> CSSViewlet = viewlet.CSSViewlet('resource.css')
- >>> print CSSViewlet(content, request, view, manager)().strip()
+ >>> print CSSViewlet(content, request, view, manager).render().strip()
<link type="text/css" rel="stylesheet"
href="/@@/resource.css" media="all" />
You can also change the media type and the rel attribute:
>>> CSSViewlet = viewlet.CSSViewlet('resource.css', media='print', rel='css')
- >>> print CSSViewlet(content, request, view, manager)().strip()
+ >>> print CSSViewlet(content, request, view, manager).render().strip()
<link type="text/css" rel="css" href="/@@/resource.css"
media="print" />
@@ -470,19 +482,20 @@
... def __init__(self, context, request, view):
... self.context = context
... self.request = request
- ... self.view = view
+ ... self.__parent__ = view
...
- ... def rows(self):
+ ... def update(self):
... rows = []
... for name, value in self.context.items():
... rows.append(
... [zope.component.getMultiAdapter(
- ... (value, self.request, self.view, self),
+ ... (value, self.request, self.__parent__, self),
... interfaces.IViewlet, name=colname)
... for colname in shownColumns])
- ... return rows
+ ... [entry.update() for entry in rows[-1]]
+ ... self.rows = rows
...
- ... def __call__(self, *args, **kw):
+ ... def render(self, *args, **kw):
... return self.index(*args, **kw)
Now we need a template to produce the contents table:
@@ -492,7 +505,7 @@
... <table>
... <tr tal:repeat="row view/rows">
... <td tal:repeat="column row">
- ... <tal:block replace="structure column" />
+ ... <tal:block replace="structure column/render" />
... </td>
... </tr>
... </table>
@@ -541,9 +554,13 @@
>>> class NameViewlet(object):
...
... def __init__(self, context, request, view, manager):
+ ... self.__parent__ = view
... self.context = context
...
- ... def __call__(self):
+ ... def update(self):
+ ... pass
+ ...
+ ... def render(self):
... return self.context.__name__
and register it:
@@ -611,9 +628,13 @@
>>> class SizeViewlet(object):
...
... def __init__(self, context, request, view, manager):
+ ... self.__parent__ = view
... self.context = context
...
- ... def __call__(self):
+ ... def update(self):
+ ... pass
+ ...
+ ... def render(self):
... return size.interfaces.ISized(self.context).sizeForDisplay()
>>> zope.component.provideAdapter(
@@ -750,9 +771,9 @@
... def __init__(self, context, request, view):
... self.context = context
... self.request = request
- ... self.view = view
+ ... self.__parent__ = view
...
- ... def rows(self):
+ ... def update(self):
... values = self.context.values()
...
... if sortByColumn:
@@ -764,12 +785,13 @@
... for value in values:
... rows.append(
... [zope.component.getMultiAdapter(
- ... (value, self.request, self.view, self),
+ ... (value, self.request, self.__parent__, self),
... interfaces.IViewlet, name=colname)
... for colname in shownColumns])
- ... return rows
+ ... [entry.update() for entry in rows[-1]]
+ ... self.rows = rows
...
- ... def __call__(self, *args, **kw):
+ ... def render(self, *args, **kw):
... return self.index(*args, **kw)
As you can see, the concern of sorting is cleanly separated from generating
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/directives.txt 2005-10-25 04:01:56 UTC (rev 39607)
@@ -54,7 +54,8 @@
True
>>> manager.template is None
True
- >>> manager()
+ >>> manager.update()
+ >>> manager.render()
u''
However, this registration is not very useful, since we did specify a specific
@@ -88,7 +89,8 @@
True
>>> manager.template is None
True
- >>> manager()
+ >>> manager.update()
+ >>> manager.render()
u''
Next let's see what happens, if we specify a template for the viewlet manager:
@@ -125,7 +127,8 @@
True
>>> manager.template
<BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn> ...>>
- >>> print manager().strip()
+ >>> manager.update()
+ >>> print manager.render().strip()
<div class="column">
</div>
@@ -163,7 +166,8 @@
True
>>> manager.template
<BoundPageTemplateFile of ...<ViewletManager providing ILeftColumn> ...>>
- >>> print manager().strip()
+ >>> manager.update()
+ >>> print manager.render().strip()
<div class="column">
</div>
@@ -214,12 +218,13 @@
>>> viewlet = zope.component.getMultiAdapter(
... (content, request, view, manager), interfaces.IViewlet,
... name='weather')
- >>> viewlet().strip()
+ >>> viewlet.render().strip()
u'<div>sunny</div>'
The manager now also gives us the output of the one and only viewlet:
- >>> print manager().strip()
+ >>> manager.update()
+ >>> print manager.render().strip()
<div class="column">
<div class="entry">
<div>sunny</div>
@@ -299,7 +304,7 @@
>>> viewlet = zope.component.getMultiAdapter(
... (content, request, view, manager), interfaces.IViewlet,
... name='stock')
- >>> viewlet()
+ >>> viewlet.render()
u'SRC $5.19'
A final feature the ``viewlet`` directive supports is the additional
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/interfaces.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/interfaces.py 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/interfaces.py 2005-10-25 04:01:56 UTC (rev 39607)
@@ -19,7 +19,7 @@
import zope.interface
import zope.schema
-from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.i18n import ZopeMessageIDFactory as _
from zope.contentprovider.interfaces import IContentProvider
from zope.interface.common.mapping import IReadMapping
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/manager.py 2005-10-25 04:01:56 UTC (rev 39607)
@@ -33,17 +33,18 @@
zope.interface.implements(interfaces.IViewletManager)
def __init__(self, context, request, view):
+ self.__updated = False
+ self.__parent__ = view
self.context = context
self.request = request
- self.view = view
def __getitem__(self, name):
"""See zope.interface.common.mapping.IReadMapping"""
# Find the viewlet
viewlet = zope.component.queryMultiAdapter(
- (self.context, self.request, self.view, self), interfaces.IViewlet,
- name=name)
+ (self.context, self.request, self.__parent__, self),
+ interfaces.IViewlet, name=name)
# If the viewlet was not found, then raise a lookup error
if viewlet is None:
@@ -52,15 +53,14 @@
# If the viewlet cannot be accessed, then raise an
# unauthorized error
- if not zope.security.canAccess(viewlet, '__call__'):
+ if not zope.security.canAccess(viewlet, 'render'):
raise zope.security.interfaces.Unauthorized(
'You are not authorized to access the provider '
'called `%s`.' %name)
- # Return the rendered viewlet.
+ # Return the viewlet.
return viewlet
-
def get(self, name, default=None):
"""See zope.interface.common.mapping.IReadMapping"""
try:
@@ -76,7 +76,7 @@
"""
# Only return viewlets accessible to the principal
return [(name, viewlet) for name, viewlet in viewlets
- if zope.security.canAccess(viewlet, '__call__')]
+ if zope.security.canAccess(viewlet, 'render')]
def sort(self, viewlets):
"""Sort the viewlets.
@@ -86,24 +86,31 @@
# By default, use the standard Python way of doing sorting.
return sorted(viewlets, lambda x, y: cmp(x[1], y[1]))
- def __call__(self, *args, **kw):
+ def update(self):
"""See zope.contentprovider.interfaces.IContentProvider"""
+ self.__updated = True
# Find all content providers for the region
viewlets = zope.component.getAdapters(
- (self.context, self.request, self.view, self), interfaces.IViewlet)
+ (self.context, self.request, self.__parent__, self),
+ interfaces.IViewlet)
viewlets = self.filter(viewlets)
viewlets = self.sort(viewlets)
# Just use the viewlets from now on
- viewlets = [viewlet for name, viewlet in viewlets]
+ self.viewlets = [viewlet for name, viewlet in viewlets]
+ # Update all viewlets
+ [viewlet.update() for viewlet in self.viewlets]
+
+ def render(self):
+ """See zope.contentprovider.interfaces.IContentProvider"""
# Now render the view
if self.template:
- return self.template(viewlets=viewlets)
+ return self.template(viewlets=self.viewlets)
else:
- return u'\n'.join([viewlet() for viewlet in viewlets])
+ return u'\n'.join([viewlet.render() for viewlet in self.viewlets])
def ViewletManager(interface, template=None, bases=()):
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metaconfigure.py 2005-10-25 04:01:56 UTC (rev 39607)
@@ -64,7 +64,7 @@
new_class = manager.ViewletManager(provides, bases=(class_, ))
# Register some generic attributes with the security dictionary
- for attr_name in ('browserDefault', '__call__', 'publishTraverse'):
+ for attr_name in ('browserDefault', 'update', 'render', 'publishTraverse'):
required[attr_name] = permission
# Register the ``provides`` interface and register fields in the security
@@ -100,7 +100,7 @@
_context, name, permission,
for_=Interface, layer=IDefaultBrowserLayer, view=IBrowserView,
manager=interfaces.IViewletManager, class_=None, template=None,
- attribute='__call__', allowed_interface=None, allowed_attributes=None,
+ attribute='render', allowed_interface=None, allowed_attributes=None,
**kwargs):
# Security map dictionary
@@ -114,7 +114,7 @@
raise ConfigurationError("Must specify a class or template")
# Make sure that all the non-default attribute specifications are correct.
- if attribute != '__call__':
+ if attribute != 'render':
if template:
raise ConfigurationError(
"Attribute and template cannot be used together.")
@@ -134,7 +134,7 @@
# Make sure the has the right form, if specified.
if class_:
- if attribute != '__call__':
+ if attribute != 'render':
if not hasattr(class_, attribute):
raise ConfigurationError(
"The provided class doesn't have the specified attribute "
@@ -172,7 +172,7 @@
_context, kwargs.keys(), permission, required)
viewmeta._handle_allowed_attributes(
_context,
- (attribute, 'browserDefault', '__call__', 'publishTraverse'),
+ (attribute, 'browserDefault', 'update', 'render', 'publishTraverse'),
permission, required)
# Register the interfaces.
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/metadirectives.py 2005-10-25 04:01:56 UTC (rev 39607)
@@ -20,7 +20,7 @@
import zope.configuration.fields
import zope.schema
-from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.i18n import ZopeMessageIDFactory as _
from zope.app.publisher.browser import metadirectives
from zope.app.publisher.interfaces import browser
Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py 2005-10-25 02:59:26 UTC (rev 39606)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py 2005-10-25 04:01:56 UTC (rev 39607)
@@ -21,7 +21,7 @@
import sys
import zope.interface
-from zope.app.pagetemplate.simpleviewclass import simple
+from zope.app.pagetemplate import simpleviewclass
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from zope.app.publisher.browser import BrowserView
from zope.app.traversing import api
@@ -36,34 +36,40 @@
def __init__(self, context, request, view, manager):
super(ViewletBase, self).__init__(context, request)
- # TODO: We need to evaluate whether we really want to expose all those
- # objects in the object. Theoretically, we only want `request` and
- # `manager`.
+ self.__parent__ = view
self.context = context
self.request = request
- self.view = view
self.manager = manager
- def __call__(self):
+ def update(self):
+ pass
+
+ def render(self):
raise NotImplementedError(
- '`__call__` method must be implemented by subclass.')
+ '`render` method must be implemented by subclass.')
class SimpleAttributeViewlet(ViewletBase):
"""A viewlet that uses a specified method to produce its content."""
- def __call__(self, *args, **kw):
+ def render(self, *args, **kw):
# If a class doesn't provide it's own call, then get the attribute
# given by the browser default.
attr = self.__page_attribute__
- if attr == '__call__':
- raise AttributeError("__call__")
+ if attr == 'render':
+ raise AttributeError("render")
meth = getattr(self, attr)
return meth(*args, **kw)
+class simple(simpleviewclass.simple):
+ """Simple viewlet class supporting the ``render()`` method."""
+
+ render = simpleviewclass.simple.__call__
+
+
def SimpleViewletClass(template, offering=None, bases=(), attributes=None,
name=u''):
"""A function that can be used to generate a viewlet from a set of
@@ -99,7 +105,7 @@
request=self.request)
return resource()
- def __call__(self, *args, **kw):
+ def render(self, *args, **kw):
return self.index(*args, **kw)
More information about the Zope3-Checkins
mailing list