Zoe's UI (A Walkthrough)
Editors Note: Zoe was originally constructed using Apple's WebObjects. We
are currently in the process of
replacing WebObjects with Tapestry,
which is, essentially, an open source version of WebObjects. This
document refers to the code as it was originally created.
I thought it could help to go through a little "technical" walk through
on how ZOE UI is structured as that will be the first thing to port...
So here it goes:
- The basic unit in WebObjects is a component. A component is typically
made of three parts: an html template (.html), a definition file (.szd)
that describe the bindings between the html and the Java code and
finally a Java class (.java).
- One use composition (eg: a patchwork of component dumped together) to
define a page or such. There is *no* html. HTML is *generated* by the
- To understand how ZOE's UI works, the most important concept to grasp
is the notion of an "inspector". An inspector is a component that knows
which object it can display. It also provides all UI information related
to an object. What does it mean in practice? It means that the
relationship between different components are resolve at runtime through
an "object link" (aka SZLink). It means that there are no "hard coded"
internal references (<a></a>) in ZOE. The "links" are resolved
dynamically at runtime based on the type of object.
Lets take a closer look at SZLink as an example of how an "inspector"
- SZLink component (.html / .java / .szd ) has only *one* mandatory binding which is what
"object" you want to create a link for.
- From this object value, all the other informations are derived:
- Do I link to a external "href": SZInspector.hrefForObject
( this.value(), this.context() )
- Do I go to another internal "page":
SZInspector.inspectorNameForObject( this.value() )
- What description should I display for this link:
SZInspector.descriptionForObject( this.value() )
- And so on and so forth.
The end result of all that would be, for example, a link to a "date"
that could look like this:
- Generated html rendered as "Sat Jul 27"
<a title="Saturday July 27th 2002"
5.1.0"><span class = "Label">Sat Jul 27</span></a>
- HTML template
<webobject name = "Date"></webobject>
- The component definition
value = value.creationDate;
class = "Label";
So how does it work? Lets take a look at SZLink more closely:
- To figure out where to go, SZLink will ask SZInspector for a component
name given an object: SZInspector.inspectorNameForObject().
- SZInspector will query the runtime for the existence of a relevant
component Class based on a naming convention: anObject.getClass() +
aSuffix. In this case it will look for SZDateInspector, the class of the
value binding "SZDate", plus the standard inspector extension
"Inspector". This is what SZInspector.inspectorClassWithObject() does
-with the help of SZRuntime.classForObjectInPackageWithSuffix() as I
used this pattern quiet extensively.
- The same process will take place for all the other informations. For
example to know what to display for a given link, SZLink will query
SZInspector.descriptionForObject(). Again, SZInspector will retrieve
which *concrete* inspector class handles a given object and ask this
class for a description. Because there is no real class method in Java
all this circus is done through the reflection API... :-( In any case,
SZInspector will retrieve a concrete inspector class (SZDateInspector)
and invoke its descriptionForObject() class method.
All the other SZLink methods follow the same pattern.
This turn out to be quiet a powerful setup as it let the system deal
with how exactly links should be resolve and free the developer from
such trivial matters. To handle a new type of object simply add a new
concrete inspector and the entire system will know how do deal with
it... A great time saver... :-)
This concept is used all over the places. For example there is a pretty
useful table component that takes a collection of objects and a list of
keys and automatically create a table out of all this information with
all the right links at the right place thanks to SZLink. So to get a
full blown table you simply define: