durusmail: quixote-users: separation of code and presentation
separation of code and presentation
2001-11-05
2001-11-05
separation of code and presentation
Greg Ward
2001-11-05
On 05 November 2001, Quinn Dunkan said:
> Hey, I have another quixote question.  The fact that we have .ptl files in
> addition to plain python code implies a separation of HTML generating code
> from "main logic" code.

Correct.  More generally, a separation of UI code from the main logic.
For the web, HTML generation is a large part of the UI.  (The other half
is form processing; I suppose the inevitable third half would be
JavaScript coding/generation.)

> I suppose the lesson learned from DTML, though, is
> that the HTML-generating bits are sometimes so complicated and intertwined
> with the actual objects that it's not always desirable to try to keep them
> separate.

Not quite.  The lesson *I* learned from DTML is that HTML generation can
be sufficiently complex that you need real control structures, and that
holds double for form processing.  Our first crack at PTL looked a lot
like Cheetah: HTML with Python in it, but "more obviously" Python than
DTML.  That turned out to suck as much as all other embed-a-real-
language-in-a-markup-language approaches, so Neil came up with what you
now see as PTL.  The great strength of PTL is that you have all the
facilities of a *real* language -- sensible quoting, control flow -- at
your disposal, instead of being bound to the brain-damage of a markup
language.

Separation of UI from main logic is essential in any case.  Having real
control flow (and objects, and access to the filesystem, etc. etc.) at
the UI level is also essential, but for very different reasons.

> Still, I don't like the idea of cluttering up my quixote objects with HTML
> generating code, so what I've been doing is giving each object a "view"
> wrapper object.

Not sure what you mean by "Quixote objects" -- do you mean the
namespaces that Quixote traverses when resolving a URL?  On the web, the
URL is a part of the UI, so as soon as you define a namespace for
Quixote to traverse, you have woven your UI into that namespace.
Therefore, that namespace is for the UI, and your main logic doesn't
belong there.

The way we handle this is to have a separate namespace for URL
traversal.  For example, the MEMS Exchange virtual fab (the project for
which Quixote was written) uses the "mems" package for domain logic, and
the "mems.ui" package for the UI.  Eg. mems.ui.catalog is the package
that implements http://fab.mems-exchange.org/catalog/ .

(Or, to put it more simply, mems.ui is the package that implements
http://fab.mems-exchange.org/ -- the whole web site is a single Quixote
app.)

Since that URL points to our process catalog, it relies heavily on our
process library, which is implemented in the mems.process package.
mems.ui.catalog therefore imports code from mems.process, and renders
instances of classes defined in mems.process, but never vice-versa.

The situation is similar with SPLAT!, a bug database I wrote to 1)
showcase Quixote, 2) learn about Berkeley DB, and 3) track bugs in the
MEMS Exchange virtual fab.  The "splat" package provides class like
splat.bug.Bug and splat.database.BugDatabase -- this is where the bug
database itself is implemented.  The web interface -- a namespace
traversed by Quixote when we visit http://fab.mems-exchange.org/bugs/
(don't try it, it's only for internal use) -- is implemented by
splat.web.

> In "views have objects" you have a User_view which wraps a user, and
> has a 'self.prefs' which is a Prefs_view wrapping the Prefs.  So
> you're setting up two parallel hierarchies of objects (has-a goes both
> vertically and horizontally if that makes any sense):
>
> User_view           has-a ->    User
>     Prefs_view                      Prefs
>     Foo_view                        Foo

This looks a lot like the structure we use.  Eg. in SPLAT!, the Bug
class has attributes like bug_id, summary, and submitter_id, and methods
like set_priority(), assign_bug(), and resolve_bug().  The BugUI class,
which is mostly implemented in PTL, has only two attributes: bug and
bug_db (the BugDatabase, where information about users and other bugs
comes from).  It has four exported methods: edit(), comment(),
resolve(), and reopen() (those are the PTL methods).  And it has some
methods for processing the forms generated by edit(), comment(), and
resolve(): edit_bug(), resolve_bug(), and add_comment().

Obviously, all the code in the BugUI class -- whether Python or PTL --
depends heavily on the code and data in the Bug class.  That's why every
BugUI has a Bug object.  But it most certainly is not the other way
around, because Bug objects are used for more than just the web UI
(there's an email UI and a command-line UI, for example).

Does that make any sense?  Does it provide any reassurance that you're
on the right track (it sounds to me like you are).  I find having two
separate classes helps to clarify the separation between UI and domain
logic.  Also, there is by no means a 1-to-1 mapping between domain
classes and UI classes; for example, SPLAT! has a BugDatabase but no
BugDatabaseUI.  (The list of all bugs is generated by a couple of PTL
templates -- nothing OO needed there.)

Generally, the only time we have a FooUI class is when _q_getname() is
used, ie. for "dynamic" URLs.  For static URLs, everything is just a
function (template) in a module somewhere.

        Greg
--
Greg Ward - software developer                gward@mems-exchange.org
MEMS Exchange                            http://www.mems-exchange.org


reply