This tests the middleware, using a basically static site and applying themes. First we'll setup the site, using urlmap: >>> from paste.urlmap import URLMap >>> from webob import Request, Response >>> app = URLMap() A theme: >>> app['/theme.html'] = Response('''\ ... ... ... This is a theme title ... ... ... ... ... ... ...
... ...
... This content will be replaced. ...
... Back to top ...
... ... ... ... ... ''') The rule xml: >>> rule_xml = '''\ ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ''' Rule files can be published and fetched with a subrequest: >>> app['/mytheme/rules.xml'] = Response(rule_xml, content_type="application/xml") Rule files can also be read directly from the filesystem. Here's one: >>> import tempfile >>> rule_filename_pos, rule_filename = tempfile.mkstemp() >>> f = open(rule_filename, 'w+') >>> f.write(rule_xml) >>> f.close() Now let's set up some pages for Deliverance to work with: >>> app['/blog/index.html'] = Response('''\ ... A blog post ... ... ... ... Some junk ...
the blog post with some style
... some more junk ... ...
Creative Commons License
... ... ''') >>> app['/about.html'] = Response('''\ ... About this site ... ... This is all about this site. ... ... ... ''') >>> app['/magic'] = Response('''\ ... A simple page''') >>> app['/magic'].headers['x-no-deliverate'] = '1' >>> app['/magic2'] = Response('''\ ... something''') >>> app['/foo'] = Response('''\ ... ...
Good!
...
Bad.
''') We'll set up one DeliveranceMiddleware using the published rules, and another using the rule file: >>> from deliverance.middleware import DeliveranceMiddleware, FileRuleGetter, SubrequestRuleGetter >>> from deliverance.log import PrintingLogger >>> import logging >>> deliv_filename = DeliveranceMiddleware(app, FileRuleGetter(rule_filename), ... PrintingLogger, ... log_factory_kw=dict(print_level=logging.WARNING)) >>> deliv_url = DeliveranceMiddleware(app, SubrequestRuleGetter('http://localhost/mytheme/rules.xml'), ... PrintingLogger, ... log_factory_kw=dict(print_level=logging.WARNING)) Now let's look at some plain content and its deliverated equivalent. Here's a helper function to make the content easy to compare: >>> def compare_request(path, deliv): ... # work around WebOb bug fixed here: ... # http://bitbucket.org/ianb/webob/changeset/b8671bd53cf4/ ... app[path].body = app[path].body ... ... raw_res = Request.blank(path).get_response(app) ... result = 'Original content:\n' + raw_res.body.strip() ... themed_res = Request.blank(path).get_response(deliv) ... result += '\nThemed content:\n' + themed_res.body.strip() ... return result First we'll look at the blog, fairly simple: >>> print compare_request('/blog/index.html', deliv_filename) # doctest: +REPORT_UDIFF Original content: A blog post Some junk
the blog post with some style
some more junk
Creative Commons License
Themed content: A blog post
the blog post with some style
Back to top
Should be the same in both cases: >>> first = compare_request('/blog/index.html', deliv_filename) >>> second = compare_request('/blog/index.html', deliv_url) >>> first == second True Now the about page, with its breakout style: >>> print compare_request('/about.html', deliv_url) # doctest: +REPORT_UDIFF Original content: About this site This is all about this site. Themed content: About this site
This is all about this site.
Now the magic response, which shouldn't get themed at all: >>> print compare_request('/magic', deliv_filename) Original content: A simple page Themed content: A simple page >>> print compare_request('/magic2', deliv_url) Original content: something Themed content: something Deliverance should not blow up if the content response is empty: >>> app['/empty'] = Response('') >>> print compare_request('/empty', deliv_filename) Original content: Themed content: Let's also make sure Deliverance correctly preserves HTML entities: >>> app['/html'] = Response("One … two") >>> print compare_request('/html', deliv_filename) # doctest: +REPORT_UDIFF Original content: One … two Themed content: This is a theme title

One … two

Back to top
When you are using a rule file from the filesystem, it will not be re-read on every request by default. So if we change the rules on the filesystem and make a request through the existing DeliveranceMiddleware, there will be no change. Here we'll remove the theming rules for the blog: >>> new_rule_xml = '''\ ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ''' >>> f = open(rule_filename, 'w+') >>> f.write(new_rule_xml) >>> f.close() >>> print compare_request('/blog/index.html', deliv_filename) # doctest: +REPORT_UDIFF Original content: A blog post Some junk
the blog post with some style
some more junk
Creative Commons License
Themed content: A blog post
the blog post with some style
Back to top
However, if we set always_reload=True in the FileRuleGetter, the rules will be re-read from the file on every request: >>> f = open(rule_filename, 'w+') >>> f.write(rule_xml) >>> f.close() >>> deliv_filename = DeliveranceMiddleware(app, ... FileRuleGetter(rule_filename, always_reload=True), ... PrintingLogger, ... log_factory_kw=dict(print_level=logging.WARNING)) >>> print compare_request('/blog/index.html', deliv_filename) # doctest: +REPORT_UDIFF Original content: A blog post Some junk
the blog post with some style
some more junk
Creative Commons License
Themed content: A blog post
the blog post with some style
Back to top
>>> f = open(rule_filename, 'w+') >>> f.write(new_rule_xml) >>> f.close() >>> print compare_request('/blog/index.html', deliv_filename) # doctest: +REPORT_UDIFF Original content: A blog post Some junk
the blog post with some style
some more junk
Creative Commons License
Themed content: A blog post
Some junk
the blog post with some style
some more junk
Creative Commons License
Back to top
The content's DOCTYPE should be respected. So if the content's DOCTYPE is XHTML, the merged output should preserve that DOCTYPE, and self-closing tags should be preserved rather than being rewritten as unclosed tags. Let's see: >>> app['/magic'].body = """ ... ... ... ... ... ... A simple page""" >>> del app['/magic'].headers['x-no-deliverate'] Now let's see what happens when Deliverance is done with it: >>> print compare_request('/magic', deliv_filename) # doctest: +REPORT_UDIFF Original content: A simple page Themed content: This is a theme title
A simple page
Back to top
It worked. There's a new id="top" attribute on the tag; it's required for XHTML: http://www.w3.org/TR/xhtml1/#h-4.10 Test that rule matches work: >>> print compare_request('/foo', deliv_filename) # doctest: +REPORT_UDIFF Original content:
Good!
Bad.
Themed content: This is a theme title
Test that HTML comments inside SCRIPT and STYLE tags aren't escaped: >>> app['/scriptcomments'] = Response('''\ ... ... ... ... foo ... ... ''') >>> print compare_request('/scriptcomments', deliv_filename) # doctest: +REPORT_UDIFF Original content: foo Themed content: This is a theme title Works fine; what about XHTML? >>> app['/scriptcomments'] = Response('''\ ... ... ... ... ... foo ... ... ''') >>> print compare_request('/scriptcomments', deliv_filename) # doctest: +REPORT_UDIFF Original content: foo Themed content: This is a theme title CDATA sections in XHTML documents should be preserved! lxml has a tendency to escape the angle brackets that start and end a CDATA section in XHTML documents (but not HTML) -- so Deliverance will munge the markers that start and end CDATA sections before passing the documents to lxml, and then unmunge them after getting a merged string back from lxml. Let's make sure they're properly preserved in both theme and content documents: >>> app['/cdata'] = Response(''' ... ... ... ... ... ... ... ... ... ... ''') >>> app['/theme.html'] = Response('''\ ... ... ... This is a theme title ... ... ... ... ... ... ...
... ...
... This content will be replaced. ...
... Back to top ...
... ... ... ... ... ''') >>> print compare_request('/cdata', deliv_filename) # doctest: +REPORT_UDIFF Original content: Themed content:
Back to top
We should also see what happens to CDATA sections merged in from external content documents, when the `href` attribute is used in a rule action: >>> new_rule_xml = '''\ ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ''' >>> f = open(rule_filename, 'w+') >>> f.write(new_rule_xml) >>> f.close() >>> app['/newfooter'] = Response('''\ ... ... ... ... ... ... ''') >>> print compare_request('/cdata', deliv_filename) # doctest: +REPORT_UDIFF Original content: Themed content:
Back to top
Note that there's a small chance of false positives, if a document happens to contain the markers that we use internally for the CDATA start and end: >>> app['/newfooter'] = Response('''\ ... ... ... ... ... ... ''') >>> print compare_request('/cdata', deliv_filename) # doctest: +REPORT_UDIFF Original content: Themed content:
Back to top
lxml will properly parse html documents only if the meta tag with charset declaration occurs before any chars outside ASCII (per the HTML spec). To play nicely with content that breaks that assumption, Deliverance will move the charset declaration before passing the document to lxml, to make sure the resulting content isn't mangled. >>> app['/reddot'] = Response('''\ ... ... \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e ... ... ... \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e ... ... ''') >>> print compare_request('/reddot', deliv_filename) # doctest: +REPORT_UDIFF Original content: 日本語 日本語 Themed content: 日本語
日本語
Back to top
>>> app['/reddot'] = Response('''\ ... ... \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e ... ... ... \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e ... ... ''') >>> print compare_request('/reddot', deliv_filename) # doctest: +REPORT_UDIFF Original content: 日本語 日本語 Themed content: 日本語
日本語
Back to top
Let's test that for the theme document also. We'll put the Japanese title and misplaced charset declaration in the theme instead of the content. The resulting title should be the correct HTML sequence: >>> app['/reddot'] = Response('''\ ... ... ... foo ... ... ''') >>> app['/theme.html'] = Response('''\ ... ... ... \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e ... ... ... ... ... ... ... ...
... ...
... This content will be replaced. ...
... Back to top ...
... ... ... ... ... ''') >>> print compare_request('/reddot', deliv_filename) # doctest: +REPORT_UDIFF Original content: foo Themed content: 日本語 Some non-ASCII characters can end up mangled when they pass through lxml.html:: >>> from lxml.html import fromstring, tostring >>> x = "…" >>> print tostring(fromstring(x)) … That should have been "…", which is the HTML code for the ellipsis character. The way to fix this is to decode the string to unicode before lxml.html gets it:: >>> print tostring(fromstring(x.decode('utf8'))) … So, internally, Deliverance will use webob.Response.unicode_body, which uses the response's charset to figure out how to decode the string. Let's make sure that these characters aren't mangled when they are themed through Deliverance:: >>> app['/mangled'] = Response('''\ ... ... ... … ... ... ''') >>> print compare_request('/mangled', deliv_filename) # doctest: +REPORT_UDIFF Original content: … Themed content: 日本語 By default Deliverance keeps track of the source of elements in the theme. If content elements are merged into the theme, later actions won't be able to find those elements in the theme. So in the following case:: >>> new_rule_xml = '''\ ... ... ... ... ... ... ... ''' >>> f = open(rule_filename, 'w+') >>> f.write(new_rule_xml) >>> f.close() >>> app['/collapse-theme'] = Response(""" ... ...
Theme div
...
Another theme div
... Fleem ... """) >>> app['/collapse-content'] = Response(""" ... ...
Content div!
... """) The
Content div!
from the content source will remain in the output, since the action only applies to elements that were in the original theme, even though that div has landed in the theme by the time the action occurs:: >>> print compare_request('/collapse-content', deliv_filename) # doctest: +REPORT_UDIFF Original content:
Content div!
Themed content: Fleem
Content div!
It is occasionally useful to tell Deliverance to ignore this distinction and act on elements that were placed in the theme by an earlier rule. To do this, use a ````. If this attribute is set for an action, then elements that are moved into the theme during that action will be immediately merged with the theme so that later actions on the theme can act upon them. So in our example, setting ``collapse-sources="1"`` on the action will cause the final output to contain no
s at all:: >>> new_rule_xml = '''\ ... ... ... ... ... ... ... ''' >>> f = open(rule_filename, 'w+') >>> f.write(new_rule_xml) >>> f.close() >>> print compare_request('/collapse-content', deliv_filename) # doctest: +REPORT_UDIFF Original content:
Content div!
Themed content: Fleem