[Zope3-dev] ZCML alternative
Shane Hathaway
shane@zope.com
Sun, 02 Jun 2002 02:06:54 -0400
This is a multi-part message in MIME format.
--------------050302030707050301020605
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
I'm proposing a ZCML alternative that I'd like feedback on before
wikifying. But first some background.
I've been thinking about the difficulty Itamar had recently. Several
months ago, he integrated Twisted (an Internet protocol framework) into
Zope 3 with very little effort. It was an exciting thing to be able to
do. But after we converted the startup procedure to use ZCML, Itamar
tried again to integrate Twisted and failed, because the startup
mechanisms were too hard to decipher. I tried to help, but I couldn't
wrap my head around what it would take to add a different kind of server
either. Itamar blamed ZCML, and I tried to convince him otherwise, but
now I'm having second thoughts.
ZCML fulfills an important role. As the Wiki says, it separates
component configuration from component software. That's a very
powerful, important concept. But maybe the separation shouldn't be
enforced by having a separate language.
Some specific problems with ZCML:
- It's hard to find the code that processes a given directive.
- It's hard to tell what a ZCML file depends on without reading every line.
- You can't easily use debugging tools, like "print" statements, pdb, or
the interactive interpreter. We have tracebacks, but they aren't very
clear.
- We've had to invent syntax to cover things that XML does not address,
like Python imports and lists.
I'd like to suggest that configurations are objects. Object
encapsulation gives you separation. Besides being objects,
configurations are also components, so they implement an interface.
With that in mind, I took the JobBoard ZCML file and rewrote it as a
Python class. I liked the results. I'm sure there are typos.
The approach provided easy answers to a lot of the ZCML syntax issues.
register() is the one method required. Namespaces weren't really
needed, so I didn't use them. There are groupings in the ZCML using
comments, so I turned those into separate methods. I used Python
imports instead of ".xxx.". I used lists. I avoided using Python
keywords as argument names through the use of positional arguments.
Comments?
Shane
--------------050302030707050301020605
Content-Type: text/plain;
name="JobBoardConfiguration.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="JobBoardConfiguration.py"
from Zope.Configuration import Configuration
import IJob
import Job
import JobView
import JobList
import JobListView
import ApproveJobs
class JobBoardConfiguration (Configuration):
def register(self):
self.zmi = self.getConfigurator('zmi')
self.security = self.getConfigurator('security')
self.browser = self.getConfigurator('browser')
self.registerContentClasses()
self.registerViews()
self.registerActions()
def registerContentClasses(self):
# JobList
self.zmi.factoryFromClass(
JobList.JobList, name='JobList', permission='Zope.ManageContent',
title='JobList', description='A Job Board')
self.security.protectClass(JobList.JobList, permission='Zope.Public')
# Job
self.zmi.factoryFromClass(
Job.Job, name='Job', permission='Zope.Public',
title="A job to be placed within a JobList",
creation_markers=IJob.IJobCreator)
self.security.protectClass(Job.Job, permission='Zope.Public')
def registerViews(self):
# pre-creation job view
self.browser.defaultView(IJob.IJobCreator, name='create',
factory=JobView.CreateJobView)
self.security.protectClass(
JobView.CreateJobView, permission='Zope.Public',
names=['index', 'action', 'preview', 'cancel'])
# JobListTraverser
self.browser.view(IJobList.IJobList, name='_traverse',
factory=JobListView.JobListTraverser)
# JobListSummaryView
self.browser.defaultView(IJobList.IJobList, name="summary",
factory=JobListView.JobListSummaryView)
self.security.protectClass(JobListView.JobListSummaryView,
permission="Zope.Public",
names=['index', 'getApprovedJobs'])
# JobView
self.browser.view(IJob.IJob, name="JobView",
factory=JobView.JobView)
self.browser.defaultView(IJob.IJob, name="display",
factory=JobView.JobView)
self.security.protectClass(
JobView.JobView, permission="Zope.View",
names=["index", "getSubmitter", "getSummary", "getDescription",
"getContact"])
# JobEditView
self.browser.view(IJob.IJob, name="edit", factory=JobView.JobEditView)
self.security.protectClass(
JobView.JobEditView, permission="Zope.Public",
names=["index", "preview", "cancel", "edit", "submit"])
def registerActions(self):
# ApproveJobs
self.browser.view(IJobList.IJobList, name="ApproveJobs",
factory=ApproveJobs.ApproveJobs)
self.security.protectClass(
ApproveJobs.ApproveJobs, permission="Zope.View",
names=["index", "back", "submit", "getPendingJobs"])
--------------050302030707050301020605--