[Twisted-web] fragments with child lookup

Valentino Volonghi aka Dialtone dialtone at divmod.com
Mon Sep 25 10:38:14 CDT 2006


On Mon, 25 Sep 2006 15:49:27 +0200, Markus Schiltknecht <markus at bluegap.ch> wrote:

>I see... unfortunately I don't know how to explain this better. I don't
>understand why writing a pastebin, a blog, or a news system should not
>be written as a component / Fragment, which can be embedded into other
>sites. And by embedded I mean URL _and_ template embedding. Isn't that
>obvious to give a lot of flexibility?
>>def render_content(self, level):
>>    def _(ctx, data):
>>        try:
>>            fragment_name = inevow.ICurrentSegments(ctx)[level]
>>        ...
>
>Hm... looks tricky. I didn't know about def _(...). But who is expected
>to pass the level to the renderer? The designer?

There's no magic in def _()... it's just a closure. Yes, the designer should pass the level to the renderer.

>>Used:
>>
>><nevow:invisible nevow:render="content 1">...</nevow:invisible>
>
>That way I still have the level hard coded into the template.

That's why I told you to always use -1 or simply avoid this confusing thing.

>Uh.. macros? This is where it gets frustrating for me, because I think you 
>still don't known what I want to achieve. Macros don't know a lot. Their 
>context is very limited. How am I supposed to resolve the all the 
>locateChild machinery? How do I give that information to the 'child', the 
>pastebin in my example?

macros are a much better way to integrate a pre-existing template into a basic template. They work at TEMPLATE level not at Page or Fragment level.

>That portion of the code works, but with the macro I don't know how pass 
>necessary information around.

Code conventions is a clear option. Passing arguments is another one.

>Hm... check 'C++ template metaprogramming'  *joking*
>How static is that?   :-)

The point is how easy it is to build abstractions on that stuff? It isn't 
that's why you need to rely on external configuration or whatever machinery, 
because it's too hard to work at code level.

>My macro try:

Maybe I haven't been very clear at first. In order to embed an application in another one you MUST DESIGN one of the two to be embeddable. You CANNOT embed a Page instance in another Page instance, it's wrong and won't work even if you think it might.

Going back to the 'problem':

I have an application and this is the directory structure:

main/
    static/ -- contains images, css, javascript files
    template/ -- contains the application templates
    appname/ -- contains the code

Since we know about Object Oriented Programming we work by providing a base
class that can be inherited to provide some default behavior:

class BasePage(rend.Page):
    docFactory = loaders.xmlfile('mymacro.xhtml', templateDir=templateDir)

    # we define that this BasePage can fill some default macros:
    def macro_content(self, ctx):
        return ctx.tag[loaders.xmlfile(self.__class__.__name__+'.html', ignoreDocType=True).load()]

    def macro_sidebar(self, ctx):
        return ctx.tag[loaders.xmlfile(self.__class__.__name__+'_sidebar.html', ignoreDocType=True).load()]


what does this class do? It allows the classes that inherit from it to embed
html fragments (not the rend.Fragment class) inside a base template. How?

class Root(BasePage):
    #whatever functionality

This class will look for 2 files: root.html and root_sidebar.html.

Let's say that mymacro.xhtml (the default macro template) is the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
      xmlns:nevow="http://nevow.com/ns/nevow/0.1">
    <head>
        Wowow
    </head>

    <body>
        <div id="content" nevow:macro="content" />
        <div id="sidebar" nevow:macro="sidebar" />
    </body>
</html>

Then the 2 root.html and root_sidebar.html templates are:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<nevow:invisible xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
                 xmlns:nevow="http://nevow.com/ns/nevow/0.1">

    I'm the content of the page arrrrrrrrr!
    aye I define whatever behavior I want, the only thing I care is that
    Root class can provide me with this functionality!

</nevow:invisible>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<nevow:invisible xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
                 xmlns:nevow="http://nevow.com/ns/nevow/0.1">

    I'm the sidebar of the ship, could you believe it?
    <div nevow:render="greeting" />

</nevow:invisible>

When you first try to render this page Nevow runs all the nevow:macro 
directives it can find (because they are pre-compilable, unlike nevow:render and nevow:data directives), this results in the following final template:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
      xmlns:nevow="http://nevow.com/ns/nevow/0.1">
    <head>
        Wowow
    </head>

    <body>
        <div id="content">

    I'm the content of the page arrrrrrrrr!
    aye I define whatever behavior I want, the only thing I care is that
    Root class can provide me with this functionality!

        </div>
        <div id="sidebar">

    I'm the sidebar of the ship, could you believe it?
    <div nevow:render="greeting" />

        </div>
    </body>
</html>

This template will be used for the Root class ONLY. Any other subclass will re-pre-compile the macro to get its own template.

After having worked in this way... How do you embed applications then?
SIMPLE!! You Change the macro template and instead of providing that one, you 
simply provide a new one that fits your web application defaults while 
keeping the macro slots defined in there so that the application you want to 
embed won't be surprised by the new template. How is that simple? It's so 
simple I shouldn't even tell you... templateDir is a configuration option, 
change it in the configuration and give it a new location where the new 
templates are available.

There is no other way and there's no cleaner way to do this in my opinion.
Why? Because you cannot embed applications randomly without having first planned for this option in the application that needs to be embedded because it has certain requirements about the API and simply moving around class cannot work anyway. Is this good or bad? I don't know, I think it's good. Is it possible to just put applications running in another one like they are completely separate? Of course... it's even easier, just return one's root page from the appropriate link.

The same can be done with Fragments and in fact Mantissa does exactly the 
same thing just with Fragments.



More information about the Twisted-web mailing list