Skip to main content

Zope Resource Templates

Project description

This package provides a very simple templating system for non-SGML data files such as CSS and Javascript.

Templated Resources

One of the design goals of Zope is to allow designers to check in HTML template, CSS and Javascript files, which just work (with some additional information). For HTML code we use Zope’s Page Templates to accomplish this objective. For CSS and Javascript we did not need such a feature until now, since those files were largely static or variables could be inserted using other ways at runtime.

However, in CSS URLs – for example for background images – are now frequently inserted into CSS directives. However, the path layout for the designer might not equal the resource file structure. This package provides a simple mechanism to replace strings by another.

To accomplish this, a templated resource is provided. The template syntax is provided in a way that it does not interfere with the syntax of the resource. For both, Javascript and CSS, this is a comment of the form /* ... */.

Here is the general syntax:

<COMMAND-BEGIN> <ZRT-COMMAND>: <COMMAND-ARGUMENTS> <COMMAND-END>

Here is an example for CSS:

/* zrt-replace: ".." "@@" */

To demonstrate this feature, we first have to create a CSS file.

>>> import tempfile
>>> fn = tempfile.mktemp('.css')
>>> open(fn, 'w').write('''\
... /* zrt-replace: "../img1" "++resource++/img" */
... /* zrt-replace: "fontName" "Arial, Tahoma" */
... h1 {
...   color: red;
...   font: fontName;
...   background: url('../img1/mybackground.gif');
... }
...
... h2 {
...   color: red;
...   background: url('../img2/mybackground.gif');
... }
... /* zrt-replace: "../img2" "++resource++/img" */
... ''')

The global replace command replaces a string with another. It is only active in the lines after it was declared. Thus, in this case, the second command is meaningless.

Now we create a ZRT resource from the resource factory …

>>> from z3c.zrtresource import ZRTFileResourceFactory
>>> cssFactory = ZRTFileResourceFactory(fn, None, 'site.css')
>>> from zope.publisher.browser import TestRequest
>>> css = cssFactory(TestRequest())

and render the resource:

>>> print css.GET()
h1 {
  color: red;
  font: Arial, Tahoma;
  background: url('++resource++/img/mybackground.gif');
}
<BLANKLINE>
h2 {
  color: red;
  background: url('../img2/mybackground.gif');
}

As you can see only the first URL was replaced, because of the incorrect position of the second statement.

And that’s all! In your ZCML you can use this factory as follows:

<zrt-resource
    name="site.css"
    path="css/site.css"
    />

Replacing Strings

The zrt-replace command replaces any matches with the output string as many times as specified. Here is the syntax:

zrt-replace: <EXPR-TYPE>”<INPUT-EXPR>” <EXPR-TYPE>”<OUTPUT-EXPR>” <NUM>

As seen in the example above, zrt-replace calls can be placed anywhere in the file. Let’s make sure that some special cases work as well:

>>> from z3c.zrtresource import processor, replace
>>> def process(text):
...     p = processor.ZRTProcessor(
...         text, commands={'replace': replace.Replace})
...     return p.process(None, None)
>>> print process('''\
...        /* zrt-replace: "foo" "bar" */
... foo''')
bar
>>> print process('''\
... /*      zrt-replace: "foo" "bar"      */
... foo''')
bar
>>> print process('''\
... /* zrt-replace:   "foo"         "bar" */
... foo''')
bar

But the following does not work:

>>> print process('''\
... /* zrt-replace : "foo" "bar" */
... foo''')
/* zrt-replace : "foo" "bar" */
foo
>>> print process('''\
... /* zrt -replace : "foo" "bar" */
... foo''')
/* zrt -replace : "foo" "bar" */
foo

Until now we have only considered multiple replacements. Let’s now restrict the number of replacements with the final argument. Initially all occurences of a matching string are replaced:

>>> print process('''\
... /* zrt-replace: "foo" "bar" */
... foo foo foo foo foo''')
bar bar bar bar bar

When we specify a number of replacements, then only that amount is replaced:

>>> print process('''\
... /* zrt-replace: "foo" "bar" 1 */
... foo foo foo foo foo''')
bar foo foo foo foo
>>> print process('''\
... /* zrt-replace: "foo" "bar" 3 */
... foo foo foo foo foo''')
bar bar bar foo foo
>>> print process('''\
... /* zrt-replace: "foo" "bar" 6 */
... foo foo foo foo foo''')
bar bar bar bar bar

The String Expression

Until now we have only dealt with simple string replacement, since it is the default expression type. Another way of spelling the expression type is:

>>> print process('''\
... /* zrt-replace: str"foo" "bar" */
... foo''')
bar
>>> print process('''\
... /* zrt-replace: "foo" str"bar" */
... foo''')
bar
>>> print process('''\
... /* zrt-replace: str"foo" str"bar" */
... foo''')
bar

The Regex Expression

Regular expressions make only sense as input expressions, so they are only supported there:

>>> print process('''\
... /* zrt-replace: re"foo" "bar" */
... foo''')
bar
>>> print process('''\
... /* zrt-replace: re"[a-z]*foo" "bar" */
... myfoo''')
bar

We also support groups:

>>> print process('''\
... /* zrt-replace: re"([a-z]*)foo" "bar" */
... myfoo''')
bar
>>> print process('''\
... /* zrt-replace: re"([a-z]*)foo" "bar" */
... myfoo''')
bar
>>> print process('''\
... /* zrt-replace: re"([a-z]*)foo" "bar" */
... myfoo mybar''')
bar mybar

Yes, even group replacement works:

>>> print process('''\
... /* zrt-replace: re"([a-z]*)foo" "bar\\1" */
... myfoo a9foo''')
barmy a9bar
>>> print process('''\
... /* zrt-replace: re"(?P<prefix>[a-z]*)foo" "bar\\g<prefix>" */
... myfoo a9foo''')
barmy a9bar

The TALES Expression

What would be a Zope-based templating language without TALES expressions? This is particularly useful, if you want to create absolute URLs and other dynamic bits based on the request and the context:

>>> import zope.interface
>>> from zope.traversing.interfaces import IContainmentRoot
>>> class Root(object):
...     zope.interface.implements(IContainmentRoot)
>>> from zope.publisher.browser import TestRequest
>>> def process(text):
...     p = processor.ZRTProcessor(
...         text, commands={'replace': replace.Replace})
...     return p.process(Root(), TestRequest())
>>> print process('''\
... /* zrt-replace: "foo" tal"string:${context/@@absolute_url}/@@/foo" */
... foo''')
http://127.0.0.1/@@/foo

Custom ZRT Command

We can create custom ZRT commands. For this we should register a named IZRTCommandFactory utility

>>> import re
>>> from zope import interface
>>> from zope.component import provideUtility
>>> from z3c.zrtresource import interfaces
>>> class MyCustomCommand(object):
...   interface.implements(interfaces.IZRTCommand)
...
...   data = {'color1': 'red', 'color2': 'green'}
...
...   isAvailable = True
...
...   def __init__(self, args, start, end):
...      self.args = args
...      self.start = start
...      self.end = end
...
...   def process(self, text, context, request):
...      for key, value in self.data.items():
...         regex = re.compile(re.escape(key))
...         text = regex.subn(value, text)[0]
...
...      return text
>>> from zope.component.factory import Factory
>>> my_command = Factory(MyCustomCommand, 'mycommand')
>>> interface.directlyProvides(my_command, interfaces.IZRTCommandFactory)
>>> provideUtility(my_command, interfaces.IZRTCommandFactory, name='mycommand')
>>> open(fn, 'w').write('''\
... /* zrt-replace: "../img1" "++resource++/img" */
... /* zrt-replace: "fontFamily" "Arial, Tahoma" */
... /* zrt-mycommand: */
... /* oh, and we're testing that when the file changes, it is reloaded */
... h1 {
...   color: color1;
...   font: fontFamily;
...   background: url('../img1/mybackground.gif');
... }
...
... h2 {
...   color: color2;
...   background: url('../img2/mybackground.gif');
... }
... /* zrt-replace: "../img2" "++resource++/img" */
... ''')

We have to recreate the ZRTFileResourceFactory to reload the changed file contents (don’t worry – in real life Zope creates these anew for every request, since resources are actually registered as IResourceFactoryFactory utilities).

>>> cssFactory = ZRTFileResourceFactory(fn, None, 'site.css')
>>> css = cssFactory(TestRequest())
>>> print css.GET()
/* oh, and we're testing that when the file changes, it is reloaded */
h1 {
  color: red;
  font: Arial, Tahoma;
  background: url('++resource++/img/mybackground.gif');
}
<BLANKLINE>
h2 {
  color: green;
  background: url('../img2/mybackground.gif');
}
<BLANKLINE>

Edge case

On windows blank lines were left in the result

>>> open(fn, 'w').write('''\
... some-head
... /* zrt-replace: "../img1" "++resource++/img" */
... /* zrt-replace: "fontName" "Arial, Tahoma" */
... some-in-the-middle
... /* zrt-replace: "../img2" "++resource++/img" */
... some-at-the-end
... ''')
>>> cssFactory = ZRTFileResourceFactory(fn, None, 'site.css')
>>> from zope.publisher.browser import TestRequest
>>> css = cssFactory(TestRequest())

and render the resource:

>>> css.GET().splitlines()
['some-head', 'some-in-the-middle', 'some-at-the-end']

zrt-resource Directive

This package provides a new directive to use the special resource directive. Let’s register it first:

>>> from zope.configuration import xmlconfig
>>> context = xmlconfig.string('''
... <configure i18n_domain="zope">
...   <include package="z3c.zrtresource" file="meta.zcml" />
... </configure>
... ''')

Now we can register a resource:

>>> import tempfile
>>> fn = tempfile.mktemp('.css')
>>> open(fn, 'w').write('''\
... /* zrt-replace: "../img1" "++resource++/img" */
... h1 {
...   color: red;
...   background: url('../img1/mybackground.gif');
... }
... ''')
>>> context = xmlconfig.string('''
... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
...   <zrt-resource
...       name="test.css"
...       file="%s" />
... </configure>
... ''' %fn, context=context)

Now let’s see whether the adapter has been registered:

>>> import zope.component
>>> import zope.interface
>>> from zope.publisher.browser import TestRequest
>>> resource = zope.component.getAdapter(
...     TestRequest(), zope.interface.Interface, name='test.css')

Now run it:

>>> print resource.GET()
h1 {
  color: red;
  background: url('++resource++/img/mybackground.gif');
}

We can also register a ZRT resource using standard browser:resource directive. The configure.zcml file in this package registers a ZRT resource factory for files with “zrt” extension, so any file with “zrt” extension will be a ZRT resource.

First, let’s include the browser:resource directive and the resource factory registration:

>>> context = xmlconfig.string('''
... <configure i18n_domain="zope">
...   <include package="zope.browserresource" file="meta.zcml" />
...   <include package="zope.component" file="meta.zcml" />
...   <include package="z3c.zrtresource" />
... </configure>
... ''')

Now we need to create a file with “zrt” extension:

>>> fn = tempfile.mktemp('.zrt')
>>> open(fn, 'w').write('''\
... /* zrt-replace: "../img1" "++resource++/img" */
... h1 {
...   color: red;
...   background: url('../img1/mybackground.gif');
... }
... ''')

And register it as a resource using browser:resource directive.

>>> context = xmlconfig.string('''
... <configure xmlns="http://namespaces.zope.org/browser" i18n_domain="zope">
...   <resource
...       name="test2.css"
...       file="%s" />
... </configure>
... ''' %fn, context=context)

Let’s see whether the adapter has been registered:

>>> resource2 = zope.component.getAdapter(
...     TestRequest(), zope.interface.Interface, name='test2.css')

Now, let’s render it and check if ZRT mechanism works for it.

>>> print resource2.GET()
h1 {
  color: red;
  background: url('++resource++/img/mybackground.gif');
}

CHANGES

1.4.0 (2013-05-31)

  • Made tests compatible with zope.browserresource 3.11, thus requiring at least this version.

  • Fix: work around windows: it has a 2 char next line sequence

1.3.0 (2009-08-27)

  • Use new zope.browserresource package instead of zope.app.publisher, as the resources mechanism was moved there to reduce dependencies.

  • Register ZRTFileResourceFactory as a resource factory with name “zrt” in package’s configure.zcml. This makes ZRT resources created automatically when using browser:resource directive for files with “zrt” extensions. It will also make *.zrt files inside resourse directories a ZRTFileResource.

  • Get rid of zope.app.testing test dependency.

  • Remove unused SETUP.CFG and z3c.zrtresource-meta.zcml files.

1.2.0 (2009-06-25)

  • Got rid of dependency on zope.app.component and zope.app.pagetemplate.

  • Fixed home page and author email address.

1.1.0 (2007-12-01)

  • Fix bug with spaces in replace expression

  • Added custom ZRT commands

1.0.1 (2007-10-30)

  • Fix long description of package to be valid restructured text.

1.0.0 (2007-10-30)

  • Initial release.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

z3c.zrtresource-1.4.0.zip (30.7 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page