PageRenderTime 86ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/site/ucw-boxset/ucw_ajax/docs/ucw-intro/ucw-intro.html

https://github.com/vikram/lisplibraries
HTML | 1448 lines | 1402 code | 19 blank | 27 comment | 0 complexity | bbd353aeba5610d7e4175fbb3d19ecb0 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, CC-BY-SA-3.0, LGPL-3.0, BSD-3-Clause, GPL-2.0
  1. <html>
  2. <head>
  3. <title>UCW intro</title>
  4. <link rel='stylesheet' type='text/css' href='style.css'>
  5. </head>
  6. <!--Creative Commons License-->
  7. <a rel="license"
  8. href="http://creativecommons.org/licenses/by-nc-sa/2.5/"><img
  9. alt="Creative Commons License"
  10. align='right'
  11. src="http://creativecommons.org/images/public/somerights20.png"
  12. border="0">
  13. </a>
  14. <!--/Creative Commons License--><!-- <rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/"
  15. xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  16. <Work rdf:about="">
  17. <license rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.5/" />
  18. <dc:title>UCW intro</dc:title>
  19. <dc:date>2006</dc:date>
  20. <dc:description>You don't have to mention
  21. my name or where you got the content from if you want to use this
  22. work for your own purposes, just keep by this license (why would of
  23. course be another question ;)). If you want
  24. to use any of this for commercial use or in another way that's not
  25. covered by the licence, just send me an email (see somewhere on the
  26. page if present) and we can probably work something out.</dc:description>
  27. <dc:creator><Agent><dc:title>Ties Stuij</dc:title></Agent></dc:creator>
  28. <dc:rights><Agent><dc:title>Ties Stuij</dc:title></Agent></dc:rights>
  29. <dc:source rdf:resource="www.fallenfrukt.com" />
  30. </Work>
  31. <License
  32. rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"><permits
  33. rdf:resource="http://web.resource.org/cc/Reproduction"/><permits
  34. rdf:resource="http://web.resource.org/cc/Distribution"/><requires
  35. rdf:resource="http://web.resource.org/cc/Notice"/><requires
  36. rdf:resource="http://web.resource.org/cc/Attribution"/><prohibits
  37. rdf:resource="http://web.resource.org/cc/CommercialUse"/><permits
  38. rdf:resource="http://web.resource.org/cc/DerivativeWorks"/><requires
  39. rdf:resource="http://web.resource.org/cc/ShareAlike"/></License></rdf:RDF> -->
  40. <body>
  41. <h1>UCW intro</h1>
  42. <p>advertisement: ucw is now easily installable!! (experimental)
  43. <br>
  44. Get <a href='http://common-lisp.net/project/ucw/ucw-boxset.tar.gz'>http://common-lisp.net/project/ucw/ucw-boxset.tar.gz</a>, untar it, cd into the dir and execute:</p>
  45. <pre>
  46. YOURLISPHERE (-)-load start.lisp
  47. </pre>
  48. <p>last modified 4th of may 2006</p>
  49. <p>- added ucw-boxset info
  50. <br>
  51. - added debugging tools chapter
  52. <br>
  53. - added tabbed pane info in the templating chapter
  54. <br>
  55. - added info on slightly changed startup procedure
  56. <br>
  57. - fixed bugs concerning variable passing between pages in basics.lisp
  58. <br>
  59. - fixed little bug in the spliced-href slot</p>
  60. <p>table of content:
  61. <ul>
  62. <li><a href='#intro'>Introduction</a></li>
  63. <li><a href='#install'>Installation</a></li>
  64. <li><a href='#extra'>Add-ons</a></li>
  65. <li><a href='#testing'>Testing/Quickstart</a></li>
  66. <li><a href='#configure'>Configuration</a></li>
  67. <li><a href='#environment'>Setting up the environment (and a hello world on the side)</a></li>
  68. <li><a href='#mechanics'>Basic ucw mechanics</a></li>
  69. <li><a href='#templating'>Templating: nested components, containers and TAL</a></li>
  70. <li>Login sessions</li>
  71. <li>Downloading files</li>
  72. <li>Advanced form handling</li>
  73. <li>To backtrack or not to backtrack</li>
  74. <li>Ajax</li>
  75. <li>Really, really, really cool example application</li>
  76. <li><a href='#debugging'>Debugging tools</a></li>
  77. <li><a href='#source'>Source</a></li>
  78. <li><a href='#miscellaneous'>Miscellaneous</a></li>
  79. </ul>
  80. </p>
  81. <p><span id='intro'></span></p>
  82. <h2>Introduction</h2>
  83. <p>This text will introduce you in a mild mannered pace to the ways of this as of yet UnCommon Web
  84. framework, which is a framework to write web applications as you would write normal applications; e.g. with state. Check ucw's
  85. <a href='http://common-lisp.net/project/ucw/features.html'>features page</a> to get an impression of its features. Ucw is an
  86. open source project. Various people donate code on a regular basis. Check the
  87. <a href='http://common-lisp.net/cgi-bin/mailman/listinfo/bese-devel'>mailing list</a> for a flavour of the current topics. If you're
  88. wondering about the merits and weaknesses of either state or stateless web development check
  89. <a href='http://common-lisp.net/project/ucw/docs/html/rest/rest.html'>this rather nice one-page email conversation</a>
  90. between the creator of ucw and a non-believer. If you want to use ucw on a windows machine, I have got no experience or information on
  91. workability, but i would very much like to hear about your experiences.</p>
  92. <p>This introduction wants to introduce UCW in a slow and concise manner. We will start with
  93. the installation and an initial setup. Then slowly, through simple examples, the reader will be introduced to the
  94. various components. We will delve from the outside in and will perhaps some day hit upon the burning hot core. For the
  95. brilliant and the impatient this pace will just be to damn slow. Them I advice to read and watch some
  96. other tutorials and documentation. They are:</p>
  97. <ul>
  98. <li><a href='http://www.cliki.net/ucw%20Tutorial'>UCW Tutorial</a> - a cliki tutorial in progress, not dissimilar to mine, which
  99. sets up a quick working environment and explains the different components with a lot of code.</li>
  100. <li><a href='http://common-lisp.net/project/ucw/documentation.html'>the ucw documentation page</a> - diverse documentation sources
  101. which are a bit out of date. The email conversations are very interesting to get a good grasp of the use and limitations of ucw, and the
  102. video, although out of date, is interesting because you get to see ucw in action. Be warned: the source-code and the wiki example are a bit
  103. out of date last time I checked. Better to just download the source code, in which you will also find the wiki example.</li>
  104. <li>the source code - the docs section of the source code unlocks apache setup code, two quickstarts which may be a bit out of date, and
  105. more. Also check the examples directory relative to the root. It gives you a good idea how to code up various aspects of ucw and it harbors
  106. the wiki-example. It might just be a bit obvious but in the source code you can also read the source code; it offers great insight in the way ucw is put together.</li>
  107. </ul>
  108. <p><span id='install'></span></p>
  109. <h2>Installation</h2>
  110. <p>First off UCW doesn't do version numbers. Some people may remember it did and it did, but it doesn't anymore. Still there are two versions
  111. of UCW to choose from: ucw_dev and ucw_public, both held in a Darcs repository. ucw_dev is controlled by Marco Baringer, creator of
  112. ucw. Ucw_public is world writable. At the moment I would advise to go with ucw_dev which is also the focus of this intro. _public is not always in sync with _dev, although lately it does a good job of keeping up.</p>
  113. <h3>The (experimental) easy way</h3>
  114. <p>There's a shortcut nowadays. Marco Baringer made a UCW candybox
  115. with all the dependencies boxed up and ready to fire. At the moment
  116. it's quite experimental, but if it doesn't work at least you've got all
  117. the dependencies in one go. Try:</p>
  118. <p>wget <a href='http://common-lisp.net/project/ucw/ucw-boxset.tar.gz'>http://common-lisp.net/project/ucw/ucw-boxset.tar.gz</a>
  119. tar -zxf ucw-boxset.tar.gz
  120. cd ucw-boxset
  121. YOURLISPHERE (-)-load start.lisp</p>
  122. <p>This will start UCW with the built in httpd server backend. But if you
  123. want to get the dependencies seperately or want a different initial setup,
  124. read and get some of the following:</p>
  125. <h3>The (old and boring) hard way</h3>
  126. <p>To get _dev or _public, install Darcs on your computer and execute:</p>
  127. <pre>
  128. darcs get http://common-lisp.net/project/ucw/repos/ucw_dev
  129. </pre>
  130. <p>or</p>
  131. <pre>
  132. darcs get http://common-lisp.net/project/ucw/repos/ucw_public
  133. </pre>
  134. <p>Daily snapshots for ucw_dev are available at
  135. <a href='http://common-lisp.net/project/ucw/tarballs/ucw_dev-latest.tar.gz'>http://common-lisp.net/project/ucw/tarballs/ucw_dev-latest.tar.gz</a></p>
  136. <p>Daily snapshots for ucw_public are available at
  137. <a href='http://common-lisp.net/project/ucw/tarballs/ucw_public-latest.tar.gz'>http://common-lisp.net/project/ucw/tarballs/ucw_public-latest.tar.gz</a></p>
  138. <p>Now it is time for the dependencies. A word of caution: ucw has it's own versions for a number of libraries so even if
  139. you already have the latest version of some package, check this documentation or the readme supplied with the
  140. sources. As a rule of thumb, get the latest of everything and the ucw version if supplied.</p>
  141. <p>At the moment UCW requires the following
  142. dependencies:</p>
  143. <ul>
  144. <li><a href='#implementation'>a lisp implementation - one of the following: sbcl, cmucl, allegro, lispworks, openmcl</a></li>
  145. <li><a href='#arnesi'>arnesi - an all purpose lisp toolkit</a></li>
  146. <li><a href='#yaclml'>yaclml - yet another common lisp markup language</a></li>
  147. <li><a href='#parenscript'>parenscript - javascript in common-lisp</a></li>
  148. <li><a href='#iterate'>iterate - a lispy loop</a></li>
  149. <li><a href='#rfc2388'>rfc2388 - implements multipart/form specification</a></li>
  150. <li><a href='#rfc2109'>rfc2109 - implements cookie specification</a></li>
  151. <li><a href='#split-sequence'>split-sequence - splits sequences</a></li>
  152. <li><a href='#slime'>slime - Superior Lisp Interaction Mode for Emacs</a></li>
  153. <li><a href='#cl-ppcre'>cl-ppcre - regular expressions lib</a></li>
  154. <li><a href='#trivial-sockets'>trivial-sockets - handles stream sockets for undemanding network applications</a></li>
  155. <li><a href='#puri'>net.uri or PURI - parses URI's according to the RFC 2396 specification</a></li>
  156. <li><a href='#cl-fad'>cl-fad - a portable pathname library based on code from PCL</a></li>
  157. <li><a href='#cl-launch'>cl-launch - invokes lisp code from the command line</a></li>
  158. <li><a href='#detachtty'>detachtty - a screen-like detachment tool to be able to detach and reattach the lisp server from and to a console</a></li>
  159. <li><a href='#backend'>a server backend - be it mod_lisp (apache 1 or 2), araneida, allegroserve, portable aserve or the built in httpd server</a></li>
  160. <li><a href='#asdf'>asdf - a package loader facility</a></li>
  161. </ul>
  162. <p>A note on compatibility: Some configurations of implementation and server work better than others. Just report your bugs in the
  163. <a href='http://common-lisp.net/cgi-bin/mailman/listinfo/bese-devel'>mailinglist</a>, the list is quite responsive. As a pointer, an
  164. implementation/server-pair of sbcl or openmcl and mod_lisp seem too see the heaviest testing. The built-in httpd server also works well but
  165. isn't fit for production use.</p>
  166. <p><span id='implementation'></span></p>
  167. <h4>Implementation.</h4>
  168. <p>Pick one in the list above.</p>
  169. <h4>Debian</h4>
  170. <p>Someone from Debian (Luca Capello) is working hard to to support ucw out of the box, but he is not quite there yet. Keep an eye on the
  171. cl-debian mailing list
  172. (<a href='http://common-lisp.net/pipermail/cl-debian/2006-March/001113.html'>UCW status, seeing the light at the end of the tunnel</a>) for the latest developments.</p>
  173. <p><span id='arnesi'></span></p>
  174. <h4>Dependencies</h4>
  175. <h5>arnesi</h5>
  176. <p>Arnesi is a Common Lisp utility suite. It contains various "bits 'n
  177. pieces" of code which were useful while developing other code. It can
  178. be found on <a href='http://common-lisp.net/project/bese/arnesi.html'>http://common-lisp.net/project/bese/arnesi.html</a>.</p>
  179. <p>You will need the latest version:</p>
  180. <pre>
  181. darcs get http://common-lisp.net/project/bese/repos/arnesi_dev/
  182. </pre>
  183. <p>Daily snapshots are available at
  184. <a href='http://common-lisp.net/project/bese/tarballs/arnesi_dev-latest.tar.gz'>http://common-lisp.net/project/bese/tarballs/arnesi_dev-latest.tar.gz</a></p>
  185. <p><span id='yaclml'></span></p>
  186. <h5>yaclml</h5>
  187. <p>yaclml is a collection of macros and utilities for generating XML/HTML
  188. like markup from lisp code. It can be found on
  189. <a href='http://common-lisp.net/project/bese/yaclml.html'>http://common-lisp.net/project/bese/yaclml.html</a>. </p>
  190. <p>You will need the latest version:</p>
  191. <pre>
  192. darcs get http://common-lisp.net/project/bese/repos/yaclml/
  193. </pre>
  194. <p>Daily snapshots are available at: <a href='http://common-lisp.net/project/bese/tarballs/yaclml-latest.tar.gz'>http://common-lisp.net/project/bese/tarballs/yaclml-latest.tar.gz</a></p>
  195. <p><span id='parenscript'></span></p>
  196. <h5>parenscript - ucw version</h5>
  197. <p>Parenscript is a javascript compiler. You will need to get the latest development version:</p>
  198. <pre>
  199. darcs get http://common-lisp.net/project/ucw/repos/parenscript/
  200. </pre>
  201. <p>Daily snapshots are available at: <a href='http://common-lisp.net/project/ucw/tarballs/parenscript-latest.tar.gz'>http://common-lisp.net/project/ucw/tarballs/parenscript-latest.tar.gz</a></p>
  202. <p><span id='iterate'></span></p>
  203. <h5>iterate</h5>
  204. <p>Iterate is an iteration construct for Common Lisp. It can be found on</p>
  205. <a href='http://common-lisp.net/project/iterate/'>http://common-lisp.net/project/iterate/</a>
  206. <p>Download link: <a href='http://common-lisp.net/project/iterate/releases/iterate-current.tar.gz'>http://common-lisp.net/project/iterate/releases/iterate-current.tar.gz</a></p>
  207. <p><span id='rfc2388'></span></p>
  208. <h5>rfc2388 - ucw version</h5>
  209. <p>Rfc2388 is a lisp implemantation of RFC 2388, which is used to process form
  210. data posted with HTTP POST method using enctype "multipart/form-data".</p>
  211. <p>UCW uses its own fork of rfc2388. You can get the latest code from the
  212. darcs repository located at
  213. <a href='http://common-lisp.net/project/ucw/repos/rfc2388'>http://common-lisp.net/project/ucw/repos/rfc2388</a>:</p>
  214. <pre>
  215. darcs get http://common-lisp.net/project/ucw/repos/rfc2388/
  216. </pre>
  217. <p>Daily snapshots are available at <a href='http://common-lisp.net/project/ucw/tarballs/rfc2388-latest.tar.gz'>http://common-lisp.net/project/ucw/tarballs/rfc2388-latest.tar.gz</a></p>
  218. <p><span id='rfc2109'></span></p>
  219. <h5>rfc2109 - ucw version</h5>
  220. <p>Rfc2109 is the lisp implementation of the cookie protocol. You can get
  221. it at:
  222. <a href='http://common-lisp.net/project/rfc2109'>http://common-lisp.net/project/rfc2109/</a>:</p>
  223. <pre>
  224. darcs get http://common-lisp.net/project/rfc2109/rfc2109
  225. </pre>
  226. <p>Daily snapshots are available at
  227. <a href='http://www.common-lisp.net/project/rfc2109/release/rfc2109-latest.tar.gz '>http://www.common-lisp.net/project/rfc2109/release/rfc2109-latest.tar.gz </a></p>
  228. <p><span id='split-sequence'></span></p>
  229. <h5>split-sequence</h5>
  230. <p>splits sequences</p>
  231. <p>info: <a href='http://www.cliki.net/SPLIT-SEQUENCE'>http://www.cliki.net/SPLIT-SEQUENCE</a></p>
  232. <p>download: <a href='http://ww.telent.net/cclan/split-sequence.tar.gz'>http://ww.telent.net/cclan/split-sequence.tar.gz</a>
  233. (you might have to select a mirror first)</p>
  234. <p>If said link is dead, go to <a href='http://ww.telent.net/cclan-choose-mirror'>http://ww.telent.net/cclan-choose-mirror</a>
  235. to delete your CCLAN-SITE cookie and choose another mirror. At least some of them are definitely working. </p>
  236. <p><span id='slime'></span></p>
  237. <h5>SLIME</h5>
  238. <p>The Superior Lisp Interaction Mode for Emacs.</p>
  239. <p>Download and install the latest CVS version of SLIME from
  240. <a href='http://common-lisp.net/project/slime'>http://common-lisp.net/project/slime</a>.</p>
  241. <p>To checkout from CVS you must first login to the repository:</p>
  242. <pre>
  243. export CVSROOT=:pserver:anonymous@common-lisp.net:/project/slime/cvsroot
  244. cvs login
  245. </pre>
  246. <p>Enter anonymous when prompted for the password. You can then check out the latest version with:</p>
  247. <pre>
  248. cvs checkout slime
  249. </pre>
  250. <p>It's swank you want to add to your asd systems dir. To solve a current problem
  251. with cl-launch you might want to overwrite your swank.asd file with this one:</p>
  252. <a href='http://cl-debian.alioth.debian.org/repository/pvaneynd/slime/debian/swank.asd'>http://cl-debian.alioth.debian.org/repository/pvaneynd/slime/debian/swank.asd</a>
  253. <p>Check the description on cl-launch to see why.</p>
  254. <p><span id='cl-ppcre'></span></p>
  255. <h5>cl-ppcre</h5>
  256. <p>Edi Weitz's regular expression library: <a href='http://www.weitz.de/cl-ppcre/'>http://www.weitz.de/cl-ppcre/</a>.</p>
  257. <p>Download the latest version: <a href='http://weitz.de/files/cl-ppcre.tar.gz'>http://weitz.de/files/cl-ppcre.tar.gz</a></p>
  258. <p><span id='trivial-sockets'></span></p>
  259. <h5>trivial-sockets</h5>
  260. <p>Networking library to create small server applications. Download the latest version from:
  261. <a href='http://ww.telent.net/cclan/trivial-sockets.tar.gz'>http://ww.telent.net/cclan/trivial-sockets.tar.gz</a>
  262. (you might have to select a mirror first.</p>
  263. <p>If said link is dead, go to <a href='http://ww.telent.net/cclan-choose-mirror'>http://ww.telent.net/cclan-choose-mirror</a>
  264. to delete your CCLAN-SITE cookie and choose another mirror. At least some of them are definitely working. </p>
  265. <p>Or use asdf install.</p>
  266. <p><span id='puri'></span></p>
  267. <h5>net.uri/PURI</h5>
  268. <p>All of the backends (except araneida) depend on Franz's open-source
  269. net.uri library. It only works on Allegro but Kevin Rosenberg has made a portable version called PURI.</p>
  270. <p>For allegro users, NET.URI can be downloaded from here (it may be included in your
  271. version of acl):
  272. <a href='http://opensource.franz.com/uri/index.html'>http://opensource.franz.com/uri/index.html</a> </p>
  273. <p>If you're not using allegro you'll need puri which can be
  274. downloaded from <a href='http://puri.b9.com/download.html'>http://puri.b9.com/download.html</a></p>
  275. <p><span id='cl-fad'></span></p>
  276. <h5>cl-fad</h5>
  277. <p>A portable pathname library based on code from Peter Seibels book Practical Common Lisp. </p>
  278. <p>info: <a href='http://www.cliki.net/CL-FAD'>http://www.cliki.net/CL-FAD</a></p>
  279. <p>download: <a href='http://weitz.de/files/cl-fad.tar.gz'>http://weitz.de/files/cl-fad.tar.gz</a></p>
  280. <p><span id='cl-launch'></span></p>
  281. <h5>cl-launch</h5>
  282. <p>A bash script to make your Lisp software easily invokable from the shell command-line.</p>
  283. <p>info: <a href='http://www.cliki.net/cl-launch'>http://www.cliki.net/cl-launch</a></p>
  284. <p>download page: <a href='http://fare.tunes.org/files/cl-launch/'>http://fare.tunes.org/files/cl-launch/</a></p>
  285. <p>Download and add to your shell executable search path so ucw can find it while loading. Cl-launch can either be called cl-launch or
  286. cl-launch.sh, ucw will check for both. Also cl-launch depends on swank behavior other than that of the current cvs code, at least under sbcl, eg via swank-loader it looks for files under the load path, which is not what you want if you made a symbolic link to another dir. The easiest way to get around this problem is to overwrite your swank.asd file (in the slime directory root), with this one:</p>
  287. <a href='http://cl-debian.alioth.debian.org/repository/pvaneynd/slime/debian/swank.asd'>http://cl-debian.alioth.debian.org/repository/pvaneynd/slime/debian/swank.asd</a>
  288. <p>The downside might be that swank won't work anymore as it should, although i haven't heard anyone about this yet. Another approach is to copy your swank*.fasls from the slime directory to the relevant shadow directory under ~/.cache/...</p>
  289. <p>Check this thread for more info:</p>
  290. <a href='http://common-lisp.net/pipermail/slime-devel/2006-March/004664.html'>http://common-lisp.net/pipermail/slime-devel/2006-March/004664.html</a>
  291. <p>Yes, ucw is bleeding edge ;)</p>
  292. <p><span id='detachtty'></span></p>
  293. <h5>detachtty - ucw version</h5>
  294. <p>A screen-like detachment tool to be able to detach and re-attach the lisp server from and to a console.
  295. Ucw uses a custom detachtty version, e.g. detachtty-9 plus a patch by Kevin Rosenberg. It accepts an eval argument when
  296. invoking a lisp, with obvious benefits. Get it through Darcs:</p>
  297. <pre>
  298. darcs get http://common-lisp.net/project/bese/repos/detachtty/
  299. </pre>
  300. <p>Go to the directory root and compile and install:</p>
  301. <pre>
  302. make
  303. make install
  304. </pre>
  305. <p><span id='backend'></span></p>
  306. <h4>the backend</h4>
  307. <p>Ucw supports all the popular lisp backends. Whichever floats your boat. If you've got no clue which to choose I'd advice
  308. to go for mod_lisp. It works well with other languages, you get the benefit of apaches options and extras and all it does
  309. is pump the code straight to ucw so there's no trouble with quirks in the intermediate layer. Apache requires some work
  310. to function correctly, read the text below. The other backends should work out of the box, as long as they're recognized by
  311. asdf. aserve as well as portable aserve are in the package systems of gentoo and debian. Gentoo also supports mod_lisp
  312. and mod_lisp2 for apache and araneida. Read <a href='http://bc.tech.coop/blog/051219.html'>this blog post</a> by
  313. Bill Clementson to form a more balanced opinion.</p>
  314. <h5>mod_lisp/mod_lisp2 for apache</h5>
  315. <p>You can choose between apache 1 and 2. First of course you have to have a Apache web
  316. server up and runnnig; then you have to add the mod-lisp module by Marc
  317. Battyani which you find at <a href='http://www.fractalconcept.com/asp/html/mod_lisp.html'>http://www.fractalconcept.com/asp/html/mod_lisp.html</a></p>
  318. <p>First download the mod_lisp c file. Watch out, don't follow the logic of the website. Except for the windows version the
  319. info is outdated concerning the apache 1 version and info about apache 2 is non-existent. Go to subversion from the
  320. download page or click the links below:</p>
  321. <ul>
  322. <li>apache 1 .c file -
  323. <a href='http://www.fractalconcept.com:8000/public/open-source/mod_lisp/mod_lisp.c'>mod_lisp.c</a></li>
  324. <li>apache 1 binaries for windows - there is a link on the site for version 2.32 but it leads to nowhere</li>
  325. <li>apache 2 .c file - <a href='http://www.fractalconcept.com:8000/public/open-source/mod_lisp/mod_lisp2.c'>mod_lisp2.c</a></li>
  326. <li>apache 2 windows binary -
  327. <a href='http://www.fractalconcept.com:8000/public/open-source/mod_lisp/windows/mod_lisp2.zip'>mod_lisp2.zip</a></li>
  328. </ul>
  329. <p>As stated in the intro, if you want to use ucw on a windows machine you're on your own. I have got no experience or information.</p>
  330. <p>If you went for the c module on a unix clone, use the command.</p>
  331. <pre>
  332. apxs -i -c mod_lisp.c
  333. </pre>
  334. <p>or</p>
  335. <pre>
  336. apxs2 -i -c mod_lisp2.c
  337. </pre>
  338. <p>for respectively apache 1 or 2. This will install the module in the appropriate apache directory. Debian users using apt-get should get one
  339. of the apache dev versions to get apxs(2) installed. Then add the following lines to 'httpd.conf' or other relevant configure file in your
  340. apache configuration directory for both apache 1 and 2. Just be sure to change mod_lisp.so to mod_lisp2.so for apache 2.</p>
  341. <pre>
  342. LoadModule lisp_module /path/to/modules/directory/mod_lisp.so
  343. </pre>
  344. <pre>
  345. LispServer 127.0.0.1 3001 ucw
  346. </pre>
  347. <pre>
  348. &lt;LocationMatch "/path/.*\.ucw"&gt;
  349. SetHandler lisp-handler
  350. &lt;/LocationMatch&gt;
  351. </pre>
  352. <p>This means that ucw INTERNALLY talks to apache on port 3001 at the internet address 127.0.0.1 (so in this case your
  353. localhost). The locationmatch part redirects all traffic from (in this case) 127.0.0.1 with prefix "/path/" and files ending
  354. on .ucw to ucw. So http://127.0.0.1/path/something.ucw as well as
  355. http://127.0.0.1/path/anotherpath/somethingelse.ucw will be redirected, but not http://127.0.0.1/somethingfaulty.ucw or
  356. http://127.0.0.1/path/somethingfaulty.html .</p>
  357. <h5>allegroserve or portableaserve</h5>
  358. <p>Should you want to use the aserve backend you will need to download
  359. and install either the AllegroServe if you use acl or portableaserve for any other. AllegroServe doesn't seem to have an
  360. asdf file, you're going to have to load it manually or however AllegroServe handles it's own files. Portableaserve does however.</p>
  361. <p>AllegroServe can be downloaded from here (it may be included in
  362. your version of acl): <a href='http://opensource.franz.com/aserve/'>http://opensource.franz.com/aserve/</a></p>
  363. <p>portableaserve is hosted on sourceforge: <a href='http://portableaserve.sf.net'>http://portableaserve.sf.net</a></p>
  364. <p>portableaserve download: <a href='http://constantly.at/lisp/aserve.tar.gz'>http://constantly.at/lisp/aserve.tar.gz</a></p>
  365. <p>To use portableaserve you'll also need the Allegro Common Lisp compatibility package (acl-compat). It's part of
  366. portableaserve and so it's home is also the before-mentioned sourceforge site.</p>
  367. <p>acl-compat download: <a href='http://constantly.at/lisp/acl-compat.tar.gz'>http://constantly.at/lisp/acl-compat.tar.gz</a></p>
  368. <h5>araneida</h5>
  369. <p>Should you want to use the araneida backend you will need to download araneida. Get the latest version just to be on the
  370. safe side. Originally araneida was meant to live behind apache and it can of course still be configured as
  371. such. Explaining this is beyond the scope of this document but have a look at this
  372. <a href='http://www.accela.net/~dankna/guide.html'>site</a> for more info.</p>
  373. <p>info: <a href='http://www.cliki.net/Araneida'>http://www.cliki.net/Araneida</a>}</p>
  374. <p>download: <a href='http://common-lisp.net/project/araneida/release/araneida-latest.tar.gz'>http://common-lisp.net/project/araneida/release/araneida-latest.tar.gz</a></p>
  375. <p>or use darcs, although the connection seems a bit slow:</p>
  376. <pre>
  377. darcs get http://verisons.telent.net/araneida/
  378. </pre>
  379. <h5>httpd</h5>
  380. <p>Ucw's in-house server. It's a simple server bundled with ucw. Nice for testing.</p>
  381. <p><span id='asdf'></span></p>
  382. <h3>asdf</h3>
  383. <p>To download it or read up on it go to <a href='http://cliki.net/ASDF'>http://cliki.net/ASDF</a></p>
  384. <p>Asdf makes sure common lisp packages and the files in those packages get loaded in the correct order. I would
  385. suggest you read the manual at
  386. <a href='http://constantly.at/lisp/asdf/'>http://constantly.at/lisp/asdf/</a>. Really the first
  387. <a href='http://constantly.at/lisp/asdf/Using-asdf-to-load-systems.html#Using%20asdf%20to%20load%20system'>link</a>
  388. is all you need to know right now.</p>
  389. <p>Basically every lisp package you downloaded so far has defined an .asd file. Usually in the root of the directory. This
  390. file has to appear in the search path of asdf, which is a list of directories defined by the special variable
  391. asdf:*central-registry*. The directories are typically called "systems". Under a unix clone, make a symbolic link to a systems dir
  392. as described in the document or as shown below. If a package defines
  393. more than one .asd file, just shove the whole dir on the *central-registry*, again as described in the document or as shown below.</p>
  394. <p>For example: on some linux installations the directory /usr/share/common-lisp/systems is in the list of asdf:*central-registry*.
  395. Now create a symlink from a particular .asd file to
  396. /usr/share/common-lisp/systems :</p>
  397. <pre>
  398. ln -s /path/to/library/lib.asd /usr/share/common-lisp/systems
  399. </pre>
  400. <p>To shove a whole directory on the *central-registry* do the following in your lisp startup file or in the repl:</p>
  401. <pre>
  402. (push #P"/path/to/asd/files/" asdf:*central-registry*)
  403. </pre>
  404. <p>All the required packages need to be recognized by asdf if you don't want to load every file by yourself.</p>
  405. <p><span id='extra'></span></p>
  406. <h2>Add-ons</h2>
  407. <p>Ucw also provides some packages which are kept out of the core so as not to bloat the program. Well two to be exact: the ucw presentations package and the ucw forms package.</p>
  408. <h5>presentations</h5>
  409. <p>An advanced CRUD interface for UCW.</p>
  410. <pre>
  411. darcs get http://common-lisp.net/project/ucw/repos/ucw-presentations
  412. </pre>
  413. <h5>forms</h5>
  414. <p>A more advanced HTML form toolkit for ucw than the one that is used in the core package, uses yaclml and dojo.</p>
  415. <pre>
  416. darcs get http://common-lisp.net/project/repos/ucw-forms
  417. </pre>
  418. <p><span id='testing'></span></p>
  419. <h2>Testing/Quickstart (for the non-boxset way)</h2>
  420. <p>When you have set up all your dependencies correctly, you can test ucw by first creating the directory /etc/ucw/applications.d, firing up your favorite distribution and:</p>
  421. <pre>
  422. (load "/path/to/ucw/etc/start.lisp")
  423. </pre>
  424. <p>If all went well, you have started ucw with the httpd backend. Surf to
  425. <a href='http://127.0.0.1:8080'>http://127.0.0.1:8080</a> and again if all went well you should see the example page. That's it! Start hacking!!</p>
  426. <p><span id='configure'></span></p>
  427. <h2>Configuration</h2>
  428. <p>At the moment the methods to configure and start ucw are a bit under development. But things seem to be stabling out. The elegant production compliant way of starting ucw is by launching ucwctl, found in /path/to/ucw/bin/ucwctl. This will start your lisp as a deamon. To let ucw find the proper configuration files, copy the files under /path/to/ucw/etc/ to either /your/home/.ucw or /etc/ucw.</p>
  429. <p>Ucwctl depends on cl-launch, to which a number of ucwtl's configure options are passed. It provides a suite of launch options to invoke lisp from the command-line. Be sure to put a link to cl-launch somewhere in your application search path. For general cl-launch understanding, read the cl-launch documentation.</p>
  430. <p>It's of course convenient to put ucwctl in the *nix executable search path but contrary to cl-launch it is not mandatory. Here's the configuration part of the ucwctl help output; -a, -c, -l, -k, -s and -t are passed to cl-launch:</p>
  431. <pre>
  432. -a, --asdf-root DIRECTORY Register asdf search DIRECTORY
  433. -c, --config-file FILE Read FILE as config file.
  434. -h, --help Display this help and exit.
  435. -k, --stop-file FILE Use FILE as stop file.
  436. -l, --lisp NAME Use NAME (without PATH) as Common Lisp
  437. implementation.
  438. --log-root DIRECTORY Save the logs in DIRECTORY, overriding the
  439. --var-root option.
  440. --run-root DIRECTORY Created pid/socket in DIRECTORY, overriding the
  441. --var-root option.
  442. -s, --start-file FILE Use FILE as start file.
  443. -t, --timeout SECONDS Set timeout in SECONDS when trying to stop a
  444. a running session.
  445. -u, --ucwctl-file FILE Read FILE as the ucwctl.conf file.
  446. --var-root DIRECTORY Save the logs in DIRECTORU/log/ucw and create
  447. pid/socket in DIRECTORY/run/ucw, if the two
  448. options --log-root and --run-root are not set.
  449. </pre>
  450. <p>In /path/to/configdir/ucwctl.conf default values are provided except for the lisp implementation and the .asd directory. So at the least you'd want to change them to point to the proper lisp and dir, or just specify those on the commandline. If you want to specify different paths for --log-root and --run-root in your config file, add LOGROOT=/path/to/log-root and RUNROOT=/path/to/run-root, leaving VARROOT empty. If you run ucwctl as user and didn't change ucwctl or supplied different paths for log-root and run-root (which default to paths under /var) on the commandline, they will default to paths under /your/home/dir/var. You can also specify a different ucwctl.conf file instead of /path/to/config-dir.</p>
  451. <p>To be clear this is the hierarchy of where ucwctl will pick its configuration from. Higher up the list will be chosen over lower on the list:</p>
  452. <p>1) command line options
  453. <br>
  454. 2) -u|--ucwctl-file (if specified, exit if it cannot be read)
  455. <br>
  456. 3) ~/.ucw/ucwctl.conf (if it exists)
  457. <br>
  458. 4) /etc/ucw/ucwctl.conf (if it exists)</p>
  459. <p>A deeper discussion is available at:</p>
  460. <p>http://common-lisp.net/pipermail/bese-devel/2006-April/001932.html
  461. <br>
  462. http://common-lisp.net/pipermail/bese-devel/2006-April/001961.html
  463. <br>
  464. </p>
  465. <p>Now you can start ucw by invoking:</p>
  466. <pre>
  467. ucwctl start
  468. </pre>
  469. <p>Without having configured anything (in /path/to/conf.lisp) this will start ucw with the built-in httpd server, just as with cl-launch. But
  470. now you have log files and more importantly you are not tied to a particular console. Point your browser at
  471. <a href='http://127.0.0.1:8080/'>http://127.0.0.1:8080/</a> and hope for the best. Hook up slime through port 4005 if you also actually want to code something.</p>
  472. <p>To stop ucw type:</p>
  473. <pre>
  474. ucwctl stop
  475. </pre>
  476. <p>To attach to a running ucw server type:</p>
  477. <pre>
  478. ucwctl attach
  479. </pre>
  480. <p>This calls attachtty, installed alongside detachtty, and passes attachy the socket, the path of which is defined in the ucwctl startup script. De-attach attachtty by giving it a sighup signal, eg by closing your terminal window or by pressing ctl-\. Note that you should be a bit careful when you attach to a socket and you start typing away at the repl. By default cl-launch will terminate the running process when the debugger is invoked. This behavior is changeable; check the cl-launch script (or not, someone on the mailing-list pointed out a bug).</p>
  481. <p>To test if ucw works with your chosen backend open /path/to/conf.lisp (to be found in /etc/ucw/conf.lisp if you copied it from
  482. /path/to/ucw_dev/bin/etc/) and change:</p>
  483. <pre>
  484. (setf *ucw-backend-type* ':httpd)
  485. (setf *ucw-backend-host* "127.0.0.1")
  486. (setf *ucw-backend-port* 8080)
  487. </pre>
  488. <p>to:</p>
  489. <pre>
  490. (setf *ucw-backend-type* ':mod-lisp)
  491. (setf *ucw-backend-host* "127.0.0.1")
  492. (setf *ucw-backend-port* 3001)
  493. </pre>
  494. <p>or to:</p>
  495. <pre>
  496. (setf *ucw-backend-type* ':araneida)
  497. (setf *ucw-backend-host* "127.0.0.1")
  498. (setf *ucw-backend-port* 8080)
  499. </pre>
  500. <p>or to:</p>
  501. <pre>
  502. (setf *ucw-backend-type* ':aserve)
  503. (setf *ucw-backend-host* "127.0.0.1")
  504. (setf *ucw-backend-port* 8080)
  505. </pre>
  506. <p>Repeat your chosen startup procedure for ucwctl with th note that if you use mod_lisp you have
  507. to point your browser at
  508. <a href='http://127.0.0.1/index.ucw'>http://127.0.0.1/path/you/specified/in/apache/this/link/goes/to/the/root/index.ucw</a>, without
  509. the 8080 (defaults to the server root here).</p>
  510. <p>note also that the default conf.lisp configuration defaults to the configuration in /path/to/ucw_dev/src/default.lisp which is the default
  511. configuration ;-) So if you don't change anything in conf.lisp you might just as well omit it. </p>
  512. <p>The variables in conf.lisp that we didn't cover yet are:</p>
  513. <pre>
  514. (setf *ucw-swank-port* 4005)
  515. </pre>
  516. <p>- used to connect to emacs, et al.</p>
  517. <pre>
  518. (setf *ucw-server-class* 'standard-server)
  519. </pre>
  520. <p>- leave as is. I'm not even sure if there is a non-standard server we should be interested in.</p>
  521. <pre>
  522. (setf *ucw-inspector* t)
  523. </pre>
  524. <p>- let's you inspect ucw inside the html pages you made when set to t. Seeing is believing. Set to nil to see the page as normal.</p>
  525. <pre>
  526. (setf *ucw-debugger* t)
  527. </pre>
  528. <p>- sends debugging html to the client when set to t. Seeing is believing. Set to nil to not send debugging info to the client and to invoke the debugger in your editor.</p>
  529. <pre>
  530. (setf *ucw-log-level* '+INFO+)
  531. </pre>
  532. <p>- Sets the log-level threshold for the information that will be logged by the base ucw logger and which will be pumped to the ucw.log
  533. file. these are the log levels to choose from, together with their value:</p>
  534. <p>+dribble+ = 0
  535. <br>
  536. +debug+ = 1
  537. <br>
  538. +info+ = 2
  539. <br>
  540. +warn+ = 3
  541. <br>
  542. +error+ = 4
  543. <br>
  544. +fatal+ = 5</p>
  545. <p>So in our example debug info and dribble will not be logged to ucw.log.</p>
  546. <pre>
  547. (setf *ucw-applications-directory* "/etc/ucw/applications.d/")
  548. </pre>
  549. <p>- sets the applications directory, see next chapter</p>
  550. <pre>
  551. (setf *ucw-systems* '(:ucw.admin :ucw.examples))
  552. (setf *ucw-applications* '(it.bese.ucw::*admin-application*
  553. it.bese.ucw-user::*example-application*))
  554. </pre>
  555. <p>- supply ucw applications to load. See next chapter.</p>
  556. <p><span id='environment'></span></p>
  557. <h2>Setting up the environment (and a hello world on the side)</h2>
  558. <p>Note that this procedure is not strict necessary, you could just fiddle around in ucw-user, but you will want to have
  559. your own proper environment sooner or later. To get the best of both worlds (being lazy and having a proper set up
  560. environment), just download the <a href='ucw-intro_source.tar'>code</a> we're going to create below (mod_lisp
  561. users, check the www-dir stuff below for a correct setup). For the less lazy, create a directory where you want
  562. to keep your application code. (halfway ripped from the cliki tutorial. thanks ;-)).</p>
  563. <p>Create an asdf file called ucw-intro.asd will contain:</p>
  564. <pre>
  565. (defpackage :ucw.intro.system
  566. (:use :cl :asdf))
  567. </pre>
  568. <pre>
  569. (in-package :ucw.intro.system)
  570. </pre>
  571. <pre>
  572. (defsystem ucw-intro
  573. :version "0.0.0.0.1"
  574. :depends-on (:ucw)
  575. :author "a lonely clown"
  576. :components ((:file "packages")
  577. (:file "config" :depends-on ("packages"))
  578. (:file "hello-world" :depends-on ("config"))))
  579. </pre>
  580. <p>and a package file called packages.lisp:</p>
  581. <pre>
  582. (in-package :cl-user)
  583. </pre>
  584. <pre>
  585. (defpackage :ucw-intro
  586. (:use :common-lisp
  587. :it.bese.ucw
  588. :it.bese.arnesi
  589. :it.bese.yaclml))
  590. </pre>
  591. <p>make a symlink from your asd file to the applications.d dir you defined in the last chapter.</p>
  592. <p>Important: when ucw loads it checks this directory for .asd files of your ucw applications. To let them load
  593. correctly you have to synchronize four names in your applications with standard pre- and suffixes:
  594. <ul>
  595. <li>the name of your applications asd file: name.asd (in this case ucw-intro.asd)</li>
  596. <li>the name of your applications package: :name (in this case :ucw-intro)</li>
  597. <li>the name of your application: *name-application* (in this case *ucw-intro-application*, see below in the ucw-intro.lisp code)</li>
  598. <li>the asdf system name of your package as defined with defsystem: :name (in this case :ucw-intro)</li>
  599. </ul>
  600. </p>
  601. <p>This procedure might be streamlined in the future, but for now we have to deal with it.</p>
  602. <p>There is also an alternative way of loading your applications when loading ucw. In /etc/ucw/conf.lisp (or where-ever you decided
  603. to put the file), push the name of your applications asd system name on *ucw-systems* and push it's application name (see below in the
  604. ucw-intro.lisp code), on *ucw-applications* like it's done for the ucws example applications:</p>
  605. <pre>
  606. (defvar *ucw-systems* '(:ucw.admin :ucw.examples))
  607. (defvar *ucw-applications* '(it.bese.ucw::*admin-application*
  608. it.bese.ucw-user::*example-application*))
  609. </pre>
  610. <p>If you don't want them to load, take them away from both conf.lisp and /path/to/ucw_dev/src/default.lisp.</p>
  611. <p>Just loading the three files we define(d) in this chapter while ucw is running will also register them correctly, no matter how we name the
  612. before-mentioned names.</p>
  613. <p>We also need a www root directory. This is the directory where we store static files for the http server to see: css and
  614. javascript files, pictures and the like. Lisp servers handle the www directory a bit different than mod_lisp so we have
  615. to make apt provisions. Lisp servers can themselves map a path from an incoming uri to the underlying directories on a
  616. server. When we use mod_lisp we have to tell apache as well as lisp where to go.</p>
  617. <p>When you use a lisp server backend, just create a directory in the tutorial root and call it www if you
  618. want to be in sync with this tutorial. With mod_lisp make a link from a directory somewhere in the apache www tree and in range of
  619. the mod_lisp search path as explained in the installation section. With apache 2 a typical path would be
  620. /var/www/locahost/htdocs/. The apache 1 www root normally also lies somewhere within that vicinity.</p>
  621. <pre>
  622. ln -s /path/to/ucw-intro/www var/www/localhost/htdocs/ucw-intro
  623. </pre>
  624. <p>Or link the other way around. </p>
  625. <p>Now we create two new file in our example application root called ucw-intro.lisp and config.lisp. config.lisp:</p>
  626. <pre>
  627. (in-package :ucw-intro)
  628. </pre>
  629. <pre>
  630. (defvar *www-root*
  631. (merge-pathnames
  632. (make-pathname :directory '(:relative "www"))
  633. (asdf:component-pathname (asdf:find-system :ucw-intro))))
  634. </pre>
  635. <pre>
  636. (defparameter *ucw-intro-application*
  637. (make-instance 'cookie-session-application
  638. :url-prefix "/ucw-intro/"
  639. :www-roots (list *www-root*)
  640. :dispatchers (list (make-instance 'ucw::action-dispatcher)
  641. ;; hello world
  642. (url-dispatcher "hello-world.ucw"
  643. (call 'hello-world)))
  644. :debug-on-error t))
  645. </pre>
  646. <p>hello-world.lisp:</p>
  647. <pre>
  648. (in-package :ucw-intro)
  649. </pre>
  650. <pre>
  651. (defcomponent hello-world (simple-window-component) ()
  652. (:default-initargs :title "hi"
  653. :stylesheet '("stylesheet.css" "stylesheet2.css")
  654. :content-type "text/html; charset=utf-8;"
  655. :javascript '((:src "dojo.js")
  656. (:js (dojo.require "dojo.event.*"))
  657. (:script "var foo = 3;")))
  658. ;; (:entry-point "hello-world.ucw" (:application *ucw-intro-application* :class url-dispatcher))
  659. (:render () (&lt;:as-html "hello world")))
  660. </pre>
  661. <p>Load the files into your image and surf to http://127.0.0.1(:8080)/ucw-intro/hello-world.ucw, cross your fingers... and... now you've printed "hello world"! (If the heavens are willing).</p>
  662. <p>In the next chapter we will see what applications actually are, and we will examine the contents of ucw-intro.lisp. Also we will discuss
  663. the mechanics of ucw. Actually the next chapter is the most important chapter of this intro. Finally the foreplay is
  664. over.</p>
  665. <p><span id='mechanics'></span></p>
  666. <h2>Basic ucw mechanics</h2>
  667. <p>In the course of this chapter we will take a flight through website-making history. We begin with traditional website-making
  668. techniques and we will quickly work our way up. In the process you
  669. can't help but be introduced to the basic building blocks of a ucw program: components, actions, entry-points, applications and
  670. render methods. <a href='ucw-intro_source.tar'>Ready made source</a> is available.</p>
  671. <h4>Components, render methods, applications and entry points</h4>
  672. <h5>Components</h5>
  673. <p>Component have more than one role. First they act as your
  674. basic storage unit. They are classes and you can define them in the same way, but they have added
  675. functionality given through meta object protocol modifications. To explain these modifications we need some background
  676. on the inner workings of ucw: When ucw receives a http request it checks if the request is a part of user session
  677. already in progress. If not it creates a new one. Such a session is made up of frames. Every new request makes a new
  678. frame which is referenced by a random string. Every frame copies the values of relevant components and their slots from
  679. the frame before, except of course when the values of a given class have changed. If a user wants to go back to a
  680. previous page, the appropriate frame is looked up with the help of the identifier string (be it through get or a cookie),
  681. and the original values are restored. So frames are the memory of a ucw session. Some of the modifications made to
  682. components facilitate this process of getting and setting values in frames under the cover.</p>
  683. <p>Components are also clothes-hangers for html output. You generate html by defining render methods on them. In the case of
  684. our example the method renders (as you say) the simple-window-component hello-world, which is a pre-defined component to create an empty
  685. canvas. You can nest render methods of other components, in which case you piece the data together to form a complete page. With this
  686. information we can step through conf.lisp and hello-world.lisp from the last chapter:</p>
  687. <p>config.lisp:</p>
  688. <pre>
  689. (in-package :ucw-intro)
  690. </pre>
  691. <pre>
  692. (defvar *www-root*
  693. (merge-pathnames
  694. (make-pathname :directory '(:relative "www"))
  695. (asdf:component-pathname (asdf:find-system :ucw-intro))))
  696. </pre>
  697. <pre>
  698. (defparameter *ucw-intro-application*
  699. (make-instance 'cookie-session-application
  700. :url-prefix "/ucw-intro/"
  701. :www-roots (list *www-root*)
  702. :dispatchers (list (make-instance 'ucw::action-dispatcher)
  703. ;; hello world
  704. (url-dispatcher "hello-world.ucw"
  705. (call 'hello-world)))
  706. :debug-on-error t))
  707. </pre>
  708. <pre>
  709. (in-package :ucw-intro)
  710. </pre>
  711. <p>hello-world.lisp:</p>
  712. <pre>
  713. (defcomponent hello-world (simple-window-component) ()
  714. (:default-initargs :title "hi"
  715. :stylesheet '("stylesheet.css" "stylesheet2.css")
  716. :content-type "text/html; charset=utf-8;"
  717. :javascript '((:src "dojo.js")
  718. (:js (dojo.require "dojo.event.*"))
  719. (:script "var foo = 3;")))
  720. ;; (:entry-point "hello-world.ucw" (:application *ucw-intro-application* :class url-dispatcher))
  721. (:render () (&lt;:as-html "hello world")))
  722. </pre>
  723. <h5>Applications</h5>
  724. <p>First we define the filesystem directory which is going to function as www-root for static data such as images, style-sheets and
  725. the like. We feed it to the definition of our
  726. first application object. An application is an arbitrary conceptual construct with which you define a number of
  727. options for a block of web-pages/site-logic. It lets you specify if you want to use cookies, what charset you want to use,
  728. etc. A quick tour of the chosen options: In this instance we want to use cookies, which means that ucw starts searching
  729. for certain cookie information after it has looked for information in other places. The url
  730. prefix is the browser path relative to the server root. Www-roots is a list of pathnames the application searches to get
  731. to static data relative to the the APPLICATION root. They are set relative to the
  732. url-prefix, eg in this example from the outside the www-root of ucw-intro is 127.0.0.1/ucw-intro/. Debug-on-error will or will not throw you in the debugger when things go wrong in this application, depending on if it is set to t or nil. The dispatchers we will cover later on.</p>
  733. <h5>defcomponent and render methods</h5>
  734. <p>The simple-window-component class and the defmethod macro that defines it can together be seen as as a convenience layer which
  735. constructs the underlying building blocks a standard application wants to have. In this section you will learn the
  736. semantics of both and you will learn how to define the building blocks without them.</p>
  737. <p>A simple-window-component sets up your basic html layout. You can feed a title, stylesheets, content-types, and
  738. different ways to insert javascript to :default-initargs. All of them accept a list as well as a
  739. basic unit suitable for the keyword, although you probably don't want to pass a list to the title argument as that will just print a quoted
  740. list. You can specify one or more javascript source files with the :src key, using :script lets you insert javascript as
  741. is and :js lets you insert javascript in parenscript syntax. With this information it sets up a whole page, except for
  742. the body. That's left for the render method.</p>
  743. <p>Defcomponent defines components almost the same as defclass defines classes. :default-initargs, slots, etc... are simply passed
  744. to the underlying defclass. But it has some extra options. You can supply a default backtrack function with :default-backtrack, you can supply a render method with :render, you can supply an entry-point with
  745. :entry-point and you can supply an action with :action. More on those last two in the next section, and more on :default-backtrack in a future chapter.</p>
  746. <p>As stated before, a render method is a regular method that specializes on a given component to output html. It is called when the program
  747. decides when it's time to put together a page to show the user. More on program flow in the next section of this chapter. The
  748. defcomponent macro passes the arguments from the :render form to a defmethod and let's it specialize on the name given
  749. to defcomponent. If no name is given the name defaults to the name given to defclass. There are two standard ways to
  750. output html in a render method. First you can use the &lt;:as-html (or &lt;:ah) macro and just type big slabs of html-escaped text or use &lt;:as-is (or &lt;:ai) to produce un-html-escaped text, but you know as well as I that lispers would rather be tortured for weeks by the US government than not lispify foreign syntax. Ucw uses yaclm for this, which is the in-house html markup lispifier. Html tag names start with &lt;:, attributes are keywords followed by their value. The standard html format that ucw spits out is xhtml 1 (eg html 4 but more strict). The whole of xhtml 1 is defined in yaclml and work has just started on svg tags. That's all there is to it.</p>
  751. <p>Without defcomponent and simple-window-component you could have written the code like this:</p>
  752. <pre>
  753. (defclass hello-world () ()
  754. (:metaclass standard-component-class))
  755. </pre>
  756. <pre>
  757. (defentry-point "hello-world.ucw"
  758. (:application *ucw-intro-application* :class url-dispatcher)
  759. () (call 'hello-world))
  760. </pre>
  761. <pre>
  762. (defmethod render ((hello-world hello-world))
  763. (&lt;:as-is "&lt;!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/transitional.dtd\"&gt;"
  764. #\Newline)
  765. (&lt;:html
  766. (&lt;:head
  767. (&lt;:meta :http-equiv "Content-Type" :content "text/html; charset=utf-8;")
  768. (&lt;:title (&lt;:as-html "hi"))
  769. (&lt;:link :rel "stylesheet"
  770. :href "stylesheet.css"
  771. :type "text/css")
  772. (&lt;:link :rel "stylesheet"
  773. :href "stylesheet2.css"
  774. :type "text/css")
  775. (&lt;:script
  776. ;; most browsers (firefox, safari and ie at least) really,
  777. ;; really, really don't like empty script tags. The "" forces
  778. ;; yaclml to generate a seperate closing tag.
  779. :type "text/javascript" :src "dojo.js" "")
  780. (&lt;:script :type "text/javascript" (&lt;:as-is (js:js* '(dojo.require "dojo.event.*"))))
  781. (&lt;:script :type "text/javascript" (&lt;:as-is "var foo = 3;")))
  782. (&lt;:body (&lt;:as-html "hello world"))))
  783. </pre>
  784. <p>What we have omitted in this example is giving the components extra slots. Declaring slots in components is like defining slots for regular
  785. classes with two extra options which are :component and :backtrack. The former is for nesting components, the latter for backtracking values
  786. in other frames. Both will be discussed in future chapters. </p>
  787. <h5>Entry points, actions, forms and control flow</h5>
  788. <p>You might have heard some fellow that touted ucw as being hard. Well,
  789. this is the hard part. That is, to figure out how it ticks. But we don't need to know that right now. We just
  790. need a working model of basic control mechanics.</p>
  791. <p>First, to clear some confusion. You have already seen the :dispatchers part of cookie-session-application, the commented out :entry-point part of defcomponent and the commented out defentry-point. They all do the same thing. :entry-point and defenty-point are the old way of adding a new page to an application. The :dispatcher way centralizes things a bit and is intended to replace defentry-point. The old way was reintroduced as a macro to not break existing applications to much. In this tutorial :dispatchers is used as default in explanations with commented out defentry-points and :entry-point to show the alternative way.</p>
  792. <p>When a user calls a ucw page for the first time, without being redirected by
  793. another ucw page and without information by some ucw cookie that hasn't timed out yet, the machinery checks the last
  794. form you just typed at the :dispathers part of your application constructor, in this case (call 'hello-world). It calls the component hello-world, which means it locks that component as being the main component to be rendered to the
  795. user. Whenever ucw gets user input it makes a new frame and after the preliminaries are over, it calls the render method on the component
  796. that's at that moment the main component and the page is served. The end part of the :dispathers thingy and so called actions handle the control flow of a ucw program between page calls. You can type any number of forms, which will be parsed as a body.</p>
  797. <p>Btw, at the end part of defentry-points you have an empty form before the last one. We will turn to it later.</p>
  798. <p>On the face of it that call to hello-world doesn't seem to do so much out of the ordinary, and that's the whole point. As you might know the
  799. web poses some problems for standard program execution because web-pages slash your flow in pieces. Those pieces have to be pieced together
  800. again somehow. It turns out that cps (continuation programming style) is quite handy for this purpose. Basically it means being able to
  801. capture the state of your program anywhere you want in a function. You can call that function again whenever you want and how often you want
  802. to pick up the execution of your program where you left off. Some languages have facilities built in to just capture a program state like that but lisp doesnt. You have
  803. to transform the code to be able to do this. You could do this by hand if you're mad, but ucw has a cps transformer on board that does it for
  804. you. This information might be a bit vague. If you want to read up on the subject i suggest you read
  805. <a href='http://www.bookshelf.jp/texi/onlisp/onlisp_20.html'>chapter 20 of On Lisp</a>.</p>
  806. <p>Now all of this transforming is done under the cover so you
  807. won't have so much to do with cps transformation but there is actually also a practical purpose for
  808. knowing this, and to explain that we need yet some more theory. A cps transformer must sort of understand the semantics of the code it's
  809. working with so it needs a code walker to interpret for example if forms are macros or how to keep track of environment variables. Every
  810. implementation obviously has one but language users don't have access to it. You have to roll your own and ucw did. But walking code ain't
  811. as easy as it not sounds. Code walkers usually don't capture lisp perfectly and the ucw one is no exception. Usually stuff works out fine
  812. but don't be surprised if your code doesn't do what it should at the end of a dispatcher in the :dispatchers list or inside an action. When you have to handle a
  813. lot of code inside an action, try to delegate as much as possible to function calls, because functions outside an action are not subject to
  814. these laws of an alternate lisp universe.</p>
  815. <p>Just some more theory so you fully appreciate the example beneath. At some point a dispatcher should call a component with
  816. (call ... , which initializes the component with initargs passed to it and the component is set as THE component for that frame. It is
  817. rendered, perhaps together with sub-components it specifies to render in it's render method. As the user is presented with a page,
  818. she/he gets her/his first chance to interact with the program through the usual mechanics: forms, links, buttons. The user clicks or writes
  819. something and another request is inbound. The traditional (some whisper 'pre-ucw') way to handle a request is to check get and post values
  820. and act on them. Ucw sports this facility. In our example the form before the call to hello-world is empty. In that form you could have
  821. specified which parameters from the page you want to use. If you want to give the parameter a default value you can put parameter and
  822. default between parenthesis. These values are now available for use in the forms you write after them.</p>
  823. <p>But of course ucw has more tricks up it's sleeve. For some html tags ucw has it's own yaclml equivalent with extended functionality: a,
  824. area, button, form, select, option, textarea and input. For convenience ucw gives the input attributes text, password and submit their own
  825. tag. Furthermore ucw sports such outlandish tag-names as integer-range-select, month-day-select, render-component and script. These last
  826. ones we will cover in a later chapter. All these tags become invocable macro-forms by putting &lt;ucw: in front of them. See the example.</p>
  827. <p>The extra functionality comes from the extra keywords you can pass them: :action, :accessor, :reader and writer. The macro's can handle
  828. one or more of these, depending on the macro, but the keywords are mutually exclusive. With the :action keyword you can specify an action
  829. form which will be executed with the relevant parameters when the form returns. With :accessor you can specify a place. It's value is the
  830. initial value of the specified tag. If the tag returns a value, the place will store it. :reader and :writer are used as a
  831. team to provide more flexibility than :accessor does. If provided, the value of :reader is used as the initial value of
  832. place. The value of writer should be a function which accepts one argument: the value returned by the tag.</p>
  833. <p>Continuing our control flow we assume the user uses one of the before-mentioned ucw forms to once again bother the server with info. If the
  834. user hasn't sparked off one of the ucw forms with one of those so-called actions attached to them, the current component stays in
  835. place. When a new page is rendered, the render method will again specify on this component. When an action is called it can do a lot of
  836. things but seen through this prism, the options split up in three: an action can execute code without ever calling a call to another component, it can somewhere along the line call another component or it can answer a component. When it never calls</p>
  837. <pre>
  838. (call ...
  839. </pre>
  840. <p>or answer</p>
  841. <pre>
  842. (answer ....
  843. </pre>
  844. <p>the component stays in place. When it calls another component, that component gets focus and ucw stores a continuation of where
  845. that action got interrupted in that component. When an action answers, ucw restores the component that was in focus before it called the
  846. component that just answered (heh). The continuation that was stored in that component is called with an optional value which is the value
  847. that's returned by the call form. The action in which the call was made continues untill it either calls a component, answers a
  848. component or ends, in which case the component connected to the action gets rendered.</p>
  849. <p>'But how should i interpret dispatchers', you might wonder. Well dispatchers are called when ucw tries to figure out what to do with an incoming request. You specify what ucw should look at when you construct an application (or when you use a defentry-point. You can use them in conjunction.) First on your list should be an action-dispatcher, e.g.: (make-instance 'ucw::action-dispatcher). It will check if an action parameter is present in the url. If so it will try to look if it can match it against a relevant action and if it finds one it will execute it. Furthermore you should add a url-dispatcher or a regexp-dispatcher to the list for every page you want to use as an entry-point into you application. You specify url-dispatcher or regexp-dispatcher for plain url names or regular expressions relative to the application root, the string of the url or the regular expression and the set of forms you want it to execute, which should at some point at least call one component to render. Since the dispatchers (and entry-points) are the entry-points into your application they have no component to answer to. When the set of forms finishes it returns to the first form, and again, and again, e.g. they are in an infinite loop.</p>
  850. <p>It's high noon for an example. Add these to the dispather list of your config.lisp file:</p>
  851. <pre>
  852. ;; basics
  853. (regexp-dispatcher "^(basics.ucw|)$"
  854. (call 'basics))
  855. (regexp-dispatcher "^(get.ucw|)$"
  856. (with-request-params ((cow "moo"))
  857. (context.request *context*)
  858. (let ((cow (concatenate 'string "mighty " cow)))
  859. (call 'basics :cow cow))))
  860. </pre>
  861. <p>Add this to your ucw-intro.asd file:</p>
  862. <pre>
  863. (:file "basics" :depends-on ("config"))
  864. </pre>
  865. <p>And define the file basics.lisp like so:</p>
  866. <pre>
  867. (in-package :ucw-intro)
  868. </pre>
  869. <pre>
  870. (defcomponent basics (simple-window-component)
  871. ((cow :initarg :cow :accessor cow :initform "aspacia")
  872. (pig :initarg :pig :accessor pig :initform "knorrie"))
  873. (:default-initargs :title "hi")
  874. (:render ()
  875. (with-slots (cow pig)
  876. basics
  877. (&lt;:as-is "in the old days, to update the name of your lovely cow named &lt;b&gt;" #\Newline)
  878. (&lt;:as-html cow)
  879. (&lt;:as-is "&lt;/b&gt; you would update it like so: ")
  880. (&lt;:form :action "get.ucw"
  881. :method "GET"
  882. (&lt;:text :name "cow")
  883. (&lt;:submit :value "name your cow"))
  884. (&lt;:p (&lt;:a :href "get.ucw" "go to get without any value to get to the default value of the entry point"))
  885. (&lt;:p
  886. (&lt;:as-html "but we have left them behind. In the brave new world we will update the name of our lovely pig like: ")
  887. (&lt;ucw:form :method "POST"
  888. :action (refresh-component basics)
  889. (&lt;ucw:text :accessor pig)
  890. (&lt;:submit :value "name your pig")))
  891. (&lt;:p
  892. (&lt;:as-html "to pass a value and control to another component we do like so:")
  893. (let ((stuff-to-say ""))
  894. (&lt;ucw:form :action (get-stuff-to-say basics stuff-to-say)
  895. :method "POST"
  896. (&lt;ucw:textarea :accessor stuff-to-say)
  897. (&lt;:br)
  898. (&lt;:submit :value "have something to say")))))))
  899. </pre>
  900. <pre>
  901. ;;(defentry-point "get.ucw"
  902. ;; (:application *ucw-intro-application*)
  903. ;; ((cow "moo"))
  904. ;; (+ 2 2)
  905. ;; (let ((cow (concatenate 'string "mighty " cow)))
  906. ;; (call 'basics :cow cow)))
  907. </pre>
  908. <pre>
  909. (defaction get-stuff-to-say ((basics basics) stuff-to-say)
  910. (let ((stuff-to-say (concatenate 'string stuff-to-say " (boring!!!)")))
  911. (setf (cow basics) (call 'the-other-one :stuff-to-say stuff-to-say))))
  912. </pre>
  913. <pre>
  914. (defaction ok ((c component) &amp;optional (value c))
  915. (answer value))
  916. </pre>
  917. <pre>
  918. (defcomponent the-other-one (simple-window-component)
  919. ((stuff-to-say :initarg :stuff-to-say :accessor stuff-to-say))
  920. (:default-initargs :title "hi"
  921. :content-type "text/html; charset=utf-8;")
  922. (:render ()
  923. (&lt;:p
  924. (&lt;:as-html "- "(stuff-to-say the-other-one)))
  925. (&lt;:p
  926. (&lt;:as-html "yeah, very 'enlightning'. Dazzle me some more: "))
  927. (&lt;ucw:form :method "POST"
  928. :action (refresh-component the-other-one)
  929. (&lt;ucw:textarea :reader (stuff-to-say the-other-one)
  930. :writer #'(lambda (x)(setf (stuff-to-say the-other-one) (concatenate 'string x " (boring
  931. !!!!!)"))))
  932. (&lt;:br)
  933. (&lt;:submit :value "have something to say"))
  934. (&lt;:br)
  935. (&lt;ucw:a :action (ok the-other-one "nice to be back") "back to the last component")))
  936. </pre>
  937. <p>First we observe the added slot to the basics component called cow. We use it to store the values passed by our forms. The first form shows the old
  938. way: The name cow is picked up by the relevant url dispatcher, which we also let calculate 2 + 2 for no reason at all and in which we add
  939. mighty or puny to cow. The link below it shows that if no parameter named cow turns up the value of cow is set to moo. Notice
  940. however that if an empty form is submitted the name of cow is set to zero and our cow is nameless but still mighty or puny. Our second form
  941. has to be an ucw form if we want the &lt;ucw:text tag to work. The text tag only needs :accessor and a refresh-component action to work correctly; no need for pages to redirect to and no need for a name. The submit button could have been omitted, just as with the normal form. The :accessor method circumvents the entry-point for the value of pig and the refresh component action refreshes the current component. The form method could have been either get or post. For the correct working of ucw it doesn't matter which you choose. If you click the back to entry point link, you pass control back to the entry-point action. which proceeds to it's next call. The ok defaction is already provided by ucw.</p>
  942. <p>The third form shows how to pass control
  943. to another component. We (I at least) want to fill the slot stuff-to-say of component the-other-one with a value passed by the form. Since
  944. they of course don't share the same slot we make a temporary variable to have something to hang on to. When we submit the form we execute
  945. the action get-stuff-to-say which passes control to stuff-to-say with the slightly modified stuff-to-say. So now we can contain
  946. the generalized behavior of an entry-point within a function which can be tailor-made to fit one tag. Notice it doesn't matter if you call
  947. the next component in either hello-world.ucw or get.ucw. The only interesting thing in the form on the the-other-one page is that it uses reader and writer. The link below it gives control back to basics. As you can see you can give ok and answer an extra value which will be
  948. the value returned by the call form. Notice the value of pig is how you left it. If you hadn't changed the name of your cow it would also still be the same.</p>
  949. <p>Btw, as you can see, the way to specify parameters to be picked up in a dispatch method is a bit more complicated than when you use a defentry-point. The defentry-point simply lets you specify the parameters in it's third argument. The parameter is transformed into the with-parameters macro that is called in the dispatch way. The parameters can be supplied as a symbol if you don't care about giving it an initial value or as a list of a symbol and a string if you do want to give it an initial value.</p>
  950. <p>There you have it. You mastered the basic ucw mechanics, but of course you would like some more tools in your kit to let ucw work on code in
  951. stead of you. As you saw in the index: templating, extended form handling, login code and more is provided. This kind of
  952. functionality is the subject of the next few chapters.</p>
  953. <p><span id='templating'></span></p>
  954. <h2>Templating: nested components, containers and tal</h2>
  955. <p>There are two ways of templating in this chapter that scramble for the same namespace. The first is concerned with component manipulation;
  956. this includes nested components and containers. The second, the ucw TAL (Template Attribute Language) implementation, is a tool used by ucw
  957. to insert (almost) plain html into ucw code so that non-lispers (for example designers) can easily contribute to a ucw project. Get the
  958. <a href='ucw-intro_source.tar'>code</a>.</p>
  959. <h3>nested components</h3>
  960. <p>Nested components are components that occupy the slot of another component. They are not that different from regular slot values except for
  961. that you want them to inherit the proper values from past frames. To set these values, nested components use :component as their
  962. initialization keyword when placed in a component's slot. In day to day use they behave just as other slot values. If you want to use them
  963. to nest html layout, define a render method on them and invoke it in their parents render method.</p>
  964. <h5>containers</h5>
  965. <p>Containers are used to store components. Their main use is to be able to select one of those components as the
  966. component to be rendered to the user. The components in the container are stored as a list and can be referenced by key or index. This is
  967. done through several methods associated with containers to manipulate their contents.</p>
  968. <p>Containers come in three flavors: container, simple-container and list-container. A bog-standard container is a component with three extra
  969. slots:
  970. <ul>
  971. <li>contents - An alist of (key . component) holding the components - accessor = container.contents</li>
  972. <li>key-generator - A lambda that generates the keys from a component when they are not specified. Defaults to identity - accessor = container.key-generator</li>
  973. <li>key-test - A function used to compare two keys which defaults to eql - accessor = container.key-test</li>
  974. </ul>
  975. </p>
  976. <p>Containers are very nice for letting people navigate a site. You nest one inside a window-component with :component and present the user
  977. with a list of navigation links to which you attach a switch-component action (already provided by ucw) which accepts a so called switching-container and a key name as an argument. The action sets the key of the component which is associated by key as the
  978. current-component-key. </p>
  979. <p>The switching-container component class is generally used as the super class for navigatation components and tabbed-pane like components. it's like the regular container but serves to manage a set of components which share the same place in the user interface. It provides an implementation of render which simply renders its current component. It adds one extra slot called current-component-key, which is referenced to render the appropriate component.</p>
  980. <p>A list-container is a child of container-component as well as widget-component. It's render method renders the components in the container as an ordered list. A list container has a custom initialize-instance method defined on it, which accepts an additional orientation
  981. keyword which can be set to either :horizontal or :vertical. If one of these keywords is supplied, it is added to that component's css
  982. class list (see the widget-component documentation in chapter to-be-written). </p>
  983. <p>The container-manipulating methods are in order of appearance:
  984. <ul>
  985. <li>clear-container - accepts a container - clears contents of container</li>
  986. <li>find-component - accepts a container and a key - returns the component and t if the component is present. Otherwise nil and nil.</li>
  987. <li>(setf find-component) - (setf (container key) component) - sets a component at the position of key. If no such key exists,
  988. the key and component are added at the end of the contents list.</li>
  989. <li>add-component - accepts a container a component and an optional symbol as key name - adds a component to the end of the contents
  990. list. If key is supplied, key is used as key for component, otherwise the index of the component in the contents list is used.</li>
  991. <li>component-at - accepts a container and an index. If a component is set at the index of integer in the contents list, the component is
  992. returned, together with t, otherwise nil and nil are returned.</li>
  993. <li>(setf component-at) - (setf (container index optional-key) component) - Overrides the current component in list if it's available. In
  994. which case key isn't used. If the provided index is one past the last component, component is set at end of list with key as key if
  995. provided, otherwise index is used as key. If the index given is two or more past the last item in the list, a recoverable
  996. error is signaled.</li>
  997. <li>container.current-component - accepts a container - returns component with key of current-component-name as with find-component</li>
  998. <li>ensure-valid-component-key - accepts a container and a key - Returns t if key names one of the components in container,
  999. otherwise a restartable error is signaled.</li>
  1000. <li>map-contents - accepts a function and a container - applies a two argument function to the key and the component of every
  1001. key-component pair in contents of container.</li>
  1002. </ul>
  1003. </p>
  1004. <p>The following example demonstrates the use of a simple-container, nested in a window-component. It's a simple mock tragic adventure
  1005. fragment. When all drawers are checked, a new component is shoved onto the containers contents list. The flet at the end and the
  1006. simple-container initialization in the room class are the most interesting. The switching-container mechanics code is mostly
  1007. taken from the example application, bundled with ucw. Start it up to see a more traditional use of containers.</p>
  1008. <p>First add this to your ucw-intro.asd file:</p>
  1009. <pre>
  1010. (:file "containers" :depends-on ("config"))
  1011. </pre>
  1012. <p>Add this to the list of dispatchers in your config.lisp file:</p>
  1013. <pre>
  1014. ;; containers
  1015. (regexp-dispatcher "^(containers.ucw|)$"
  1016. (call 'office))
  1017. </pre>
  1018. <p>And then define containers.lisp like so:</p>
  1019. <pre>
  1020. (in-package :ucw-intro)
  1021. </pre>
  1022. <pre>
  1023. (defcomponent timeline (widget-component)
  1024. ((history :initarg :history :accessor history))
  1025. (:render () (&lt;:as-html (history timeline))))
  1026. </pre>
  1027. <pre>
  1028. (defcomponent drawer (widget-component)
  1029. ((contents :initarg :contents :accessor contents))
  1030. (:render () (&lt;:as-html (contents drawer))))
  1031. </pre>
  1032. <pre>
  1033. (defcomponent office (simple-window-component)
  1034. ((what-happened :initform '(0 0 0) :accessor what-happened)
  1035. (closet :component (switching-container
  1036. :current-component-key 'in-office
  1037. :contents
  1038. `((in-office . ,(make-instance 'timeline :history "You stand alone in the office of your evil boss. In
  1039. front of you you see a closet in which you are sure you will find valuables with which you can pay off your
  1040. insurmountable debts."))
  1041. (top-drawer . ,(make-instance 'drawer
  1042. :contents "you find memoirs of a broken soul"))
  1043. (middle-drawer . ,(make-instance 'drawer
  1044. :contents "you find an agenda of grinding daily chores"))
  1045. (bottom-drawer . ,(make-instance 'drawer
  1046. :contents "you find an ointment to take the edge off"))))
  1047. :accessor closet))
  1048. (:render ()
  1049. (with-slots (what-happened closet)
  1050. office
  1051. (unless (find-component closet 'leave)
  1052. (case (container.current-component-key closet)
  1053. ('top-drawer (setf (car what-happened) 1))
  1054. ('bottom-drawer (setf (cadr what-happened) 1))
  1055. ('middle-drawer (setf (caddr what-happened) 1)))
  1056. (when (and (= (car what-happened) 1)
  1057. (= (cadr what-happened) 1)
  1058. (= (caddr what-happened) 1))
  1059. (add-component closet
  1060. (make-instance 'timeline :history "You leave the office ashamed and angry. You know now your evil boss
  1061. is also a human being and you will have to feel compassion for him forever.")
  1062. 'leave)))
  1063. (render closet)
  1064. (&lt;:br)
  1065. (&lt;:as-html "What will you do next?")
  1066. (&lt;:br)
  1067. (if (eql (container.current-component-key closet) 'leave)
  1068. (flet ((drawer-link (name text)
  1069. (&lt;ucw:a :action (switch-component (closet office) name)
  1070. (&lt;:as-html text))))
  1071. (&lt;:ul
  1072. (&lt;:li (drawer-link 'top-drawer "inspect top drawer"))
  1073. (&lt;:li (drawer-link 'middle-drawer "inspect middle drawer"))
  1074. (&lt;:li (drawer-link 'bottom-drawer "inspect bottom drawer"))
  1075. (&lt;:li (drawer-link 'in-office "recall what your doing here"))
  1076. (when (find-component closet 'leave)
  1077. (&lt;:li (drawer-link 'leave "leave")))))
  1078. (&lt;:ai "&lt;br/&gt;Nothing, just stand here a bit")))))
  1079. </pre>
  1080. <h4>Tabbed-panes</h4>
  1081. <p>UCW also has a convenience class on board to quickly render different components/pages in a tabbed-pane, which builds upon switching-container. It basically spits out a main wintod and an options row/colomn of choosable components, all wrapped in divs, so you can style them every way you want. An example that wraps all the components we made thus far, and the tal components later in this chapter in a tabbed pane:</p>
  1082. <pre>
  1083. (defcomponent pane (tabbed-pane simple-window-component) ()
  1084. (:default-initargs
  1085. :title "pane"
  1086. :stylesheet "sheet.css"
  1087. :contents `(("hello world" . ,(make-instance 'hello-world))
  1088. ("basics" . ,(make-instance 'basics))
  1089. ("tal" . ,(make-instance 'tal-component))
  1090. ("more tal" . ,(make-instance 'more-tal)))
  1091. :current-component-key "hello world")
  1092. (:render ()
  1093. (call-next-method)))
  1094. </pre>
  1095. <p>There you go. We also let our component inherit from the simple-window-component so it will render a whole page and not just a div. The initialization part is the same as a normal switching-container, but it has a render-method defined on it that wraps the divs, and assigns actions. So we just call the next method in our render method. Use the sheet.css in the supplied sources to conjure up a nicely formatted tabbed-pane. This example is a bit wasteful cause now you're wrapping whole html pages in divs, but you get the picture.</p>
  1096. <h3>TAL</h3>
  1097. <p>Ucw's template attribute language is loosely based on
  1098. <a href='http://www.zope.org/Wikis/DevSite/Projects/ZPT/TAL'>Zope's TAL</a>. It also uses xml to express it's intentions and it uses a few of it operators. Moreover it uses its idea: a way to embed a few programming constructs in xml so that the xml can be parsed back into the language as a design template with some flexibility. Programmers can easily modify the html/xml that designers present to make it fit for code. As can the designers themselves up until a point.</p>
  1099. <h5>How it works</h5>
  1100. <p>Tal code is confined in .tal files. Those files are called from lisp code to render a page on a component, much like a render method. You can either replace a render method or embed tal files in a render method. You can also embed render methods in tal files, tal files in render methods and you can embed tal files in other tal files. Tal files can harbor control structures, variables and lisp code. When a component is rendered, the the tal control structures, variables and lisp code in a tal file are evaluated and the specified tal files are transformed into yaclml code, which on it's turn is transformed into xml/html.</p>
  1101. <p>An example. First add the tal-generator key-value pair to the make-instance of our *ucw-intro-application* in config.lisp and add two entry-point to our dispatcher list. With the stuff added in previous chapters it should look something like this:</p>
  1102. <pre>
  1103. (defparameter *ucw-intro-application*
  1104. (make-instance 'cookie-session-application
  1105. :url-prefix "/ucw-intro/"
  1106. :www-roots (list *www-root*)
  1107. :tal-generator (make-instance 'yaclml:file-system-generator
  1108. :cachep t
  1109. :root-directories (list *www-root*))
  1110. :dispatchers (list (make-instance 'ucw::action-dispatcher)
  1111. ;; hello world
  1112. (url-dispatcher "hello-world.ucw"
  1113. (call 'hello-world))
  1114. ;; basics
  1115. (regexp-dispatcher "^(basics.ucw|)$"
  1116. (call 'basics))
  1117. (regexp-dispatcher "^(get.ucw|)$"
  1118. (with-request-params ((cow "moo"))
  1119. (context.request *context*)
  1120. (let ((cow (concatenate 'string "mighty " cow)))
  1121. (call 'basics :cow cow))))
  1122. ;; containers
  1123. (regexp-dispatcher "^(containers.ucw|)$"
  1124. (call 'office))
  1125. ;; tal
  1126. (regexp-dispatcher "^(tal.ucw|)$"
  1127. (call 'tal-component))
  1128. (regexp-dispatcher "^(more-tal.ucw|)$"
  1129. (call 'more-tal)))
  1130. :debug-on-error t))
  1131. </pre>
  1132. <p>Then we create the first part of tal.lisp:</p>
  1133. <pre>
  1134. (in-package :ucw-intro)
  1135. </pre>
  1136. <pre>
  1137. (defcomponent tal-component (template-component)
  1138. ((page-name :initarg :page-name :accessor page-name)
  1139. (contents :initarg :contents :accessor contents))
  1140. ;; (:entry-point "^(|tal.ucw)$" (:application *ucw-intro-application* :class regexp-dispatcher))
  1141. (:default-initargs :template-name "intro.tal" :page-name "Que tal!" :contents "&lt;blink&gt;Qual blink!!!&lt;/blink&gt;"
  1142. :flag t))
  1143. </pre>
  1144. <p>Observe the :tal-generator bit in the application constructor, which specifies the root directories of the search paths to the tal files. Furthermore we let tal-component inherit from template-component, which adds the :template-name slot to standard component, next to some custom methods that do undercover work. In default-initargs you specify your tal file to render. Let's make that file:</p>
  1145. <pre>
  1146. &lt;html xmlns:tal="http://common-lisp.net/project/bese/tal/core"
  1147. tal:in-package=":ucw-intro"&gt;
  1148. &lt;head&gt;
  1149. &lt;title tal:content="$page-name"&gt;page name goes here&lt;/title&gt;
  1150. &lt;/head&gt;
  1151. &lt;body&gt;
  1152. &lt;h1 tal:content="(content $component" tal:escape-html="nil"&gt;content goes here&lt;/h1&gt;
  1153. &lt;h1 tal:replace="$contents" tal:escape-html="t"&gt;content goes here&lt;/h1&gt;
  1154. &lt;/body&gt;
  1155. &lt;/html&gt;
  1156. </pre>
  1157. <p>In the html tag you see the xmlns bits. They are xml name spaces. You can look them up in your browser but they lead to nowhere. But inside lisp they are mapped to different packages where custom defined tags, attributes and parameters reside. In this case :it.bese.yaclml.tal. tal:in-package of course maps to in-package. We for example reach the tal tag and attribute definitions by putting tal: in front of them. They are:</p>
  1158. <ul>
  1159. <li>tags: tal, lisp, include</li>
  1160. <li>attributes: content, replace, when, dolist, in-package</li>
  1161. </ul>
  1162. <p>When a tag (a tal tag, or a html/xml tag) is parsed, tal checks, from left to right, if the tag and/or any of it's attributes are part of a namespace which begs them to be evaluated by tal. A tag can contain multiple tal attributes and they can be mixed with html/xml attributes. If so tal determines which handler is fit and hands it the tag to be operated upon. (For your understanding, when it concerns an attribute, tal usually eats that attribute, performs the specified operation, and processes the tag again without it.)</p>
  1163. <p>To continue the treatment of our example, we examine the attributes content and replace. Content replaces the content of the tag it's a part of (in this case "content goes here") with what's contained in it's variable (the result of "(contents $component). Replace replaces the complete tag with what's in it's variable. They both check for the value of escape-html which doesn't escapes html if the value is nil and which does escape html whenever the value is non-nil. If escape-html isn't supplied it defaults to t.</p>
  1164. <p>For completeness: we could also have had our tal-component class inherit from simple-component and leave out everything except for the contents of the body, and ucw would have added the code around it.</p>
  1165. <p>But where are variables defined? How can tal interact with the class it renders? And what about those other tags and attributes? Enter the next section:</p>
  1166. <h5>Tal and lisp</h5>
  1167. <p>Basically where a tag or an attribute accepts some sort of parameter you usually have a window into lisp. This is the case with lisp, content, replace, when and dolist. The text inside " and " is evaluated, except for tal:lisp which evaluates the text inside its beginning- and end-tag. Variables start with the character $ and are matched against values inside the so-called tal environment. This environments equates to a collection of supplied classes, alists and hash-tables in which tal tries to match the variable. When it does it returns the value. In our example some methods on template-component make sure the component that the tal file is rendered on is included and can be referenced with $component. As you can see you can either use the slot function of contents or you can refer to contents directly by pasting a $ to its front, as you can refer directly to the key of a cons in an a-list or the key in a hash table. Later on you will see ways to extend the tal environment.</p>
  1168. <p>Sometimes you want to use a variable, or the output of some lisp code in an ordinary attribute. You can do that by putting "${..your lisp code...}" after the attribute (see the next example). If you for some reason want to splice the contents of a list after an attribute you can replace the $ with a @.</p>
  1169. <p>Let's review the rest of the tal tags and attributes. We start of with another example. This time we create a component called more-ucw, two simple classes and a hash table for use with the tal file:</p>
  1170. <pre>
  1171. (defcomponent more-tal (template-component)
  1172. ((flag :initarg :flag :accessor flag)
  1173. (da-var :initform "da-var from more-tal class" :accessor da-var)
  1174. (tal-name :initform "more-wrapper.tal" :accessor tal-name)
  1175. (another-var :initform "another-var from more-tal class" :accessor another-var)
  1176. (a-href :initform "http://planet.lisp.org" :accessor a-href)
  1177. (a-class :initform (make-instance 'in-list) :accessor a-class)
  1178. (spliced-href :accessor spliced-href :initform '("http://" "planet." "lisp." "org"))
  1179. (my-list :accessor my-list
  1180. :initform `((,(make-instance 'in-list)
  1181. ,(make-instance 'another-in-list))
  1182. (((da-var . "da-var from a-list") (another-var . "another-var from a-list")))
  1183. (,(make-instance 'in-list :da-var "da-var from class in another bullet")
  1184. ((da-var . "da-var from another a-list") (another-var . "another-var from another alist")))
  1185. (,*tal-table*))))
  1186. ;; (:entry-point "^(|more-tal.ucw)$" (:application *ucw-intro-application* :class regexp-dispatcher))
  1187. (:default-initargs :template-name "more-tal.tal" :flag nil))
  1188. </pre>
  1189. <pre>
  1190. (defclass in-list ()
  1191. ((da-var :initarg :da-var :initform "da-var from in-list class" :accessor da-var)))
  1192. </pre>
  1193. <pre>
  1194. (defclass another-in-list ()
  1195. ((another-var :initform "another-var from in-list class" :accessor another-var)))
  1196. </pre>
  1197. <pre>
  1198. (defparameter *tal-table* (make-hash-table))
  1199. </pre>
  1200. <pre>
  1201. (setf (gethash 'another-var *tal-table*) "another-var from hash")
  1202. </pre>
  1203. <p>and we make a .tal file called more-tal.tal:</p>
  1204. <pre>
  1205. &lt;html xmlns:tal="http://common-lisp.net/project/bese/tal/core"
  1206. xmlns:ucw="http://common-lisp.net/project/ucw/core"
  1207. xmlns:param="http://common-lisp.net/project/bese/tal/params"
  1208. tal:in-package=":ucw-intro"&gt;
  1209. &lt;head&gt;
  1210. &lt;title&gt;more tal&lt;/title&gt;
  1211. &lt;/head&gt;
  1212. &lt;tal:include tal:name="more-wrapper.tal" param:little-class="(a-class $component)"&gt;
  1213. &lt;param:contents&gt;
  1214. &lt;div tal:when="$flag" tal:content="$another-var"&gt;
  1215. flag is set?
  1216. &lt;/div&gt;
  1217. &lt;ul&gt;
  1218. &lt;li tal:dolist="$my-list"&gt;
  1219. &lt;b tal:content="$da-var"&gt;&lt;/b&gt;&lt;br/&gt;
  1220. &lt;tal:tal tal:content="$another-var"&gt;another var&lt;/tal:tal&gt;
  1221. &lt;/li&gt;
  1222. &lt;/ul&gt;
  1223. &lt;p&gt;&lt;a href="${(a-href $component)}"&gt;attribute insertion&lt;/a&gt;&lt;/p&gt;
  1224. &lt;p&gt;&lt;a href="@{(spliced-href $component)}"&gt;spliced attribute insertion&lt;/a&gt;&lt;/p&gt;
  1225. &lt;tal:lisp&gt;
  1226. (setf (a-href $component) "http://www.cliki.net")
  1227. &lt;/tal:lisp&gt;
  1228. &lt;a href="${(a-href $component)}"&gt;new value of href&lt;/a&gt;
  1229. &lt;/param:contents&gt;
  1230. &lt;/tal:include&gt;
  1231. &lt;/html&gt;
  1232. </pre>
  1233. <p>We leave tal:include and the param: tag and attribute alone for a moment because they need some extra code to explain. We explain the rest first:</p>
  1234. <ul>
  1235. <li>tal:when - an attribute - which is of course not dissimilar to lisps when. When the value produced by the expression inside the quotation marks equates to nil, attributes after the when tag are not processed. If this means that no tal attribute will supply the tag with content, the whole tag, and it's contents are removed. If a previous attribute already supplied the tag with content through tal:content the tag will not be removed and that content will be shown. If content was previously supplied with tal:replace the tag will be replaced by the content of tal:replace, as this is the behaviour of tal:replace</li>
  1236. <li>tal:dolist - an attribute - This is a rather cool and nifty tag. It works not dissimilar to dolist. The code between the quotation marks of dolist should equate to a list of lists containing alists, classes hash-tables or a combination of the former. Dolist iterates over the list of lists. On each iteration the tag dolist is a part of and it's children are evaluated and added to the yaclml tree in the pending tal environment extended with the list which is at that point the list dolist is iterating over. See the initform of the my-list slot of the more-tal component to get a feel for it.</li>
  1237. <li>tal:tal - a tag - In the dolist tag tree in more-tal.tal you see tal:tal which is simply a placeholder for attributes that doesn't show up in the html/xml code you want to produce.</li>
  1238. <li>the $ AND @ ones after a normal attribute - as discussed earlier these are used when you want to use the result of some lisp code output or a list of some lisp code output respectively after an ordinary attribute.</li>
  1239. <li>tal:lisp - a tag - The text enclosed within a tal:lisp tag is processed for side effects. No output is passed back into the tag tree.</li>
  1240. </ul>
  1241. <p>Now to explain tal:include, which basically does what you expect. Tal:include extends the current tal environment with the parameters that are in the param name-space and that are enclosed in the include tag (so everything with param: in front of it). This environment is passed to the calling tal file which it uses to evaluate it's own tal tags and attributes. You can pass param: tags and param: attributes, which behave differently. Param tags (you may call them whatever you wish) stuff all the evaluated content enclosed in them in a variable. (in the case of our example this will be in $contents). All the tags enclosed in the include tag are evaluated together with their attributes and the attributes of the param: tag before they are passed to the included tal file. So the content of $contents will be a big (or a little) string. By using attributes on the other hand, you can pass variables that refer to lisp objects. The value of the variable is the value returned by the code enclosed within the quotations that stick to it via the equal sign. The template that tal:include calls can be specified by either a tal:name or tal:name-expression attribute. Tal:name simply maps to the specified tal file. Name-expression evaluates the expression typed inside it's quotation marks, which should equate to a valid tal-file path. With this information we look at the .tal file we call from more-tal, called more-wrapper:</p>
  1242. <pre>
  1243. &lt;body
  1244. xmlns:tal="http://common-lisp.net/project/bese/tal/core"
  1245. xmlns:ucw="http://common-lisp.net/project/ucw/core"
  1246. tal:in-package=":ucw-intro"&gt;
  1247. &lt;tal:tal tal:content="$contents" tal:escape-html="nil"&gt;here goes more-tal&lt;/tal:tal&gt;
  1248. &lt;tal:lisp&gt;
  1249. (setf (da-var $little-class) "this is the new and quite boring contents of little class")
  1250. &lt;/tal:lisp&gt;
  1251. &lt;p tal:content="(da-var $little-class)"&gt;contents of little-class&lt;/p&gt;
  1252. &lt;/body&gt;
  1253. </pre>
  1254. <p>Should be pretty clear. For some more clarity, you could also have specified the file to include with:</p>
  1255. <pre>
  1256. tal:name-expression="more${(print (string-downcase '-wrapper.tal))}"
  1257. </pre>
  1258. <p>Now granted, this looks rather useless and hackerish, but the point is to show how you can use arbitrary expressions enclosed in ${} to get to your filename.</p>
  1259. <p>But when would you want to use name-expression? Well, imagine you want to reference a tal file based on the a component you have in a certain slot of the component you are rendering. Then you could write:</p>
  1260. <p>&lt;tal:include tal:name-expression="details/${ (class-name (class-of (datum $component))) }"
  1261. param:datum="(datum $component)"/&gt;</p>
  1262. <p>This will include a different file in the details directory, depending on the class of the datum slot, and pass the datum object itself to the included file. (example and explanation from Marco Baringer)</p>
  1263. <p>Note that the code inside ${} will only return the name of a class. tal:name-expression automagically appends .tal to the end if it's not already present, as does tal:name.</p>
  1264. <h5>Ucw-specific tal</h5>
  1265. <p>Ucw also has a couple of custom defined tal attributes and tags. They are:</p>
  1266. <ul>
  1267. <li>ucw:select - tag</li>
  1268. <li>ucw:option - tag</li>
  1269. <li>ucw:action - attribute</li>
  1270. <li>ucw:accessor - attribute</li>
  1271. <li>ucw:render-component - tag</li>
  1272. </ul>
  1273. <p>Which simply map to their yaclml equivalent, except for render-component which doesn't have one. Surprisingly enough it allows you to render a component from within tal. Typical well-formed tal strings using ucw would and actually do look like the tags below because they're straight from tal files found in the source code:</p>
  1274. <pre>
  1275. &lt;a href="" ucw:action="(respond $component $value)" tal:content="$text"&gt;text&lt;/a&gt;
  1276. &lt;input type="file" ucw:accessor="(file-upload-example.file $component)"/&gt;
  1277. &lt;ucw:render-component ucw:component="$selected-component"/&gt;
  1278. </pre>
  1279. <p>Well, it should all be self-explanatory. Only render-component is slightly out of context, but even my obsessive over-explaining-gene doesn't feel the need to explain it.</p>
  1280. <h5>Rendering tal files from within render methods</h5>
  1281. <p>Sometimes you want to render tal files from within render methods. You can do this by using the render-template method, which is also used under the cover for children of template-component. A render-template method expects a request-context class, the location of a tal file and a tal environment. A basic one looks like this:</p>
  1282. <pre>
  1283. (render-template ucw:*context* "tal-file.tal" nil)
  1284. </pre>
  1285. <p>In this example the method grabs the standard ucw context, but what is a context? From the source code:</p>
  1286. <p>A request-context object contains all the information associated with one particular request/response loop. The request context is usually accessed via the special variable *context* and contains the currently relevant application, session, session-frame, request and response object.</p>
  1287. <p>So you should almost always use ucw:*context*, unless you let the code do things advanced enough you don't need to read this tutorial to explain this particular aspect.</p>
  1288. <p>Furthermore our example above specifies a tal file and a request context of nil. So if you want to render that particular tal file, all variables you want the file to look up in the request context will equate to nil.</p>
  1289. <p>For explanations sake, we change the tal-component component so it doesn't inherit from template-component anymore, eg change:</p>
  1290. <pre>
  1291. (defcomponent tal-component ()
  1292. ...
  1293. </pre>
  1294. <p>to:</p>
  1295. <pre>
  1296. (defcomponent tal-component (template-component)
  1297. ...
  1298. </pre>
  1299. <p>Then we define a render method on the component:</p>
  1300. <pre>
  1301. (defmethod render ((component tal-component))
  1302. (render-template ucw:*context* "intro.tal" nil))
  1303. </pre>
  1304. <p>Now this one will actually report an error. Since our tal environment is nil, intro.tal tries to get to the slot component of an inexistent class. Let's add a class to our environment:</p>
  1305. <pre>
  1306. (defmethod render ((component tal-component))
  1307. (render-template ucw:*context* "intro.tal" `(((component . ,component)))))
  1308. </pre>
  1309. <p>Since a tal-environment consists of lists of alists, components and hash tables intro.tal now finds our component by referencing the specified alist in the tal-environment. The $contents variable in the tal file however equates to nil, because the component itself isn't listed as an object the tal-environment references to look up values. To add both the component itself and use it as a variable we might as well use the function make-standard-environment which is meant for this purpose:</p>
  1310. <pre>
  1311. (defmethod render ((component tal-component))
  1312. (render-template ucw:*context* "intro.tal" (make-standard-environment `((component . ,component)) component)))
  1313. </pre>
  1314. <p>And now tal-component renders like before.</p>
  1315. <p>This pretty much rounds up tal, but this intro is way from complete... Will the horror never end?</p>
  1316. <p><span id='debugging'></span></p>
  1317. <h2>Debugging</h2>
  1318. <p>Next to your standard debugging aids, ucw sports a few extra methods like debugging output deferred to the callers web-page and calling components from the repl.</p>
  1319. <p>First for completeness the different debugging switches and levers described at their respective sections:
  1320. The ucw-inspector and ucw-debugger web-switch described in the configuration section, the log-level lever described in the configuration section, and the debug-on-error switch of an application described in the initial environment section. </p>
  1321. <p>Furthermore, your standard ucw-sources come supplied with an administration page that is divided in a few sections. Click UCW web-admin from the examples page and log in with guest:guest. Ah, just play around. It's got some info screens, configure switches, a web-repl and a web inspector.</p>
  1322. <p>Last but certainly not least: Perhaps you would like to test your components at the repl. There is a problem here because calling components requires you to include some context-information that's normally only present when ucw actually tries to handle incoming requests. To circumvent this problem ucw has a with-dummy-request macro on board. It accepts a list of keys and a body. The keys are :render :action and :application. Render defaults to t, which means the component will be rendered and the output will be shown on *trace-output*. If nill no output. If :action is true (by default), the body will be treated as an action, if not it will be wrapped in a progn. With application you can pass an application object. If it's not given, the macro will try *default-application*, else a standard-application will be created. With url-prefix /DUMMY/, although that is a bit irrelevant. The output of with-dummy-request-context is the output of the render method followed by the component under scrutiny, so you wish to inspect it:</p>
  1323. <pre>
  1324. UCW-INTRO&gt; (with-dummy-context (:action nil :application *ucw-intro-application*)
  1325. (call 'tal-component))
  1326. </pre>
  1327. <pre>
  1328. --&gt;
  1329. 16:36 UCW-LOGGER/+INFO+: CALL'ing to /#&lt;BASICS {AE183A1}&gt;
  1330. &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/transitional.dtd"&gt;
  1331. &lt;html
  1332. &gt;&lt;head
  1333. ...
  1334. &gt;&lt;/html
  1335. &gt;
  1336. #&lt;BASICS {AE183A1}&gt;
  1337. </pre>
  1338. <pre>
  1339. UCW-INTRO&gt; (with-dummy-context ()
  1340. (get-stuff-to-say * "bla"))
  1341. </pre>
  1342. <pre>
  1343. --&gt;
  1344. 16:40 UCW-LOGGER/+INFO+: Serving action (/#&lt;BASICS {B00FDA9}&gt;/UCW-INTRO::GET-STUFF-TO-SAY #&lt;BASICS {B00FDA9}&gt; "bla")
  1345. 16:40 UCW-LOGGER/+INFO+: CALL'ing to /#&lt;THE-OTHER-ONE {B1707B1}&gt;
  1346. &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/transitional.dtd"&gt;
  1347. &lt;html
  1348. &gt;&lt;head
  1349. ...
  1350. &gt;&lt;/html
  1351. &gt;
  1352. </pre>
  1353. <pre>
  1354. UCW-INTRO&gt; (with-dummy-context (:render nil :application *ucw-intro-application*)
  1355. (call 'tal-component))
  1356. </pre>
  1357. <pre>
  1358. --&gt;
  1359. ; in: LAMBDA NIL
  1360. ; (IT.BESE.UCW:CONTEXT.WINDOW-COMPONENT IT.BESE.UCW:*CONTEXT*)
  1361. ; ==&gt;
  1362. ; IT.BESE.UCW:*CONTEXT*
  1363. ;
  1364. ; note: deleting unreachable code
  1365. ;
  1366. ; compilation unit finished
  1367. ; printed 1 note
  1368. 16:48 UCW-LOGGER/+INFO+: CALL'ing to /#&lt;TAL-COMPONENT {B6EEE21}&gt;
  1369. #&lt;TAL-COMPONENT {B6EEE21}&gt;
  1370. </pre>
  1371. <p><span id='source'></span></p>
  1372. <h2>Source</h2>
  1373. <p>is <a href='ucw-intro_source.tar'>here</a>.</p>
  1374. <p><span id='miscellaneous'></span></p>
  1375. <h2>Miscellaneous</h2>
  1376. <h5>Changes</h5>
  1377. <p>last modified 4th of may 2006</p>
  1378. <p>- added debugging tools chapter
  1379. <br>
  1380. - added info on slightly changed startup procedure
  1381. <br>
  1382. - added tabbed pane info in the templating chapter
  1383. <br>
  1384. - added ucw-boxset info
  1385. <br>
  1386. - fixed bugs concerning variable passing between pages in basics.lisp
  1387. <br>
  1388. - fixed little bug in the spliced-href slot</p>
  1389. <p>25th of april 2006:
  1390. <br>
  1391. - overhauled code examples and related text to make them easier to maintain/install/use
  1392. <br>
  1393. - changed text and code examples to reflect dispatch methods/changed defentry-point due to big backend code-changes
  1394. <br>
  1395. - changed text and code examples to reflect new container behaviour
  1396. <br>
  1397. - changed text to conform to changed configuration options
  1398. - added &lt;:ah + &lt;:ai info
  1399. <br>
  1400. - removed legacy ucw-install
  1401. <br>
  1402. - fixed some minor bugs</p>
  1403. <p>21th of april 2006 - added tal explanation section
  1404. <br>
  1405. 15th of april 2006 - fixed mod_lisp install errors
  1406. <br>
  1407. 11th of april 2006 - updated install instructions and a lot of minor fixes
  1408. <br>
  1409. 5th of april 2006 - first buggy version, landed on the net by accident</p>
  1410. <h5>Todo</h5>
  1411. <p>The rest</p>
  1412. <h5>Contact</h5>
  1413. <p>to mail, put these in the right order: <b>at dot fallenfrukt stuij</b> </p>
  1414. </body>
  1415. </html>