[Zope3-dev] [DRAFT] Uniform resource identifiers (cpsskins)
Jean-Marc Orliaguet
jmo at ita.chalmers.se
Mon Mar 27 17:17:43 EST 2006
Hi!
I've began formalizing some ideas about how to identify application
resources:
http://svn.z3lab.org/trac/z3lab/file/cpsskins/branches/jmo-perspectives/io/README.txt
I post to zope3-dev too in case someone has some ideas about it. A lot
of the points described are pertinent to zope3.
The background is the following:
Being able to identify application resources is important. First we want
to identify what *type* of resource we have (a portlet, a widget, a
style, ..) then we want to identify the resource itself in the context
of the application, for instance if a portlet named '12345' is a unique
instance of an object, a slot named '12345' will refer to a collection
of objects, not to an individual instance.
So to start with the resource's own identifier:
The object's id in a container is not necessarily the id that is
interesting (for instance if an item gets moved from a folder to another
folder there can be id conflicts even though being located in a folder
is nothing special for a resource, it might be an accidental thing).
In cpsskins the resource's id is the name of the resource in the
*context of a relation*. Hence resources are IRelatable:
unicode(IRelatable(resource))
returns the resource's name in a relation. It is the only identifier
that is used throughout the application to refer to the resource, ie. to
register the resource, to customize it, to set it in relation with
another resource, etc.
Note that in the case of containment there are implicit container <->
parent container relations, so that item ids are also defined in the
context of a relation it is just that these ids are only unique inside a
same container.
Then we have the resource's type(s). Zope3 uses dotted names (e.g.
widget.textinput) as well as interfaces (e.g.
zope.form.widgets.interfaces.ITextInput) to identify object types. Both
approaches provide a way of having unique identifiers mostly by creating
namespaces.
The issue with dotted names is that there are no explicit rules for
creating the names between the dots. Enforcing a policy can be difficult
especially across different applications. The categorizations may change
and renaming resources is not trivial. However dotted names are easy to
transport (they are URL-friendly, they can be embedded in any export
format XML, json, ...).
The issue with interfaces used as identifiers is that packages are often
reoganized and the resources often change name. The coupling is very
tight between the application's package organization on the filesystem
and the way the data is described. This can be seen already now in ZCML:
whenever packages are moved, a lot of search and replace must be done on
existing configuration files. Using interface names (with the entire
package page) in an XML export does not guarantee that the data will be
usable in upcoming versions of the same application. We are not even
talking about trans-typing, but just about tracking resource types that
change names. The positive aspect about interfaces is that it is
possible to query the type of an object using
queryType(object, InterfaceType)
to get the type of the object.
The solution sketched here tries to combine the advantage of dotted
names with the flexibility of interfaces.
First we do a categorization of resources according to three different
types. In cpsskins we have:
IElementType, IResourceType, IContentType
IElementType is a first-level categorization, IResourceType a
second-level categorization and IContentType is a third level (it is
zope.app.content.interfaces.IContentType). There could be more levels,
but this is enough for now.
Typically portlets are part of a 'canvas' so their element type is
'canvas', their resource type is 'portlet' and their content type is for
instance 'cpsskins.actions', so the URI of a portlet would be:
'canvas-portlet-cpsskins.actions-12345'
(if '12345' is the identifier of the portlet instance)
which we can obtain directly with:
IIdentifiable(resource).getURI()
Let us say we have a 'widget', -- in cpsskins a "format element" because
it adds a format to displayed elements, then it will be identified as:
'format-widget-cpsskins.somewidget-12345'
whereas a widget in the context of a form would be identified as:
'form-widget-formlib.textinput-12345'
the names between the '-' signs are tagged names attached to interfaces.
It doesn't matter if two different interfaces have the same name since
unicity is created by combining the names. For instance we have:
resource = Style()
resource_type = queryType(resource, IResourceType)
resource_type.getTaggedValue('name')
=> u'style'
or one can also write:
IType(resource).resourcename
So a short name is associated to an interface type that is the basis for
creating a URI, and conversely it is possible to match URIs to interfaces:
for instance IActionPortlet ('cpsskins.actions') extends ICanvas
('canvas') and IPortlet ('portlet'), and ActionPortlet implements all three.
The association IActionPortlet <-> IContentType is done with:
alsoProvides(IActionPortlet, IContentType)
when the resource is defined.
then we have:
alsoProvides(IPortlet, IResourceType)
alsoProvides(ICanvas, IElementType)
[...]
So basically and to summarize: it is the application's responsibility to
categorize resources by type and to generate URIs from these, not the
developer's responsibility.
any thoughts?
regards /JM
More information about the Zope3-dev
mailing list