[PEAK] A simple schema language
Phillip J. Eby
pje at telecommunity.com
Fri Oct 8 19:41:55 EDT 2004
As I was trying to flesh out the implementation plan today for peak.web's
configuration files, I considered at one point using peak.model to define
the schema for processing the various <location>/<container>/<content>
elements, because it fits so well with peak.model's structure.
However, within a few minutes I became frustrated with trying to use
peak.model as a schema *design* tool. It's much more useful as an
implementation tool, which probably shouldn't be too surprising since it
was originally intended to use models generated from UML rather than
written by hand. So I began fooling around with trying to develop a
simpler way of specifying a schema. Here's about 60+ lines of the
"bulletins" example schema, condensed to about 20 lines here:
User = schema.Element(loginID=str, fullName=str)
Bulletin = schema.Element(
id = int,
category = 'Category.bulletins',
fullText = str,
postedBy = User,
postedOn = datetime,
editedBy = User,
editedOn = datetime,
hidden = (model.Boolean, False),
)
Category = schema.Element(
pathName = PathPart,
title = str,
sortPosn = schema.Attr(int, default=0)
sortBulletinsBy = schema.Attr(SortBy,
default=SortBy.MOST_RECENTLY_EDITED),
postingTemplate = schema.Attr(str, default=''),
editingTemplate = schema.Attr(str, default=''),
bulletins = [Bulletin.category],
)
The idea being shown here is that a function call with keyword arguments
defines the features of a schema. If the argument is a type or the name of
a type, then the feature is of that type. If it's a feature of a type, or
the name of a feature, then it's a bidirectional link. If it's a list
containing one of the above, then it's a collection of items. If it's an
explicit invocation of 'Attr', you can define other properties like default
value, etc.
An approach like this offers some additional, interesting possibilities,
like adding constraint expressions for validation. The above could be
considered an "abstract schema", which in itself is purely
informational. For example, the idea of 'datetime' or 'str' as a type need
not be implemented by those exact types. Instead, a concrete schema could
declare that 'datetime' is actually implemented by a database timestamp
type, for example, and also declare a custom parsing/formatting
syntax. Also, a concrete schema could be used to generate today's
'peak.model' classes and objects, to provide an implementation, perhaps
automatically mixing in domain methods defined in a separate module. Or
perhaps you'll do something like this:
class Category(model.Element):
schema.Features(
pathName = PathPart,
title = str,
sortPosn = schema.Attr(int, default=0),
sortBulletinsBy = schema.Attr(SortBy,
default=SortBy.MOST_RECENTLY_EDITED),
postingTemplate = schema.Attr(str, default=''),
editingTemplate = schema.Attr(str, default=''),
bulletins = [Bulletin.category],
)
# methods go here...
def post(self, user, text, timestamp=None):
# ...
in order to define the domain methods. Actually, we could probably do a
bit better, using a slight modification to today's model.Attribute:
f = model.Attribute
class Category(model.Element):
pathName = f(PathPart)
title = f(str)
sortPosn = f(int, defaultValue=0)
sortBulletinsBy = f(SortBy, defaultValue=SortBy.MOST_RECENTLY_EDITED)
postingTemplate = f(str, defaultValue='')
editingTemplate = f(str, defaultValue='')
bulletins = f([Bulletin.category])
# methods go here...
def post(self, user, text, timestamp=None):
# ...
Or substitute whatever you like for 'f'. It's not quite as clear, and is
more verbose, but it could probably actually be implemented relatively quickly.
Anyway, I haven't yet decided whether I'll implement it, even in this
simpler "shorthand" sense, but the big picture is interesting to keep in
mind for when I come back around to working on peak.storage and peak.query.
More information about the PEAK
mailing list