[Zope3-checkins] SVN: Zope3/branches/roger-contentprovider/src/zope/viewlet/ Finally finished testing the viewlet package.

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Oct 15 06:45:14 EDT 2005


Log message for revision 39461:
  Finally finished testing the viewlet package.
  

Changed:
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/css_viewlet.pt
  U   Zope3/branches/roger-contentprovider/src/zope/viewlet/javascript_viewlet.pt
  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-15 05:03:48 UTC (rev 39460)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/README.txt	2005-10-15 10:45:13 UTC (rev 39461)
@@ -209,8 +209,157 @@
 Viewlet Base Classes
 --------------------
 
+To make the creation of viewlets simpler, a set of useful base classes and
+helper functions are provided:
 
+  >>> from zope.viewlet import viewlet
 
+The first class is a base class that simply defines the constructor:
+
+  >>> base = viewlet.ViewletBase('context', 'request', 'view', 'manager')
+  >>> base.context
+  'context'
+  >>> base.request
+  'request'
+  >>> base.view
+  'view'
+  >>> base.manager
+  'manager'
+
+But a default `__call__()` method implementation is not provided:
+
+  >>> base()
+  Traceback (most recent call last):
+  ...
+  NotImplementedError: `__call__` 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
+used to convert any class quickly into a viewlet:
+
+  >>> class FooViewlet(viewlet.SimpleAttributeViewlet):
+  ...     __page_attribute__ = 'foo'
+  ...
+  ...     def foo(self):
+  ...         return 'output'
+
+The `__page_attribute__` attribute provides the name of the function to call for
+rendering.
+
+  >>> foo = FooViewlet('context', 'request', 'view', 'manager')
+  >>> foo.foo()
+  'output'
+  >>> foo()
+  'output'
+
+If you specify `__call__` as the attribute an error is raised to prevent
+infinite recursion:
+
+  >>> foo.__page_attribute__ = '__call__'
+  >>> foo()
+  Traceback (most recent call last):
+  ...
+  AttributeError: __call__
+
+The same is true if the specified attribute does not exist:
+
+  >>> foo.__page_attribute__ = 'bar'
+  >>> foo()
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'FooViewlet' object has no attribute 'bar'
+
+To create simple template-based viewlets you can use the
+``SimpleViewletClass()`` function. This function is very similar to its view
+equivalent and is used by the ZCML directives to create viewlets. The result
+of this function call will be a fully functional viewlet class. Let's start by
+simply specifying a template only:
+
+  >>> template = os.path.join(temp_dir, 'demoTemplate.pt')
+  >>> open(template, 'w').write('''<div>contents</div>''')
+
+  >>> Demo = viewlet.SimpleViewletClass(template)
+  >>> print Demo(content, request, view, manager)()
+  <div>contents</div>
+
+Now let's additionally specify a class that can provide additional features:
+
+  >>> class MyViewlet(object):
+  ...     myAttribute = 8
+
+  >>> Demo = viewlet.SimpleViewletClass(template, bases=(MyViewlet,))
+  >>> MyViewlet in Demo.__bases__
+  True
+  >>> Demo(content, request, view, manager).myAttribute
+  8
+
+The final important feature is the ability to pass in further attributes to
+the class:
+
+  >>> Demo = viewlet.SimpleViewletClass(
+  ...     template, attributes={'here': 'now', 'lucky': 3})
+  >>> demo = Demo(content, request, view, manager)
+  >>> demo.here
+  'now'
+  >>> demo.lucky
+  3
+
+As for all views, they must provide a name that can also be passed to the
+function:
+
+  >>> Demo = viewlet.SimpleViewletClass(template, name='demoViewlet')
+  >>> demo = Demo(content, request, view, manager)
+  >>> demo.__name__
+  'demoViewlet'
+
+In addition to the the generic viewlet code above, the package comes with two
+viewlet base classes and helper functions for inserting CSS and Javascript
+links into HTML headers, since those two are so very common. I am only going
+to demonstrate the helper functions here, since those demonstrations will
+fully demonstrate the functionality of the base classes as well.
+
+The viewlet will look up the resource it was given and tries to produce the
+absolute URL for it:
+
+  >>> class JSResource(object):
+  ...     def __init__(self, request):
+  ...         self.request = request
+  ...
+  ...     def __call__(self):
+  ...         return '/@@/resource.js'
+
+  >>> from zope.app.testing import ztapi
+  >>> ztapi.browserResource('resource.js', JSResource)
+
+  >>> JSViewlet = viewlet.JavaScriptViewlet('resource.js')
+  >>> print JSViewlet(content, request, view, manager)().strip()
+  <script type="text/javascript" src="/@@/resource.js">
+  </script>
+
+The same works for the CSS resource viewlet:
+
+  >>> class CSSResource(object):
+  ...     def __init__(self, request):
+  ...         self.request = request
+  ...
+  ...     def __call__(self):
+  ...         return '/@@/resource.css'
+
+  >>> ztapi.browserResource('resource.css', CSSResource)
+
+  >>> CSSViewlet = viewlet.CSSViewlet('resource.css')
+  >>> print CSSViewlet(content, request, view, manager)().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()
+  <link type="text/css" rel="css" href="/@@/resource.css"
+        media="print" />
+
+
 A Complex Example
 -----------------
 

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/css_viewlet.pt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/css_viewlet.pt	2005-10-15 05:03:48 UTC (rev 39460)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/css_viewlet.pt	2005-10-15 10:45:13 UTC (rev 39461)
@@ -1,4 +1,4 @@
 <link type="text/css" rel="stylesheet" href="somestyle.css" media="all"
-      tal:attributes="rel viewlet/getRel;
-                      href viewlet/getURL;
-                      media viewlet/getMedia" />
+      tal:attributes="rel view/getRel;
+                      href view/getURL;
+                      media view/getMedia" />

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/javascript_viewlet.pt
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/javascript_viewlet.pt	2005-10-15 05:03:48 UTC (rev 39460)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/javascript_viewlet.pt	2005-10-15 10:45:13 UTC (rev 39461)
@@ -1,3 +1,3 @@
 <script type="text/javascript" src="some-library.js"
-        tal:attributes="src viewlet/getURL">
+        tal:attributes="src view/getURL">
 </script>

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py	2005-10-15 05:03:48 UTC (rev 39460)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/tests.py	2005-10-15 10:45:13 UTC (rev 39461)
@@ -22,7 +22,7 @@
 import zope.security
 from zope.testing import doctest
 from zope.testing.doctestunit import DocTestSuite, DocFileSuite
-from zope.app.testing import setup
+from zope.app.testing import setup, ztapi
 
 class TestParticipation(object):
     principal = 'foobar'
@@ -31,6 +31,12 @@
 def setUp(test):
     setup.placefulSetUp()
 
+    # resource namespace setup
+    from zope.app.traversing.interfaces import ITraversable
+    from zope.app.traversing.namespace import resource
+    ztapi.provideAdapter(None, ITraversable, resource, name="resource")
+    ztapi.provideView(None, None, ITraversable, "resource", resource)
+
     from zope.app.pagetemplate import metaconfigure
     from zope.contentprovider import tales
     metaconfigure.registerType('provider', tales.TALESProviderExpression)

Modified: Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py
===================================================================
--- Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py	2005-10-15 05:03:48 UTC (rev 39460)
+++ Zope3/branches/roger-contentprovider/src/zope/viewlet/viewlet.py	2005-10-15 10:45:13 UTC (rev 39461)
@@ -29,30 +29,28 @@
 from zope.viewlet import interfaces
 
 
-class ViewletPageTemplateFile(ViewPageTemplateFile):
-
-    def pt_getContext(self, instance, request, **_kw):
-        namespace = super(ViewletPageTemplateFile, self).pt_getContext(
-            instance, request, **_kw)
-        namespace['view'] = instance.view
-        namespace['viewlet'] = instance
-        return namespace
-
-
-class SimpleViewlet(BrowserView):
+class ViewletBase(BrowserView):
     """Viewlet adapter class used in meta directive as a mixin class."""
 
     zope.interface.implements(interfaces.IViewlet)
 
-    def __init__(self, context, request, view, providerType):
-        super(SimpleViewlet, self).__init__(context, request)
+    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.context = context
+        self.request = request
         self.view = view
+        self.manager = manager
 
+    def __call__(self):
+        raise NotImplementedError(
+            '`__call__` method must be implemented by subclass.')
 
-class SimpleAttributeViewlet(SimpleViewlet):
 
-    def publishTraverse(self, request, name):
-        raise NotFound(self, name, request)
+class SimpleAttributeViewlet(ViewletBase):
+    """A viewlet that uses a specified method to produce its content."""
 
     def __call__(self, *args, **kw):
         # If a class doesn't provide it's own call, then get the attribute
@@ -68,14 +66,17 @@
 
 def SimpleViewletClass(template, offering=None, bases=(), attributes=None,
                        name=u''):
+    """A function that can be used to generate a viewlet from a set of
+    information.
+    """
     # Get the current frame
     if offering is None:
         offering = sys._getframe(1).f_globals
 
     # Create the base class hierarchy
-    bases += (SimpleViewlet, simple)
+    bases += (simple, ViewletBase)
 
-    attrs = {'index' : ViewletPageTemplateFile(template, offering),
+    attrs = {'index' : ViewPageTemplateFile(template, offering),
              '__name__' : name}
     if attributes:
         attrs.update(attributes)
@@ -87,7 +88,10 @@
 
 
 class ResourceViewletBase(object):
+    """A simple viewlet for inserting references to resources.
 
+    This is an abstract class that is expected to be used as a base only.
+    """
     _path = None
 
     def getURL(self):
@@ -104,8 +108,8 @@
     src = os.path.join(os.path.dirname(__file__), 'javascript_viewlet.pt')
 
     klass = type('JavaScriptViewlet',
-                 (ResourceViewletBase, SimpleViewlet),
-                  {'index': ViewletPageTemplateFile(src),
+                 (ResourceViewletBase, ViewletBase),
+                  {'index': ViewPageTemplateFile(src),
                    '_path': path})
 
     return klass
@@ -128,8 +132,8 @@
     src = os.path.join(os.path.dirname(__file__), 'css_viewlet.pt')
 
     klass = type('CSSViewlet',
-                 (CSSResourceViewletBase, SimpleViewlet),
-                  {'index': ViewletPageTemplateFile(src),
+                 (CSSResourceViewletBase, ViewletBase),
+                  {'index': ViewPageTemplateFile(src),
                    '_path': path,
                    '_media':media,
                    '_rel':rel})



More information about the Zope3-Checkins mailing list