During the sprint I was (at least a little) working with Tom Lazar on the markup stuff.
The idea here was to enable more markups via PortalTransforms and then make the default
configurable. This means that for any TextField not defining allowable_types
a user defined
default markup should be used. For this part Martin Aspeli proposed to use utilities as sort of
a registry. So I tried to implement it the following way:
- create a base interface
IMarkup
for all the markups - create derived interfaces for each implemented markup like Markdown, Structured Text, etc.
- create a utility for each markup just returning name and mimetype(s)
- register these utilities
- for getting the list of all possible markups retrieve the list of utilities
implementingIMarkup
.
Now the problem was how to actually write the utilities as I somehow couldn’t find it
in the Zope3 book (just local ones but maybe I just missed the global ones. The reason
for using global ones was that the local ones will be overhauled soon and thus it would
be easier for now to use global ones which do the job, too, for now).
So I asked around and discussed it with Martijn Pieters a bit. He proposed at first
glance not to use derived interfaces but simply named utilities so all these would
just implement IMarkup
. So I wonder now when it’s best to use named ones and
when derived ones (probably if the derived ones add more functionality).
So I went with named utilities and Martijn also showed me how to use them. So here
is how I did it for all those who are interested. But first maybe let me tell what
utilities are all about.
Utilities
Actually utilities are very simple components. They are basically a class implementing
a more or less simple functionality like e.g. returning a list of items or computing
things. So for our markup application a utility for doing the markup could look as follows:
class MarkdownSupport: implements(IMarkup) def getName(self): """return our name""" return "Markdown" def mimetype(self): """return our mimetype""" return "text/x-web-markdown" So it needs to define an interface which does look like this:: class IMarkup(Interface): """markup utility""" def getName(): """return name""" def mimetype(): """return mimetype""" In my implementation I actually went a different way in using the transforms of PortalTransforms for the utilities as these exist already and know about their mimetypes and name. The only reason not to do so is maybe a conceptual one as the PortalTransforms module should actually hide them and the transforms are never called directly. So it seems a bit strange to use them now.
Registering utilities
While the implementation was quite clear to me registering them with ZCML wasn’t. So here’s how it’s being
done (but using the transforms now):
<utility name="Restructured Text" provides="Products.markup.interface.IMarkup" factory="Products.PortalTransforms.transforms.rest.rest" /> <utility name="Structured Text" provides="Products.markup.interface.IMarkup" factory="Products.PortalTransforms.transforms.st.st" />
As you see, I defined them here for the actual transforms. I also used named utilities and only
one common interface. Because I used the transforms directly I need to make them implement ‚IMarkup‘:
<five:implements class="Products.PortalTransforms.transforms.rest.rest" interface="Products.markup.interface.IMarkup" /> <five:implements class="Products.PortalTransforms.transforms.st.st" interface="Products.markup.interface.IMarkup" />
I actually used a different IMarkup
interface here to fit the methods of the transforms. Basically
it should be the same as itransforms
already used inside PortalTransforms.
Retrieving Utilities
So the last part is actually to get the utilites back. If we need a list of all markups registered the
above way we need to ask the utility machinery for that interface:
from zope.app.zapi import getUtilitiesFor us = getUtilitiesFor(IMarkup)
This will return a generator we can iterate over. Each iteration gives a tuple with the name used
in configure.zcml
and the utility instance.
Discussion
I actually wasn’t that fond of using utilities as we might not really need the actual utilities (we
already have the name) but „misuse“ them for a repository purpose. I’d rather have some other configuration
possibility in Zope3 (I also vaguely remember that I’ve seen other such reuses when visiting Philipp but
I am not too sure anymore what it was ;-).
My other worry was that for each list generation we would need to create all the utility instances
although we only might want the name or at least the name. But checking things a bit revealed that
utilities are actually only instanciated once. So no too big overhead. Another good thing would probably
to have a call like getUtilityNamesFor()
which just returns the names. Might be handy..