[Zope3-dev] Re: ZCML
Shane Hathaway
shane at zope.com
Tue Aug 19 12:13:48 EDT 2003
Philipp von Weitershausen wrote:
> Shane Hathaway wrote:
>> In the Ape project, I found a way to achieve minimal overrides that
>> wasn't too burdensome. I'd like to discuss how we can improve upon
>> the approach Ape takes, without losing the schema-based configuration
>> we have now. I have some ideas.
>
> Let's bring it on!
Ok, thanks for listening. I'll try to keep this short. You might want
to refer to apeconf.py. By no means is it perfect, but it demonstrates
a lot of what I'm saying:
http://cvs.zope.org/Products/Ape/lib/apelib/config/apeconf.py?rev=1.4&content-type=text/vnd.viewcvs-markup
I love the idea you guys came up with: parsing XML using Zope schemas is
excellent and useful well beyond configuration files. I didn't do that
and it would enhance Ape. Instead of one schema per directive, though,
I think the parsing should be oriented toward one schema per *element*.
It would make a tighter fit with Python in general. An implementation
suggestion: the parser should maintain a registry of element name to
(schema, handler), where the parser converts the tag to an object that
fits the required schema, then passes that object to the handler.
Next, ZCML tries to allow developers to override configurations in
chunks, but the chunks aren't as small as they could be. To reduce the
size of the chunks in Ape, I borrowed some ideas from relational theory.
By representing the configuration using relational tables, I realized
that the problem of combining configurations from independent sources
would become manageable. If each row represents a minimal configuration
directive, and the primary keys along with the table names form the
discriminators, all I have to do is try to insert directives into the
tables. If two directives have the same primary key but the other
fields have different values, the configuration conflicts somehow (and
the insert will fail). Two directives with different primary keys can
live in harmony.
The process of storing the directives in relational tables also made it
simpler to think about how to divide large directives into smaller
directives. Each row in a table should specify only one property. If
you need two properties, store them in two rows or two tables.
I realized that I was approaching the general idea of triples and RDF
principles, but those notions are still a little too abstract for me to
grasp intuitively. Relational tables were a more practical approach.
To represent multiple directives in a single tag, I created a layer that
translates XML into directives. The tag handlers receive information
about a tag from the XML processor and add corresponding directives to a
list containing all directives. (This layer would be simpler and report
better errors if it used Zope schemas.)
After reading the entire configuration, the configuration machinery
searches for conflicting directives and reports errors. If it finds no
conflicts, it places all of the directives in relational tables.
(In the process, I wrote a little relational table class that might be
useful elsewhere. It's in minitables.py. Interestingly, I didn't need
static types. I also didn't need a "delete" method, so I didn't write
one. It was a YAGNI. ;-) )
Note that the configuration is purely declarative. The order of the
directives does not matter. I think that is an important feature.
With all of the directives in relational tables, now comes the part I'm
not quite happy with yet. You need to assemble objects based on the
tabulated directives. You can't just iterate over all of the tables and
apply each directive in sequence, since some directives require other
directives to be applied first. There might even be circular dependencies.
In Ape, I decided to create "assembler" objects. They are responsible
for reading the tables and applying the configuration. The assemblers
don't have any trouble with dependencies, but the MapperAssembler class
turned out more complex than I hoped. It also had to do a lot of querying.
So, if you're still following along ;-), that's where I'm stuck. Ape's
assemblers work, but they should be simpler. In fact, it's tempting to
say that the directive objects should also be assemblers, but that
didn't work out in Ape. Maybe that strategy would work in Zope 3.
> I totally agree. I recently refactored the browser:*form and *wizard
> stuff, so I would be willing to take this at least one step further to::
>
> <browser:editform
> schema="zope.app.content.image.IImage"
> name="upload.html"
> label="Upload an image"
> permission="zope.ManageContent"
> class=".image.ImageUpload."
> template="imageedit.pt"
> />
> <browser:menu
> id="zmi_views"
> title="Upload"
> />
> </browser:editform>
>
> That the 'title' property was actually for the menu and not for the form
> had always bothered me... Any further suggestions before I get started
> with this?
I'm guessing that the discriminator for this directive includes the
"name" and "class" attributes, but not the other attributes. To avoid
conflicts, configuration authors need to be aware of exactly which
attributes are included in the discriminator. I wonder if it would make
sense to move all attributes that are not part of the discriminator into
child elements. That could be too heavy, though. Alternatively, we
could just say that as a matter of style, discriminator attributes
should come first.
Shane
More information about the Zope3-dev
mailing list