Template Engine Integration

From design paper

The first step, a simple prototype to get the integration started, has been put together the last few weeks. It appears to MoinMoin as multiple themes, by scanning configured template directories (see Config.genshi_templates) for sub directories, using the name of the subdirectory as a theme name. To completely render a page with such a theme, images and css have to be in place (wiki-instance htdocs) like for regular themes at the moment.

The theme output normally done by ThemeBase was abstracted into a _base.html living under the genshi_templates directory (still needs to be put somewhere more central)

Since normally the output of the actual page content happened outside the theme, output capturing was used in between calls to .send_title and .send_footer, to make the rendering of the page content available to the template.

For the actual rendering with Genshi, a preliminary context was assembled in .send_title and .send_closing_html, to give templates access to useful resources (the actual page, names of standard system pages, user trail, message to be shown, etc.)

... to design problems

To have more control over the output (for themes and also in general) it would be advisable to turn around the way output is written to the output stream. Currently Page and PageEditor output themselves (via .send_page and .sendEditor) and also several actions access the output stream (directly via request.write or indirectly by calling .send_page themselves).

The next step would have been refactoring .send_page (actually killing it in the process) by moving it's code into the distinct actions as needed. The idea was to split .send_page into reusable little methods, maintaining backward compatibility for plugins, while pulling the inner workings into the included actions step by step.

Sadly i failed in this task. The complexity of the actual code flow through even a simple request (showing a page) left me somehow clueless on where to actually start. In the following paragraphs i will try to outline the problems i encountered.

General problems

Some general problems and style issues became apparent while trying to refactor:

  1. Tight coupling: When trying to refactor/rearrange code one often has to realize that this would break a great deal of other code, some even unexpected. Breaking code is fine when refactoring on a large scale, but when trying to concentrate on a small part and clean up things, it's quite cumbersome to follow all the implications.

  2. Side-effect style programming: Many methods all around Moin are programmed in a way, that they don't return any values and the programmer relies on the side-effects applied by those functions. Especially the many set* methods in different areas make it hard to follow. It's often not apparent where a set instance variable comes from. Another example, the use of Request.write everywhere for output, is explained in more detail later in this text.

Page.send_page considered unrefactorable

Page.send_page has been marked for refactoring since 2002. Yet it was never done and instead it even grew more out of proportions. In a quick comparison from 2002 until 2007, it seemed send_page double to tripled in terms of lines of code. Moin has the concept of actions on a Page item, the default action being 'show' but the actual code for displaying them lies almost completely in .send_page. This renders send_page the central all-purpose 'controller' in the model-view-controller concept, making actions merely dispatchers on the parameters to send_page.

The following list gives a short summary of what responsibilities are currently found in .send_page:

As stated many actions (and some other code) become merely dispatching calls to .send_page, expecting it to handle the many different cases. Since send_page drives so many aspects of the output, it becomes hard to to override or interfere with this behaviour. As a consequence, it seems, modifications to the output behaviour where added to send_page over time instead of refactoring it as it was intended 2002.

The following list may give an impression of how send_page is used from other code:

PageEditor.sendEditor worsens things

With send_page being the central controller for displaying html and actions to the user, sendEditor follows a similar pattern for the single action of 'edit'. Even bigger in proportions and also using send_page for tasks like displaying a preview or giving informational messages to the user, it's also quite hard to follow.

Request.write

The main way for almost every part of MoinMoin to add something to the output is by calling Request.write. The request object is passed around into every corner of MoinMoin and therefore using Request.write is easy and quite obvious in the first place. But still there are several quite negative implications:

Request.write is called at 365 places (cached byte code not included). Its used in

Putting it into context

In the preceeding sections some design problems have been outlined. All of them affect the integration of new code that is related to output in some way or another. In the following the integration of the template engine will serve as an example of how hard it is to integrate new code/concepts. The different mentioned parts will be set in relation to the mentioned problems:

Using a template engine for HTML output requires the rest of the application to supply a meaningful context for the engine. In a typical web application build in some way or another on the principle of Model-View-Controller, this context is established in the Controller-component. Furthermore output is left exclusively to the View-component, the actual template provided by the template engine (regardless of the actual delivery happening on a lower level at the request level).

Here the first two problems arise:

The controllers in MoinMoin (the actions on a Page) for the most common activities don't provide any meaningful context. As mentioned above most of them are merely dispatchers on the all-embracing Page.send_page. Additionally (as mentioned in the 'Request.write'-section) output to the lowest level is done from higher levels, especially by the formatter/parser combination.

This leaves the programmer integrating with the following choices, all of them flawed in some way or another:

Where to go from here

I talked to my mentor, FlorianFesti, and he wanted me to write a design proposal. The proposal should describe a request handling architecture, that would ideally suit MoinMoin. It will be available shortly under ../ReDesign

MoinMoin: FlorianKrupicka/SOC2007/DesignPaper (last edited 2008-05-03 13:59:33 by FlorianKrupicka)