[Grok-dev] grokcore.resource

Jan-Jaap Driessen jdriessen at minddistrict.com
Tue May 1 08:42:56 UTC 2012


In the train back home from the grok sprint yesterday I took some
parts of megrok.resource [1] to combine with fanstatic [2] into
grokcore.resource [3].

With grokcore.resource you can make resources part of your View classes like so:

"""
class MyView(grok.View):
    grokcore.resource.include(css_a, css_b)
"""

This is a handsome replacement of the "old" way of declaring resource,
often in the update method of the view:

"""
    def update(self):
        css_a.need()
        css_b.need()
"""

(This "old" way is still supported.)

The "include" directive code looks like this:

"""
class include(martian.Directive):
    scope = martian.CLASS
    store = martian.MULTIPLE
    validate = validateInclusion

    def factory(self, *resources):
        zope.interface.declarations.addClassAdvisor(
            _resources_advice, depth=3)
        return resources

def _resources_advice(cls):
    if include.bind().get(cls):
        if not grokcore.resource.interfaces.IResourcesIncluder.implementedBy(
                cls):
            zope.interface.classImplements(
                cls,
                grokcore.resource.interfaces.IResourcesIncluder)
    return cls
"""

The result of using the "include" directive is that the class which it
is used on will be marked with a marker interface
"IResourcesIncluder".
We add an event listener for IBeforeTraverseEvent that will listen to
this interface and will need the resources at that time::

"""
@grokcore.component.subscribe(
    grokcore.resource.interfaces.IResourcesIncluder,
    zope.app.publication.interfaces.IBeforeTraverseEvent)
def handle_inclusion(includer, event):
    includer = zope.security.proxy.removeSecurityProxy(includer)
    needs = set()
    # XXX Need to fix this?
    for class_ in includer.__class__.__mro__:
        if grokcore.resource.interfaces.IResourcesIncluder.implementedBy(class_):
            father = zope.security.proxy.removeSecurityProxy(class_)
            for resources in \
                grokcore.resource.directives.include.bind().get(father):
                needs.update(resources)
    for resource in needs:
        resource.need()
"""

I am not sure whether there is a smarter way of finding all resources
needed by a View if this View subclasses from another View. Maybe it
is just my limited understanding of how martian works. I need a second
pair of eyes here.

At this point, we can use grokcore.resource for grok.View classes, but
not yet for viewlets and on layouts (grokcore.layout).

In the zope.viewlet code, a BeforeUpdateEvent is sent, which we
subscribe to in grokcore.resource and need the resources for viewlets:

"""
@grokcore.component.subscribe(
    grokcore.resource.interfaces.IResourcesIncluder,
    zope.contentprovider.interfaces.IBeforeUpdateEvent)
def handle_inclusion(includer, event):
    # Same inclusion code as before.
"""

In order to make this work for Layouts I don't see another way than
emitting an event right before the layout is rendered in
grokcore.layout:

"""
def __call__(self, view):
    self.view = view
    zope.event.notify(BeforeUpdateEvent(self, self.request))
    self.update()
    return self.render()
"""

Is this OK? We could make the event grokcore.layout specific of course.

I would appreciate your feedback on the questions above. Afterwards, I
can finish grokcore.resource (readme, tests for viewlet/layout) and
would like to integrate the directive in grok (the package) under the
name 'resource', so you can write:

"""
class MyView(grok.View):
    grok.resource(css_a, css_b)
"""

Thanks for reading this far,

JJ

1) http://pypi.python.org/pypi/megrok.resource/0.5
2) http://fanstatic.org
3) http://svn.zope.org/grokcore.resource/trunk/


More information about the Grok-dev mailing list