[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