Well, came up with something kind of useful that I thought I would share with the groups. One of the hidden gems of TG is that it is actually almost ridiculously easy to make your applications extensible. Plugins, Themes, Extensions, entire new site components, you name it TG probably has an easy way to make it happen. (and that easy way probably uses setuptools to do the dirty work.)
Here, let's take a look at an easy way to make your application themeable. Although you can get a lot done with just CSS, I am going to be showing how to replace templates to accomplish theming as it is more useful. I'll be linking to useful resources if I am covering anything that might not be immediately easy to grasp. All of these examples come from a webmail project I am working on (currently dubbed ThoughtMail). The first thing we are going to do is make a change to our setup.py file that declares (and fills) an entry point for themes. (for more info see the first section of: http://docs.turbogears.org/1.0/CommandPlugins) Here is a sample entry point: """ [thoughtmail.themes] default = thoughtmail.themepackages:Default """ Briefly what we are doing here is declaring a name (thoughtmail.themes) that we will use later to have setuptools look for code that is available to use. We are also informing setuptools that the "Default" class in the "thoughtmail.themepackages" module should be included in the list for "thoughtmail.themes" and named "default". Now we are just a quick package registration (python setup.py develop) away from getting started. Since I don't want to keep a list of every template name, lets cheat a bit. In the themepackages.py file we'll build our Default class so all it does is put the appropriate namespace on the name it is given. Here is what it looks like: class Default: desc = "Default theme for ThoughtMail" # This is not required by setuptools, but is useful. def __getattr__(self, item): """ Overload getattr so calls to Default().attr return "kid:thoughtmail.templates.default.attr" """ if item[0]=='_': return item return "kid:thoughtmail.templates.default." + item So a call to Default().index will return "kid:thoughtmail.templates.default.index". This is only half the equation though, as we still need to allow our users to select a theme. One way of solving that is to rely on a combination of our previously defined entry points and the TurboGears config system. When it is started ThoughtMail will look for a thoughtmail.theme config item grab a list of available themes through setuptools, and use the match if it is found. Here is the code that does that: import turbogears.config import pkg_resources class ThoughtMail: def __init__(self): get = turbogears.config.get # look for any provided theme libraries, if none are given use the default theme = get("thoughtmail.theme",None) if not theme: for entrypoint in pkg_resources.iter_entry_points("thoughtmail.themes"): if entrypoint.name == theme: my = entrypoint.load() else: my = pkg_resources.load_entry_point('thoughtmail','thoughtmail.themes','default') self.theme = my() Here's the important bits: We are using turbogears.config.get to retrieve the value (if any) of thoughtmail.theme from our application's config files. Then, if we found a match the relevant class is imported and set to the wonderfully descriptive (but usefully short) name 'my'. The above mentioned 'Default' theme is used if we can't find anything else. Now that that is done we are just about home free. Since I am storing this information as a member variable of the ThoughtMail class, it will normally be invisible to @expose. One way around this is to set the template in the returned dictionary through the 'tg_template' key. Here is a sample index using this method. @expose() def index(self): return dict(data="foo", tg_template=self.templates.index) Now the template for the index page will be set as though you had used @expose to directly reference the template library for the theme you are using. This is kind of messy though, and I would hate to add this tg_template key to each of my methods, so lets cheat, again. def tdict(self, name, **kw): """ Create a dictionary with the given args and a tg_template key set to 'name' """ return dict(tg_template=name).update(kw) Now we can change our index to make it a little shorter: @expose() def index(self): return tdict(self.templates.index, data="foo") You can possibly get something shorter as well, if you want to spend the effort writing a custom decorator. Writing a new theme package now becomes a matter of creating an egg containing your theme files, classes that point to those files, and some entry_point listings that point to those classes. That is pretty much it. A smattering of little bits of code in a few places and all someone has to do is install a theme library and set a config variable to change the look of your application. The nice part about this solution is that, since it is all done at runtime, it is pretty easy to allow for per-user themes as well. Although the above code only deals with themes; plugins and extensions are conceptually similar. You define an entry point and find a way to choose a specific item out of the list for that entry point, and it is pretty much strait Python classes from there. Hope you enjoyed it, -Adam --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "TurboGears" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/turbogears?hl=en -~----------~----~----~----~------~----~------~--~---

