fix html/templates to use a Container type (inherited by Tag) for flat lists of tags
--- a/degal/html.py Fri Jun 05 23:59:14 2009 +0300
+++ b/degal/html.py Wed Jun 10 21:59:49 2009 +0300
@@ -78,7 +78,70 @@
# default .render method
render = render_unicode
-class Tag (IRenderable) :
+class Container (IRenderable) :
+ """
+ A container holds a sequence of other renderable items.
+ """
+
+ @classmethod
+ def process_contents (cls, contents) :
+ """
+ Postprocess contents iterable to return new list.
+
+ Items that are None will be omitted from the return value.
+
+ >>> list(Container.process_contents([]))
+ []
+ >>> list(Container.process_contents([None]))
+ []
+ >>> list(Container.process_contents([u'foo']))
+ [u'foo']
+ """
+
+ for content in contents :
+ if content is None :
+ continue
+
+ else :
+ # normal, handle as unicode data
+ yield content
+
+ def __init__ (self, *contents) :
+ """
+ Construct this container with the given sub-items
+ """
+
+ # store postprocessed
+ self.contents = list(self.process_contents(contents))
+
+ def render_raw_lines (self, **render_opts) :
+ """
+ Render our contents as a series of non-indented lines, with the contents handling indentation themselves.
+
+ >>> list(Container(5).render_raw_lines())
+ [u'5']
+ >>> list(Container('line1', 'line2').render_raw_lines())
+ [u'line1', u'line2']
+ >>> list(Container('a', Tag('b', 'bb'), 'c').render_raw_lines())
+ [u'a', u'<b>', u'\\tbb', u'</b>', u'c']
+ >>> list(Container(Tag('hr'), Tag('foo')('bar')).render_raw_lines())
+ [u'<hr />', u'<foo>', u'\\tbar', u'</foo>']
+ """
+
+ for content in self.contents :
+ if isinstance(content, IRenderable) :
+ # sub-items
+ for line in content.render_raw_lines(**render_opts) :
+ yield line
+
+ else :
+ # escape raw values
+ yield escape(unicode(content))
+
+ def __repr__ (self) :
+ return 'Container(%s)' % ', '.join(repr(c) for c in self.contents)
+
+class Tag (Container) :
"""
A HTML tag, with attributes and contents, which can a mixture of data and other renderables(tags).
@@ -86,25 +149,6 @@
"""
@staticmethod
- def process_contents (contents) :
- """
- Postprocess contents iterable to return new list.
-
- Items that are None will be omitted from the return value.
-
- XXX: flatten
-
- >>> Tag.process_contents([])
- []
- >>> Tag.process_contents([None])
- []
- >>> Tag.process_contents([u'foo'])
- [u'foo']
- """
-
- return [c for c in contents if c is not None]
-
- @staticmethod
def process_attrs (attrs) :
"""
Postprocess attributes.
@@ -113,15 +157,15 @@
TODO: only remove one underscore
- >>> Tag.process_attrs(dict())
+ >>> dict(Tag.process_attrs(dict()))
{}
- >>> Tag.process_attrs(dict(foo='bar'))
+ >>> dict(Tag.process_attrs(dict(foo='bar')))
{'foo': 'bar'}
- >>> Tag.process_attrs(dict(class_='bar', frob=None))
+ >>> dict(Tag.process_attrs(dict(class_='bar', frob=None)))
{'class': 'bar'}
"""
- return dict((k.rstrip('_'), v) for k, v in attrs.iteritems() if v is not None)
+ return ((k.rstrip('_'), v) for k, v in attrs.iteritems() if v is not None)
def __init__ (self, name, *contents, **attrs) :
"""
@@ -138,12 +182,13 @@
>>> Tag('foo', class_='ten')
Tag('foo', class='ten')
"""
-
+
+ # store contents as container
+ super(Tag, self).__init__(*contents)
+
+ # store postprocessed stuff
self.name = name
-
- # store filtered/processed versions
- self.contents = self.process_contents(contents)
- self.attrs = self.process_attrs(attrs)
+ self.attrs = dict(self.process_attrs(attrs))
def __call__ (self, *contents, **attrs) :
"""
@@ -196,28 +241,6 @@
return " ".join(self.format_attr(n, v) for n, v in self.attrs.iteritems())
- def render_contents (self, **render_opts) :
- """
- Render the contents of the tag as a series of indented lines, with given render_lines options for subtags
-
- >>> list(Tag('x', 5).render_contents())
- [u'5']
- >>> list(Tag('x', 'line1', 'line2').render_contents())
- [u'line1', u'line2']
- >>> list(Tag('x', 'a', Tag('b', 'bb'), 'c').render_contents())
- [u'a', u'<b>', u'\\tbb', u'</b>', u'c']
- """
-
- for content in self.contents :
- if isinstance(content, IRenderable) :
- # sub-tags
- for line in content.render_raw_lines(**render_opts) :
- yield line
-
- else :
- # escape raw values
- yield escape(unicode(content))
-
def render_raw_lines (self, indent=u'\t') :
"""
Render the tag and indented content
@@ -230,10 +253,11 @@
attrs_stuff = (" " + self.render_attrs()) if self.attrs else ""
if self.contents :
- # tag with content
+ # wrapping tags
yield u"<%s%s>" % (self.name, attrs_stuff)
-
- for line in self.render_contents(indent=indent) :
+
+ # subcontents
+ for line in super(Tag, self).render_raw_lines(indent=indent) :
yield indent + line
yield u"</%s>" % (self.name, )
@@ -271,6 +295,10 @@
class XHTMLDocument (IRenderable) :
"""
+ A full XHTML document with XML header, doctype, head and body.
+
+ XXX: current rendering is a bit of a kludge
+
<?xml version="..." encoding="..." ?>
<!DOCTYPE ...>
@@ -350,6 +378,7 @@
return Tag(name)
# pretty names
+container = Container
tag = Tag
tags = TagFactory()
raw = Text
--- a/degal/templates.py Fri Jun 05 23:59:14 2009 +0300
+++ b/degal/templates.py Wed Jun 10 21:59:49 2009 +0300
@@ -5,21 +5,21 @@
import html
from html import tags
-def link_from (from_page, to_page) :
+def link_from (source, target) :
"""
Returns a partial a tag linking from the given page to the given page
XXX: URLEncode unicode -> str!
"""
- return tags.a(href=to_page.path_from(from_page))
+ return tags.a(href=source.path_to(target))
-def image_link (from_page, image, image_html) :
+def image_link (from_page, image, target) :
"""
Link to the given image
"""
- return link_from(from_page, image_html)(
+ return link_from(from_page, target)(
tags.img(src=image.path_from(from_page))
)
@@ -28,7 +28,7 @@
The per-image view
"""
- return [
+ return html.container(
tags.div(id_='image')(
# title
tags.h1(image.title) if image.title else None,
@@ -50,10 +50,10 @@
),
# extended info, metadata
- tags.div(id_='info')(
- tags.p("%s: " % name, value) for name, value in image.metadata
- ),
- ]
+ tags.div(id_='info')(*(
+ tags.p(("%s: " % name), value) for name, value in image.metadata.iteritems()
+ )),
+ )
def folder_link (from_page, folder, page=0) :
"""
@@ -82,7 +82,7 @@
if cur_page > 0 else
tags.li(tags.span(html.raw("« Prev"))),
- ((
+ html.container((
# page link
tags.li(folder_page_link(folder, page)(page + 1))
if page != cur_page else
@@ -104,7 +104,7 @@
# render the paginate-view once
paginate = folder_paginate(folder, cur_page)
- return [
+ return html.container(
# title
tags.h1(folder.title) if folder.title else None,
@@ -121,9 +121,9 @@
paginate,
# image thumbnails
- ((
+ html.container(*((
image_link(folder, image, image.html)
- ) for image in folder.images),
+ ) for image in folder.images)),
# lower paginate
paginate,
@@ -132,7 +132,7 @@
tags.p(id='description')(folder.description) if folder.description else None,
# shorturl
- ]
+ )
def breadcrumb_trail (gallery, page) :
"""
@@ -150,7 +150,8 @@
yield html.raw("»")
# link from this page to sub-page
- yield link_from(page, segment)(segment.title)
+ # XXX: title
+ yield link_from(page, segment)(segment.name.title())
def breadcrumb (gallery, page) :
@@ -159,7 +160,7 @@
"""
return tags.div(id='breadcrumb')(
- breadcrumb_trail(gallery, page)
+ *breadcrumb_trail(gallery, page)
)
def master (gallery, title, page, body) :
@@ -168,14 +169,14 @@
"""
return html.XHTMLDocument(
- head=[
+ head=html.container(
tags.title(title),
# stylesheet
tags.link(rel='Stylesheet', type='text/css', href=gallery.stylesheet.path_from(page))
- ],
+ ),
- body=[
+ body=html.container(
# top-of-page breadcrumb nav
breadcrumb(gallery, page),
@@ -184,6 +185,6 @@
# footer
tags.p(id='about')(tags.a(href='http://projects.qmsk.net/degal')('Degal'), gallery.version)
- ],
+ ),
)