The Skin Browser and Lovely Systems’ new Development Workflow
I just checked in the new Skin Browser APIDOC module and started to write a
huge check in message justifying and explaining the this new package. But then
I thought this would be a great opportunity to write my first blog entry
ever. So here we go.
In recent months Lovely Systems has had significant growth in human
resources. While this is really great, it also adds engineering
overhead. Lovely Systems now employs people that do either design, templating
or Zope 3 development. Thus we needed ways to effectively communicate between
the different types of developers and allow them to work independently without
having much overhead.
Zope’s Page Templates (specifically the TAL standard) promised from the
beginning to allow the separation of template and view code
development. However, over the years common (lazy) patterns developed that
made this separation impossible. Also, other resources such as style sheets,
images and JavaScript files were not templatable and it was thus impossible to
reuse those resources without modification on the server.
To solve this problem, the Lovely developers/consultants (mainly Juergen
Kartnaller, Bernd Dorn, Roger Ineichen, Jodok Batlogg and I) developed a set
of packages. The first package was the z3c.zrtresource, which is a very
simple, but very versatile template language that was designed to do string
replacements in style sheets and JavaScript files. Zope Resource Templates
(ZRT) allow JavaScript scripters and CSS template designers to map local path
descriptions to valid server URLs. ZRT allows by default simple, regex and
TAL-based string replacement. Here are some examples:
/* Replace "foo" with "bar", but only 3 times */
/* zrt-replace: "foo" "bar" 3 */
/* Do a group regex string substitution */
/* zrt-replace: re"(?P<prefix>[a-z]*)foo” “bar\g<prefix>” */
/* Replace the strng “foo: with the resource URL at runtime, everywhere */
/* zrt-replace: “foo” tal”string:${context/@@absolute_url}/@@/foo” */
This was a real success with our designers. Instead of regularly scheduling
meetings with the designers to integrate the latest version of resources into
the code base, they are no able to test offline and in the system at the same
time!
After some progress with the development, we noticed that there was a far too
tight connection between the Python view code and the templates. After some
discussion we were convinced that the Python view code belongs into the layer
and the page templates into the skin. But the big problem was that templates
and view code always had to be combined to a page together using the
browser:page directive. To solve this problem, we developed
z3c.viewtemplate which allows you to define a view on one hand and use a
second directive to register templates for this view. While this requires two
instead of one directive per view (a clear development overhead), it allowed
us to completely separate template from Python view code development. As
Juergen said: “I finally do not have to care about templates at all anymore;
when my tests pass I am happy.” Here is an example of the ZCML:
<browser:page name="blog.html" for="myproject.interfaces.IBlog" class="myproject.browser.blog.BlogList" layer="myproject.browser.skin.IProjectSkin" permission="zope.Public" /> <browser:template template="blog.pt" layer="myproject.browser.IProjectLayer" for="myproject.browser.blog.BlogList" />
The BlogList view class only has to inherit
z3c.viewtemplate.baseview.BaseView. This finally allowed us to work
completely separately. To take this new approach even further, we also exiled
macros from our development. We are only using one master template that simply
calls all sorts of content providers and viewlet managers. Then we register
viewlets for particular view combinations. This appraoch effectively reduced
pages to a traversal step in the URL and a hook to register content
providers/viewlets. Jodok reasoned for this approach saying that it was very
confusing to a template developer to know at what point the “view” TAL
namespace would represent what object. After having set up a complete project
using this appraoch to UI Zope 3 development, we are confident that this
approach works. Here is a final example at how we work the configuration:
<!-- And example of a page. You really only need the class to have a registration point. --> <browser:page name="blog.html" for="myproject.interfaces.IBlog" class="myproject.browser.blog.BlogPage" layer="myproject.browser.IProjectLayer" permission="zope.Public" /> <!-- The main template used by all pages. It only contains the very basic structure and a bunch of viewlet managaer calls. --> <browser:template template="main.pt" layer="myproject.browser.skin.IProjectSkin" for="myproject.browser.IProjectPage" /> <!-- This is a typical main content viewlet that actually implements functionality for the blog page. --> <browser:viewlet name="bloglist" for="myproject.interfaces.IBlog" manager="myproject.browser.interfaces.IContent" view="myproject.browser.blog.BlogPage" class="myproject.browser.blog.BlogList" layer="myproject.browser.IProjectLayer" permission="zope.Public" /> <!-- This is the template is registered for the blog list viewlet. --> <browser:template template="bloglist.pt" layer="myproject.browser.skin.IProjectSkin" for="myproject.browser.blog.BlogList" />
This is almost the final version, actually. Eventually the template designer
came back saying that he would like to define multiple viewlets in one page
template. But how to select snippets in a template? Aha! The revival of
macros. So eventually we have the following:
<!-- This is the template contains all possible blog views, and we just select the blog list. --> <browser:template template="blog.pt" macro="bloglist" layer="myproject.browser.skin.IProjectSkin" for="myproject.browser.blog.BlogList" />
Okay, this is the current state of the art at Lovely Systems. We have a clear
separation among designers, scripters, template coders and Zope 3
programmers. In fact, a meeting a few minutes ago went like this:
- Michel (Project Manager):
- How do we display a list of users and what info is shown?
- Juergen (Zope Programmer):
- Don’t look at me, I do everything you tell me to.
- Olli (Designer):
- I have already developed a mockup for lists, let’s use that.
- Jodok (Template Developer):
- Michel, let’s talk about that. We define what we want. Then I am going to
use Olli’s design and make a template. From this template I will provide
Juergen with an API to implement.
From conversations like that one, it became evident that we needed a tool that
let us discover the views of the system and communicate that API. And so I
developed the Skin Browser APIDOC module (lovely.skinbrowser), which
allows us to discover all view template driven views. This tool does not only
reconnect the view code to the template for comprehension, but also gives
reports on the existing and desired API of the view code. The desired API is
communicated to the Zope developer via the interface of the view class.
While this process is total overkill for a one or two man shop, it provides
all the necessary human and programming interfaces to allow independent,
separated development of the different groups, while documenting the code in a
formal and “standard” manner that can be easily viewed.
Okay, that’s all for now. I will be back with more stuff I did in a little
while.
technorati tags:views