Commit | Line | Data |
---|---|---|
2aff8b5c | 1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
4 | <html xmlns="http://www.w3.org/1999/xhtml"> | |
5 | <head> | |
6 | <title>Roadmap to UCW Codebase</title> | |
7 | <meta name="generator" content="muse.el" /> | |
8 | <meta http-equiv="Content-Type" | |
9 | content="text/html; charset=utf-8" /> | |
11f9bd69 CE |
10 | <meta name="viewport" |
11 | content="width=device-width, initial-scale=1.0" /> | |
98266870 | 12 | <link href="https://feeds.unknownlamer.org/rss/site-updates" |
7404d4e1 | 13 | rel="alternate" type="application/rss+xml" title="Updates Feed" /> |
14 | ||
15 | <link rel="stylesheet" href="default.css" /> | |
2aff8b5c | 16 | </head> |
17 | <body> | |
18 | <h1>Roadmap to UCW Codebase</h1> | |
19 | <div class="contents"> | |
20 | <dl> | |
21 | <dt> | |
22 | <a href="#sec1">Abstract</a> | |
23 | </dt> | |
24 | <dt> | |
25 | <a href="#sec2">Roadmap</a> | |
26 | </dt> | |
27 | <dd> | |
28 | <dl> | |
29 | <dt> | |
30 | <a href="#sec3">Applications</a> | |
31 | </dt> | |
32 | <dd> | |
33 | <dl> | |
34 | <dt> | |
35 | <a href="#sec4">Cookie</a> | |
36 | </dt> | |
37 | <dt> | |
38 | <a href="#sec5">L10n</a> | |
39 | </dt> | |
40 | <dt> | |
41 | <a href="#sec6">Secure</a> | |
42 | </dt> | |
43 | </dl> | |
44 | </dd> | |
45 | <dt> | |
46 | <a href="#sec7">Components</a> | |
47 | </dt> | |
48 | <dd> | |
49 | <dl> | |
50 | <dt> | |
51 | <a href="#sec8">Windows</a> | |
52 | </dt> | |
53 | <dt> | |
54 | <a href="#sec9">Containers</a> | |
55 | </dt> | |
56 | <dt> | |
57 | <a href="#sec10">Dialogs</a> | |
58 | </dt> | |
59 | <dt> | |
60 | <a href="#sec11">Forms</a> | |
61 | </dt> | |
62 | <dt> | |
63 | <a href="#sec12">Templates</a> | |
64 | </dt> | |
65 | <dt> | |
66 | <a href="#sec13">Utility Mixin Components</a> | |
67 | </dt> | |
68 | </dl> | |
69 | </dd> | |
70 | <dt> | |
71 | <a href="#sec14">Control Flow</a> | |
72 | </dt> | |
73 | <dd> | |
74 | <dl> | |
75 | <dt> | |
76 | <a href="#sec15">Calling</a> | |
77 | </dt> | |
78 | <dt> | |
79 | <a href="#sec16">Actions</a> | |
80 | </dt> | |
81 | <dt> | |
82 | <a href="#sec17">Entry Points</a> | |
83 | </dt> | |
84 | </dl> | |
85 | </dd> | |
86 | <dt> | |
87 | <a href="#sec18">Dispatching</a> | |
88 | </dt> | |
89 | <dd> | |
90 | <dl> | |
91 | <dt> | |
92 | <a href="#sec19">Simple Dispatcher</a> | |
93 | </dt> | |
94 | </dl> | |
95 | </dd> | |
96 | <dt> | |
97 | <a href="#sec20">Server</a> | |
98 | </dt> | |
99 | <dt> | |
100 | <a href="#sec21">Debugging</a> | |
101 | </dt> | |
102 | <dd> | |
103 | <dl> | |
104 | <dt> | |
105 | <a href="#sec22">Inspector</a> | |
106 | </dt> | |
107 | </dl> | |
108 | </dd> | |
109 | </dl> | |
110 | </dd> | |
111 | <dt> | |
112 | <a href="#sec23">Tips</a> | |
113 | </dt> | |
114 | <dd> | |
115 | <dl> | |
116 | <dt> | |
117 | <a href="#sec24">Getting dojo to load</a> | |
118 | </dt> | |
119 | <dt> | |
120 | <a href="#sec25">Specials Bound During Rendering</a> | |
121 | </dt> | |
122 | <dt> | |
123 | <a href="#sec26">Printing to the yaclml stream</a> | |
124 | </dt> | |
125 | </dl> | |
126 | </dd> | |
127 | </dl> | |
128 | </div> | |
129 | ||
130 | ||
11f9bd69 CE |
131 | <!-- Page published by Emacs Muse begins here --> |
132 | <h2><a name="sec1" id="sec1"></a> | |
2aff8b5c | 133 | Abstract</h2> |
134 | ||
135 | <p><a href="http://common-lisp.net/project/ucw/">UnCommon Web</a> is a very powerful and mature web framework for Common | |
136 | Lisp, but is a bit difficult to learn. It is documented | |
137 | extensively—in the form of docstrings. These are extremely helpful | |
138 | once you've figured out the rough structure of UCW, but they are of no | |
139 | help when first learning unless you just read most of the source. I | |
140 | ended up having to do that, and after some urging along by folks in | |
141 | <code>#ucw</code> I decided to clean up my planner notes and publish them for | |
142 | public consumption.</p> | |
143 | ||
144 | <p>The roadmap is presented with major sections ordered in a logical | |
145 | order for learning the framework. The sections are ordered internally | |
146 | in order of most immediately useful to least, but it may be worth | |
147 | hopping between major sections before reading all of the details. I | |
148 | have used abridged class definitions and docstrings with occasional | |
149 | commentary to clarify things.</p> | |
150 | ||
151 | ||
152 | <h2><a name="sec2" id="sec2"></a> | |
153 | Roadmap</h2> | |
154 | ||
155 | <h3><a name="sec3" id="sec3"></a> | |
156 | Applications</h3> | |
157 | ||
158 | <p class="first">Applications are a bundle of entry points. The base class is, | |
159 | naturally, <code>standard-application</code>, but you should instead derive your | |
160 | application class from <code>modular-application</code> and any standard or custom | |
161 | application mixins you find useful.</p> | |
162 | ||
163 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/standard-classes.lisp">src/rerl/standard-classes.lisp</a></p> | |
164 | ||
165 | <pre class="src"> | |
7404d4e1 | 166 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">standard-application</span> (application) |
167 | ((url-prefix <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:url-prefix</span> | |
4222507d | 168 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A string specifying the |
2aff8b5c | 169 | start (prefix) of all the urls this app should handle. |
170 | ||
171 | This value is used by the standard-server to decide what app a | |
172 | particular request is aimed at and for generating links to | |
173 | actions within the app. "</span>) | |
7404d4e1 | 174 | (www-roots <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:www-roots</span> |
4222507d | 175 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A list of directories (pathname |
2aff8b5c | 176 | specifiers) or cons-cell (URL-subdir . pathname) to use when looking for static files."</span>) |
7404d4e1 | 177 | (dispatchers <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:dispatchers</span> |
4222507d | 178 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A list of request |
2aff8b5c | 179 | dispatchers. The user supplied list of dispatchers is extended |
180 | with other dispatchers that are required for UCW to function | |
181 | properly (action-dispatcher, a parenscript-dispatcher, etc). If | |
182 | you want full control over the active dispatchers use the (setf | |
183 | application.dispatchers) accessor or, if you want control over | |
184 | the order of the dispathcers, (slot-value instance | |
185 | 'dispatchers)."</span>)) | |
4222507d | 186 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The default UCW application class."</span>)) |
2aff8b5c | 187 | </pre> |
188 | ||
189 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/modular-application/modular-application.lisp">src/rerl/modular-application/modular-application.lisp</a></p> | |
190 | ||
191 | <pre class="src"> | |
7404d4e1 | 192 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">modular-application-mixin</span> () |
2aff8b5c | 193 | () |
4222507d | 194 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Superclass for all application mixins."</span>)) |
2aff8b5c | 195 | |
7404d4e1 | 196 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">modular-application</span> (standard-application modular-application-mixin) |
2aff8b5c | 197 | ...) |
198 | </pre> | |
199 | ||
200 | <h4><a name="sec4" id="sec4"></a> | |
201 | Cookie</h4> | |
202 | ||
203 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/modular-application/cookie-module.lisp">src/rerl/modular-application/cookie-module.lisp</a></p> | |
204 | ||
205 | <pre class="src"> | |
7404d4e1 | 206 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">cookie-session-application-module</span> (modular-application-mixin) |
4222507d | 207 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Class for applications which use cookies for sesion tracking. |
2aff8b5c | 208 | |
209 | Cookie session applications work exactly like | |
210 | standard-applications except that when the session is not found | |
211 | using the standard mechanisms the id is looked for in a cookie."</span>)) | |
212 | </pre> | |
213 | ||
214 | <p>This is the most useful of the application components. It makes your | |
215 | application urls readable by stashing the session id into a cookie | |
216 | rather than as a set of long and ugly GET parameters.</p> | |
217 | ||
218 | ||
219 | <h4><a name="sec5" id="sec5"></a> | |
220 | L10n</h4> | |
221 | ||
222 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/modular-application/l10n-module.lisp">src/rerl/modular-application/l10n-module.lisp</a></p> | |
223 | ||
224 | <pre class="src"> | |
7404d4e1 | 225 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">l10n-application-module</span> (modular-application-mixin) |
4222507d | 226 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Application class which can handle l10n requests."</span>)) |
2aff8b5c | 227 | </pre> |
228 | ||
229 | ||
230 | <h4><a name="sec6" id="sec6"></a> | |
231 | Secure</h4> | |
232 | ||
233 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/modular-application/security-module.lisp">src/rerl/modular-application/security-module.lisp</a></p> | |
234 | ||
235 | <pre class="src"> | |
7404d4e1 | 236 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">secure-application-module</span> (modular-application-mixin) |
237 | (<span class="emacs-face-builtin">:documentation</span> | |
4222507d | 238 | <span class="emacs-face-doc">"Mixin class for applications which require authorized access. |
2aff8b5c | 239 | Concrete application must specialize the following methods: |
240 | APPLICATION-FIND-USER (APPLICATION USERNAME) | |
241 | APPLICATION-CHECK-PASSWORD (APPLICATION USER PASSWORD) | |
242 | APPLICATION-AUTHORIZE-CALLZE-CALL (APPLICATION USER FROM-COMPONENT TO-COMPONENT)."</span>)) | |
243 | </pre> | |
244 | ||
245 | ||
246 | ||
247 | <h3><a name="sec7" id="sec7"></a> | |
248 | Components</h3> | |
249 | ||
250 | <p class="first">A component is a special class that handles the complexities of | |
251 | continuation suspension and such for you. New components are derived | |
252 | from the existing ones by using <code>defcomponent</code> instead of <code>defclass</code>. This | |
253 | adds a few extra slot and class options, and ensures that the proper | |
254 | metaclass is set.</p> | |
255 | ||
256 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/standard-component/standard-component.lisp">src/rerl/standard-component/standard-component.lisp</a></p> | |
257 | ||
258 | <pre class="src"> | |
7404d4e1 | 259 | (<span class="emacs-face-keyword">defmacro</span> <span class="emacs-face-function-name">defcomponent</span> (name supers slots <span class="emacs-face-type">&rest</span> options) |
260 | <span class="emacs-face-doc">"Macro for defining a component class. | |
2aff8b5c | 261 | |
262 | This macro is used to create component classes and provides | |
263 | options for easily creating the methods which often accompany a | |
264 | component definition. | |
265 | ||
266 | NAME, SUPERS and SLOTS as treated as per defclass. The following | |
267 | extra options are allowed: | |
268 | ||
269 | (:ENTRY-POINT url (&key application class)) - Define an | |
270 | entry-point on the url URL which simply calls an instance of | |
271 | this component. Any request parameters passed to the entry-point | |
272 | are used to initialize the slots in the component. This option | |
273 | may appear multiple times. | |
274 | ||
275 | (:DEFAULT-BACKTRACK function-designator) - Unless the slots | |
276 | already have a :backtrack option FUNCTION-DESIGNATOR is | |
277 | added. As with the 'regular' :backtrack options if you pass T | |
278 | here it is assumed to mean #'IDENTITY. | |
279 | ||
280 | (:RENDER (&optional COMPONENT) &body BODY) - Generate a render | |
281 | method specialized to COMPONENT. COMPONENT may be a symbol, in | |
282 | which case the method will be specialized on the componnet | |
283 | class. If COMPONNET is omited the component is bound to a | |
284 | variable with the same name as the class. | |
285 | ||
286 | (:ACTION &optional NAME) - Generate a defaction form named | |
287 | NAME (which defaults to the name of the component) which simply | |
288 | CALL's this component class passing all the arguments passed to | |
289 | the action as initargs."</span>) | |
290 | ||
7404d4e1 | 291 | <span class="emacs-face-comment-delimiter">;;; </span><span class="emacs-face-comment">Extra Slot Options |
292 | </span><span class="emacs-face-string">"Other than the initargs for standard slots the following | |
2aff8b5c | 293 | options can be passed to component slots: |
294 | ||
295 | :backtrack [ T | NIL | FUNCTION-NAME ] - Specify that this slot | |
296 | should be backtracked (or not if NIL is passed as the value). If | |
297 | the value is neither T nor NIL then it must be a function which | |
298 | will be used as the copyer. | |
299 | ||
300 | :component [ TYPE | ( TYPE &rest INITARGS ) ] - Specify that this | |
301 | slot is actually a nested component of type TYPE. When instances | |
302 | of the class are created this slot will be set to an instance of | |
303 | type TYPE and it's place will be set to this slot. If a list is | |
304 | passed to :component then TYPE (which isn't evaluated) will be | |
305 | passed as the first argument to make-instance. The INITARGS will | |
306 | be eval'd and apply'd to make-instance. The result of this call | |
307 | to make-instance will be used as the effective component | |
308 | object."</span> | |
309 | </pre> | |
310 | ||
311 | <h4><a name="sec8" id="sec8"></a> | |
312 | Windows</h4> | |
313 | ||
314 | <p class="first">A window-component represents a top level browser window, naturally.</p> | |
315 | ||
316 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/window.lisp">src/components/window.lisp</a></p> | |
317 | ||
318 | <pre class="src"> | |
7404d4e1 | 319 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">window-component</span> () |
2aff8b5c | 320 | ((content-type))) |
321 | ||
7404d4e1 | 322 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">simple-window-component</span> (window-component) |
2aff8b5c | 323 | ((title) |
324 | (stylesheet) | |
4222507d | 325 | (javascript <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"List of javascript includes. |
2aff8b5c | 326 | |
327 | Each element must be a list whose first value is either the | |
328 | symbol :SRC or :JS. | |
329 | ||
330 | (:SRC url) - writes <script src=\"URL\"></script> tag. | |
331 | (:JS form) - equivalent to (:SCRIPT (js:js* form)) | |
332 | (:SCRIPT string) - write <script>STRING</script>. | |
333 | ||
334 | The elements will be rendered in order."</span>) | |
335 | ...)) | |
336 | </pre> | |
337 | ||
338 | <p><code>window-component</code> could be useful for doing things like dumping binary | |
339 | data to the user, or just deriving your own funky top level window | |
340 | type.</p> | |
341 | ||
342 | <p><code>simple-window-component</code> is the easiest for displaying standard | |
343 | webpage. It provides a wrapping method on render that displays the | |
344 | html boilerplate based on your component slot values which is what one | |
345 | wants most of the time. The initargs to <code>simple-window-component</code> have | |
346 | the same names as the slots.</p> | |
347 | ||
348 | <h5>Status Bar</h5> | |
349 | ||
350 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/status-bar.lisp">src/components/status-bar.lisp</a></p> | |
351 | ||
352 | <p>There is a generic status bar interface. Messages severity is one of | |
353 | <code>(:error :warn :info)</code>. Note that the default status bar render method | |
354 | just shows a div with status messages. A derivative could be defined | |
355 | to insert messages into the browser status bar.</p> | |
356 | ||
357 | <pre class="src"> | |
358 | (defcomponent status-bar () | |
4222507d | 359 | ((messages <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"An ALIST of the messages to |
2aff8b5c | 360 | show. Each element is a cons of the form (SEVERITY . |
361 | MESSAGE). SEVERITY is one of :ERROR, :WARN, :INFO and MESSAGE is | |
362 | a string which will be html-escaped."</span>) | |
363 | ...) | |
4222507d | 364 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Stateless status bar to display messages."</span>)) |
2aff8b5c | 365 | |
7404d4e1 | 366 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">add-message</span> (status-bar msg <span class="emacs-face-type">&key</span> severity <span class="emacs-face-type">&allow-other-keys</span>) |
4222507d | 367 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Add the message text MSG to STATUS-BAR with |
2aff8b5c | 368 | severity SEVERITY."</span>)) |
369 | </pre> | |
370 | ||
371 | <pre class="src"> | |
372 | (defcomponent status-bar-mixin () | |
7404d4e1 | 373 | ((status-bar <span class="emacs-face-builtin">:accessor</span> status-bar |
374 | <span class="emacs-face-builtin">:initarg</span> status-bar | |
375 | <span class="emacs-face-builtin">:component</span> (status-bar)))) | |
2aff8b5c | 376 | |
7404d4e1 | 377 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">show-status-bar</span> ((win status-bar-mixin)) |
2aff8b5c | 378 | (render (status-bar win))) |
379 | ||
7404d4e1 | 380 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">show-message</span> (msg <span class="emacs-face-type">&key</span> severity <span class="emacs-face-type">&allow-other-keys</span>) |
4222507d | 381 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Show a message in the status bar. Only works if |
2aff8b5c | 382 | current window is a status-bar-mixin"</span>)) |
383 | </pre> | |
384 | ||
385 | ||
386 | <h5>Redirect</h5> | |
387 | ||
388 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/redirect.lisp">src/components/redirect.lisp</a></p> | |
389 | ||
390 | <pre class="src"> | |
7404d4e1 | 391 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">redirect-component</span> () |
392 | ((target <span class="emacs-face-builtin">:accessor</span> target <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:target</span>)) | |
393 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class) | |
4222507d | 394 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Send a client redirect. |
2aff8b5c | 395 | |
396 | This component, which must be used as a window-component, | |
397 | redirects the client to the url specified in the target slot. A | |
398 | 302 (as opposed to 303) response code is sent to ensure | |
399 | compatability with older browsers. | |
400 | ||
401 | The redirect component never answers."</span>)) | |
402 | </pre> | |
403 | ||
404 | <p>There is also a <code>meta-refresh</code> procedure.</p> | |
405 | ||
406 | <pre class="src"> | |
407 | (defun/cc meta-refresh () | |
7404d4e1 | 408 | <span class="emacs-face-string">"Cause a meta-refresh (a freshly got (GET) url) at this point. |
2aff8b5c | 409 | This is useful in order to have a GET url after a form POST's |
410 | actions have completed running. The user can then refresh to his | |
411 | heart's content."</span>) | |
412 | </pre> | |
413 | ||
414 | ||
415 | ||
416 | <h4><a name="sec9" id="sec9"></a> | |
417 | Containers</h4> | |
418 | ||
419 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/container.lisp">src/components/container.lisp</a></p> | |
420 | ||
421 | <pre class="src"> | |
7404d4e1 | 422 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">container</span> () |
2aff8b5c | 423 | (...) |
7404d4e1 | 424 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class) |
4222507d | 425 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Allow multiple components to share the same place. |
2aff8b5c | 426 | |
427 | The container component serves to manage a set of components. | |
428 | It does not provide any render impementation, which is the | |
429 | resposibility of the subclasses (e.g. switching-container or | |
430 | list-container). | |
431 | ||
432 | Each contained component has a \"key\" associated with it which | |
433 | is used to retrieve a particular component. Keys are compared with | |
434 | container.key-test. | |
435 | ||
436 | The :contents inintarg, if provided, must be either a list of (key . | |
437 | component) or a list of components. In the latter case it will | |
438 | be converted into (component . component) form."</span>)) | |
439 | </pre> | |
440 | ||
441 | <h5>Protocol</h5> | |
442 | ||
443 | <ul> | |
444 | <li><code>child-components</code></li> | |
445 | <li><code>find-component CONTAINER KEY</code></li> | |
446 | <li><code>remove-component</code></li> | |
447 | <li><code>(setf find-component CONTAINER KEY) COMPONENT</code> -> | |
448 | <code>add-component CONTAINER COMPONENT KEY</code></li> | |
449 | </ul> | |
450 | ||
451 | ||
452 | <h5>Switching Container</h5> | |
453 | ||
454 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/container.lisp">src/components/container.lisp</a></p> | |
455 | ||
456 | <pre class="src"> | |
7404d4e1 | 457 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">switching-container</span> ... |
4222507d | 458 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A simple renderable container component. |
2aff8b5c | 459 | |
460 | This component is like the regular CONTAINER but serves to manage a set | |
461 | of components which share the same place in the UI. Therefore it provides | |
462 | an implementation of RENDER which simply renders its current component. | |
463 | ||
464 | The switching-container component class is generally used as the super | |
465 | class for navigatation components and tabbed-pane like | |
466 | components."</span>)) | |
467 | </pre> | |
468 | ||
469 | <p>Subclass and <code>(defmethod render :around ...)</code> to render navigation using | |
470 | <code>(call-next-method)</code> to render the selected component.</p> | |
471 | ||
472 | <h5>Protocol</h5> | |
473 | ||
474 | <ul> | |
475 | <li><code>container.current-component COMPONENT</code></li> | |
476 | <li><code>(setf container.current-component CONTAINER) COMPONENT</code></li> | |
477 | </ul> | |
478 | ||
479 | ||
480 | ||
481 | <h5>Tabbed Pane</h5> | |
482 | ||
483 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/tabbed-pane.lisp">src/components/tabbed-pane.lisp</a></p> | |
484 | ||
485 | <pre class="src"> | |
486 | (defcomponent tabbed-pane (switching-container) | |
4222507d | 487 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Component for providing the user with a standard \"tabbed pane\" GUI widget."</span>)) |
2aff8b5c | 488 | </pre> |
489 | ||
490 | <p>Provides a generic tabbed pane that renders a nested div split into a | |
491 | naviation and content box. The navigation box is a set of styled divs | |
492 | containing the navigation links.</p> | |
493 | ||
494 | ||
495 | ||
496 | <h4><a name="sec10" id="sec10"></a> | |
497 | Dialogs</h4> | |
498 | ||
499 | <p class="first">A few convenience dialogs are provided for grabbing data from the | |
500 | user.</p> | |
501 | ||
502 | <h5>login</h5> | |
503 | ||
504 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/login.lisp">src/components/login.lisp</a></p> | |
505 | ||
506 | <pre class="src"> | |
7404d4e1 | 507 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">login</span> () |
2aff8b5c | 508 | ((username) (password) (message)) |
4222507d | 509 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Generic login (input username and password) component. |
2aff8b5c | 510 | |
511 | This component, which must be embedded in another component, | |
512 | presents the user with a simple two fielded login form. | |
513 | ||
514 | When the user attempts a login the action try-login is called, | |
515 | try-login calls the generic function check-credentials passing it | |
516 | the login component. If check-credentials returns true then the | |
517 | login-successful action is called, otherwise the message slot of | |
518 | the login component is set (to a generic \"bad username\" | |
519 | message). | |
520 | ||
521 | The default implementaion of login-successful simply answers t, | |
522 | no default implementation of check-credentials is | |
523 | provided. Developers should use sub-classes of login for which | |
524 | all the required methods have been definined."</span>) | |
7404d4e1 | 525 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 526 | </pre> |
527 | ||
528 | <pre class="src"> | |
7404d4e1 | 529 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">check-credentials</span> (login) |
4222507d | 530 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Returns T if LOGIN is valid."</span>)) |
2aff8b5c | 531 | |
532 | (defaction login-successful ((l login)) | |
533 | (answer t)) | |
534 | </pre> | |
535 | ||
536 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/user-login.lisp">src/components/user-login.lisp</a></p> | |
537 | ||
538 | <pre class="src"> | |
539 | (defcomponent user-login (simple-window-component status-bar-mixin) | |
540 | ((username string-field) (password password-field))) | |
541 | </pre> | |
542 | ||
543 | <p>Used by <code>secure-application-module</code> to provide a user login. Relevant | |
544 | protocol details follow.</p> | |
545 | ||
546 | <pre class="src"> | |
7404d4e1 | 547 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">check-credentials</span> ((self user-login)) |
548 | (<span class="emacs-face-keyword">let*</span> ((username (value (username self))) | |
2aff8b5c | 549 | (password (value (password self))) |
550 | (user (find-application-user username))) | |
f6d19803 | 551 | (<span class="emacs-face-keyword">when</span> (and user (<span class="emacs-face-warning">check-user-password</span> user password)) |
2aff8b5c | 552 | user))) |
553 | ||
7404d4e1 | 554 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">application-find-user</span> (application username) |
4222507d | 555 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Find USER by USERNAME for APPLICATION."</span>)) |
2aff8b5c | 556 | </pre> |
557 | ||
558 | ||
559 | <h5>error</h5> | |
560 | ||
561 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/error.lisp">src/components/error.lisp</a></p> | |
562 | ||
563 | <pre class="src"> | |
7404d4e1 | 564 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">error-message</span> (simple-window-component) |
565 | ((message <span class="emacs-face-builtin">:accessor</span> message <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:message</span> <span class="emacs-face-builtin">:initform</span> <span class="emacs-face-string">"ERROR [no message specified]"</span>)) | |
4222507d | 566 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Generic component for showing server side |
2aff8b5c | 567 | error messages."</span>) |
7404d4e1 | 568 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 569 | |
7404d4e1 | 570 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">error-component</span> (error-message) |
571 | ((condition <span class="emacs-face-builtin">:accessor</span> error.condition <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:condition</span> <span class="emacs-face-builtin">:initform</span> nil) | |
572 | (backtrace <span class="emacs-face-builtin">:accessor</span> error.backtrace <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:backtrace</span>)) | |
4222507d | 573 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Generic component for showing server side |
2aff8b5c | 574 | error conditions. Unlike ERROR-MESSAGE this component also |
575 | attempts to display a backtrace."</span>) | |
7404d4e1 | 576 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 577 | </pre> |
578 | ||
579 | ||
580 | <h5>message</h5> | |
581 | ||
582 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/message.lisp">src/components/message.lisp</a></p> | |
583 | ||
584 | <pre class="src"> | |
7404d4e1 | 585 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">info-message</span> () |
586 | ((message <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:message</span> <span class="emacs-face-builtin">:accessor</span> message) | |
587 | (ok-text <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:ok-text</span> <span class="emacs-face-builtin">:accessor</span> ok-text <span class="emacs-face-builtin">:initform</span> <span class="emacs-face-string">"Ok."</span>)) | |
4222507d | 588 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Component for showing a message to the user. |
2aff8b5c | 589 | |
590 | If the OK-TEXT slot is non-NIL component will use that as the | |
591 | text for a link which, when clicked, causes the component to | |
592 | answer. It follows that if OK-TEXT is NIL this component will | |
593 | never answer."</span>) | |
7404d4e1 | 594 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 595 | </pre> |
596 | ||
597 | ||
598 | <h5>option-dialog</h5> | |
599 | ||
600 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/option-dialog.lisp">src/components/option-dialog.lisp</a></p> | |
601 | ||
602 | <pre class="src"> | |
7404d4e1 | 603 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">option-dialog</span> (template-component) |
2aff8b5c | 604 | ((message) (options) (confirm)) |
7404d4e1 | 605 | (<span class="emacs-face-builtin">:default-initargs</span> <span class="emacs-face-builtin">:template-name</span> <span class="emacs-face-string">"ucw/option-dialog.tal"</span>) |
4222507d | 606 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Component for querying the user. |
2aff8b5c | 607 | |
608 | The value of the slot MESSAGE is used as a general heading. | |
609 | ||
610 | The OPTIONS slot must be an alist of (VALUE . LABEL). LABEL (a | |
611 | string) will be used as the text of a link which, when clikced, | |
612 | will answer VALUE. | |
613 | ||
614 | If the CONFIRM slot is T the user will be presented with a second | |
615 | OPTION-DIALOG asking the user if they are sure they want to | |
616 | submit that value."</span>) | |
7404d4e1 | 617 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 618 | </pre> |
619 | ||
620 | <p>A macro to present an option dialog is provided.</p> | |
621 | ||
622 | <pre class="src"> | |
7404d4e1 | 623 | (<span class="emacs-face-keyword">defmacro</span> <span class="emacs-face-function-name">option-dialog</span> ((message-spec <span class="emacs-face-type">&rest</span> message-args) <span class="emacs-face-type">&body</span> options) |
2aff8b5c | 624 | ...) |
625 | </pre> | |
626 | ||
627 | <p><code>message-spec</code> is passed to <code>format</code> if <code>message-args</code> are supplied, and | |
628 | used as a string literal otherwise. This does not provide a way to set | |
629 | the confirm property which makes the macro not so generally useful.</p> | |
630 | ||
631 | ||
632 | ||
633 | <h4><a name="sec11" id="sec11"></a> | |
634 | Forms</h4> | |
635 | ||
636 | <p class="first">Reasonably useful forms library that integrates easily with TAL.</p> | |
637 | ||
638 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/form.lisp">src/components/form.lisp</a></p> | |
639 | ||
640 | <pre class="src"> | |
7404d4e1 | 641 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">form-field</span> () |
4222507d | 642 | ((validators <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"List of validators which will be |
2aff8b5c | 643 | applied to this field."</span>) |
4222507d | 644 | (initially-validate <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"When non-NIL the |
2aff8b5c | 645 | validators will be run as soon as the page |
646 | is rendered."</span>))) | |
647 | ||
7404d4e1 | 648 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">value</span> (form-field) |
4222507d | 649 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The lispish translated value that represents the form-field."</span>)) |
7404d4e1 | 650 | |
4222507d | 651 | (<span class="emacs-face-keyword">defgeneric</span> (<span class="emacs-face-function-name">setf value</span>) (new-value form-field) |
652 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Set the value of a form-field with translation to client."</span>)) | |
7404d4e1 | 653 | |
654 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">generic-html-input</span> (form-field html-element) | |
655 | ((client-value <span class="emacs-face-builtin">:accessor</span> client-value <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:client-value</span> | |
656 | <span class="emacs-face-builtin">:initform</span> <span class="emacs-face-string">""</span> | |
4222507d | 657 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The string the client submitted along with this field."</span>) |
7404d4e1 | 658 | (name <span class="emacs-face-builtin">:accessor</span> name <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:name</span> <span class="emacs-face-builtin">:initform</span> nil) |
659 | (accesskey <span class="emacs-face-builtin">:accessor</span> accesskey <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:accesskey</span> <span class="emacs-face-builtin">:initform</span> nil) | |
660 | (tooltip <span class="emacs-face-builtin">:accessor</span> tooltip <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:tooltip</span> <span class="emacs-face-builtin">:initform</span> nil) | |
661 | (tabindex <span class="emacs-face-builtin">:accessor</span> tabindex <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:tabindex</span> <span class="emacs-face-builtin">:initform</span> nil)) | |
662 | (<span class="emacs-face-builtin">:default-initargs</span> <span class="emacs-face-builtin">:dom-id</span> (js:gen-js-name-string <span class="emacs-face-builtin">:prefix</span> <span class="emacs-face-string">"_ucw_"</span>))) | |
2aff8b5c | 663 | </pre> |
664 | ||
665 | <p>Fields are rendered into the extended <code><ucw:input</code> yaclml tag which | |
666 | supports a few fancy features. The <code>:accessor</code> for all form elements is | |
667 | set to <code>(client-value FIELD)</code>, and you should use <code>value</code> to access the | |
668 | Lisp value associated with it.</p> | |
669 | ||
670 | <pre class="src"> | |
7404d4e1 | 671 | (deftag-macro <ucw:input (<span class="emacs-face-type">&attribute</span> accessor action reader writer name id (default nil) |
672 | <span class="emacs-face-type">&allow-other-attributes</span> others) | |
673 | <span class="emacs-face-string">"Generic INPUT tag replacement. | |
2aff8b5c | 674 | |
675 | If the ACCESSOR attribute is specified then it must be a PLACE | |
676 | and it's value will be used to fill the input, when the form is | |
677 | submitted it will be set to the new value. | |
678 | ||
679 | If ACTION is specefied then when the form is submitted via this | |
680 | input type=\"submit\" tag the form will be eval'd. when the | |
681 | submit (or image) is clicked. DEFAULT means that the ACTION | |
682 | provided for this input tag will be the default action of the | |
683 | form when pressing enter in a form field. If more then one, then | |
684 | the latest wins."</span>) | |
685 | </pre> | |
686 | ||
687 | <p>Validation of form fields are supported by adding to the validators | |
688 | list.</p> | |
689 | ||
690 | <pre class="src"> | |
7404d4e1 | 691 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">validator</span> () |
692 | ((message <span class="emacs-face-builtin">:accessor</span> message <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:message</span> <span class="emacs-face-builtin">:initform</span> nil))) | |
2aff8b5c | 693 | |
7404d4e1 | 694 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">validate</span> (field validator) |
4222507d | 695 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Validate a form-field with a validator."</span>)) |
2aff8b5c | 696 | |
7404d4e1 | 697 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">javascript-check</span> (field validator) |
4222507d | 698 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Generate javascript code for checking FIELD against VALIDATOR. |
2aff8b5c | 699 | |
700 | This is the convenience entry point to generate-javascript-check, | |
701 | methods defined on this generic funcition should return a list of | |
702 | javascript code (as per parenscript) which tests against the | |
703 | javascript variable value."</span>)) | |
704 | ||
7404d4e1 | 705 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">javascript-invalid-handler</span> (field validator) |
4222507d | 706 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The javascript code body for when a field is invalid."</span>)) |
2aff8b5c | 707 | |
7404d4e1 | 708 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">javascript-valid-handler</span> (field validator) |
4222507d | 709 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Generate the javascript body for when a field is valid."</span>)) |
2aff8b5c | 710 | </pre> |
711 | ||
712 | <h5>Standard Form Fields</h5> | |
713 | ||
714 | <pre class="src"> | |
7404d4e1 | 715 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">string-field</span> (generic-html-input) |
2aff8b5c | 716 | ((input-size) (maxlength))) |
717 | ||
7404d4e1 | 718 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">password-field</span> (string-field)) |
719 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">number-field</span> (string-field)) | |
720 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">integer-field</span> (number-field)) | |
2aff8b5c | 721 | |
7404d4e1 | 722 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">in-field-string-field</span> (string-field) |
4222507d | 723 | ((in-field-label <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"This slot, if non-NIL, will be |
2aff8b5c | 724 | used as an initial field label. An initial |
725 | field label is a block of text which is placed | |
726 | inside the input element and removed as soon | |
727 | as the user edits the field. Obviously this | |
728 | field is overidden by an initial :client-value | |
729 | argument."</span>))) | |
730 | ||
7404d4e1 | 731 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">textarea-field</span> (generic-html-input) |
2aff8b5c | 732 | ((rows) (cols))) |
733 | ||
7404d4e1 | 734 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">date-field</span> (form-field widget-component) |
2aff8b5c | 735 | ((year) (month) (day))) |
736 | ||
7404d4e1 | 737 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">dmy-date-field</span> (date-field) |
4222507d | 738 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Date fields which orders the inputs day/month/year"</span>)) |
7404d4e1 | 739 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">mdy-date-field</span> (date-field)) |
2aff8b5c | 740 | |
7404d4e1 | 741 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">select-field</span> (generic-html-input) |
4222507d | 742 | ((data-set <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The values this select chooses |
2aff8b5c | 743 | from."</span>)) |
4222507d | 744 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Form field used for selecting one value from a |
2aff8b5c | 745 | list of available options."</span>)) |
746 | ||
7404d4e1 | 747 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">render-value</span> (select-field value) |
4222507d | 748 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"This function will be passed each value in the field's |
2aff8b5c | 749 | data-set and must produce the body of the corresponding |
750 | <ucw:option tag."</span>)) | |
751 | ||
7404d4e1 | 752 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">mapping-select-field</span> (select-field) |
4222507d | 753 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Class used when we want to chose the values of |
2aff8b5c | 754 | a certain mapping based on the keys. We render the keys in the |
755 | select and return the corresponding value from the VALUE | |
756 | method."</span>)) | |
757 | ||
7404d4e1 | 758 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">hash-table-select-field</span> (mapping-select-field)) |
759 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">alist-select-field</span> (mapping-select-field)) | |
760 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">plist-select-field</span> (mapping-select-field)) | |
2aff8b5c | 761 | |
7404d4e1 | 762 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">radio-group</span> (generic-html-input) |
2aff8b5c | 763 | ((value-widgets))) |
764 | ||
7404d4e1 | 765 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">radio-button</span> (generic-html-input) |
2aff8b5c | 766 | ((value) |
4222507d | 767 | (group <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The RADIO-GROUP this button is a part |
2aff8b5c | 768 | of."</span>)) |
4222507d | 769 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A widget representing a single radio |
2aff8b5c | 770 | button. Should be used in conjunction with a RADIO-GROUP."</span>)) |
771 | ||
7404d4e1 | 772 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">add-value</span> ((group radio-group) value) |
4222507d | 773 | <span class="emacs-face-doc">"Adds radio-button with value to group"</span>) |
2aff8b5c | 774 | |
7404d4e1 | 775 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">checkbox-field</span> (generic-html-input)) |
776 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">file-upload-field</span> (generic-html-input)) | |
777 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">submit-button</span> (generic-html-input) | |
2aff8b5c | 778 | ((label))) |
779 | </pre> | |
780 | ||
781 | <h5>File Upload Field</h5> | |
782 | ||
783 | <p>Calling <code>value</code> on a <code>file-upload-field</code> returns a mime encoded body | |
784 | part. <code>(mime-part-body (value FIELD))</code> will return a <strong>binary stream</strong> | |
785 | attached to the contents of the file. The <code>Content-Type</code> header should | |
786 | be set to the MIME type of the file being uploaded.</p> | |
787 | ||
788 | <pre class="src"> | |
7404d4e1 | 789 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">mime-part-headers</span> (mime-part) |
4222507d | 790 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Returns an alist of the headers of MIME-PART. |
2aff8b5c | 791 | |
792 | The alist must be of the form (NAME . VALUE) where both NAME and | |
793 | VALUE are strings."</span>)) | |
794 | ||
7404d4e1 | 795 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">mime-part-body</span> (mime-part) |
4222507d | 796 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Returns the body of MIME-PART."</span>)) |
2aff8b5c | 797 | </pre> |
798 | ||
799 | ||
800 | ||
801 | <h5>Standard Validators</h5> | |
802 | ||
803 | <pre class="src"> | |
7404d4e1 | 804 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">not-empty-validator</span> (validator)) |
2aff8b5c | 805 | |
7404d4e1 | 806 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">value-validator</span> (validator) |
4222507d | 807 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Validators that should only be applied if there is a value. |
2aff8b5c | 808 | That is, they always succeed on nil."</span>)) |
809 | ||
7404d4e1 | 810 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">length-validator</span> (value-validator) |
811 | ((min-length <span class="emacs-face-builtin">:accessor</span> min-length <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:min-length</span> | |
812 | <span class="emacs-face-builtin">:initform</span> nil) | |
813 | (max-length <span class="emacs-face-builtin">:accessor</span> max-length <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:max-length</span> | |
814 | <span class="emacs-face-builtin">:initform</span> nil))) | |
2aff8b5c | 815 | |
7404d4e1 | 816 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">string=-validator</span> (validator) |
817 | ((other-field <span class="emacs-face-builtin">:accessor</span> other-field <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:other-field</span>)) | |
4222507d | 818 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Ensures that a field is string= to another one."</span>)) |
2aff8b5c | 819 | |
7404d4e1 | 820 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">regex-validator</span> (value-validator) |
821 | ((regex <span class="emacs-face-builtin">:accessor</span> regex <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:regex</span> <span class="emacs-face-builtin">:initform</span> nil))) | |
2aff8b5c | 822 | |
7404d4e1 | 823 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">e-mail-address-validator</span> (regex-validator)) |
2aff8b5c | 824 | |
7404d4e1 | 825 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">phone-number-validator</span> (regex-validator)) |
2aff8b5c | 826 | |
7404d4e1 | 827 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">is-a-number-validator</span> (value-validator)) |
828 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">is-an-integer-validator</span> (is-a-number-validator)) | |
2aff8b5c | 829 | |
7404d4e1 | 830 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">number-range-validator</span> (is-a-number-validator) |
831 | ((min-value <span class="emacs-face-builtin">:accessor</span> min-value <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:min-value</span> <span class="emacs-face-builtin">:initform</span> nil) | |
832 | (max-value <span class="emacs-face-builtin">:accessor</span> max-value <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:max-value</span> <span class="emacs-face-builtin">:initform</span> nil))) | |
2aff8b5c | 833 | </pre> |
834 | ||
835 | ||
836 | <h5>Simple Form Helper</h5> | |
837 | ||
838 | <p>UCW provides a helper class for developing forms. Subclass and add the | |
839 | elements you wish to include in the form. A <code>:wrapping</code> method renders | |
840 | the form boilerplate and then calls your <code>render</code>.</p> | |
841 | ||
842 | <pre class="src"> | |
843 | (defcomponent simple-form (html-element) | |
7404d4e1 | 844 | ((submit-method <span class="emacs-face-builtin">:accessor</span> submit-method |
845 | <span class="emacs-face-builtin">:initform</span> <span class="emacs-face-string">"post"</span> | |
846 | <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:submit-method</span>) | |
847 | (dom-id <span class="emacs-face-builtin">:accessor</span> dom-id | |
848 | <span class="emacs-face-builtin">:initform</span> (js:gen-js-name-string <span class="emacs-face-builtin">:prefix</span> <span class="emacs-face-string">"_ucw_simple_form_"</span>) | |
849 | <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:dom-id</span>)) | |
850 | (<span class="emacs-face-builtin">:default-initargs</span> <span class="emacs-face-builtin">:dom-id</span> <span class="emacs-face-string">"ucw-simple-form"</span>)) | |
2aff8b5c | 851 | </pre> |
852 | ||
853 | ||
854 | ||
855 | <h4><a name="sec12" id="sec12"></a> | |
856 | Templates</h4> | |
857 | ||
858 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/template.lisp">src/components/template.lisp</a></p> | |
859 | ||
860 | <p>Infrastructure for loading TAL templates as a view of a component.</p> | |
861 | ||
862 | <pre class="src"> | |
7404d4e1 | 863 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">template-component</span> (component)) |
2aff8b5c | 864 | (defcomponent simple-template-component (template-component) |
7404d4e1 | 865 | ((environment <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:environment</span> <span class="emacs-face-builtin">:initform</span> nil))) |
2aff8b5c | 866 | |
7404d4e1 | 867 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">template-component-environment</span> (component) |
4222507d | 868 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Create the TAL environment for rendering COMPONENT's template. |
2aff8b5c | 869 | |
870 | Methods defined on this generic function must return a TAL | |
871 | environment: a list of TAL binding sets (see the documentation | |
872 | for YACLML:MAKE-STANDARD-ENVIRONMENT for details on TAL | |
873 | environments.)"</span>) | |
7404d4e1 | 874 | (<span class="emacs-face-builtin">:method-combination</span> nconc)) |
2aff8b5c | 875 | |
7404d4e1 | 876 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">template-component-environment</span> nconc ((component template-component)) |
877 | <span class="emacs-face-string">"Create the basic TAL environment. | |
2aff8b5c | 878 | |
879 | Binds the symbol ucw:component to the component object itself, | |
880 | also puts the object COMPONENT on the environment (after the | |
881 | binding of ucw:component) so that slots are, by default, | |
882 | visable."</span> | |
883 | (make-standard-environment `((component . ,component)) component)) | |
884 | ||
7404d4e1 | 885 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">render</span> ((component template-component)) |
4222507d | 886 | <span class="emacs-face-doc">"Render a template based component. |
2aff8b5c | 887 | |
888 | Calls the component's template. The name of the template is the | |
889 | value returned by the generic function | |
890 | template-component.template-name, the template will be rendered | |
891 | in the environment returned by the generic function | |
892 | template-component-environment."</span> | |
893 | (render-template *context* | |
894 | (template-component.template-name component) | |
895 | (template-component-environment component))) | |
896 | ||
897 | </pre> | |
898 | ||
899 | <p>Subclass and override methods. <code>simple-template-component</code> only provides | |
900 | the ability to set environment variables in initarg. Subclass to | |
901 | provide automagic template file name generation and such.</p> | |
902 | ||
903 | ||
904 | <h4><a name="sec13" id="sec13"></a> | |
905 | Utility Mixin Components</h4> | |
906 | ||
907 | <h5>Range View</h5> | |
908 | ||
909 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/range-view.lisp">src/components/range-view.lisp</a></p> | |
910 | ||
911 | <pre class="src"> | |
7404d4e1 | 912 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">range-view</span> (template-component) |
913 | (<span class="emacs-face-builtin">:default-initargs</span> <span class="emacs-face-builtin">:template-name</span> <span class="emacs-face-string">"ucw/range-view.tal"</span>) | |
4222507d | 914 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Component for showing the user a set of data one \"window\" at a time. |
2aff8b5c | 915 | |
916 | The data set is presented one \"window\" at a time with links to | |
917 | the the first, previous, next and last window. Each window shows | |
918 | at most WINDOW-SIZE elements of the data. The data is passed to | |
919 | the range-view at instance creation time via the :DATA initarg. | |
920 | ||
921 | The generic function RENDER-RANGE-VIEW-ITEM is used to render | |
922 | each item of DATA. | |
923 | ||
924 | In order to change the rendering of the single elements of a | |
925 | range view developer's should create a sub class of RANGE-VIEW | |
926 | and define their RENDER-RANGE-VIEW-ITEM methods on that."</span>) | |
7404d4e1 | 927 | (<span class="emacs-face-builtin">:metaclass</span> standard-component-class)) |
2aff8b5c | 928 | </pre> |
929 | ||
930 | <pre class="src"> | |
7404d4e1 | 931 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">render-range-view-item</span> (range-view item) |
4222507d | 932 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Render a single element of a range-view."</span>) |
7404d4e1 | 933 | (<span class="emacs-face-builtin">:method</span> ((range-view range-view) (item t)) |
934 | <span class="emacs-face-string">"Standard implementation of RENDER-RANGE-VIEW-ITEM. Simply | |
2aff8b5c | 935 | applies ITEM to princ (via <:as-html)."</span> |
7404d4e1 | 936 | (<span class="emacs-face-keyword">declare</span> (ignore range-view)) |
2aff8b5c | 937 | (<:as-html item))) |
938 | </pre> | |
939 | ||
940 | ||
941 | <h5>Widget</h5> | |
942 | ||
943 | <p>Mixin with existing component to wrap in a div or span. This is handy | |
944 | for defining lightweight widgets embedded within other components.</p> | |
945 | ||
946 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/html-element.lisp">src/components/html-element.lisp</a></p> | |
947 | ||
948 | <pre class="src"> | |
7404d4e1 | 949 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">html-element</span> (component) |
2aff8b5c | 950 | ((css-class) |
951 | (dom-id) | |
952 | (css-style) | |
953 | (extra-tags) | |
954 | (events)) | |
4222507d | 955 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"An HTML element. |
2aff8b5c | 956 | |
957 | HTML elements control aspects that are relevant to almost all tags. | |
958 | ||
959 | Firstly they provide a place to store the class, id, and style of the | |
960 | component. The specific render methods of the components themselves | |
961 | must pass these values to whatever code is used to render the actual | |
962 | tag. | |
963 | ||
964 | Secondly, they allow javascript event handlers to be registered for a | |
965 | tag. The events slot can be filled with a list of lists in the form | |
966 | ||
967 | (event parenscript-statement*) | |
968 | ||
969 | For example (\"onclick\" (alert \"You clicked!\") (return nil)). If | |
970 | the element has a dom-id, these event handlers are automatically | |
971 | added."</span>)) | |
972 | </pre> | |
973 | ||
974 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/widget.lisp">src/components/widget.lisp</a></p> | |
975 | ||
976 | <pre class="src"> | |
7404d4e1 | 977 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">widget-component</span> (html-element) |
2aff8b5c | 978 | () |
4222507d | 979 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A widget which should be wrapped in a <div>."</span>)) |
2aff8b5c | 980 | |
7404d4e1 | 981 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">inline-widget-component</span> (html-element) |
2aff8b5c | 982 | () |
4222507d | 983 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A widget which should be wrapped in <span> and not <div>"</span>)) |
2aff8b5c | 984 | |
7404d4e1 | 985 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">render</span> <span class="emacs-face-builtin">:wrap-around</span> ((widget widget-component))) |
986 | (<span class="emacs-face-keyword">defmethod</span> <span class="emacs-face-function-name">render</span> <span class="emacs-face-builtin">:wrap-around</span> ((widget inline-widget-component))) | |
2aff8b5c | 987 | </pre> |
988 | ||
989 | ||
990 | <h5>Transactions</h5> | |
991 | ||
992 | <p>A mixin to provide transactions. <code>(open-transaction component)</code> and | |
993 | <code>(close-transaction component)</code> open and closed nested | |
994 | transactions. After a transaction has been closed an attempt to | |
995 | backtrack into a step inside the transaction will result in jumping up | |
996 | one level of transactions (or out of the transaction entirely if at | |
997 | the top level). This ensures that the transaction is only run once, | |
998 | naturally.</p> | |
999 | ||
1000 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/transaction-mixin.lisp">src/components/transaction-mixin.lisp</a></p> | |
1001 | ||
1002 | <pre class="src"> | |
1003 | (defcomponent transaction-mixin () | |
1004 | (...)) | |
1005 | ||
1006 | (defmethod/cc open-transaction ((comp transaction-mixin))) | |
1007 | (defmethod/cc close-transaction ((comp transaction-mixin))) | |
1008 | </pre> | |
1009 | ||
1010 | ||
1011 | <h5>Task</h5> | |
1012 | ||
1013 | <p><code>(defaction start ...)</code> on subclass to run a series of actions bundled | |
1014 | into a task.</p> | |
1015 | ||
1016 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/task.lisp">src/components/task.lisp</a></p> | |
1017 | ||
1018 | <pre class="src"> | |
7404d4e1 | 1019 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">task-component</span> (standard-component) |
2aff8b5c | 1020 | (...) |
4222507d | 1021 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A controller for a single task or operation to |
2aff8b5c | 1022 | be performed by the user. |
1023 | ||
1024 | A task component's START action is called as soon as the | |
1025 | component is instantiated. Task components do not have their own | |
1026 | RENDER method, in fact they have no graphical representation but | |
1027 | serve only to order a sequence of other components."</span>)) | |
1028 | ||
1029 | (defgeneric/cc start (task) | |
4222507d | 1030 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"action which gets called automatically when |
2aff8b5c | 1031 | task-component is active. Use defaction to define your own |
1032 | \"start\" action"</span>)) | |
1033 | </pre> | |
1034 | ||
1035 | ||
1036 | <h5>Cached</h5> | |
1037 | ||
1038 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/components/cached.lisp">src/components/cached.lisp</a></p> | |
1039 | ||
1040 | <pre class="src"> | |
1041 | (defcomponent cached-component () | |
7404d4e1 | 1042 | ((cached-output <span class="emacs-face-builtin">:accessor</span> cached-output <span class="emacs-face-builtin">:initform</span> nil |
4222507d | 1043 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"A string holding the output to |
2aff8b5c | 1044 | use for this component. This string will be |
1045 | written directly to the html stream and is | |
1046 | changed by the REFRESH-COMPONENT-OUTPUT | |
1047 | method."</span> ) | |
7404d4e1 | 1048 | (timeout <span class="emacs-face-builtin">:accessor</span> timeout <span class="emacs-face-builtin">:initarg</span> <span class="emacs-face-builtin">:timeout</span> |
4222507d | 1049 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"An value specifying how often this |
2aff8b5c | 1050 | component needs to be refreshed. The exact |
1051 | interpretation of the value depends on the type of | |
1052 | caching used class."</span>)) | |
4222507d | 1053 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Component which caches its output. |
2aff8b5c | 1054 | |
1055 | The component caching API is built around the generic functions | |
1056 | COMPONENT-DIRTY-P and REFRESH-COMPONENT-OUTPUT and a method on | |
1057 | RENDER, see the respective docstrings for more details. | |
1058 | ||
1059 | Do not use CACHED-COMPONENT directly, use one its subclasses."</span>)) | |
1060 | ||
7404d4e1 | 1061 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">component-dirty-p</span> (component) |
4222507d | 1062 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Returns T is COMPONENT's cache is invalid."</span>)) |
2aff8b5c | 1063 | |
7404d4e1 | 1064 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">update-cache</span> (component) |
4222507d | 1065 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Update COMPONENT's cache variables after a refresh."</span>)) |
2aff8b5c | 1066 | |
1067 | (defcomponent timeout-cache-component (cached-component) | |
7404d4e1 | 1068 | ((last-refresh <span class="emacs-face-builtin">:accessor</span> last-refresh <span class="emacs-face-builtin">:initform</span> nil |
4222507d | 1069 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"The time, exrpessed as a |
2aff8b5c | 1070 | universal time, when the component was last rendered."</span>)) |
7404d4e1 | 1071 | (<span class="emacs-face-builtin">:default-initargs</span> |
1072 | <span class="emacs-face-builtin">:timeout</span> (* 30 60 60)) | |
4222507d | 1073 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Render the component at most every TIMEOUT seconds."</span>)) |
2aff8b5c | 1074 | |
1075 | (defcomponent num-hits-cache-component (cached-component) | |
7404d4e1 | 1076 | ((hits-since-refresh <span class="emacs-face-builtin">:accessor</span> hits-since-refresh |
1077 | <span class="emacs-face-builtin">:initform</span> nil | |
4222507d | 1078 | <span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Number of views since last refresh."</span>)) |
7404d4e1 | 1079 | (<span class="emacs-face-builtin">:default-initargs</span> <span class="emacs-face-builtin">:timeout</span> 10) |
4222507d | 1080 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Render the component every TIMEOUT views."</span>)) |
2aff8b5c | 1081 | </pre> |
1082 | ||
1083 | <p>Subclass and override <code>component-dirty-p</code> to do something useful | |
1084 | (e.g. flip mark bit when object being presented changes).</p> | |
1085 | ||
1086 | ||
1087 | ||
1088 | ||
1089 | <h3><a name="sec14" id="sec14"></a> | |
1090 | Control Flow</h3> | |
1091 | ||
1092 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/standard-component/control-flow.lisp">src/rerl/standard-component/control-flow.lisp</a></p> | |
1093 | ||
1094 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/standard-action.lisp">src/rerl/standard-action.lisp</a></p> | |
1095 | ||
1096 | <h4><a name="sec15" id="sec15"></a> | |
1097 | Calling</h4> | |
1098 | ||
1099 | <p class="first">Most of what you do in UCW will be calling components so this is a bit | |
1100 | important. Note that calling interrupts the current control flow so if | |
1101 | you want to render a component in place as part of another component | |
1102 | just call <code>render</code> on it instead.</p> | |
1103 | ||
1104 | <pre class="src"> | |
7404d4e1 | 1105 | (<span class="emacs-face-keyword">defmacro</span> <span class="emacs-face-function-name">call</span> (component-type <span class="emacs-face-type">&rest</span> component-init-args) |
1106 | <span class="emacs-face-doc">"Stop the execution of the current action and pass control to | |
2aff8b5c | 1107 | a freshly created component of type COMPONENT-TYPE. |
1108 | ||
1109 | COMPONENT-INIT-ARGS are passed directly to the underlying | |
1110 | make-instance call. This form will return if and when the call'd | |
1111 | component calls answer, the value returned by this form is | |
1112 | whatever the call'd component passed to answer. | |
1113 | ||
1114 | Notes: | |
1115 | ||
1116 | This macro assumes that the lexcial variable UCW:SELF is bound to | |
1117 | the calling component."</span>) | |
1118 | ||
4222507d | 1119 | (answer VAL) <span class="emacs-face-comment">; answer parent component ONLY IN ACTIONS |
2aff8b5c | 1120 | </span> |
4222507d | 1121 | (ok SELF VAL) <span class="emacs-face-comment">; Used to answer a component anywhere and what answer |
1122 | </span> <span class="emacs-face-comment">; expands into | |
2aff8b5c | 1123 | </span> |
4222507d | 1124 | (jump COMPONENT-NAME <span class="emacs-face-type">&REST</span> ARGS) <span class="emacs-face-comment">; is similar to call, but replaces |
1125 | </span> <span class="emacs-face-comment">; the current component with the new | |
1126 | </span> <span class="emacs-face-comment">; one and drops any backtracks (back | |
1127 | </span> <span class="emacs-face-comment">; button will no longer work) | |
2aff8b5c | 1128 | </span></pre> |
1129 | ||
1130 | <p><code>(call COMPONENT-NAME &ARGS INIT-ARGS)</code> calls <code>COMPONENT-NAME</code> and returns | |
1131 | the value returned by <code>(ok SELF RETURN-VALUE)</code> called from within | |
1132 | <code>COMPONENT-NAME</code></p> | |
1133 | ||
1134 | ||
1135 | <h4><a name="sec16" id="sec16"></a> | |
1136 | Actions</h4> | |
1137 | ||
1138 | <p class="first">Actions are methods on components. The first argument <strong>must</strong> be a | |
1139 | component for most of UCW to work.</p> | |
1140 | ||
1141 | <pre class="src"> | |
1142 | (defaction NAME (first ...) ...) | |
4222507d | 1143 | <span class="emacs-face-comment">; (roughly) expands into |
2aff8b5c | 1144 | </span>(defmethod/cc NAME (first ...) |
7404d4e1 | 1145 | (<span class="emacs-face-keyword">let</span> ((self first)) |
2aff8b5c | 1146 | ...)) |
1147 | </pre> | |
1148 | ||
1149 | <p><code>Self</code> being bound in the current lexical environment is required for | |
1150 | most UCW control flow things to work. <code>defaction</code> hides this from you, | |
1151 | and was a big source of confusion for me early on (mostly "hmm, why is | |
1152 | this not working ... where did that come from in the | |
1153 | macroexpansion!").</p> | |
1154 | ||
1155 | ||
1156 | <h4><a name="sec17" id="sec17"></a> | |
1157 | Entry Points</h4> | |
1158 | ||
1159 | <pre class="src"> | |
7404d4e1 | 1160 | (defentry-point url (<span class="emacs-face-builtin">:application</span> APPLICATION |
1161 | <span class="emacs-face-builtin">:class</span> DISPATCHER-CLASS) | |
4222507d | 1162 | (PARAM1 ... PARAMN) <span class="emacs-face-comment">; GET / POST vars, bound in body |
2aff8b5c | 1163 | </span> body) |
1164 | </pre> | |
1165 | ||
1166 | <p>An entry point is what it sounds like: a static URL matched using the | |
1167 | mater of <code>DISPATCHER-CLASS</code> that enters into <code>APPLICATION</code> running the | |
1168 | code in <code>body</code>. An example from a test program I have written | |
1169 | follows. The entry point allows files to be streamed to user when the | |
1170 | url audio.ucw?file=FOO is used.</p> | |
1171 | ||
1172 | <pre class="src"> | |
7404d4e1 | 1173 | (defentry-point <span class="emacs-face-string">"^(audio.ucw|)$"</span> (<span class="emacs-face-builtin">:application</span> *golf-test-app* |
1174 | <span class="emacs-face-builtin">:class</span> regexp-dispatcher) | |
2aff8b5c | 1175 | (file) |
1176 | (call 'audio-file-window | |
7404d4e1 | 1177 | <span class="emacs-face-builtin">:audio-file</span> (make-instance 'audio-file |
1178 | <span class="emacs-face-builtin">:type</span> <span class="emacs-face-builtin">:vorbis</span> | |
1179 | <span class="emacs-face-builtin">:data</span> (file->bytes (open | |
2aff8b5c | 1180 | file |
7404d4e1 | 1181 | <span class="emacs-face-builtin">:element-type</span> 'unsigned-byte))))) |
2aff8b5c | 1182 | </pre> |
1183 | ||
1184 | ||
1185 | ||
1186 | <h3><a name="sec18" id="sec18"></a> | |
1187 | Dispatching</h3> | |
1188 | ||
1189 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/rerl/standard-dispatcher.lisp">src/rerl/standard-dispatcher.lisp</a></p> | |
1190 | ||
1191 | <pre class="src"> | |
7404d4e1 | 1192 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">matcher-match</span> (matcher application context) |
4222507d | 1193 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Abstract method for subclasses to implement a |
2aff8b5c | 1194 | matcher. This method would return multiple-values according to |
1195 | matcher internal nature. | |
1196 | ||
1197 | No methods defined on this function may rebind *context*, nor | |
1198 | change CONTEXT's application. Only if the method matches the | |
1199 | request, it is allowed to modify CONTEXT or APPLICATION, even in | |
1200 | that case methods defined on this function must not modify | |
1201 | CONTEXT's application nor rebind *context*."</span>)) | |
1202 | ||
7404d4e1 | 1203 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">handler-handle</span> (handler application context matcher-result) |
4222507d | 1204 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Abstract function for handler classes to |
2aff8b5c | 1205 | implement in order to handle a request matched by relevant |
1206 | matcher. | |
1207 | ||
1208 | These methods may modify context as they wish since they'r | |
1209 | matched, request will be closed after this method is run."</span>)) | |
1210 | ||
7404d4e1 | 1211 | (<span class="emacs-face-keyword">defgeneric</span> <span class="emacs-face-function-name">dispatch</span> (dispatcher application context) |
4222507d | 1212 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"Entry point into a dispatcher. Must return T |
2aff8b5c | 1213 | if the context has been handled or NIL if it hasn't. |
1214 | ||
1215 | No methods defined on this function may rebind *context*, nor | |
1216 | change CONTEXT's application. Only if the method returns T is it | |
1217 | allowed to modify CONTEXT or APPLICATION, even in that case | |
1218 | methods defined on this function must not modify CONTEXT's | |
1219 | application nor rebind *context*."</span>)) | |
1220 | </pre> | |
1221 | ||
1222 | <pre class="src"> | |
7404d4e1 | 1223 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">my-matcher</span> (abstract-matcher) ...) |
1224 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">my-handler</span> (abstract-handler) ...) | |
1225 | (<span class="emacs-face-keyword">defclass</span> <span class="emacs-face-type">my-dispatcher</span> (abstract-dispatcher my-matcher my-handler) | |
2aff8b5c | 1226 | ...) |
1227 | </pre> | |
1228 | ||
1229 | <h4><a name="sec19" id="sec19"></a> | |
1230 | Simple Dispatcher</h4> | |
1231 | ||
1232 | <pre class="src"> | |
4222507d | 1233 | (<span class="emacs-face-builtin">:documentation</span> <span class="emacs-face-doc">"This class of dispatchers avoids all of UCW's |
2aff8b5c | 1234 | standard call/cc (and therefore frame/backtracking/component) |
1235 | mechanism. | |
1236 | ||
1237 | Unlike all other UCW dispatchers a simple-dispatcher must not use | |
1238 | CALL, and must perform the rendering directly within the handler."</span>) | |
1239 | </pre> | |
1240 | ||
1241 | ||
1242 | ||
1243 | <h3><a name="sec20" id="sec20"></a> | |
1244 | Server</h3> | |
1245 | ||
1246 | <p><a href="http://www.uncommon-web.com/darcsweb/darcsweb.cgi?r=ucw_dev;a=headblob;f=/src/control.lisp">src/control.lisp</a></p> | |
1247 | ||
1248 | <pre class="src"> | |
7404d4e1 | 1249 | (<span class="emacs-face-keyword">defun</span> <span class="emacs-face-function-name">create-server</span> (<span class="emacs-face-type">&key</span> |
1250 | (backend `(,*ucw-backend-type* <span class="emacs-face-builtin">:host</span> ,*ucw-backend-host* | |
1251 | <span class="emacs-face-builtin">:port</span> ,*ucw-backend-port*)) | |
2aff8b5c | 1252 | (applications *ucw-applications*) |
1253 | (start-p t) | |
1254 | (server-class *ucw-server-class*) | |
1255 | (log-root-directory (truename *ucw-log-root-directory*)) | |
1256 | (log-level *ucw-log-level*)) | |
7404d4e1 | 1257 | <span class="emacs-face-doc">"Creates and returns a UCW server according to SERVER-CLASS, HOST and |
2aff8b5c | 1258 | PORT. Affects *DEFAULT-SERVER*. |
1259 | ||
1260 | BACKEND is a list of (BACKEND-TYPE &rest INITARGS). BACKEND-TYPE | |
1261 | may be :HTTPD, :MOD-LISP, :ASERVE, :ARANEIDA, an existing | |
1262 | backend, an existing UCW server backend or :DEFAULT in which case | |
1263 | it attempts to return a sane default from the UCW backends loaded | |
1264 | and available, or any other value for which a valid MAKE-BACKEND | |
1265 | method has been defined. INITARGS will be passed, unmodified, to | |
1266 | MAKE-BACKEND. | |
1267 | ||
1268 | APPLICATIONS is a list of defined applications to be loaded into the | |
1269 | server. | |
1270 | ||
1271 | Logs are generated in verbosity defined by LOG-LEVEL and directed to | |
1272 | LOG-ROOT-DIRECTORY if defined."</span> | |
1273 | ... | |
4222507d | 1274 | server) <span class="emacs-face-comment">; return server, naturally |
2aff8b5c | 1275 | </span></pre> |
1276 | ||
1277 | ||
1278 | <h3><a name="sec21" id="sec21"></a> | |
1279 | Debugging</h3> | |
1280 | ||
1281 | <h4><a name="sec22" id="sec22"></a> | |
1282 | Inspector</h4> | |
1283 | ||
1284 | <p><a href="/home/clinton/src/ucw/darcs/ucw_dev/src/components/ucw-inspector.lisp">/home/clinton/src/ucw/darcs/ucw_dev/src/components/ucw-inspector.lisp</a></p> | |
1285 | ||
1286 | <pre class="src"> | |
1287 | (defaction call-inspector ((component component) datum) | |
7404d4e1 | 1288 | <span class="emacs-face-string">"Call an inspector for DATUM on the component COMPONENT."</span> |
1289 | (call 'ucw-inspector <span class="emacs-face-builtin">:datum</span> datum)) | |
2aff8b5c | 1290 | </pre> |
1291 | ||
1292 | ||
1293 | ||
1294 | ||
1295 | <h2><a name="sec23" id="sec23"></a> | |
1296 | Tips</h2> | |
1297 | ||
1298 | <h3><a name="sec24" id="sec24"></a> | |
1299 | Getting dojo to load</h3> | |
1300 | ||
1301 | <p class="first">I had some trouble getting dojo to work properly with UCW. The way | |
1302 | that the <code>:www-roots</code> option for an application works is a bit | |
1303 | confusing, and it is unforgiving if you mess the pathname up. A | |
1304 | directory <strong>must</strong> have a <code>/</code> at the end, and the directory you are serving | |
1305 | must also have the <code>/</code> (which is counterintuitive given the behavior of | |
1306 | most unix things that don't want the <code>/</code> at the end of the name).</p> | |
1307 | ||
1308 | <pre class="src"> | |
7404d4e1 | 1309 | <span class="emacs-face-builtin">:www-roots</span> (list '(<span class="emacs-face-string">"dojo/"</span> . |
1310 | #P<span class="emacs-face-string">"/home/clinton/src/ucw/darcs/ucw_dev/wwwroot/dojo/"</span>)) | |
2aff8b5c | 1311 | </pre> |
1312 | ||
1313 | ||
1314 | <h3><a name="sec25" id="sec25"></a> | |
1315 | Specials Bound During Rendering</h3> | |
1316 | ||
1317 | <p class="first">The current request context is bound to <code>ucw:*context*</code>, and the current | |
1318 | component is bound to <code>ucw:*current-component*</code> in the dynamic extent of | |
1319 | <code>render</code>.</p> | |
1320 | ||
1321 | ||
1322 | <h3><a name="sec26" id="sec26"></a> | |
1323 | Printing to the yaclml stream</h3> | |
1324 | ||
1325 | <p class="first">Occasionally it can be useful to do something like write a byte array | |
1326 | as an ascii string to the client. Inside of <code>render</code> the variable | |
1327 | <code>yaclml:*yaclml-stream*</code> is bound to the stream that you can write to if | |
1328 | you wish to have content interspersed with yaclml tags.</p> | |
1329 | ||
1330 | ||
1331 | ||
1332 | <!-- Page published by Emacs Muse ends here --> | |
1333 | ||
1334 | <p class="cke-buttons"> | |
1335 | <!-- validating badges, any browser, etc --> | |
98266870 CE |
1336 | <a href="https://validator.w3.org/check/referer"><img |
1337 | src="https://www.w3.org/Icons/valid-xhtml10" | |
2aff8b5c | 1338 | alt="Valid XHTML 1.0!" /></a> |
1339 | ||
98266870 | 1340 | <a href="https://www.anybrowser.org/campaign/"><img |
2aff8b5c | 1341 | src="img/buttons/w3c_ab.png" alt="[ Viewable With Any Browser |
1342 | ]" /></a> | |
1343 | ||
98266870 | 1344 | <a href="https://www.debian.org/"><img |
2aff8b5c | 1345 | src="img/buttons/debian.png" alt="[ Powered by Debian ]" /></a> |
1346 | ||
98266870 | 1347 | <a href="https://hcoop.net/"> |
2aff8b5c | 1348 | <img src="img/buttons/hcoop.png" |
1349 | alt="[ Hosted by HCoop]" /> | |
1350 | </a> | |
1351 | ||
98266870 | 1352 | <a href="https://www.fsf.org/register_form?referrer=114"> |
2aff8b5c | 1353 | <img src="img/buttons/fsf_member.png" |
1354 | alt="[ FSF Associate Member ]" /> | |
1355 | </a> | |
1356 | </p> | |
1357 | ||
11f9bd69 | 1358 | <p class="cke-footer">If nothing in the world can change our children will inherit nothing |
2aff8b5c | 1359 | </p> |
1360 | <p class="cke-timestamp">Last Modified: | |
f6d19803 | 1361 | January 21, 2013</p> |
2aff8b5c | 1362 | </body> |
1363 | </html> |