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