[Twisted-web] Menu flattening

Donovan Preston twisted-web@twistedmatrix.com
Thu, 11 Dec 2003 11:53:38 -0500


--Apple-Mail-3-512621281
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed


On Dec 11, 2003, at 10:35 AM, Justin Johnson wrote:

> Hi,
>
> I'm trying to write a method that creates a menu given the following
> information.
>
> menu_items = {
>     "A": [ "a", "b", "c" ],
>     "B": [ "d", "e", "f" ],
>     "C": [ "g", "h", {"i": ["x", "y", "z"]} ],
> }
>
> This dict contains the hierarchy of menus.

First, let me start by saying that nevow is all about flexibility. You 
have an implementation of a function which does exactly what you want, 
except it uses string concatenation instead of stan to generate some 
HTML. If it is going to be easier for you to debug and understand this 
code, then by all means use it. I have shown an example of using your 
string-concatenation code as a nevow renderer, by calling your code and 
wrapping the output in an xml object, before returning it.

Second, stan doesn't have to be about huge python expressions. It's 
just as easy to use in a standard procedural way, creating objects and 
then appending them to lists. I have constructed an example of 
rendering your menu structure using stan with clarity and readability 
in mind.

dp


--Apple-Mail-3-512621281
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="nestedmenus.tac"
Content-Disposition: attachment;
	filename=nestedmenus.tac



import random

from twisted.application import service, internet
from twisted.web import server

from nevow import renderer
from nevow import tags


def flatten_menu(data):
    html_menu = "<ul>"
    for k, v in data.items():
        html_menu += "<li>%s</li>" % k
        html_menu += "<ul>"
        for i in v:
            if isinstance(i, dict):
                html_menu += flatten_menu(i)
            else:
                html_menu += "<li>%s</li>" % i
        html_menu += "</ul>"
    html_menu += "</ul>"
    return html_menu


class Menus(renderer.Renderer):
    document = tags.html[
    tags.head[tags.title["Hello"]],
    tags.body[
        tags.div(style="border: 1px dotted black; margin: 1em")[
            tags.ul(renderer=tags.directive("menu"))
        ],
        tags.div(style="border: 1px dotted black; margin: 1em")[
            tags.ul(renderer=tags.directive("stan_menu"))
        ]
    ]
]
    def render_menu(self, context, data):
        return tags.xml(flatten_menu(data))

    def render_stan_menu(self, context, data):
        """Render a dictionary of string keys and list of string/dict values."""
        menu = context.tag
        for k, v in data.items():
            # Add a list item with our key as the label
            menu.children.append(tags.li[k])
            # Add an ul tag to this menu for the submenu
            submenu = tags.ul()
            menu.children.append(submenu)

            # For every element of the value list
            for i in v:
                # if it's a dict, cause render_stan_menu to be called on it, and the output placed in the submenu tag
                if isinstance(i, dict):
                    submenu.children.append(tags.ul(data=i, renderer=self.render_stan_menu))
                # if it's a string, just add a list item with the string to the menu
                else:
                    submenu.children.append(tags.li[ i ])
        return menu


menu_items = {
    "A": ["a", "b", "c"],
    "B": ["d", "e", "f"],
    "C": ["g", "h", {"i":
        ["x", "y", "z"]
    }]
}


application = service.Application("nestedmenus")
internet.TCPServer(
    8080, 
    server.Site(
        Menus(
            menu_items
        )
    )
).setServiceParent(application)

--Apple-Mail-3-512621281--