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