]> git.sesse.net Git - xml-template/blob - doc/intro.txt
218c754292ec3da12caf92756a0153af4a7ee3aa
[xml-template] / doc / intro.txt
1 Introduction to XML::Template (name suggestions welcome)
2
3 [In plain text; HTML coming soon. Note that this is mainly a rough brain
4 dump, even though the code is out there and working; nothing is finished
5 yet, especially as I haven't used it in a bigger project yet.]
6
7 XML::Template is a templating system; there are already many others, so if
8 you do not like it, look into a different one (its design is inspired by
9 at least Template Toolkit and Kid, probably also including elements from
10 others). XML::Template is (like Kid or TAL) designed to guarantee that your
11 output is well-formed XML, which is a good step on the road to give you
12 valid XHTML.
13
14 You can get the latest version of XML::Template with bzr; get bzr from your
15 favourite distribution and do "bzr get http://bzr.sesse.net/xml-template/"
16 to check out the code and this documentation.
17
18 There is a lot to be said about design philosophy, but let's first give a
19 simple example to give you the feel of how it works. (The example is in Perl,
20 but there are also functionally equivalent PHP, Python and Ruby versions;
21 ports to other languages would be welcome.)
22
23 Template (simple.xml):
24
25   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
26   <!DOCTYPE
27     html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
28     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
29   <html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://template.sesse.net/" xml:lang="en">
30     <head>
31       <title />
32     </head>
33     <body>
34       <p t:id="hello">This will be replaced.</p>
35     </body>
36   </html>
37
38 Code (simple.pl):
39
40   #! /usr/bin/perl
41   use XML::Template;
42   
43   my $doc = XML::Template::process_file('../xml/simple.xml', {
44         'title' => 'A very basic example',
45         '#hello' => 'Hello world!'
46   });
47   print $doc->toString;
48
49 Result:
50
51   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
52   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
53   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
54     <head>
55       <title>A very basic example</title>
56     </head>
57     <body>
58       <p>Hello world!</p>
59     </body>
60   </html>
61
62 This is about as simple as it gets, but we've already touched on most of the
63 functionality we want or need. A few points are worth commenting on:
64
65  - We template by first _selecting_ certain elements (either by tag name, or
66    by ID -- the syntax is borrowed from CSS since you probably already know
67    it), then _replace_ their contents. (Soon, we'll also _clone_ elements.)
68  - We get a DOM tree out, which we can either print out or do other things
69    with (say, style further if XML::Template should not prove enough).
70    (Actually, we start with a DOM tree as well, but process_file is a
71    shortcut to read in an XML file and parse it into a DOM tree first, since
72    that's usually what we want.)
73  - All traces of our templating system have been removed -- there is a flag
74    you can give to prohibit this "cleaning" in case you don't want that.
75
76 Note how little syntax we need to do simple things -- XML::Template is
77 designed to _keep simple things simple_, since you want to do simple things
78 most of the time. (I don't believe in "lines of code" as the primary metric
79 for API usability in general, though.)
80
81 We move on to another useful operation, cloning.
82
83 Template (clone.xml):
84
85   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
86   <!DOCTYPE
87     html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
88     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
89   <html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://template.sesse.net/" xml:lang="en">
90     <head>
91       <title>Cloning test</title>
92     </head>
93     <body>
94        <p>My favourite color is <t:color />; I like that very much.
95          All my favourite things:</p>
96       <ul t:id="things">
97         <li />
98       </ul>
99     </body>
100   </html>
101
102 Code (clone.pl):
103
104   #! /usr/bin/perl
105   use XML::Template;
106   
107   my $doc = XML::Template::process_file('../xml/clone.xml', {
108         'color' => 'blue',
109         '#things' => [
110                 { 'li' => 'Raindrops on roses' },
111                 { 'li' => 'Whiskers on kittens' },
112                 { 'li' => 'Bright copper kettles' },
113                 { 'li' => 'Warm, woolen mittens'} 
114         ]
115   });
116   print $doc->toString;
117
118 Result:
119
120   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
121   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
122   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
123     <head>
124       <title>Cloning test</title>
125     </head>
126     <body>
127        <p>My favourite color is blue; I like that very much.
128          All my favourite things:</p>
129       <ul>
130         <li>Raindrops on roses</li>
131       
132         <li>Whiskers on kittens</li>
133       
134         <li>Bright copper kettles</li>
135       
136         <li>Warm, woolen mittens</li>
137       </ul>
138     </body>
139   </html>
140
141 This isn't much harder than the example above; we've done a simple replacement
142 of the contents of <t:color> to "blue" (and after that just removed the tag;
143 any tag you use in the templating namespace will automatically get stripped
144 away), and then cloned the contents of our "things" bullet list. Note that
145 XML::Template automatically recurses after the cloning, since you probably
146 don't want four identical elements. You can recurse as many times as you'd like,
147 in case you'd need lists of lists or multiple tables and rows and columns --
148 you don't even have to understand what's happening to get it to work.
149
150 Note that we did all of this without any logic in the template at all. This
151 is completely intentional -- it's a bit of an experiment, really, but hopefully
152 it will all turn out well. There is no logic in the templating system at all;
153 if-s are handled with replacements (or DOM deletions), for-s are handled with
154 cloning and expressions are handled by the language you're using.
155
156 This means we have introduced all three operations we need (replacement,
157 substitution/selection and repeating/cloning), and only need two more features
158 before we're all done.
159
160 The first one is just a variation on replacement; instead of replacing with
161 a string, you can replace with a DOM tree or document. This facilitates simple
162 inclusion, since you probably want some header and footer to be the same
163 across all your pages. (No example here, you can probably work it out by
164 yourself; just send a DOM object instead of a string. There's an example in
165 the source code distribution if you need it.)
166
167 The second one is also a variation on replacement; sometimes, you want to
168 set attributes on elements instead of replacing their contents, and for that,
169 we have a small hack:
170
171 Template (clone.xml), repeated for your convenience:
172
173   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
174   <!DOCTYPE
175     html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
176     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
177   <html xmlns="http://www.w3.org/1999/xhtml" xmlns:t="http://template.sesse.net/" xml:lang="en">
178     <head>
179       <title>Cloning test</title>
180     </head>
181     <body>
182       <p>My favourite color is <t:color />; I like that very much.
183         All my favourite things:</p>
184       <ul t:id="things">
185         <li />
186       </ul>
187     </body>
188   </html>
189
190 Code (attribute.pl):
191
192   #! /usr/bin/perl
193   use XML::Template;
194
195   my $doc = XML::Template::process_file('../xml/clone.xml', {
196         'color' => 'red',
197         '#things' => [
198                 { 'li' => 'Raindrops on roses',    'li/class' => 'odd' },
199                 { 'li' => 'Whiskers on kittens',   'li/class' => 'even' },
200                 { 'li' => 'Bright copper kettles', 'li/class' => 'odd' },
201                 { 'li' => 'Warm, woolen mittens',  'li/class' => 'even' }
202         ]
203   });
204   print $doc->toString;
205
206 Result:
207
208   <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
209   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
210   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
211     <head>
212       <title>Cloning test</title>
213     </head>
214     <body>
215        <p>My favourite color is red; I like that very much.
216          All my favourite things:</p>
217       <ul>
218         <li class="odd">Raindrops on roses</li>
219       
220         <li class="even">Whiskers on kittens</li>
221       
222         <li class="odd">Bright copper kettles</li>
223       
224         <li class="even">Warm, woolen mittens</li>
225       </ul>
226     </body>
227   </html>
228
229 Naturally, you can't put anything else than a simple string into an attribute,
230 but it's not like this is a big limitation. (There's also a shortcut for doing
231 stuff like odd/even automatically, but I'll leave that for yourself to find out;
232 see the attribute2 example.)
233
234 That's it for the examples; now let's turn to the boring design philosophy.
235
236 The main thoughts behind XML::Template have been, in no particular order:
237
238  - Make the simple things simple. (A template should not be much more cumbersome
239    to write than if you wrote the page statically.) More complex things can be
240    harder if it makes the simple things simpler; that's OK.
241  - Make it easy for the user to do the right thing. (Guarantee well-formed XML,
242    and make a design that makes it easy to separate back-end logic, viewing logic
243    and HTML templating. Incidentially, I've only seen one library ever that does
244    the same properly for database logic and other back-end logic, and that is the
245    excellent libpqxx library.)
246  - Premature optimization is the root of all evil; most web systems are not
247    performance limited by their output anyway.
248  - Don't try to be everything for everyone. (XML::Template can not output to
249    plain text or PostScript, even though that would clearly be useful for some
250    people in some cases.)
251  - Be language agnostic. (DOM is rather universal, and there's a useful
252    implementation for most web-relevant languages out there.) Maintaining
253    several implementations in several languages is suboptimal, but it's better
254    than only supporting one language or having someting that needs to reimplement
255    the entire DOM with wrappers for each language. (Thankfully, by relying on
256    the DOM support in each language, the code so far is under 200 lines per
257    implementation, so maintaining this hopefully shouldn't be much work.) As
258    proof-of-concept, there are got Perl, PHP, Python and Ruby implementations
259    that work and feel largely the same (and even a SAX-based Perl
260    implementation, for larger trees that won't fit into memory) -- other
261    implementations are welcome.  This is backed up by a test suite, which
262    ensures that all the different implementations return structurally
263    equivalent XML for a certain set of test cases. Porting to a new language
264    is not difficult, and once you've got all the test cases to pass, your
265    work is most likely done.
266
267 As a side note to the second point, I've spent some time wondering exactly
268 _why_ you want to separate the back-end logic from your HTML, and why people
269 don't seem to do it. After some thought, I've decided that what I really want
270 is to get the HTML away from my code -- not the other way round. (In other
271 words, HTML uglifies code more then code uglifies HTML -- someone using a
272 WYSIWYG editor to generate their HTML might disagree, though.)
273
274 However, this also means that you want the _entire_ viewing logic away from
275 your back-end logic if you can. When you process your data, you really don't
276 want to care if you're on an odd or even row to get those styled differently
277 in the HTML; that's for another part. XML::Template, incidentially, by
278 moving the entire output logic to the end of your script, makes this easy
279 for you; you _can_ do the viewing logic "underway" if you really want to,
280 but there's no incentive to, and the natural modus operandi is to split
281 viewing and other logic into two distinct parts.
282
283 An open question is how to do internationalization on web pages; I haven't
284 yet seen a good system for handling this. To be honest, this might be something
285 handled in another layer (cf. "don't try to be everything to everyone" above),
286 but I'd be interesting to hear others' thoughts on this, especially how
287 you could achieve clean text/markup separation (stuff like gettext doesn't
288 really work well with markup in general).
289
290 More to come here at some point, probably. Now, go out and just _use_ the
291 thing -- I hope it will make your life on the web simpler. :-)
292
293   - Steinar H. Gunderson <sgunderson@bigfoot.com>, http://www.sesse.net/