import os from breve import Template from breve.tags import html from urllib import splitquery class BreveTemplatePlugin ( object ): """ Breve Template Plugin for Buffet-compatible frameworks Tested with TurboGears and Pylons """ extension = "b" tag_defs = { 'html' : html } def __init__ ( self, extra_vars_func = None, options = None ): self.get_extra_vars = extra_vars_func self.options = options or { } self.breve_opts = { } def get_config ( self, vars ): ''' Different frameworks provide needed config info at different times and in different ways (notably TurboGears and Pylons), so I've wrapped up this messy code here. It's sad and inefficient that we can't count on the config being passed at instantiation. This should be fixed by having Buffet specify how this should be done and having the frameworks follow suit. Breve now allows a url-style syntax in the template name to bypass the brokenness in the various frameworks, e.g.: index?format=html&debug=1 ''' breve_opts = { 'root': '.', 'namespace': '', 'debug': False, 'tidy': False } if 'std' in vars and 'tg' in vars: # turbogears-specific hacks cfg = vars [ 'std'] [ 'config' ] breve_opts [ 'root' ] = cfg ( 'breve.root', breve_opts [ 'root' ] ) breve_opts [ 'namespace' ] = cfg ( 'breve.namespace', breve_opts [ 'namespace' ] ) breve_opts [ 'debug' ] = cfg ( 'breve.debug', breve_opts [ 'debug' ] ) breve_opts [ 'tidy' ] = cfg ( 'breve.tidy', breve_opts [ 'tidy' ] ) else: # pylons-specific for k, v in self.options.iteritems ( ): if k.startswith ( 'breve.' ): breve_opts [ k [ 6: ] ] = v return breve_opts def load_template ( self, template_name ): """ template_name == dotted.path.to.template (without .ext) @@ 1.1: Let's dump the dotted notation. It was a bad idea, the engine that inspired it (Kid) is probably dead, and it was always meaningless to everyone else anyway. Also, let's support an extension as a synonym for "format", i.e. /blog/feed.rss would be the same as /blog/feed?format=rss """ template, args = splitquery ( template_name ) if args: args = dict ( [ a.split ( '=' ) for a in args.split ( '&' ) ] ) else: args = { } parts = template.split ( '.' ) template_filename = parts.pop ( ) template_path = '' if parts: template_path = os.path.join ( *parts ) return template_path, template_filename, args def render ( self, info, format = "html", fragment = False, template = None ): """ info == dict of variables to stick into the template namespace format == output format if applicable fragment == special rules about rendering part of a page template == dotted.path.to.template (without .ext) """ vars = info # check to see if we were passed a function get extra vars if callable ( self.get_extra_vars ): vars.update ( self.get_extra_vars ( ) ) self.breve_opts.update ( self.get_config ( vars ) ) template_path, template_filename, args = self.load_template ( template ) # self.breve_opts.update ( args ) template_root = self.breve_opts [ 'root' ] format = args.get ( 'format', format ) if template_root and template_path.startswith ( template_root ): # this feels mildly brittle template_path = template_path [ len ( template_root ) + 1: ] if format not in self.tag_defs: # this seems weak (concerns about path). Should perhaps # find a better way, but getting only a string for format # makes it difficult to do too much self.tag_defs [ format ] = __import__ ( format, { }, { } ) self.breve_opts [ 'doctype' ] = self.breve_opts.get ( 'doctype', self.tag_defs [ format ].doctype ) template_obj = Template ( tags = self.tag_defs [ format ].tags, xmlns = self.tag_defs [ format ].xmlns, **self.breve_opts ) if fragment: return template_obj.render_partial ( os.path.join ( template_path, template_filename ), vars = vars, **self.breve_opts ) return template_obj.render ( os.path.join ( template_path, template_filename ), vars = vars, **self.breve_opts )