~ubuntu-branches/ubuntu/precise/z3c.pt/precise

« back to all changes in this revision

Viewing changes to src/z3c/pt/README.txt

  • Committer: Package Import Robot
  • Author(s): Gediminas Paulauskas
  • Date: 2012-02-03 16:03:32 UTC
  • Revision ID: package-import@ubuntu.com-20120203160332-y8iyshk0u8rn4w37
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
z3c.pt
 
2
======
 
3
 
 
4
This section demonstrates the high-level template classes. All page
 
5
template classes in ``z3c.pt`` use path-expressions by default.
 
6
 
 
7
Page templates
 
8
--------------
 
9
 
 
10
  >>> from z3c.pt.pagetemplate import PageTemplate
 
11
  >>> from z3c.pt.pagetemplate import PageTemplateFile
 
12
 
 
13
The ``PageTemplate`` class is initialized with a string.
 
14
 
 
15
  >>> template = PageTemplate("""\
 
16
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
17
  ...   Hello World!
 
18
  ... </div>""")
 
19
 
 
20
  >>> print template()
 
21
  <div xmlns="http://www.w3.org/1999/xhtml">
 
22
    Hello World!
 
23
  </div>
 
24
 
 
25
The ``PageTemplateFile`` class is initialized with an absolute
 
26
path to a template file on disk.
 
27
 
 
28
  >>> template_file = PageTemplateFile('tests/helloworld.pt')
 
29
  >>> print template_file()
 
30
  <div xmlns="http://www.w3.org/1999/xhtml">
 
31
    Hello World!
 
32
  </div>
 
33
 
 
34
  >>> import os
 
35
  >>> template_file.filename.startswith(os.sep)
 
36
  True
 
37
 
 
38
If a ``content_type`` is not informed and one is not present in the
 
39
request, it will be set to 'text/html'.
 
40
 
 
41
  >>> class Response(object):
 
42
  ...     def __init__(self):
 
43
  ...         self.headers = {}
 
44
  ...         self.getHeader = self.headers.get
 
45
  ...         self.setHeader = self.headers.__setitem__
 
46
 
 
47
  >>> class Request(object):
 
48
  ...     def __init__(self):
 
49
  ...         self.response = Response()
 
50
 
 
51
  >>> template_file = PageTemplateFile('tests/helloworld.pt')
 
52
  >>> request = Request()
 
53
  >>> print request.response.getHeader('Content-Type')
 
54
  None
 
55
 
 
56
  >>> template = template_file.bind(None, request=request)
 
57
  >>> print template()
 
58
  <div xmlns="http://www.w3.org/1999/xhtml">
 
59
    Hello World!
 
60
  </div>
 
61
 
 
62
  >>> print request.response.getHeader('Content-Type')
 
63
  text/html
 
64
 
 
65
If a ``content_type`` is present in the request, then we don't override it.
 
66
 
 
67
  >>> request = Request()
 
68
  >>> request.response.setHeader('Content-Type', 'text/xml')
 
69
  >>> print request.response.getHeader('Content-Type')
 
70
  text/xml
 
71
 
 
72
  >>> template = template_file.bind(None, request=request)
 
73
  >>> print template()
 
74
  <div xmlns="http://www.w3.org/1999/xhtml">
 
75
    Hello World!
 
76
  </div>
 
77
 
 
78
  >>> print request.response.getHeader('Content-Type')
 
79
  text/xml
 
80
 
 
81
A ``content_type`` can be also set at instantiation time, and it will
 
82
be respected.
 
83
 
 
84
  >>> template_file = PageTemplateFile('tests/helloworld.pt',
 
85
  ...                                  content_type='application/rdf+xml')
 
86
 
 
87
  >>> request = Request()
 
88
  >>> print request.response.getHeader('Content-Type')
 
89
  None
 
90
 
 
91
  >>> template = template_file.bind(None, request=request)
 
92
  >>> print template()
 
93
  <div xmlns="http://www.w3.org/1999/xhtml">
 
94
    Hello World!
 
95
  </div>
 
96
 
 
97
  >>> print request.response.getHeader('Content-Type')
 
98
  application/rdf+xml
 
99
 
 
100
Both may be used as class attributes (properties).
 
101
 
 
102
  >>> class MyClass(object):
 
103
  ...     template = PageTemplate("""\
 
104
  ...       <div xmlns="http://www.w3.org/1999/xhtml">
 
105
  ...          Hello World!
 
106
  ...       </div>""")
 
107
  ...
 
108
  ...     template_file = PageTemplateFile('tests/helloworld.pt')
 
109
 
 
110
  >>> instance = MyClass()
 
111
  >>> print instance.template()
 
112
  <div xmlns="http://www.w3.org/1999/xhtml">
 
113
    Hello World!
 
114
  </div>
 
115
 
 
116
  >>> print instance.template_file()
 
117
  <div xmlns="http://www.w3.org/1999/xhtml">
 
118
    Hello World!
 
119
  </div>
 
120
 
 
121
View page templates
 
122
-------------------
 
123
 
 
124
  >>> from z3c.pt.pagetemplate import ViewPageTemplate
 
125
  >>> from z3c.pt.pagetemplate import ViewPageTemplateFile
 
126
 
 
127
  >>> class View(object):
 
128
  ...     request = u'request'
 
129
  ...     context = u'context'
 
130
  ...
 
131
  ...     def __repr__(self):
 
132
  ...         return 'view'
 
133
 
 
134
  >>> view = View()
 
135
 
 
136
As before, we can initialize view page templates with a string (here
 
137
incidentally loaded from disk).
 
138
 
 
139
  >>> from z3c.pt import tests
 
140
  >>> path = tests.__path__[0]
 
141
  >>> template = ViewPageTemplate(
 
142
  ...     open(path + '/view.pt').read())
 
143
 
 
144
To render the template in the context of a view, we bind the template
 
145
passing the view as an argument (view page templates derive from the
 
146
``property``-class and are usually defined as an attribute on a view
 
147
class).
 
148
 
 
149
  >>> print template.bind(view)(test=u'test')
 
150
  <div xmlns="http://www.w3.org/1999/xhtml">
 
151
    <span>view</span>
 
152
    <span>context</span>
 
153
    <span>request</span>
 
154
    <span>test</span>
 
155
    <span>test</span>
 
156
  </div>
 
157
 
 
158
The exercise is similar for the file-based variant.
 
159
 
 
160
  >>> template = ViewPageTemplateFile('tests/view.pt')
 
161
  >>> print template.bind(view)(test=u'test')
 
162
  <div xmlns="http://www.w3.org/1999/xhtml">
 
163
    <span>view</span>
 
164
    <span>context</span>
 
165
    <span>request</span>
 
166
    <span>test</span>
 
167
    <span>test</span>
 
168
  </div>
 
169
 
 
170
For compatibility reasons, view templates may be called with an
 
171
alternative context and request.
 
172
 
 
173
  >>> print template(view, u"alt_context", "alt_request", test=u'test')
 
174
  <div xmlns="http://www.w3.org/1999/xhtml">
 
175
    <span>view</span>
 
176
    <span>alt_context</span>
 
177
    <span>alt_request</span>
 
178
    <span>test</span>
 
179
    <span>test</span>
 
180
  </div>
 
181
 
 
182
 
 
183
Non-keyword arguments
 
184
---------------------
 
185
 
 
186
These are passed in as ``options/args``, when using the ``__call__`` method.
 
187
 
 
188
  >>> print PageTemplate("""\
 
189
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
190
  ...   <div tal:repeat="arg options/args">
 
191
  ...      <span tal:content="arg" />
 
192
  ...   </div>
 
193
  ... </div>""").__call__(1, 2, 3)
 
194
  <div xmlns="http://www.w3.org/1999/xhtml">
 
195
    <div>
 
196
       <span>1</span>
 
197
    </div>
 
198
    <div>
 
199
       <span>2</span>
 
200
    </div>
 
201
    <div>
 
202
       <span>3</span>
 
203
    </div>
 
204
  </div>
 
205
 
 
206
 
 
207
Global 'path' Function
 
208
----------------------
 
209
 
 
210
Just like ``zope.pagetemplate``, it is possible to use a globally
 
211
defined ``path()`` function in a ``python:`` expression in ``z3c.pt``:
 
212
 
 
213
  >>> template = PageTemplate("""\
 
214
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
215
  ...   <span tal:content="options/test" />
 
216
  ...   <span tal:content="python: path('options/test')" />
 
217
  ... </div>""")
 
218
 
 
219
  >>> print template(test='test')
 
220
  <div xmlns="http://www.w3.org/1999/xhtml">
 
221
    <span>test</span>
 
222
    <span>test</span>
 
223
  </div>
 
224
 
 
225
Global 'exists' Function
 
226
------------------------
 
227
 
 
228
The same applies to the ``exists()`` function:
 
229
 
 
230
  >>> template = PageTemplate("""\
 
231
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
232
  ...   <span tal:content="python: exists('options/test') and 'Yes' or 'No'" />
 
233
  ... </div>""")
 
234
 
 
235
  >>> print template(test='test')
 
236
  <div xmlns="http://www.w3.org/1999/xhtml">
 
237
    <span>Yes</span>
 
238
  </div>
 
239
 
 
240
'default' and path expressions
 
241
------------------------------
 
242
 
 
243
Another feature from standard ZPT: using 'default' means whatever the
 
244
the literal HTML contains will be output if the condition is not met.
 
245
 
 
246
This works for attributes:
 
247
 
 
248
  >>> template = PageTemplate("""\
 
249
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
250
  ...   <span tal:attributes="class options/not-existing | default"
 
251
  ...         class="blue">i'm blue</span>
 
252
  ... </div>""")
 
253
 
 
254
  >>> print template()
 
255
  <div xmlns="http://www.w3.org/1999/xhtml">
 
256
    <span class="blue">i'm blue</span>
 
257
  </div>
 
258
 
 
259
And also for contents:
 
260
 
 
261
  >>> template = PageTemplate("""\
 
262
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
263
  ...   <span tal:content="options/not-existing | default">default content</span>
 
264
  ... </div>""")
 
265
 
 
266
  >>> print template()
 
267
  <div xmlns="http://www.w3.org/1999/xhtml">
 
268
    <span>default content</span>
 
269
  </div>
 
270
 
 
271
'exists'-type expression
 
272
------------------------
 
273
 
 
274
Using 'exists()' function on non-global name and global name:
 
275
 
 
276
  >>> template = PageTemplate("""\
 
277
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
278
  ...   <span tal:content="python: exists('options/nope') and 'Yes' or 'No'">do I exist?</span>
 
279
  ...   <span tal:content="python: exists('nope') and 'Yes' or 'No'">do I exist?</span>
 
280
  ... </div>""")
 
281
 
 
282
  >>> print template()
 
283
  <div xmlns="http://www.w3.org/1999/xhtml">
 
284
    <span>No</span>
 
285
    <span>No</span>
 
286
  </div>
 
287
 
 
288
Using 'exists:' expression on non-global name and global name
 
289
 
 
290
  >>> template = PageTemplate("""\
 
291
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
292
  ...   <span tal:define="yup exists:options/nope" 
 
293
  ...         tal:content="python: yup and 'Yes' or 'No'">do I exist?</span>
 
294
  ...   <span tal:define="yup exists:nope" 
 
295
  ...         tal:content="python: yup and 'Yes' or 'No'">do I exist?</span>
 
296
  ... </div>""")
 
297
 
 
298
  >>> print template()
 
299
  <div xmlns="http://www.w3.org/1999/xhtml">
 
300
    <span>No</span>
 
301
    <span>No</span>
 
302
  </div>
 
303
 
 
304
Using 'exists:' in conjunction with a negation:
 
305
 
 
306
  >>> print PageTemplate("""\
 
307
  ... <div xmlns="http://www.w3.org/1999/xhtml">
 
308
  ...   <span tal:condition="not:exists:options/nope">I don't exist?</span>
 
309
  ... </div>""")()
 
310
  <div xmlns="http://www.w3.org/1999/xhtml">
 
311
    <span>I don't exist?</span>
 
312
  </div>
 
313
 
 
314
path expression with dictionaries
 
315
---------------------------------
 
316
 
 
317
Path expressions give preference to dictionary items instead of
 
318
dictionary attributes.
 
319
 
 
320
  >>> print PageTemplate("""\
 
321
  ... <div xmlns="http://www.w3.org/1999/xhtml"
 
322
  ...      tal:define="links python:{'copy':'XXX', 'delete':'YYY'}">
 
323
  ...   <span tal:content="links/copy">ZZZ</span>
 
324
  ... </div>""")()
 
325
  <div xmlns="http://www.w3.org/1999/xhtml">
 
326
    <span>XXX</span>
 
327
  </div>
 
328
 
 
329
 
 
330
Variable from one tag never leak into another
 
331
---------------------------------------------
 
332
 
 
333
  >>> body = """\
 
334
  ... <div xmlns="http://www.w3.org/1999/xhtml"
 
335
  ...      xmlns:tal="http://xml.zope.org/namespaces/tal"
 
336
  ...      xmlns:metal="http://xml.zope.org/namespaces/metal">
 
337
  ...   <div class="macro" metal:define-macro="greeting"
 
338
  ...        tal:define="greeting greeting|string:'Hey'">
 
339
  ...       <span tal:replace="greeting" />
 
340
  ...   </div>
 
341
  ...   <div tal:define="greeting string:'Hello'">
 
342
  ...     <metal:block metal:use-macro="python:template.macros['greeting']" />
 
343
  ...   </div>
 
344
  ...   <div>
 
345
  ...     <metal:block metal:use-macro="python:template.macros['greeting']" />
 
346
  ...   </div>
 
347
  ... </div>"""
 
348
  >>> print PageTemplate(body)()
 
349
  <div xmlns="http://www.w3.org/1999/xhtml">
 
350
    <div class="macro">
 
351
        'Hey'
 
352
    </div>
 
353
    <div>
 
354
      <div class="macro">
 
355
        'Hello'
 
356
    </div>
 
357
  <BLANKLINE>
 
358
  </div>
 
359
    <div>
 
360
      <div class="macro">
 
361
        'Hey'
 
362
    </div>
 
363
  <BLANKLINE>
 
364
  </div>
 
365
  </div>
 
366
 
 
367
 
 
368
 
 
369
TALES Function Namespaces
 
370
-------------------------
 
371
 
 
372
As described on http://wiki.zope.org/zope3/talesns.html, it is
 
373
possible to implement custom TALES Namespace Adapters. We also support
 
374
low-level TALES Function Namespaces (which the TALES Namespace
 
375
Adapters build upon).
 
376
 
 
377
  >>> import datetime
 
378
  >>> import zope.interface
 
379
  >>> import zope.component
 
380
  >>> from zope.traversing.interfaces import ITraversable
 
381
  >>> from zope.traversing.interfaces import IPathAdapter
 
382
  >>> from zope.tales.interfaces import ITALESFunctionNamespace
 
383
  >>> from z3c.pt.namespaces import function_namespaces
 
384
 
 
385
  >>> class ns1(object):
 
386
  ...     zope.interface.implements(ITALESFunctionNamespace)
 
387
  ...     def __init__(self, context):
 
388
  ...         self.context = context
 
389
  ...     def parent(self):
 
390
  ...         return self.context.parent
 
391
 
 
392
  >>> function_namespaces.registerFunctionNamespace('ns1', ns1)
 
393
 
 
394
  >>> class ns2(object):
 
395
  ...     def __init__(self, context):
 
396
  ...         self.context = context
 
397
  ...     def upper(self):
 
398
  ...         return self.context.upper()
 
399
 
 
400
  >>> zope.component.getGlobalSiteManager().registerAdapter(
 
401
  ...     ns2, [zope.interface.Interface], IPathAdapter, 'ns2')
 
402
 
 
403
  >>> class ns3(object):
 
404
  ...     def __init__(self, context):
 
405
  ...         self.context = context
 
406
  ...     def fullDateTime(self):
 
407
  ...         return self.context.strftime('%Y-%m-%d %H:%M:%S')
 
408
 
 
409
  >>> zope.component.getGlobalSiteManager().registerAdapter(
 
410
  ...     ns3, [zope.interface.Interface], IPathAdapter, 'ns3')
 
411
 
 
412
 
 
413
A really corner-ish case from a legacy application: the TALES
 
414
Namespace Adapter doesn't have a callable function but traverses the
 
415
remaining path instead::
 
416
 
 
417
  >>> from zope.traversing.interfaces import TraversalError
 
418
 
 
419
  >>> class ns4(object):
 
420
  ...     zope.interface.implements(ITraversable)
 
421
  ...
 
422
  ...     def __init__(self, context):
 
423
  ...         self.context = context
 
424
  ...
 
425
  ...     def traverse(self, name, furtherPath):
 
426
  ...         if name == 'page':
 
427
  ...             if len(furtherPath) == 1:
 
428
  ...                 pagetype = furtherPath.pop()
 
429
  ...             elif not furtherPath:
 
430
  ...                 pagetype = 'default'
 
431
  ...             else:
 
432
  ...                 raise TraversalError("Max 1 path segment after ns4:page")
 
433
  ...             return self._page(pagetype)
 
434
  ...         if len(furtherPath) == 1:
 
435
  ...              name = '%s/%s' % (name, furtherPath.pop())
 
436
  ...         return 'traversed: ' + name
 
437
  ...
 
438
  ...     def _page(self, pagetype):
 
439
  ...         return 'called page: ' + pagetype
 
440
 
 
441
  >>> zope.component.getGlobalSiteManager().registerAdapter(
 
442
  ...     ns4, [zope.interface.Interface], IPathAdapter, 'ns4')
 
443
 
 
444
  >>> class ns5(object):
 
445
  ...     zope.interface.implements(ITraversable)
 
446
  ...
 
447
  ...     def __init__(self, context):
 
448
  ...         self.context = context
 
449
  ...
 
450
  ...     def traverse(self, name, furtherPath):
 
451
  ...         name = '/'.join([name] + furtherPath[::-1])
 
452
  ...         del furtherPath[:]
 
453
  ...         return 'traversed: ' + name
 
454
 
 
455
  >>> zope.component.getGlobalSiteManager().registerAdapter(
 
456
  ...     ns5, [zope.interface.Interface], IPathAdapter, 'ns5')
 
457
 
 
458
  >>> class Ob(object):
 
459
  ...     def __init__(self, title, date, parent=None, child=None):
 
460
  ...         self.title = title
 
461
  ...         self.date = date
 
462
  ...         self.parent = parent
 
463
  ...         self.child = child
 
464
 
 
465
  >>> child = Ob('child', datetime.datetime(2008, 12, 30, 13, 48, 0, 0))
 
466
  >>> father = Ob('father', datetime.datetime(1978, 12, 30, 13, 48, 0, 0))
 
467
  >>> grandpa = Ob('grandpa', datetime.datetime(1948, 12, 30, 13, 48, 0, 0))
 
468
 
 
469
  >>> child.parent = father
 
470
  >>> father.child = child
 
471
  >>> father.parent = grandpa
 
472
  >>> grandpa.child = father
 
473
 
 
474
  >>> class View(object):
 
475
  ...     request = u'request'
 
476
  ...     context = father
 
477
  ...
 
478
  ...     def __repr__(self):
 
479
  ...         return 'view'
 
480
 
 
481
  >>> view = View()
 
482
  >>> template = ViewPageTemplateFile('tests/function_namespaces.pt')
 
483
  >>> print template.bind(view)()
 
484
  <div xmlns="http://www.w3.org/1999/xhtml">
 
485
    <span>GRANDPA</span>
 
486
    <span>2008-12-30 13:48:00</span>
 
487
    <span>traversed: link:main</span>
 
488
    <span>called page: default</span>
 
489
    <span>called page: another</span>
 
490
    <span></span>
 
491
    <span>traversed: zope.Public</span>
 
492
    <span>traversed: text-to-html</span>
 
493
    <span>traversed: page/yet/even/another</span>
 
494
  </div>