NamespacesInTemplates
NamespacesInTemplates proposal
Status
Author
Shane Hathaway
Problem/Proposal
Page templates need a better way to access the components and metadata available from Zope.
In Zope 2, page templates take advantage of implicit acquisition to find components and read data. Implicit acquisition works well, but it hurts reusability by making it difficult to discover what contracts the templates rely upon.
Zope 3 abandons implicit acquisition in favor of explicit contracts and well-defined components. While this is an important step forward, template authors relied heavily on implicit acquisition and something almost as easy needs to replace it.
This same problem crept up in the formative stages of CMF and has influenced CMF ever since. CMF tools are components, and the presentation components (DTML methods at the time) need easy access to both tools and other presentation components. Use of implicit acquisition seemed like a natural solution to the problem, but as it turned out, the use of implicit acquisition led to many sacrifices:
- To make sure tools don't get overridden by some foreign object in the acquisition path, either accidentally or maliciously, we invented the __replaceable__ attribute. This attribute tells Zope that it should not override an acquired name in subfolders.
- To avoid putting all skin methods at the base of the portal, the base of the portal has a custom __getattr__() that looks for skin methods in the skins tool.
- To avoid requiring that every object in a portal implement __getattr__(), which would hurt performance, the base of the portal has the skin methods for every kind of object.
This is not meant to be a rant about implicit acquisition. This is meant to describe how much we are willing to sacrifice in order to make life easier for people who write templates. Even with the warts, the relative ease of making templates for CMF led to useful applications like Plone.
From another point of view, it must be easy to write templates if Zope wants to appeal to PHP/ASP/JSP developers. If all but the simplest templates require the template author to write an accompanying Python module, Zope will be less inviting to a lot of people.
Proposed Solution
I propose a new syntax in path expressions and an enhancement to TAL. Here is an example of what I am proposing:
<html talns:dc="http://purl.org/dc/elements/1.1/"
talns:zope="http://namespaces.zope.org/zope/">
<head>...</head>
<body>
<p>The title of this document is
<span tal:content="context/dc:title">Some Title</span>.</p>
<p>Its path in Zope is
<span tal:content="context/zope:path">Some Path</span>.</p>
</body>
</html>
Using "talns:(prefix)" attributes, the template author declares what features from the environment are needed by the template. The template author can then use those features in ZPT path expressions. In this example, the template displays the Dublin Core title and the Zope path of the context object.
The first element of a path expression must be a variable with no prefix.
Only subsequent path elements may use a namespace prefix. The expression
"context/dc:title" should be interpreted as "load the value of the
variable context, then find a component that adapts that object to
Dublin Core and read the title attribute of the component."
Template authors don't need to declare namespace prefixes that are very common and unlikely to conflict with other namespaces. Common namespaces can be declared in the environment (Zope 3 services) rather than in the template. "zope" and "dc" are good examples of very common prefixes, so the above example can be shortened like this:
<html>
<head>...</head>
<body>
<p>The title of this document is
<span tal:content="context/dc:title">Some Title</span>.</p>
<p>Its path in Zope is
<span tal:content="context/zope:path">Some Path</span>.</p>
</body>
</html>
Namespaces that are uncommon or under active development should be declared in each template that uses them. If you write a template that makes use of a component that is still in development, your template should probably depend on a version-specific namespace. This use of URI-bound namespaces and prefixes bears similarity to XML namespaces, but is even more similar to the flat namespaces used by RDF.
The new path expressions will be used in at least three ways:
- To access APIs?. A fairly large Zope API will provide access to services, traversal features, and so on. But there will be other, smaller, APIs? like string formatting and component-specific APIs? that can be replicated outside Zope.
- To access metadata. RDF and the Semantic Web are important steps forward and this syntax will give Zope users better access to these technologies. The expression "content/dc:title" might use an RDF query mechanism. Eventually, template authors should be able to use any number of namespaces, including RDF namespaces they invent on their own, without needing to write Python code.
- To explicitly acquire. The current syntax for explicit acquisition in templates is daunting. "context/++acquire++foo" gives the impression that something mysterious happens when you use two plus symbols. Alternatively, "context/acquire:foo" draws on the common understanding that a colon in XML denotes a prefix, making the "acquire" prefix the only concept that needs to be learned.
Templates may freely use the implicit prefixes, but templates that declare the use of small, explicit namespaces bound to specific URIs? are potentially easier to reuse in environments that don't use the full Zope platform, such as testing, debugging, embedded devices, and high-volume dynamic sites.
It is worthy to note that this syntax would have solved the deep acquisition problem in CMF. Instead of acquiring skins and tools from the root of the portal, templates in CMF would have used expressions like "context/skin:main_template" or "context/tool:membership" to find features from the environment. This would have eliminated the need for complex skin machinery and would have allowed tools to exist in a sub-container, similar to the way services live in a service manager.
Implementation Ideas
This section discusses some ideas for implementation. This section may change after discussion.
To support this proposal, some Zope interfaces might be associated directly with one or more namespace URIs?. Zope schemas seem similar in many ways to RDF schemas, so perhaps all Zope schemas should be associated with URIs?. If schemas were assigned URIs?, we could look up schema adapters by either URI or interface and have the same level of confidence in either result.
If too many namespaces come about, a Semantic Web technology called DAML+OIL is designed to solve the problem of overlapping namespaces. I understand RDF and DAML+OIL this way: a bit of RDF is like a relational database, an RDF schema is a vocabulary for bits of RDF, and DAML+OIL is an RDF thesaurus. DAML+OIL declares that queries for data in one namespace can be satisfied by another namespace. So an adapter component might be constructed using a DAML+OIL document.
URIs? don't have to use the HTTP protocol, but should remain well-formed, and Zope should make use of existing URIs? where possible rather than make up its own. So "zope.dublincore" is not an appropriate URI because it specifies no protocol, but "urn:zope.dublincore" is reasonable, while "http://purl.org/dc/elements/1.1/" is best. HTTP prefixes have the advantage of linking directly to a schema and providing immediately-accessible documentation.
Risks
- This proposal introduces URI-bound namespaces in ZCML. People who write ZCML will now have to understand the concept of URI-bound namespaces.
- This proposal suggests that some namespace prefixes should be implicit to make life easier for template authors. This leads to the risk that templates designed for a distant-future Zope will unexpectedly fail when run on an slightly earlier version of Zope. Perhaps when this is a concern, template authors should use explicit namespaces so that the incompatibility can be detected early (and perhaps resolved automatically.)
- In TALES expressions outside page templates it is not always possible to declare explicit namespaces. Thus TALES expressions outside templates may not be able to access some features.
- Some may consider namespace declarations to be boilerplate code and complain about having to enter them repeatedly. One possible way to remove the typing burden while retaining the benefits of explicit namespaces is to make Zope automatically add explicit namespace declarations to the root tag of a template being edited, if the user desires.
- "talns:(prefix)" is difficult to express in an XML schema or DTD. Since (prefix) is arbitrary, it breaks from custom. We might want a different way to express namespace declarations so that template XML can pass validity checks.
Acknowledgements
Troy Farrell started the thread that led to this proposal.
http://mail.zope.org/pipermail/zope3-dev/2003-February/005592.html
Stuart Bishop suggested this idea in a slightly different form.
http://mail.zope.org/pipermail/zope3-dev/2003-February/005628.html
And lots of people supported the idea enthusiastically. Thanks!
- dreamcatcher (Apr 9, 2003 3:19 pm; Comment #1)
- Can one chain namespaces, as in:
here/acquire:foo/dc:title
- hathawsh (Apr 9, 2003 3:30 pm; Comment #2)
- dreamcatcher: yes, you can.
- jack-e (Apr 9, 2003 4:18 pm; Comment #3)
- I like this proposal.
what about being able to configure the default namespaces via ZCML directives so that one can modify/replace/add/remove namespaces that are available in Pagetemplates and TALES-Expressions ??
- evan (Apr 9, 2003 4:46 pm; Comment #4)
- I would prefer
tal:ns="dc etc."totalns:dc="etc.", for both the reason cited (DTD compatibility) and for the sake of not adding another XML namespace to the mix. It differs from other TAL statements, in that it is a compile-time declaration rather that an executable statement, but brings that declaration into the realm of TALES (inside the quotes) where its meaning actually lies.My initial reaction to namespaces in isolated TALES expressions is that ZCML ought to provide a means for well-scoped definitions of which these expressions can take advantage.
- efge (Apr 9, 2003 5:17 pm; Comment #5)
- Would the
foo/acquire:barsyntax be a special case or would it still be done by adapting foo to something that would then provide acquired attributes? - hathawsh (Apr 9, 2003 5:41 pm; Comment #6)
- jack-e: yes, default prefix-to-URI mappings should be configurable in ZCML.
evan: tal:ns might be better indeed, for the reasons you suggested.
efge: "foo/acquire:bar" could be implemented with an adapter which would be nearly equivalent to an ImplicitAcquisitionWrapper?. Alternatively, there might be a namespace resolution service that page templates use instead of looking for an adapter directly, but I'm starting to like the compactness of always finding an adapter.
- chrisw (Apr 14, 2003 5:59 am; Comment #7)
- From what I understand, ++ does make magic happen in Zope 3 ;-)
- chrisw (Apr 14, 2003 6:03 am; Comment #8)
- I take it, you'd be able to redefine a default namespace in a particular template if you want to?
- hathawsh (Apr 14, 2003 9:03 am; Comment #9)
- chrisw: yes, local namespace declarations override global declarations.
- jim (Apr 17, 2003 6:48 am; Comment #10)
> Proposed Solution > > I propose a new syntax in path expressions and an enhancement to > TAL. Here is an example of what I am proposing:: > > <html talns:dc="http://purl.org/dc/elements/1.1/" > talns:zope="http://namespaces.zope.org/zope/"> > <head>...</head> > <body> > <p>The title of this document is > <span tal:content="context/dc:title">Some Title</span>.</p> > <p>Its path in Zope is > <span tal:content="context/zope:path">Some Path</span>.</p> > </body> > </html> > > Using "talns:(prefix)" attributes, the template author declares what > features from the environment are needed by the template. The > template author can then use those features in ZPT path expressions. > In this example, the template displays the Dublin Core title and the > Zope path of the context object.
First, this has little to do with TAL. Second, I prefer Evan's suggestion of putting the prefix in the value. So I suggest:
tales:namespace="foo http://spam.org/eggs/foo; eeek http://eeek.org"
> The first element of a path expression must be a variable with no > prefix. Only subsequent path elements may use a namespace prefix. > The expression "context/dc:title" should be interpreted as "load the > value of the variable
context, then find a component that adapts > that object to Dublin Core and read the title attribute of the > component."Note that thye title need not be an attribute.
You don't say anuthing about how URIs? will be associated with adapters. This is an important omission that needs to be addressed.
One possibility is to introduce an automatic adapter scheme:
adapter://zope.app.interfaces.dublincore.IZopeDublinCore?
but this doesn't fit with the story of being able to reuse URIs? in none zope systems.
Personally, I'd be happy to support:
tales:namespace="zdc zope.app.interfaces.dublincore.IZopeDublinCore?"
that skips the URO altogether. Perhaps this could be a supported short-hand notation.
...
> The new path expressions will be used in at least three ways:
...
> 3. To explicitly acquire. The current syntax for explicit > acquisition in templates is daunting. "context/++acquire++foo" gives > the impression that something mysterious happens when you use two > plus symbols. Alternatively, "context/acquire:foo" draws on the > common understanding that a colon in XML denotes a prefix, making > the "acquire" prefix the only concept that needs to be learned.
I don't agree that "foo/++acquire++bar" is daunting. For better or worse, this is a standard syntax that developers will encounter and should be aware of. Allowing "foo/++acquire++bar" and "foo/acquire:bar" violates the "one way to do it" principle.
...
> - In TALES expressions outside page templates it is not always > possible to declare explicit namespaces. Thus TALES expressions > outside templates may not be able to access some features.
But they will be able to use predefined namespaces.
- jim (Apr 17, 2003 7:06 am; Comment #11)
- An implementation note:
The local definitions should leverage the existing local variable support, which already takes care of xml "scoping" issues. Perhaps declaring a prefix should just define a global variable with a special name, perhaps even a name that can't be spelled directly in tales (e.g. (prefix,)).
- hathawsh (Apr 17, 2003 10:01 am; Comment #12)
> First, this has little to do with TAL. Second, I prefer Evan's > suggestion of putting the prefix in the value. So I suggest: > > tales:namespace="foo http://spam.org/eggs/foo; eeek http://eeek.org"
Evan also said that we should avoid adding yet another XML namespace to the mix, hence "tal:ns" or "tal:namespaces" instead of "tales:namespaces". The argument against that is that TAL and TALES are separate and therefore should be in separate namespaces. My opinion is that the distinction is not enough justification to add another XML namespace.
> Note that thye title need not be an attribute.
What else would it be?
> You don't say anuthing about how URIs will be associated with > adapters. This is an important omission that needs to be addressed.
In the "implementation ideas" section, I suggested that schemas might be associated with one or more namespaces. The implication is that you can look up an adapter by URI.
> I don't agree that "foo/++acquire++bar" is daunting. For better or > worse, this is a standard syntax that developers will encounter and > should be aware of. Allowing "foo/++acquire++bar" and > "foo/acquire:bar" violates the "one way to do it" principle.
Unless we choose to avoid "++" altogether. "++" is unlikely to be adopted anywhere but in the Zope world and therefore will remain a Zope-ism that's unfamiliar to almost everyone.
> The local definitions should leverage the existing local variable support, > which already takes care of xml "scoping" issues. Perhaps declaring a prefix > should just define a global variable with a special name, perhaps even a > name that can't be spelled directly in tales (e.g. (prefix,)).
That would interact badly with METAL macros. Macros chop up templates and sometimes eliminate variable definitions--you might lose namespace declarations.
test --Brian, 2003/09/09 11:48 EST reply
test
