Merge lp:~exarkun/divmod.org/remove-nevow into lp:divmod.org
- remove-nevow
- Merge into trunk
Proposed by
Jean-Paul Calderone
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Tristan Seligmann | ||||
Approved revision: | 2749 | ||||
Merged at revision: | 2749 | ||||
Proposed branch: | lp:~exarkun/divmod.org/remove-nevow | ||||
Merge into: | lp:divmod.org | ||||
Diff against target: | 70998 lines | ||||
To merge this branch: | bzr merge lp:~exarkun/divmod.org/remove-nevow | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tristan Seligmann | Approve | ||
Review via email: mp+222457@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Tristan Seligmann (mithrandi) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Divmod.pth' | |||
2 | --- Divmod.pth 2014-05-18 17:48:04 +0000 | |||
3 | +++ Divmod.pth 2014-06-08 12:16:37 +0000 | |||
4 | @@ -1,9 +1,8 @@ | |||
6 | 1 | # -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,hyperbola -*- | 1 | # -*- test-case-name: axiom,combinator,epsilon,xmantissa,xquotient,reverend,sine,hyperbola -*- |
7 | 2 | Axiom | 2 | Axiom |
8 | 3 | Combinator | 3 | Combinator |
9 | 4 | Epsilon | 4 | Epsilon |
10 | 5 | Mantissa | 5 | Mantissa |
11 | 6 | Nevow | ||
12 | 7 | Quotient | 6 | Quotient |
13 | 8 | Reverend | 7 | Reverend |
14 | 9 | Sine | 8 | Sine |
15 | 10 | 9 | ||
16 | === removed directory 'Nevow' | |||
17 | === removed file 'Nevow/ChangeLog' | |||
18 | --- Nevow/ChangeLog 2006-06-14 11:54:41 +0000 | |||
19 | +++ Nevow/ChangeLog 1970-01-01 00:00:00 +0000 | |||
20 | @@ -1,743 +0,0 @@ | |||
21 | 1 | 2006-06-12 Glyph Lefkowitz <glyph@divmod.com> | ||
22 | 2 | |||
23 | 3 | * Nevow 0.9.0: see NEWS.txt for details. | ||
24 | 4 | |||
25 | 5 | 2006-04-07 Jp Calderone <exarkun@divmod.com> | ||
26 | 6 | |||
27 | 7 | * Nevow 0.8.0 released | ||
28 | 8 | |||
29 | 9 | 2006-03-30 Tommi Virtanen <tv@twistedmatrix.com> | ||
30 | 10 | |||
31 | 11 | * nevow/static.py: Fix handling of range requests on static files. | ||
32 | 12 | |||
33 | 13 | 2006-03-30 Jp Calderone <exarkun@divmod.com> | ||
34 | 14 | |||
35 | 15 | * formless/freeform-defaults.css, formless/annotate.py: Style and | ||
36 | 16 | feedback improvements. | ||
37 | 17 | |||
38 | 18 | 2006-03-29 Jp Calderone <exarkun@divmod.com> | ||
39 | 19 | |||
40 | 20 | * examples/formbuilder/: Apply Cory Dodt's patch to make this | ||
41 | 21 | example work again. | ||
42 | 22 | |||
43 | 23 | 2006-03-27 Jp Calderone <exarkun@divmod.com> | ||
44 | 24 | |||
45 | 25 | * nevow/athena.py: Add getInitialArguments method which will be | ||
46 | 26 | invoked during rendering and may return a list or tuple of objects | ||
47 | 27 | which will be be passed to the client-side Widget's __init__ method. | ||
48 | 28 | |||
49 | 29 | 2006-03-24 Allen Short <washort@divmod.com> | ||
50 | 30 | |||
51 | 31 | * Upgrade MochiKit to 1.2 | ||
52 | 32 | |||
53 | 33 | 2006-03-17 Tristan Seligmann <mithrandi@mithrandi.za.net> | ||
54 | 34 | |||
55 | 35 | * Remove usage of twisted.python.components.Interface from formless. | ||
56 | 36 | Convert TypedInterface to zope.interface. | ||
57 | 37 | |||
58 | 38 | 2006-03-17 Jp Calderone <exarkun@divmod.com> | ||
59 | 39 | |||
60 | 40 | * nevow/json.py: Improve (hopefully correct this time ;) unicode | ||
61 | 41 | support. | ||
62 | 42 | |||
63 | 43 | 2006-03-08 Valentino Volonghi <dialtone@divmod.com> | ||
64 | 44 | |||
65 | 45 | * Remove usage of twisted.python.components.Interface from nevow. | ||
66 | 46 | Remove compyCompat. | ||
67 | 47 | |||
68 | 48 | 2006-02-28 Jp Calderone <exarkun@divmod.com> | ||
69 | 49 | |||
70 | 50 | * nevow/athena.py: Make the "Connection: Close" header of Athena | ||
71 | 51 | transport responses optional (toggleable by a flag on LivePage) to | ||
72 | 52 | allow TCP connection setup overhead to be avoided. | ||
73 | 53 | |||
74 | 54 | 2006-02-15 Tristan Seligmann <mithrandi@mithrandi.za.net> | ||
75 | 55 | |||
76 | 56 | * nevow/athena.js, nevow/athena.py: Pass JavaScript call-stack | ||
77 | 57 | information back to the server and include it in logged tracebacks | ||
78 | 58 | (Firefox only). | ||
79 | 59 | |||
80 | 60 | 2006-02-14 Jp Calderone <exarkun@divmod.com> | ||
81 | 61 | |||
82 | 62 | * nevow/util.py: Delete a bunch of code duplicated from Twisted. | ||
83 | 63 | |||
84 | 64 | 2006-02-06 Glyph Lefkowitz <glyph@twistedmatrix.com> | ||
85 | 65 | |||
86 | 66 | * nevow/athena.py, nevow/athena.js: Pop up an obnoxious widget when | ||
87 | 67 | an Athena connection is lost. | ||
88 | 68 | |||
89 | 69 | 2006-02-06 Jp Calderone <exarkun@divmod.com> | ||
90 | 70 | |||
91 | 71 | * nevow/runtime.js: Add Platform.getPage, a wrapper around the | ||
92 | 72 | browser-provided HTTP request function. | ||
93 | 73 | |||
94 | 74 | 2006-02-01 Tristan Seligmann <mithrandi@mithrandi.za.net> | ||
95 | 75 | |||
96 | 76 | * nevow/athena.js: Add support for <athena:handler> element, a | ||
97 | 77 | declarative mechanism for attaching event handlers to nodes. | ||
98 | 78 | |||
99 | 79 | 2006-02-01 Tristan Seligmann <mithrandi@mithrandi.za.net> | ||
100 | 80 | |||
101 | 81 | * bin/nit: Test collector/runner for Athena widgets. | ||
102 | 82 | |||
103 | 83 | 2006-02-01 Jp Calderone <exarkun@divmod.com> | ||
104 | 84 | |||
105 | 85 | * nevow/defer.js: Deferred implementation to replace MochiKit | ||
106 | 86 | Deferreds. | ||
107 | 87 | |||
108 | 88 | 2006-01-21 Valentino Volonghi <dialtone@divmod.com> | ||
109 | 89 | |||
110 | 90 | * nevow/rend.py, formless/webform.py: Support Deferreds returned | ||
111 | 91 | from bind_ methods. | ||
112 | 92 | |||
113 | 93 | 2006-01-21 Valentino Volonghi <dialtone@divmod.com> | ||
114 | 94 | |||
115 | 95 | * nevow/flat/flatstan.py: Fix macros inside of other specials. | ||
116 | 96 | |||
117 | 97 | 2006-01-19 Jp Calderone <exarkun@divmod.com> | ||
118 | 98 | |||
119 | 99 | * nevow/athena.js: Added Divmod.Class.methods() for defining | ||
120 | 100 | multiple methods at once. | ||
121 | 101 | |||
122 | 102 | 2006-01-19 Jp Calderone <exarkun@divmod.com> | ||
123 | 103 | |||
124 | 104 | * nevow/athena.py, nevow/athena.js: Support the addition of | ||
125 | 105 | LiveFragment instances to a page after the initial render pass. | ||
126 | 106 | |||
127 | 107 | 2006-01-15 Jp Calderone <exarkun@divmod.com> | ||
128 | 108 | |||
129 | 109 | * nevow/athena.js: Introduced new form of Divmod.Class.method() for | ||
130 | 110 | adding methods to JavaScript classes. Added a child-window based | ||
131 | 111 | log viewer. | ||
132 | 112 | |||
133 | 113 | 2006-01-08 Valentino Volonghi <dialtone@divmod.com> | ||
134 | 114 | |||
135 | 115 | * examples/hello/hellohtml.py: Removed htmlfile usage | ||
136 | 116 | * nevow/loaders.py: Deprecated htmlfile/htmlstr | ||
137 | 117 | |||
138 | 118 | 2006-01-08 Glyph Lefkowitz <glyph@twistedmatrix.com> | ||
139 | 119 | |||
140 | 120 | * nevow/test/, formless/test/: Removed all usage of trial's | ||
141 | 121 | deferredResult() and util.wait() functions. | ||
142 | 122 | |||
143 | 123 | 2006-01-04 Glyph Lefkowitz <glyph@twistedmatrix.com> | ||
144 | 124 | |||
145 | 125 | * nevow/athena.py: Added fragment and widget nesting. On the | ||
146 | 126 | server side, this documents and provides a supported way of | ||
147 | 127 | setting the magical required "page" attribute. On the client | ||
148 | 128 | side, this provides a convenient hook for multiple widgets within | ||
149 | 129 | a page to communicate with each other. | ||
150 | 130 | |||
151 | 131 | 2005-12-26 Jp Calderone <exarkun@divmod.com> | ||
152 | 132 | |||
153 | 133 | * nevow/athena.py: Added athena.js and MochiKit.js to the dictionary | ||
154 | 134 | returned by allJavascriptModules(), allowing them to be referenced | ||
155 | 135 | using the JS import mechanism, in turn allowing them both to be | ||
156 | 136 | served from a single site-wide URL, rather than once per page. | ||
157 | 137 | |||
158 | 138 | 2005-12-22 Jp Calderone <exarkun@divmod.com> | ||
159 | 139 | |||
160 | 140 | * nevow/athena.py: Remove Python 2.4 dependencies. | ||
161 | 141 | |||
162 | 142 | 2005-12-21 Jp Calderone <exarkun@divmod.com> | ||
163 | 143 | |||
164 | 144 | * Nevow 0.7.0 released | ||
165 | 145 | |||
166 | 146 | 2005-12-18 Jp Calderone <exarkun@divmod.com> | ||
167 | 147 | |||
168 | 148 | * nevow/athena.py: Server-side processing of .js files to allow for | ||
169 | 149 | an import directive. Imported JavaScript modules are inserted into | ||
170 | 150 | the page during the render process in dependency order. | ||
171 | 151 | |||
172 | 152 | * nevow/widget.js: Athena Widgets class and support code definitions | ||
173 | 153 | moved here. | ||
174 | 154 | |||
175 | 155 | 2005-12-13 Matt Goodall <matt@pollenation.net> | ||
176 | 156 | |||
177 | 157 | * nevow/athena.js: Improved IE compatibility. | ||
178 | 158 | |||
179 | 159 | 2005-12-05 Jp Calderone <exarkun@divmod.com> | ||
180 | 160 | |||
181 | 161 | * nevow/athena.js: Added Divmod.Class object which provides a | ||
182 | 162 | class-based object model for JavaScript programs. Added | ||
183 | 163 | Nevow.Athena.Widget, a base class for "Live Widgets" - JavaScript | ||
184 | 164 | classes which can control a particular section of a LivePage and can | ||
185 | 165 | communicate with corresponding LiveFragment instances on the server. | ||
186 | 166 | |||
187 | 167 | * nevow/athena.py: Added a callRemote method to LiveFragment - this | ||
188 | 168 | allows the server to invoke methods on particular Widgets on the | ||
189 | 169 | client. | ||
190 | 170 | |||
191 | 171 | 2005-12-03 Jp Calderone <exarkun@divmod.com> | ||
192 | 172 | |||
193 | 173 | * nevow/rend.py: Fixed a bug in xmlfile caching which could lead to | ||
194 | 174 | a corrupt loader cache. | ||
195 | 175 | |||
196 | 176 | 2005-11-29 Jp Calderone <exarkun@divmod.com> | ||
197 | 177 | |||
198 | 178 | * nevow/appserver.py: Removed "support" (which consisted of logging | ||
199 | 179 | an error message and continuing) for returning objects which do not | ||
200 | 180 | provide IResource. | ||
201 | 181 | |||
202 | 182 | 2005-11-26 Jp Calderone <exarkun@divmod.com> | ||
203 | 183 | |||
204 | 184 | * nevow/athena.py, nevow/athena.js: Use POST for the LivePage | ||
205 | 185 | transport, rather than GET. | ||
206 | 186 | |||
207 | 187 | * nevow/json.py: Fix a bug in JSON support for floating points. | ||
208 | 188 | |||
209 | 189 | 2005-11-25 Jp Calderone <exarkun@divmod.com> | ||
210 | 190 | |||
211 | 191 | * nevow/athena.js: Go live by default. | ||
212 | 192 | |||
213 | 193 | 2005-11-22 Glyph Lefkowitz <glyph@divmod.com> | ||
214 | 194 | |||
215 | 195 | * nevow/guard.py: Removed __session_just_startd__. | ||
216 | 196 | |||
217 | 197 | 2005-11-15 Jp Calderone <exarkun@divmod.com> | ||
218 | 198 | |||
219 | 199 | * nevow/athena.py: Added LiveFragment - base class for Fragments | ||
220 | 200 | which may cooperatively share a single LivePage connection. | ||
221 | 201 | |||
222 | 202 | |||
223 | 203 | 2005-11-09 Jp Calderone <exarkun@divmod.com> | ||
224 | 204 | |||
225 | 205 | * Athena JavaScript API unified into a pseudo-namespace hierarchy. | ||
226 | 206 | |||
227 | 207 | 2005-11-07 Jp Calderone <exarkun@divmod.com> | ||
228 | 208 | |||
229 | 209 | * JSON serializer now quotes dict/object keys. | ||
230 | 210 | |||
231 | 211 | 2005-11-02 Jp Calderone <exarkun@divmod.com> | ||
232 | 212 | |||
233 | 213 | * Nevow 0.6.0 released | ||
234 | 214 | |||
235 | 215 | 2005-09-21 Jp Calderone <exarkun@divmod.com> | ||
236 | 216 | |||
237 | 217 | * nevow/athena.py: New implementation of LivePage with a | ||
238 | 218 | data-centric API: generation of JavaScript on the server is highly | ||
239 | 219 | discouraged, instead an API for passing around simple or complex | ||
240 | 220 | data structures is provided. | ||
241 | 221 | |||
242 | 222 | 2005-08-01 Matt Goodall <matt@pollenation.net> | ||
243 | 223 | |||
244 | 224 | egg-ify the distribution. "python setup.py bdist_egg" will now build | ||
245 | 225 | a .egg in dist for Python 2.3 and Python 2.4. | ||
246 | 226 | |||
247 | 227 | 2005-07-14 Donovan Preston <dp@divmod.org> | ||
248 | 228 | |||
249 | 229 | * It's no longer necessary to specify addSlash = True on | ||
250 | 230 | the root resource. nevow.appserver.NevowSite automatically | ||
251 | 231 | sets it on the first argument you pass to it (the root | ||
252 | 232 | resource). | ||
253 | 233 | |||
254 | 234 | 2005-07-12 Donovan Preston <dp@divmod.org> | ||
255 | 235 | |||
256 | 236 | * Usability improvements for formless at the expense of | ||
257 | 237 | purity of abstraction. Since the only thing anybody uses | ||
258 | 238 | formless for is rendering web forms, make it a little easier | ||
259 | 239 | to do common things. For example: | ||
260 | 240 | |||
261 | 241 | - _nevow_carryover_ does not get appended to URLs unless absolutely | ||
262 | 242 | necessary (because you return a value from an autocallable) | ||
263 | 243 | |||
264 | 244 | - TypedInterface is being deprecated in favor of using | ||
265 | 245 | IConfigurable directly, and an IConfigurable implementation | ||
266 | 246 | on Fragment/Page that is easy to use. To expose a "foo" method | ||
267 | 247 | on a page which takes a "bar" string and a "baz" integer, do this: | ||
268 | 248 | |||
269 | 249 | def bind_foo(self, ctx): | ||
270 | 250 | return [('bar', String()), ('baz', Integer())] | ||
271 | 251 | |||
272 | 252 | Previously, you would have had to create a TypedInterface, | ||
273 | 253 | declare foo and the types of the arguments in the class body, | ||
274 | 254 | declare that foo is autocallable, and declare that your Page | ||
275 | 255 | class __implements__ the interface. Now, just implement | ||
276 | 256 | bind_foo to return an IBinding or a list which can be munged | ||
277 | 257 | into one. | ||
278 | 258 | |||
279 | 259 | - It is possible to return a Page from an autocallable to have | ||
280 | 260 | that page displayed directly after posting the autocallable. | ||
281 | 261 | The URL that is used is freeform_hand, which means that the | ||
282 | 262 | Page goes into your "hand" in the session. The hand can only | ||
283 | 263 | hold one value, the most recent return result of an autocallable. | ||
284 | 264 | This isn't very back-button friendly but it makes it super | ||
285 | 265 | easy to put together a multi-step wizard interface. | ||
286 | 266 | |||
287 | 267 | See examples/tests/testformless.py and the /testformless on | ||
288 | 268 | the example server to see how to do this. | ||
289 | 269 | |||
290 | 270 | - It is possible to return a URL instance from an autocallable | ||
291 | 271 | to have the user redirected to that page after posting the | ||
292 | 272 | form successfully. This replaces and deprecates the old, | ||
293 | 273 | whacko method of setting IRedirectAfterPost on the Request. | ||
294 | 274 | |||
295 | 275 | See examples/tests/testformless.py and the /formless_redirector | ||
296 | 276 | on the example server to see how to do this. | ||
297 | 277 | |||
298 | 278 | * There is now livetest coverage of the formless examples, | ||
299 | 279 | including posting forms and checking erroneous conditions. | ||
300 | 280 | |||
301 | 281 | 2005-07-06 Donovan Preston <dp@divmod.org> | ||
302 | 282 | |||
303 | 283 | * Major non-backwards-compatible improvements to livepage. Changes | ||
304 | 284 | are designed to avoid an uncollectable garbage problem which was | ||
305 | 285 | inherent in the previous design. | ||
306 | 286 | |||
307 | 287 | The livepage javascript glue now includes a global object named | ||
308 | 288 | "server". This object has a "handle" method which takes at least | ||
309 | 289 | one argument, a string indicating the name of the handler to | ||
310 | 290 | invoke on the server. When called by client-side javascript, | ||
311 | 291 | LivePage.locateHandler is invoked. locateHandler should return | ||
312 | 292 | a callable which will be called with a context indicating | ||
313 | 293 | which client is invoking the method, and any additional arguments | ||
314 | 294 | which were passed to server.handle, as strings. | ||
315 | 295 | |||
316 | 296 | The default implementation of LivePage.locateHandler looks for | ||
317 | 297 | a correspondingly named "handle_*" method. Using livepage is | ||
318 | 298 | now as simple as subclassing LivePage and providing handle_* | ||
319 | 299 | methods: | ||
320 | 300 | |||
321 | 301 | class Foo(LivePage): | ||
322 | 302 | def handle_bar(self, ctx, something): | ||
323 | 303 | print "something!", something | ||
324 | 304 | |||
325 | 305 | And calling server.handle in javascript: | ||
326 | 306 | |||
327 | 307 | <a onclick="server.handle('bar', 'here is something')">Click me</a> | ||
328 | 308 | |||
329 | 309 | The previous behavior of registering closures or other callables | ||
330 | 310 | as event handlers and then embedding them in the page is still | ||
331 | 311 | available using the IClientHandle.transient method. These one- | ||
332 | 312 | shot handlers are only invokable by the client once before being | ||
333 | 313 | garbage collected on the server. This makes it possible to | ||
334 | 314 | implement temporary dialog boxes and the like. | ||
335 | 315 | |||
336 | 316 | 2005-04-09 Tommi Virtanen <tv@twistedmatrix.com> | ||
337 | 317 | |||
338 | 318 | * Allow remembering ILogger to specify an alternate access logging | ||
339 | 319 | method. | ||
340 | 320 | |||
341 | 321 | 2005-04-06 Matt Goodall <matt@pollenation.net> | ||
342 | 322 | |||
343 | 323 | * Added optional context argument to Page's renderString and | ||
344 | 324 | renderSynchronously methods. This provides a site-like context that is | ||
345 | 325 | useful when rendering multiple pages that need common remembered data. | ||
346 | 326 | |||
347 | 327 | 2005-3-23 Donovan Preston <dp@divmod.org> | ||
348 | 328 | |||
349 | 329 | * Releasing 0.4.1 | ||
350 | 330 | |||
351 | 331 | 2005-3-22 Donovan Preston <dp@divmod.org> | ||
352 | 332 | |||
353 | 333 | * Releasing 0.4 | ||
354 | 334 | |||
355 | 335 | 2005-02-22 Matt Goodall <matt@pollenation.net> | ||
356 | 336 | |||
357 | 337 | * Added a "data" renderer (rend.data) that replaces the tag's content | ||
358 | 338 | with the current data. i.e. <p n:data="name" n:render="data">Foo Bar</p>. | ||
359 | 339 | |||
360 | 340 | 2005-02-17 Matt Goodall <matt@pollenation.net> | ||
361 | 341 | |||
362 | 342 | * Added i18n - a gettext-like mechanism for marking translatable content | ||
363 | 343 | in an application's Python modules by wrapping a string in _(). Standard | ||
364 | 344 | Python gettext tools can be used to generate translation files. | ||
365 | 345 | |||
366 | 346 | Translation happens during rendering and depends on a list of languages | ||
367 | 347 | found in the context. By default, the browser's preferred languages are | ||
368 | 348 | used. The default behaviour can be overridden by remembering the languages | ||
369 | 349 | as inevow.ILanguages in the context; allowing the language to be selected | ||
370 | 350 | from user preferences, for example. | ||
371 | 351 | |||
372 | 352 | 2005-02-08 Matt Goodall <matt@pollenation.net> | ||
373 | 353 | |||
374 | 354 | * Extended the IDocFactory interface's load method to accept an optional | ||
375 | 355 | context. | ||
376 | 356 | |||
377 | 357 | The current loaders do not use the context but future loaders may, i.e. one | ||
378 | 358 | that loads a localised template based on some language in the context. | ||
379 | 359 | |||
380 | 360 | 2005-01-01 Donovan Preston <dp@divmod.org> | ||
381 | 361 | |||
382 | 362 | * Rewrote LivePage quoting code to be much more correct; added many unit | ||
383 | 363 | tests. Some older livepage code must be changed to use the | ||
384 | 364 | livepage.literal object instead of passing normal strings to handler or | ||
385 | 365 | the LivePage client APIs. | ||
386 | 366 | |||
387 | 367 | * Added nevow.livepage module, LivePage, and ILivePage. The name liveevil | ||
388 | 368 | is deprecated. | ||
389 | 369 | |||
390 | 370 | 2004-12-23 Phil Frost <indigo@bitglue.com> | ||
391 | 371 | |||
392 | 372 | * Added support for formless to return unicode objects. annotate.String | ||
393 | 373 | and subclasses (Text, Password, etc.) take a 'unicode' parameter to | ||
394 | 374 | enable unicode, like so: | ||
395 | 375 | |||
396 | 376 | | annotate.String(unicode=True) | ||
397 | 377 | |||
398 | 378 | The coerced value will then be a unicode object. | ||
399 | 379 | |||
400 | 380 | 2004-12-16 Matt Goodall <matt@pollenation.net> | ||
401 | 381 | |||
402 | 382 | * Added ObjectContainer - a data directive accessor for retrieving an | ||
403 | 383 | attribute of an object. If the current data in the context (the IData) is an | ||
404 | 384 | object you can register ObjectContainer as the IContainer adapter for the | ||
405 | 385 | object's class and Nevow will automatically look inside the object to fetch | ||
406 | 386 | the attribute. | ||
407 | 387 | |||
408 | 388 | Note: ObjectContainer is *not* registered as the adapter for all object | ||
409 | 389 | types. You must explicitly register the adapter for application classes as | ||
410 | 390 | needed. | ||
411 | 391 | |||
412 | 392 | 2004-12-08 Matt Goodall <matt@pollenation.net> | ||
413 | 393 | |||
414 | 394 | * Applied the foom/mesozoic patch to make Page.remember work correctly and | ||
415 | 395 | without the hack. You can now use the method reliably to remember objects at | ||
416 | 396 | Page construction time or any other time before the PageContext is created. | ||
417 | 397 | The remembered objects can be found from the context in the usual way. | ||
418 | 398 | |||
419 | 399 | class MyPage(rend.Page): | ||
420 | 400 | |||
421 | 401 | def __init__(self, original): | ||
422 | 402 | # Make original available as ISomething(ctx) for later. | ||
423 | 403 | self.remember(original, ISomething) | ||
424 | 404 | rend.Page.__init__(self, original) | ||
425 | 405 | |||
426 | 406 | * Added a similar remember method for NevowSite (and made a SiteContext | ||
427 | 407 | object the ultimate parent). You can know remember objects on the site and | ||
428 | 408 | have them available anywhere there's a context. A typical use case is making | ||
429 | 409 | some object store available to the site. This was often achieved using a | ||
430 | 410 | wrapper resource but now it's as easy as: | ||
431 | 411 | |||
432 | 412 | store = makeStore() | ||
433 | 413 | site = NevowSite(rootResource) | ||
434 | 414 | site.remember(store, IStore) | ||
435 | 415 | |||
436 | 416 | 2004-12-08 Matt Goodall <matt@pollenation.net> | ||
437 | 417 | |||
438 | 418 | * Renamed URL.fromRequest to URL.fromContext. URL.fromRequest was a little | ||
439 | 419 | confusing - the URL it returned only included the segments that had been | ||
440 | 420 | consumed so far by the locateChild process and not the whole URL as the name | ||
441 | 421 | might suggest. Eventually, fromRequest will change to return a full URL but, | ||
442 | 422 | for now, its use is deprecated. | ||
443 | 423 | |||
444 | 424 | * Renamed URL.parent to URL.parentdir. URL.parent() was logically equivalent | ||
445 | 425 | to '..' and so removed more segments than expected. URL.parent's use is now | ||
446 | 426 | deprecated and in a future release will be changed to remove exactly one | ||
447 | 427 | segment. | ||
448 | 428 | |||
449 | 429 | * Improve URL.click to normalise any segments of '.' or '..'. Browsers | ||
450 | 430 | normalise the URL so, according to the docstring, click should too. | ||
451 | 431 | |||
452 | 432 | 2004-12-04 Donovan Preston <dp@divmod.org> | ||
453 | 433 | |||
454 | 434 | * Added macros! This is the same patch as the one I attached to my | ||
455 | 435 | "Macros in Nevow" mail, with the addition of an IMacroFactory, | ||
456 | 436 | a MacroFactory implementation on rend.Page, and macro directive | ||
457 | 437 | support. Macros are like render functions that take only the | ||
458 | 438 | context (no data parameter) and run only once during the lifetime | ||
459 | 439 | of a template loader. Here is an example of the difference between | ||
460 | 440 | a macro and a renderer: | ||
461 | 441 | |||
462 | 442 | >>> class Bumper(object): | ||
463 | 443 | ... num = 0 | ||
464 | 444 | ... def bump(self): | ||
465 | 445 | ... self.num += 1 | ||
466 | 446 | ... return self.num | ||
467 | 447 | ... | ||
468 | 448 | >>> staysTheSame = Bumper() | ||
469 | 449 | >>> changes = Bumper() | ||
470 | 450 | >>> from nevow import flat, tags, loaders | ||
471 | 451 | >>> document = loaders.stan([tags.invisible(macro=lambda ctx: staysTheSame.bump()), tags.invisible(render=lambda ctx, data: changes.bump())]) | ||
472 | 452 | >>> flat.flatten(document) | ||
473 | 453 | '11' | ||
474 | 454 | >>> flat.flatten(document) | ||
475 | 455 | '12' | ||
476 | 456 | >>> flat.flatten(document) | ||
477 | 457 | '13' | ||
478 | 458 | |||
479 | 459 | 2004-12-01 Donovan Preston <dp@divmod.org> | ||
480 | 460 | |||
481 | 461 | * Added __iter__ to nevow.stan.slot to prevent infinite loops by | ||
482 | 462 | trying to do "for x in slot('foo'): print x". | ||
483 | 463 | |||
484 | 464 | * Added an IGettable adapter for nevow.stan.slot. It is now possible | ||
485 | 465 | to specify a slot as the data for a node, so the following example | ||
486 | 466 | would work: | ||
487 | 467 | |||
488 | 468 | from nevow import rend, tags | ||
489 | 469 | |||
490 | 470 | def fillEm(ctx, data): | ||
491 | 471 | ctx.fillSlots('value', [1, 2]) | ||
492 | 472 | return ctx.tag | ||
493 | 473 | |||
494 | 474 | tags.html(render=fillEm)[ | ||
495 | 475 | tags.ul(data=tags.slot('value'), render=rend.sequence)[ | ||
496 | 476 | tags.li(pattern='item')[ | ||
497 | 477 | str ]]] | ||
498 | 478 | |||
499 | 479 | 2004-09-26 Donovan Preston <dp@divmod.org> | ||
500 | 480 | |||
501 | 481 | * Added nevow.inevow.IQ interface, an interface for querying the | ||
502 | 482 | stan DOM. Eventually, this interface will contain APIs for doing | ||
503 | 483 | traditional DOM introspection, such as iterating children, | ||
504 | 484 | examining tag names, and examining attributes. For now, it contains | ||
505 | 485 | only the patternGenerator, onePattern, and allPatterns APIs. These | ||
506 | 486 | APIs have been deprecated from Context. | ||
507 | 487 | |||
508 | 488 | The main benefit of this is the ability to do: | ||
509 | 489 | |||
510 | 490 | IQ(loaders.xmlfile(...)).patternGenerator(...) | ||
511 | 491 | |||
512 | 492 | which would be nice for creating "pattern library" files containing | ||
513 | 493 | common skin idioms which can then be copied and used throughout | ||
514 | 494 | the app. | ||
515 | 495 | |||
516 | 496 | 2004-09-25 Donovan Preston <dp@divmod.org> | ||
517 | 497 | |||
518 | 498 | * Chatola received a major facelift, bringing it from cool demo | ||
519 | 499 | up to almost a full fledged web-based chat server. The helper API | ||
520 | 500 | LiveEvil.call(...) was added, which deprecates | ||
521 | 501 | LiveEvil.sendScript(callJS(...)) | ||
522 | 502 | |||
523 | 503 | 2004-09-23 Tommi Virtanen <tv@twistedmatrix.com> | ||
524 | 504 | |||
525 | 505 | * Make guard.SessionWrapper store its URL location in all requests it | ||
526 | 506 | passes to its children. This allows the children know where to post | ||
527 | 507 | the __login__ and __logout__ forms, even deep inside the resource | ||
528 | 508 | tree (fixes issue59). | ||
529 | 509 | |||
530 | 510 | * Guard now works as a non-root resource, with help from the above | ||
531 | 511 | change. Semantics of __login__ clarified in unit tests; if your guard | ||
532 | 512 | is at /foo, posting to /foo/__login__ redirects to /foo, and posting | ||
533 | 513 | to /foo/__login__/ redirects to /foo/. The two unit tests that failed | ||
534 | 514 | earlier now pass (with that change in their __login__ URLs). | ||
535 | 515 | |||
536 | 516 | * If URL-based sessions are used, login no longer loses session | ||
537 | 517 | information due to redirect to / (fixes issue56). | ||
538 | 518 | |||
539 | 519 | 2004-09-20 Matt Goodall <matt@pollenation.net> | ||
540 | 520 | |||
541 | 521 | * Added URL.secure() method to make switching between http and | ||
542 | 522 | https easier. | ||
543 | 523 | |||
544 | 524 | 2004-09-08 Donovan Preston <dp@divmod.org> | ||
545 | 525 | |||
546 | 526 | * Nevow now includes a very simple proof-of-concept WSGI Application | ||
547 | 527 | implementation, and can be used with no Twisted dependency. Nevow can | ||
548 | 528 | also be used to write CGIs, either using a simple CGI WSGI gateway | ||
549 | 529 | (which supports URL traversal), or by using Page.renderString (which does not). | ||
550 | 530 | |||
551 | 531 | * Two new context interfaces, ICurrentSegments and IRemainingSegments, | ||
552 | 532 | replace the need to examine the Request prepath and postpath attributes | ||
553 | 533 | directly. | ||
554 | 534 | |||
555 | 535 | * ISerializable is deprecated, and has been replaced with a simple | ||
556 | 536 | Flattener registry. nevow.flat.registerFlattener and | ||
557 | 537 | nevow.flat.getFlattener have been added to support this. | ||
558 | 538 | |||
559 | 539 | 2004-09-06 Donovan Preston <dp@divmod.org> | ||
560 | 540 | |||
561 | 541 | * BACKWARDS INCOMPATIBLE CHANGE. Page.configurable_ *always* returns | ||
562 | 542 | self, and a new Page.configurable_original *always* returns | ||
563 | 543 | self.original. If you were relying on Page.configurable_'s | ||
564 | 544 | introspection behavior and are now getting errors about adapting | ||
565 | 545 | to IConfigurable, change your renderForms() calls to: | ||
566 | 546 | |||
567 | 547 | renderForms('original') | ||
568 | 548 | |||
569 | 549 | This causes Page.configurable_original to be invoked and | ||
570 | 550 | introspected for form rendering. | ||
571 | 551 | |||
572 | 552 | 2004-08-23 Donovan Preston <dp@divmod.org> | ||
573 | 553 | |||
574 | 554 | * LivePage uses a simpler implementation strategy which requires | ||
575 | 555 | the browser to make one request per output event. As a result, | ||
576 | 556 | LivePage now works on Mozilla, Safari, and Internet Explorer Windows. | ||
577 | 557 | |||
578 | 558 | 2004-08-05 Donovan Preston <dp@divmod.org> | ||
579 | 559 | |||
580 | 560 | * Implemented support for IFoo(ctx) synonym syntax. It does the | ||
581 | 561 | same thing as ctx.locate(IFoo) | ||
582 | 562 | |||
583 | 563 | * Removed Resource Generators, a feature of NevowSite that nobody | ||
584 | 564 | used and wasn't really useful. | ||
585 | 565 | |||
586 | 566 | * Changed all inevow.IResource apis to take a Context object | ||
587 | 567 | where they used to take the request. Remembering objects in | ||
588 | 568 | PageContexts is now much easier, and fewer hacks are required to | ||
589 | 569 | build the context chain. The context chain now looks like: | ||
590 | 570 | |||
591 | 571 | SiteContext->RequestContext->PageContext(s)->WovenContext(s) | ||
592 | 572 | |||
593 | 573 | 2004-7-28 Donovan Preston <dp@divmod.org> | ||
594 | 574 | |||
595 | 575 | * Parameterize data_* methods in the same way as render_* methods. | ||
596 | 576 | Patch by k3mper. | ||
597 | 577 | |||
598 | 578 | For example, <div nevow:data="foo bar,baz" /> will cause | ||
599 | 579 | data_foo(self, bar, baz) to be called with the strings "bar" and | ||
600 | 580 | "baz". This data method should return a callable taking ctx, data. | ||
601 | 581 | The return value of this callable will be remembered as IData at | ||
602 | 582 | this point in the context stack. | ||
603 | 583 | |||
604 | 584 | * Added list-slicing support to ListContainer. You may now use | ||
605 | 585 | list slicing syntax in a data directive in addition to a simple | ||
606 | 586 | index. For example: | ||
607 | 587 | |||
608 | 588 | def data_aList(self, ctx, data): | ||
609 | 589 | return ["Buckle", "My", "Shoe"] | ||
610 | 590 | |||
611 | 591 | <div nevow:data="aList"> | ||
612 | 592 | <span nevow:data="1:-1" nevow:render="string" /> | ||
613 | 593 | </div> | ||
614 | 594 | |||
615 | 595 | Will render as <div><span>My</span></div> | ||
616 | 596 | |||
617 | 597 | 2004-7-20 Matt Goodall <matt@pollenation.net> | ||
618 | 598 | |||
619 | 599 | * Modified sax loader to retain doctypes, comments and xmlns attributes. | ||
620 | 600 | It's now possible to build XHTML valid pages :). | ||
621 | 601 | |||
622 | 602 | xmlns attributes are always kept but there are options to ignore the doctype | ||
623 | 603 | and comment (at the request of foom, not sure why yet). Right now, the default | ||
624 | 604 | is to retain doctypes and comments but you can use the ignoreDocType and | ||
625 | 605 | ignoreComment args to the xml loaders and flatsax parse functions. This bit | ||
626 | 606 | may change. | ||
627 | 607 | |||
628 | 608 | * Add a URL -> IResource adapter that performs a HTTP redirect. URLs can then | ||
629 | 609 | be returned from locateChild(). | ||
630 | 610 | |||
631 | 611 | 2004-06-07 Donovan Preston <dp@divmod.org> | ||
632 | 612 | |||
633 | 613 | * Added nevow.canvas, an experimental module similar to LivePage | ||
634 | 614 | which provides a Python server-side API for drawing arbitrary lines, | ||
635 | 615 | curves, and text in the browser. The implementation is socket-based | ||
636 | 616 | and asynchronous, so the server can issue drawing commands to the | ||
637 | 617 | client at any time. | ||
638 | 618 | |||
639 | 619 | The idea is to provide a server-side API to the Python programmer | ||
640 | 620 | and shield them from implementation details, but the current | ||
641 | 621 | implementation uses a pre-compiled Flash movie (which never changes; | ||
642 | 622 | we are not generating Flash bytecodes). An implementation using SVG | ||
643 | 623 | or Safari's Canvas (nevow.canvas was written before the Safari Canvas | ||
644 | 624 | announcement) would be possible. | ||
645 | 625 | |||
646 | 626 | 2004-05-26 Donovan Preston <dp@divmod.org> | ||
647 | 627 | |||
648 | 628 | * Add URLOverlay.keep, an API which lets you specify which query args | ||
649 | 629 | will be carried on from the current page render into the new URL. | ||
650 | 630 | |||
651 | 631 | 2004-05-24 Matt Goodall <matt@pollenation.net> | ||
652 | 632 | |||
653 | 633 | * Extracted Fragment from Page. Hopefully, it will make it more obvious | ||
654 | 634 | that embedding an object with data_ and render_ methods in a stan tree is | ||
655 | 635 | possible without using something as "heavy" as Page which is really meant | ||
656 | 636 | to be a web resource. | ||
657 | 637 | |||
658 | 638 | 2004-05-23 Donovan Preston <dp@divmod.org> | ||
659 | 639 | |||
660 | 640 | * Added some useful APIs to LiveEvil for manipulating the client-side | ||
661 | 641 | page: | ||
662 | 642 | |||
663 | 643 | - flt(stan): Flatten some stan, quoting apostrophes as | ||
664 | 644 | as appropriate for embedding in javascript | ||
665 | 645 | |||
666 | 646 | - set(what, to): Set the contents of the client-side node | ||
667 | 647 | with the id 'what' to the stan 'to'. | ||
668 | 648 | |||
669 | 649 | - append(where, what): Append the stan 'what' to the client- | ||
670 | 650 | side node with the id 'where' | ||
671 | 651 | |||
672 | 652 | - alert(what): Show an alert to the user with the text "what" | ||
673 | 653 | |||
674 | 654 | 2004-05-19 Jonathan Simms <slyphon@divmod.com> | ||
675 | 655 | |||
676 | 656 | * 0.2 released. | ||
677 | 657 | |||
678 | 658 | 2004-05-14 Donovan Preston <dp@divmod.org> | ||
679 | 659 | |||
680 | 660 | * nevow.url.URLPath is renamed nevow.url.URL | ||
681 | 661 | |||
682 | 662 | * URL objects are now lazier about casting things to strings; they will | ||
683 | 663 | keep track of path segments in a list and defer to the nevow rendering | ||
684 | 664 | machinery to do the conversion; This means you can do things like | ||
685 | 665 | here.child(deferred) or here.child(function) | ||
686 | 666 | |||
687 | 667 | * URL objects have a better api for manipulating query arguments | ||
688 | 668 | - add(key, value=None) adds a new query arg at the end; the value may | ||
689 | 669 | be None if only a query key needs to be added | ||
690 | 670 | - replace(key, value) removes all occurrences of 'key' and inserts a | ||
691 | 671 | new (key, value) at the same location as the previous first | ||
692 | 672 | occurrence of key | ||
693 | 673 | - clear() clears all args | ||
694 | 674 | |||
695 | 675 | 2004-05-06 Donovan Preston <dp@divmod.org> | ||
696 | 676 | |||
697 | 677 | * Merged freeform-patterned branch, a large formless/freeform refactor. | ||
698 | 678 | It is now possible to influence the rendering of forms by providing | ||
699 | 679 | "patterns" to renderForms. | ||
700 | 680 | |||
701 | 681 | * Formless is now a top-level package. Freeform has been renamed | ||
702 | 682 | formless.webform. It should be possible to use formless outside | ||
703 | 683 | the context of nevow for doing things such as validating network | ||
704 | 684 | input based on method argument type annotations. | ||
705 | 685 | |||
706 | 686 | * TypedInterface, autocallable, and all the Typed subclasses are now | ||
707 | 687 | in the formless.annotate module. | ||
708 | 688 | |||
709 | 689 | 2004-04-30 Donovan Preston <dp@divmod.org> | ||
710 | 690 | |||
711 | 691 | * Created nevow.blocks, a module containing helpful code for working | ||
712 | 692 | around display: inline-block bugs in Mozilla. | ||
713 | 693 | |||
714 | 694 | 2004-04-27 Donovan Preston <dp@divmod.org> | ||
715 | 695 | |||
716 | 696 | * IRenderer.__call__ was renamed IRenderer.rend to be more explicit. | ||
717 | 697 | |||
718 | 698 | 2004-04-21 Donovan Preston <dp@divmod.org> | ||
719 | 699 | |||
720 | 700 | * Implemented nevow.flat.flatten and nevow.flat.precompile, functions | ||
721 | 701 | useful for using stan outside of the context of Page classes. Useful | ||
722 | 702 | for generating some XML or even raw text; use it any time you want | ||
723 | 703 | to convert a tree data structure into a contiguous string! | ||
724 | 704 | |||
725 | 705 | import random | ||
726 | 706 | import string | ||
727 | 707 | |||
728 | 708 | from nevow import flat | ||
729 | 709 | |||
730 | 710 | def letters(howMany): | ||
731 | 711 | for i in range(howMany): | ||
732 | 712 | yield ' ', string.letters[i], '\n' | ||
733 | 713 | |||
734 | 714 | def outline(): | ||
735 | 715 | for i in range(5): | ||
736 | 716 | yield i, '\n' | ||
737 | 717 | yield letters(random.choice(range(7))) | ||
738 | 718 | yield '\n' | ||
739 | 719 | |||
740 | 720 | print flat.flatten(outline()) | ||
741 | 721 | |||
742 | 722 | 2004-04-20 Donovan Preston <dp@divmod.org> | ||
743 | 723 | |||
744 | 724 | * guard sessions are no longer required to use formless. | ||
745 | 725 | |||
746 | 726 | 2004-04-19 Donovan Preston <dp@divmod.org> | ||
747 | 727 | |||
748 | 728 | * Implemented lazy context factories. It is now possible to register | ||
749 | 729 | an adapter against various *Context classes and an interface. They | ||
750 | 730 | will be invoked if *Context.locate(interface) is called. | ||
751 | 731 | |||
752 | 732 | Implemented a lazy ISession adapter against RequestContext, making | ||
753 | 733 | it possible to do ctx.locate(ISession), which is nice. | ||
754 | 734 | |||
755 | 735 | 2004-04-16 Donovan Preston <dp@divmod.org> | ||
756 | 736 | |||
757 | 737 | * Added nevow.entities module, a module containing literals for all of | ||
758 | 738 | the valid XHTML entities. For example: | ||
759 | 739 | |||
760 | 740 | def render_nbsp(self, ctx, data): | ||
761 | 741 | from nevow import entities | ||
762 | 742 | return entities.nbsp | ||
763 | 743 | |||
764 | 744 | 0 | ||
765 | === removed file 'Nevow/LICENSE' | |||
766 | --- Nevow/LICENSE 2005-10-14 17:36:24 +0000 | |||
767 | +++ Nevow/LICENSE 1970-01-01 00:00:00 +0000 | |||
768 | @@ -1,35 +0,0 @@ | |||
769 | 1 | Copyright (c) 2004 | ||
770 | 2 | Donovan Preston | ||
771 | 3 | Matt Goodall | ||
772 | 4 | James Y. Knight | ||
773 | 5 | Glyph Lefkowitz | ||
774 | 6 | JP Calderone | ||
775 | 7 | Allen Short | ||
776 | 8 | Alex Levy | ||
777 | 9 | Justin Johnson | ||
778 | 10 | Christopher Armstrong | ||
779 | 11 | Jonathan Simms | ||
780 | 12 | Phil Frost | ||
781 | 13 | Tommi Virtanen | ||
782 | 14 | Michal Pasternak | ||
783 | 15 | Valentino Volonghi | ||
784 | 16 | |||
785 | 17 | |||
786 | 18 | Permission is hereby granted, free of charge, to any person obtaining | ||
787 | 19 | a copy of this software and associated documentation files (the | ||
788 | 20 | "Software"), to deal in the Software without restriction, including | ||
789 | 21 | without limitation the rights to use, copy, modify, merge, publish, | ||
790 | 22 | distribute, sublicense, and/or sell copies of the Software, and to | ||
791 | 23 | permit persons to whom the Software is furnished to do so, subject to | ||
792 | 24 | the following conditions: | ||
793 | 25 | |||
794 | 26 | The above copyright notice and this permission notice shall be | ||
795 | 27 | included in all copies or substantial portions of the Software. | ||
796 | 28 | |||
797 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
798 | 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
799 | 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
800 | 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
801 | 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
802 | 34 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
803 | 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
804 | 36 | 0 | ||
805 | === removed file 'Nevow/MANIFEST.in' | |||
806 | --- Nevow/MANIFEST.in 2010-04-06 11:05:45 +0000 | |||
807 | +++ Nevow/MANIFEST.in 1970-01-01 00:00:00 +0000 | |||
808 | @@ -1,24 +0,0 @@ | |||
809 | 1 | include ChangeLog | ||
810 | 2 | include LICENSE | ||
811 | 3 | include zomne.c | ||
812 | 4 | include formless/freeform-default.css | ||
813 | 5 | include nevow/Canvas.swf | ||
814 | 6 | include nevow/athena_private/*.png | ||
815 | 7 | include debian/* | ||
816 | 8 | recursive-include twisted *.py | ||
817 | 9 | recursive-include nevow *.css *.js | ||
818 | 10 | include bin/nevow-xmlgettext | ||
819 | 11 | graft doc | ||
820 | 12 | recursive-include examples *.css *.gif *.html *.jpg *.js *.py *.png *.tac *.mo *.po *.pot *.xml *.sql *.xul | ||
821 | 13 | include examples/i18n/update-l10n | ||
822 | 14 | include examples/files/words | ||
823 | 15 | include examples/pastebin/data | ||
824 | 16 | include examples/pastebin/static/robots.txt | ||
825 | 17 | include examples/pastebin/TODO | ||
826 | 18 | include examples/wsgi/README | ||
827 | 19 | include extras/xhtml-nevow.rnc | ||
828 | 20 | include nevow/Canvas.fla | ||
829 | 21 | include nevow/canvas.as | ||
830 | 22 | include win32/* | ||
831 | 23 | prune */.svn | ||
832 | 24 | prune doc/html/*.html | ||
833 | 25 | 0 | ||
834 | === removed file 'Nevow/NEWS.txt' | |||
835 | --- Nevow/NEWS.txt 2009-11-30 01:08:55 +0000 | |||
836 | +++ Nevow/NEWS.txt 1970-01-01 00:00:00 +0000 | |||
837 | @@ -1,244 +0,0 @@ | |||
838 | 1 | 0.10.0 (2009-11-25): | ||
839 | 2 | - Added a system for CSS dependency declarations similar to the one in | ||
840 | 3 | Athena for JavaScript. | ||
841 | 4 | - Fix Athena's transport cleanup on page unload in Internet Explorer. | ||
842 | 5 | - Fix nit's results coloring in Internet Explorer. | ||
843 | 6 | - Added an API for declaring JavaScript classes which involves less | ||
844 | 7 | repetition than the existing Divmod.Class.subclass API. | ||
845 | 8 | - Added human-readable formatting for the new flattener's error reporting; | ||
846 | 9 | rendering error stacks will now display lines from Python code as well | ||
847 | 10 | as stan and XML templates. | ||
848 | 11 | - Override the setuptools sdist command with the original distutils sdist | ||
849 | 12 | command to avoid setuptools' version number transformation. | ||
850 | 13 | - Added support for default values for slots in XML templates. | ||
851 | 14 | - Fixed a problem with setup.py which led to css files not being | ||
852 | 15 | installed. | ||
853 | 16 | - Removed the old Chatola example and replaced it with a link to the new | ||
854 | 17 | chat example. | ||
855 | 18 | - Sped up some of the JavaScript dependency calculations. | ||
856 | 19 | |||
857 | 20 | 0.9.33 (2008-12-09): | ||
858 | 21 | - Add error handling to the integration between the old flattener | ||
859 | 22 | and the new flattener so that if the new flattener fails with an | ||
860 | 23 | exception or a Failure the error is propagated properly to the old | ||
861 | 24 | flattener which invoked it. | ||
862 | 25 | - Changed nit so that it doesn't use private `twistd` APIs and | ||
863 | 26 | instead just sets up a server and runs the reactor. This makes | ||
864 | 27 | nit work with all versions of Twisted supported by Nevow. | ||
865 | 28 | - Changed Nevow's setup.py to use setuptools if setuptools is | ||
866 | 29 | available. This has the user-facing consequence of installing | ||
867 | 30 | Nevow as an egg if setuptools is available at installation time | ||
868 | 31 | and of making Nevow installable using the `easy_install´ tool. | ||
869 | 32 | - TabbedPane naively set DOM attributes, making it unusable in | ||
870 | 33 | Internet Explorer 6 and 7. Introduced a reliable method for | ||
871 | 34 | setting DOM node attributes, with name mangling, to address the | ||
872 | 35 | issue. | ||
873 | 36 | |||
874 | 37 | 0.9.32 (2008-08-12): | ||
875 | 38 | - A resource wrapper for on-the-fly gzip compression has been added. | ||
876 | 39 | - A twistd plugin, 'athena-widget', is now available for serving | ||
877 | 40 | single Athena widgets. | ||
878 | 41 | - Basic Athena support for Safari added. | ||
879 | 42 | - Added file name, line number, and column number information to | ||
880 | 43 | slots and tags parsed from XML files in order to make debugging | ||
881 | 44 | template/renderer interactions simpler. | ||
882 | 45 | - A context-free flattener has been added. Fragment and its | ||
883 | 46 | subclasses are now deprecated in favor of Element. | ||
884 | 47 | - Javascript classes derived from the tabbedpane class can now | ||
885 | 48 | override how tab selection is handled. | ||
886 | 49 | |||
887 | 50 | 0.9.31 (2008-02-06): | ||
888 | 51 | - Fixed Guard's request parameter save/restore feature to not | ||
889 | 52 | clobber request state after login succeeds when a session has | ||
890 | 53 | already been negotiated. | ||
891 | 54 | - Added a hook to nevow.guard.SessionWrapper which allows the | ||
892 | 55 | domain parameter of the session cookie to be specified. | ||
893 | 56 | |||
894 | 57 | 0.9.30 (2008-01-16): | ||
895 | 58 | - Change DeferredSerializer so that it passes failures from the | ||
896 | 59 | Deferred being serialized on to the Deferred returned by the | ||
897 | 60 | flattening function. Without this behavior, the Deferred | ||
898 | 61 | returned by the flattening function is never fired when a | ||
899 | 62 | Deferred which fails is serialized. | ||
900 | 63 | |||
901 | 64 | 0.9.29 (2008-01-02): | ||
902 | 65 | - Prevent NevowSite.handleSegment from raising IndexError in certain | ||
903 | 66 | situations. | ||
904 | 67 | - Deprecated wsgi and zomne modules. | ||
905 | 68 | |||
906 | 69 | 0.9.28 (2007-12-10): | ||
907 | 70 | - Added two APIs to Athena, one for creating the string used as the id | ||
908 | 71 | attribute of the top node of a widget and one for creating the string | ||
909 | 72 | used as the id attribute of a node which had an id attribute in the | ||
910 | 73 | widget's template document. | ||
911 | 74 | |||
912 | 75 | 0.9.27 (2007-11-27): | ||
913 | 76 | - Unicode URLs now supported. | ||
914 | 77 | |||
915 | 78 | 0.9.26 (2007-11-02): | ||
916 | 79 | - url.URL.path now correctly escapes segments in the string it | ||
917 | 80 | evaluates to. | ||
918 | 81 | - inevow.IAthenaTransportable added, along with support for | ||
919 | 82 | serialization of custom types for server-to-client Athena | ||
920 | 83 | messages. | ||
921 | 84 | - Global client-side behaviour is now customizable via a client | ||
922 | 85 | PageWidget class. | ||
923 | 86 | |||
924 | 87 | 0.9.25 (2007-10-16): | ||
925 | 88 | - The Athena message queue implementation has been improved, fixing problems | ||
926 | 89 | masked by bugs in Firebug/YSlow. | ||
927 | 90 | |||
928 | 91 | 0.9.24 (2007-09-05): | ||
929 | 92 | - ESC key no longer disconnects Athena connections. | ||
930 | 93 | - Fixed a bug where URLs with quote characters will cause the Athena | ||
931 | 94 | connection to be lost. | ||
932 | 95 | - Fixed 'twistd athena-widget' to create a fresh widget instance for each | ||
933 | 96 | hit. | ||
934 | 97 | |||
935 | 98 | 0.9.23 (2007-08-01): | ||
936 | 99 | - Fixed install script to include all JavaScript files. | ||
937 | 100 | |||
938 | 101 | 0.9.22 (2007-07-06): | ||
939 | 102 | - Mock DOM implementation for easier browser testing added. | ||
940 | 103 | - JavaScript source files are now read using universal newlines mode. | ||
941 | 104 | - athena.AutoJSPackage now excludes dotfiles. | ||
942 | 105 | - url.URL now properly subclassable. | ||
943 | 106 | - User-agent parsing added to Athena, to detect known-unsupported browsers. | ||
944 | 107 | |||
945 | 108 | 0.9.21 (2007-06-06): | ||
946 | 109 | - Debug logging messages from the reliable message delivery queue | ||
947 | 110 | disabled. | ||
948 | 111 | |||
949 | 112 | 0.9.20 (2007-05-24): | ||
950 | 113 | - Athena now no longer holds more than one idle transport open to | ||
951 | 114 | the browser. | ||
952 | 115 | |||
953 | 116 | 0.9.19 (2007-04-27): | ||
954 | 117 | - Changed the styling of the progressbar to work on IE6. | ||
955 | 118 | - Athena.Widget.detach added, to allow widgets to cleanly be removed | ||
956 | 119 | from a page. | ||
957 | 120 | - Athena.Widget.callLater added, a wrapper around setTimeout and | ||
958 | 121 | clearTimeout. | ||
959 | 122 | - 'athena-widget' twistd command added, for starting a server which | ||
960 | 123 | serves a single LiveFragment or LiveElement. | ||
961 | 124 | |||
962 | 125 | 0.9.18 (2007-02-23): | ||
963 | 126 | - Athena 'connection lost' notification now styleable via the | ||
964 | 127 | 'nevow-connection-lost' CSS class. | ||
965 | 128 | - The 'runjstests' script has been removed, now that JS tests can be | ||
966 | 129 | run with trial. | ||
967 | 130 | |||
968 | 131 | 0.9.17 (2006-12-08): | ||
969 | 132 | - More efficient JSON string parsing. | ||
970 | 133 | - Give FakeRequests a default status code of OK. Accept all of | ||
971 | 134 | FakeRequest.__init__'s arguments in the __init__ of | ||
972 | 135 | AccumulatingFakeRequest. | ||
973 | 136 | |||
974 | 137 | 0.9.16 (2006-11-17): | ||
975 | 138 | - Updated nit to work with Twisted trunk. | ||
976 | 139 | - Athena module import caching has been fixed. | ||
977 | 140 | |||
978 | 141 | 0.9.15 (2006-11-08): | ||
979 | 142 | - Changed _LiveMixin rendering to be idempotent to support the case | ||
980 | 143 | where a transport hiccup causes a LiveFragment or LiveElement to | ||
981 | 144 | be sent to the browser multiple times. | ||
982 | 145 | - Improvements to the tests. | ||
983 | 146 | |||
984 | 147 | 0.9.14 (2006-10-31): | ||
985 | 148 | - Support code for running non-browser javascript tests has been added. | ||
986 | 149 | - Added a workaround for nodeById on widgets not yet added to the document in | ||
987 | 150 | IE. | ||
988 | 151 | - Athena will now invoke the nodeInserted method (if it exists) on a widget | ||
989 | 152 | that it instantiates statically. | ||
990 | 153 | - ID rewriting, similar to existing rewriting support for 'id' attributes, | ||
991 | 154 | has been added in 'for' and 'headers' attributes of 'label' and 'td'/'th' | ||
992 | 155 | elements, respectively. | ||
993 | 156 | |||
994 | 157 | 0.9.13 (2006-10-21): | ||
995 | 158 | - Adjust non-selected panes in tabbedpane to be further out of the viewport. | ||
996 | 159 | - Convert to using the Javascript module plugin system for Nevow-provided | ||
997 | 160 | modules. | ||
998 | 161 | |||
999 | 162 | 0.9.12 (2006-10-17): | ||
1000 | 163 | - Added id rewriting for LiveElement and LiveFragment, such that id | ||
1001 | 164 | attributes in a widget template are rewritten so that they are unique to | ||
1002 | 165 | the widget instance. A client-side API, Nevow.Athena.Widget.nodeById(), | ||
1003 | 166 | is provided to allow location of these nodes. | ||
1004 | 167 | |||
1005 | 168 | 0.9.11 (2006-10-10): | ||
1006 | 169 | - Fixed dynamic widget instantiation in IE. | ||
1007 | 170 | - Added support for correctly quoting the values of slots which are used as | ||
1008 | 171 | attributes. | ||
1009 | 172 | |||
1010 | 173 | 0.9.10 (2006-10-05): | ||
1011 | 174 | - Minor update to nevow.testutil. | ||
1012 | 175 | |||
1013 | 176 | 0.9.9 (2006-09-26): | ||
1014 | 177 | - Several nit changes, including the addition of the "check" method to | ||
1015 | 178 | Failure, and the addition of an "assertFailure" method. | ||
1016 | 179 | - The ability to pass Python exceptions to Javascript has been added to | ||
1017 | 180 | Athena. | ||
1018 | 181 | - Dynamic module import has been added for the cases where it is necessary | ||
1019 | 182 | to dynamically add a widget to an existing page. | ||
1020 | 183 | |||
1021 | 184 | 0.9.8 (2009-09-20): | ||
1022 | 185 | - A bug in nit that caused it to fail if there were too many tests in a | ||
1023 | 186 | test case, and swallow failures in some cases, has been fixed. | ||
1024 | 187 | - Widgets can no longer be added to a page after render time using | ||
1025 | 188 | Divmod.Runtime.Platform.{set,append}NodeContent. Instead, they must be | ||
1026 | 189 | added using Nevow.Athena.Widget.addChildWidgetFromWidgetInfo. | ||
1027 | 190 | |||
1028 | 191 | 0.9.7 (2009-09-12): | ||
1029 | 192 | - Automatic Athena event handler registration is fixed for all supported browsers | ||
1030 | 193 | and is no longer document-sensitive (ie, it works inside tables now). | ||
1031 | 194 | - Nit has gained a new assertion method, assertIn. | ||
1032 | 195 | |||
1033 | 196 | 0.9.6 (2008-08-30): | ||
1034 | 197 | - Fixed a bug in the IE implementation of the runtime.js node fetching | ||
1035 | 198 | functions. | ||
1036 | 199 | |||
1037 | 200 | 0.9.5 (2006-08-22): | ||
1038 | 201 | - Instance attributes can now be exposed to Athena with nevow.utils.Expose | ||
1039 | 202 | and Expose.exposedMethodNames() no longer returns unexposed names. | ||
1040 | 203 | |||
1041 | 204 | 0.9.4 (2006-08-14): | ||
1042 | 205 | - Added test method discovery to nit test cases, so multiple test methods | ||
1043 | 206 | may be put in a single test case. | ||
1044 | 207 | - use XPath for certain DOM traversals when available. This yields | ||
1045 | 208 | significant speedups on Opera. | ||
1046 | 209 | - Made Divmod.Runtime.Platform.getAttribute deal with IE attribute | ||
1047 | 210 | name-mangling properly. | ||
1048 | 211 | - Javascript logging is now done in Firebug 0.4 style rather than 0.3. | ||
1049 | 212 | - Some cases where Deferred-returning render methods raised | ||
1050 | 213 | exceptions or buried failures were fixed. | ||
1051 | 214 | - Removed MochiKit. The pieces Nevow depends on have been moved to | ||
1052 | 215 | Divmod.Base in nevow/base.js. | ||
1053 | 216 | - Various doc fixes. | ||
1054 | 217 | |||
1055 | 218 | 0.9.3 (2006-07-17): | ||
1056 | 219 | - Page rendering now supports preprocessors. | ||
1057 | 220 | |||
1058 | 221 | 0.9.2 (2006-07-08): | ||
1059 | 222 | - Fixes to the typeahead demo. | ||
1060 | 223 | - Elements are now automatically serialized by json, just like Fragments. | ||
1061 | 224 | |||
1062 | 225 | 0.9.1 (2006-07-05): | ||
1063 | 226 | - Made nevow.athena.expose the mandatory means of publishing a method to | ||
1064 | 227 | the browser. The allowedMethods dictionary will no longer be respected. | ||
1065 | 228 | - Added nevow.page.Element and nevow.athena.LiveElement: these are | ||
1066 | 229 | preferred over nevow.rend.Fragment and nevow.athena.LiveFragment for all | ||
1067 | 230 | new development. | ||
1068 | 231 | |||
1069 | 232 | 0.9.0 (2006-06-12): | ||
1070 | 233 | - Fixed a bug where nested fragment sending rarely worked. | ||
1071 | 234 | - Sending large strings in Athena arguments and results is now faster due to | ||
1072 | 235 | less unnecessary unicode character quoting. | ||
1073 | 236 | - Module objects are now automatically created for all Athena imports. | ||
1074 | 237 | - Better error reporting for fragments which are rendered without a parent. | ||
1075 | 238 | - Disconnect notifiers in Athena pages will no longer clobber each other. | ||
1076 | 239 | - Many optimizations to javascript initialization. | ||
1077 | 240 | - Javascript packages are now defined with less boilerplate: a filesystem | ||
1078 | 241 | convention similar to Python's for module naming, plus one declaration in a | ||
1079 | 242 | Nevow plugin which indicates the directory, rather than a declaration for | ||
1080 | 243 | each module. | ||
1081 | 244 | - Updated README to refer to Athena rather than LivePage | ||
1082 | 245 | 0 | ||
1083 | === removed file 'Nevow/README' | |||
1084 | --- Nevow/README 2006-06-14 11:54:41 +0000 | |||
1085 | +++ Nevow/README 1970-01-01 00:00:00 +0000 | |||
1086 | @@ -1,69 +0,0 @@ | |||
1087 | 1 | |||
1088 | 2 | Divmod Nevow | ||
1089 | 3 | ============ | ||
1090 | 4 | |||
1091 | 5 | Divmod Nevow is a web application construction kit written in Python. It is | ||
1092 | 6 | designed to allow the programmer to express as much of the view logic as | ||
1093 | 7 | desired in Python, and includes a pure Python XML expression syntax named stan | ||
1094 | 8 | to facilitate this. However it also provides rich support for designer-edited | ||
1095 | 9 | templates, using a very small XML attribute language to provide bi-directional | ||
1096 | 10 | template manipulation capability. | ||
1097 | 11 | |||
1098 | 12 | Nevow also includes Divmod Athena, a "two way web" or "`COMET`_" | ||
1099 | 13 | implementation, providing a two-way bridge between Python code on the server | ||
1100 | 14 | and JavaScript code on the client. Modular portions of a page, known as | ||
1101 | 15 | "athena fragments" in the server python and "athena widgets" in the client | ||
1102 | 16 | javascript, can be individually developed and placed on any Nevow-rendered page | ||
1103 | 17 | with a small template renderer. Athena abstracts the intricacies of HTTP | ||
1104 | 18 | communication, session security, and browser-specific bugs behind a simple | ||
1105 | 19 | remote-method-call interface, where individual widgets or fragments can call | ||
1106 | 20 | remote methods on their client or server peer with one method: "callRemote". | ||
1107 | 21 | |||
1108 | 22 | Installation | ||
1109 | 23 | ------------ | ||
1110 | 24 | |||
1111 | 25 | Before installing Nevow, you should install `Twisted`_, unless you are going to | ||
1112 | 26 | write very simple CGI applications. Nevow integrates fully with the twisted.web | ||
1113 | 27 | server providing easy deployment. | ||
1114 | 28 | |||
1115 | 29 | Nevow uses the standard distutils method of installation:: | ||
1116 | 30 | |||
1117 | 31 | python setup.py install | ||
1118 | 32 | |||
1119 | 33 | If you do not have Twisted installed, you can run a subset of the tests using | ||
1120 | 34 | the test.py script. If you have twisted installed, the test.py script will | ||
1121 | 35 | issue the following trial command:: | ||
1122 | 36 | |||
1123 | 37 | trial -v nevow.test formless.test | ||
1124 | 38 | |||
1125 | 39 | .. _`Twisted`: http://twistedmatrix.com/ | ||
1126 | 40 | |||
1127 | 41 | Documentation | ||
1128 | 42 | ------------- | ||
1129 | 43 | |||
1130 | 44 | More detailed introductory documentation is available in the doc/ directory, | ||
1131 | 45 | along with the beginnings of a reference manual. A large number of examples are | ||
1132 | 46 | available in the examples/ directory. These examples require Twisted to run. A | ||
1133 | 47 | tac file (twisted application configuration) can be started by invoking twistd, | ||
1134 | 48 | the twisted daemon:: | ||
1135 | 49 | |||
1136 | 50 | twistd -noy foo.tac | ||
1137 | 51 | |||
1138 | 52 | More Information | ||
1139 | 53 | ---------------- | ||
1140 | 54 | |||
1141 | 55 | Nevow is an active project, and many new bugfixes and features are committed to | ||
1142 | 56 | the Nevow SVN repository. Information about Nevow commits is available by | ||
1143 | 57 | subscribing to the `Nevow commits`_ mailing list. The Nevow SVN repository can | ||
1144 | 58 | be checked out using:: | ||
1145 | 59 | |||
1146 | 60 | svn co http://divmod.org/svn/Divmod/trunk/Nevow Nevow | ||
1147 | 61 | |||
1148 | 62 | Discussion of Nevow occurs on the `twisted.web mailing list`_. The Nevow | ||
1149 | 63 | developers are also often available for real-time help on the `#twisted.web | ||
1150 | 64 | channel`_ on irc.freenode.net. | ||
1151 | 65 | |||
1152 | 66 | .. _`Nevow commits`: http://divmod.org/users/mailman.twistd/listinfo/nevow-commits | ||
1153 | 67 | .. _`twisted.web mailing list`: http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web | ||
1154 | 68 | .. _`#twisted.web channel`: irc://irc.freenode.net/#twisted.web | ||
1155 | 69 | .. _`COMET`: http://alex.dojotoolkit.org/?p=545 | ||
1156 | 70 | 0 | ||
1157 | === removed directory 'Nevow/benchmarks' | |||
1158 | === removed file 'Nevow/benchmarks/fragmentvselement.py' | |||
1159 | --- Nevow/benchmarks/fragmentvselement.py 2008-05-22 18:46:50 +0000 | |||
1160 | +++ Nevow/benchmarks/fragmentvselement.py 1970-01-01 00:00:00 +0000 | |||
1161 | @@ -1,147 +0,0 @@ | |||
1162 | 1 | |||
1163 | 2 | from twisted.internet import reactor | ||
1164 | 3 | from twisted.internet.defer import succeed | ||
1165 | 4 | |||
1166 | 5 | from nevow.appserver import NevowSite | ||
1167 | 6 | |||
1168 | 7 | from nevow.rend import Page, Fragment | ||
1169 | 8 | from nevow.page import Element, renderer | ||
1170 | 9 | from nevow.loaders import stan | ||
1171 | 10 | from nevow.tags import directive, div, span | ||
1172 | 11 | |||
1173 | 12 | |||
1174 | 13 | class Static: | ||
1175 | 14 | docFactory = stan("Hello, world. " * 100) | ||
1176 | 15 | |||
1177 | 16 | |||
1178 | 17 | |||
1179 | 18 | class StaticFragment(Static, Fragment): | ||
1180 | 19 | pass | ||
1181 | 20 | |||
1182 | 21 | |||
1183 | 22 | |||
1184 | 23 | class StaticElement(Static, Element): | ||
1185 | 24 | pass | ||
1186 | 25 | |||
1187 | 26 | |||
1188 | 27 | |||
1189 | 28 | class Tiny: | ||
1190 | 29 | docFactory = stan(div(render=directive("foo"))) | ||
1191 | 30 | |||
1192 | 31 | |||
1193 | 32 | |||
1194 | 33 | class TinyFragment(Tiny, Fragment): | ||
1195 | 34 | def render_foo(self, ctx, data): | ||
1196 | 35 | return ctx.tag[span["result"]] | ||
1197 | 36 | |||
1198 | 37 | |||
1199 | 38 | |||
1200 | 39 | class TinyElement(Tiny, Element): | ||
1201 | 40 | def foo(self, request, tag): | ||
1202 | 41 | return tag[span["result"]] | ||
1203 | 42 | renderer(foo) | ||
1204 | 43 | |||
1205 | 44 | |||
1206 | 45 | |||
1207 | 46 | class Huge: | ||
1208 | 47 | docFactory = stan(div[[ | ||
1209 | 48 | div(render=directive("foo")) | ||
1210 | 49 | for x in range(100)]]) | ||
1211 | 50 | |||
1212 | 51 | |||
1213 | 52 | |||
1214 | 53 | class HugeFragment(Huge, Fragment): | ||
1215 | 54 | def render_foo(self, ctx, data): | ||
1216 | 55 | return ctx.tag[span["Hello, ", "world", "!"]] | ||
1217 | 56 | |||
1218 | 57 | |||
1219 | 58 | |||
1220 | 59 | class HugeElement(Huge, Element): | ||
1221 | 60 | def foo(self, request, tag): | ||
1222 | 61 | return tag[span["Hello, ", "world", "!"]] | ||
1223 | 62 | renderer(foo) | ||
1224 | 63 | |||
1225 | 64 | |||
1226 | 65 | |||
1227 | 66 | class Nested: | ||
1228 | 67 | docFactory = stan(div(render=directive("foo"))) | ||
1229 | 68 | |||
1230 | 69 | def __init__(self, count=6): | ||
1231 | 70 | self.count = count | ||
1232 | 71 | |||
1233 | 72 | |||
1234 | 73 | |||
1235 | 74 | class NestedFragment(Nested, Fragment): | ||
1236 | 75 | def render_foo(self, ctx, data): | ||
1237 | 76 | if self.count: | ||
1238 | 77 | return span[NestedFragment(self.count - 1)] | ||
1239 | 78 | return ctx.tag["Hello"] | ||
1240 | 79 | |||
1241 | 80 | |||
1242 | 81 | |||
1243 | 82 | class NestedElement(Nested, Element): | ||
1244 | 83 | def foo(self, request, tag): | ||
1245 | 84 | if self.count: | ||
1246 | 85 | return span[NestedFragment(self.count - 1)] | ||
1247 | 86 | return tag["Hello"] | ||
1248 | 87 | renderer(foo) | ||
1249 | 88 | |||
1250 | 89 | |||
1251 | 90 | |||
1252 | 91 | class Deferred: | ||
1253 | 92 | docFactory = stan(div(render=directive('foo'))) | ||
1254 | 93 | |||
1255 | 94 | |||
1256 | 95 | |||
1257 | 96 | class DeferredFragment(Deferred, Fragment): | ||
1258 | 97 | def render_foo(self, ctx, data): | ||
1259 | 98 | return ctx.tag[succeed("foo")] | ||
1260 | 99 | |||
1261 | 100 | |||
1262 | 101 | |||
1263 | 102 | class DeferredElement(Deferred, Element): | ||
1264 | 103 | def foo(self, request, tag): | ||
1265 | 104 | return tag[succeed("foo")] | ||
1266 | 105 | renderer(foo) | ||
1267 | 106 | |||
1268 | 107 | |||
1269 | 108 | |||
1270 | 109 | class Compare(Page): | ||
1271 | 110 | def __init__(self, fragment, element): | ||
1272 | 111 | self.fragment = fragment | ||
1273 | 112 | self.element = element | ||
1274 | 113 | |||
1275 | 114 | def child_fragment(self, ctx): | ||
1276 | 115 | return Page(docFactory=stan(self.fragment)) | ||
1277 | 116 | |||
1278 | 117 | |||
1279 | 118 | def child_element(self, ctx): | ||
1280 | 119 | return Page(docFactory=stan(self.element)) | ||
1281 | 120 | |||
1282 | 121 | |||
1283 | 122 | |||
1284 | 123 | class Root(Page): | ||
1285 | 124 | def child_static(self, ctx): | ||
1286 | 125 | return Compare(StaticFragment(), StaticElement()) | ||
1287 | 126 | |||
1288 | 127 | |||
1289 | 128 | def child_tiny(self, ctx): | ||
1290 | 129 | return Compare(TinyFragment(), TinyElement()) | ||
1291 | 130 | |||
1292 | 131 | |||
1293 | 132 | def child_huge(self, ctx): | ||
1294 | 133 | return Compare(HugeFragment(), HugeElement()) | ||
1295 | 134 | |||
1296 | 135 | |||
1297 | 136 | def child_nested(self, ctx): | ||
1298 | 137 | return Compare(NestedFragment(), NestedElement()) | ||
1299 | 138 | |||
1300 | 139 | |||
1301 | 140 | def child_deferred(self, ctx): | ||
1302 | 141 | return Compare(DeferredFragment(), DeferredElement()) | ||
1303 | 142 | |||
1304 | 143 | |||
1305 | 144 | |||
1306 | 145 | if __name__ == '__main__': | ||
1307 | 146 | reactor.listenTCP(8080, NevowSite(Root())) | ||
1308 | 147 | reactor.run() | ||
1309 | 148 | 0 | ||
1310 | === removed file 'Nevow/benchmarks/json_string_tokenizer.py' | |||
1311 | --- Nevow/benchmarks/json_string_tokenizer.py 2006-11-24 23:15:26 +0000 | |||
1312 | +++ Nevow/benchmarks/json_string_tokenizer.py 1970-01-01 00:00:00 +0000 | |||
1313 | @@ -1,48 +0,0 @@ | |||
1314 | 1 | |||
1315 | 2 | from time import time | ||
1316 | 3 | |||
1317 | 4 | from twisted.python.usage import Options | ||
1318 | 5 | |||
1319 | 6 | from nevow.json import serialize, parse | ||
1320 | 7 | |||
1321 | 8 | if __name__ == '__main__': | ||
1322 | 9 | from json_string_tokenizer import main | ||
1323 | 10 | raise SystemExit(main()) | ||
1324 | 11 | |||
1325 | 12 | |||
1326 | 13 | |||
1327 | 14 | class StringTokenizer(Options): | ||
1328 | 15 | optParameters = [ | ||
1329 | 16 | ('iterations', 'i', '1000', 'Number of iterations for which to run the benchmark.'), | ||
1330 | 17 | ('scale', 's', '100', 'Factor determining the overall input size.')] | ||
1331 | 18 | |||
1332 | 19 | |||
1333 | 20 | def postOptions(self): | ||
1334 | 21 | self['iterations'] = int(self['iterations']) | ||
1335 | 22 | self['scale'] = int(self['scale']) | ||
1336 | 23 | |||
1337 | 24 | |||
1338 | 25 | BASE = u'Hello, world. "Quotes".' | ||
1339 | 26 | def benchmark(iterations, scale): | ||
1340 | 27 | """ | ||
1341 | 28 | Deserialize a string C{iterations} times. Make the string longer based | ||
1342 | 29 | on C{scale}. | ||
1343 | 30 | |||
1344 | 31 | Prints the mean time per parse call. | ||
1345 | 32 | """ | ||
1346 | 33 | s = serialize(BASE * scale) | ||
1347 | 34 | before = time() | ||
1348 | 35 | for i in xrange(iterations): | ||
1349 | 36 | parse(s) | ||
1350 | 37 | after = time() | ||
1351 | 38 | print (after - before) / iterations, 'per call' | ||
1352 | 39 | |||
1353 | 40 | |||
1354 | 41 | |||
1355 | 42 | def main(args=None): | ||
1356 | 43 | """ | ||
1357 | 44 | Benchmark nevow.json string parsing, maybe with some parameters. | ||
1358 | 45 | """ | ||
1359 | 46 | options = StringTokenizer() | ||
1360 | 47 | options.parseOptions(args) | ||
1361 | 48 | benchmark(options['iterations'], options['scale']) | ||
1362 | 49 | 0 | ||
1363 | === removed directory 'Nevow/bin' | |||
1364 | === removed file 'Nevow/bin/nevow-xmlgettext' | |||
1365 | --- Nevow/bin/nevow-xmlgettext 2005-10-14 17:36:24 +0000 | |||
1366 | +++ Nevow/bin/nevow-xmlgettext 1970-01-01 00:00:00 +0000 | |||
1367 | @@ -1,3 +0,0 @@ | |||
1368 | 1 | #!/usr/bin/env python | ||
1369 | 2 | from nevow.scripts.xmlgettext import run | ||
1370 | 3 | run() | ||
1371 | 4 | 0 | ||
1372 | === removed file 'Nevow/bin/nit' | |||
1373 | --- Nevow/bin/nit 2006-11-17 00:50:34 +0000 | |||
1374 | +++ Nevow/bin/nit 1970-01-01 00:00:00 +0000 | |||
1375 | @@ -1,3 +0,0 @@ | |||
1376 | 1 | #!/usr/bin/python | ||
1377 | 2 | from nevow.scripts.nit import run | ||
1378 | 3 | run() | ||
1379 | 4 | 0 | ||
1380 | === removed directory 'Nevow/debian' | |||
1381 | === removed file 'Nevow/debian/changelog' | |||
1382 | --- Nevow/debian/changelog 2005-11-06 18:46:50 +0000 | |||
1383 | +++ Nevow/debian/changelog 1970-01-01 00:00:00 +0000 | |||
1384 | @@ -1,56 +0,0 @@ | |||
1385 | 1 | nevow (0.6.0-1.snapshot) unstable; urgency=low | ||
1386 | 2 | |||
1387 | 3 | * SVN snapshot. | ||
1388 | 4 | |||
1389 | 5 | -- Tommi Virtanen <tv@debian.org> Sun, 6 Nov 2005 20:45:27 +0200 | ||
1390 | 6 | |||
1391 | 7 | nevow (0.6.0-1) unstable; urgency=low | ||
1392 | 8 | |||
1393 | 9 | * New upstream version. (Closes: #336027) | ||
1394 | 10 | * Acknowledge NMU (Closes: #319230), but please be more careful in the | ||
1395 | 11 | future; no NMU patch was sent to BTS | ||
1396 | 12 | * Remove setupcommon.pyc when cleaning, or dpkg-source will see a binary | ||
1397 | 13 | file content change. | ||
1398 | 14 | * Run unit tests when building. | ||
1399 | 15 | * Clean build tree, distutils fails to remove all of it. | ||
1400 | 16 | * Change priority to extra, as twisted is extra and nevow depends on it. | ||
1401 | 17 | |||
1402 | 18 | -- Tommi Virtanen <tv@debian.org> Sun, 6 Nov 2005 20:26:39 +0200 | ||
1403 | 19 | |||
1404 | 20 | nevow (0.4.1-1.1) unstable; urgency=low | ||
1405 | 21 | |||
1406 | 22 | * NMU | ||
1407 | 23 | * Add missing build depends in python2.4-dev (Closes: #319230) | ||
1408 | 24 | * lintian error: fix package description indent for list items. | ||
1409 | 25 | |||
1410 | 26 | -- Bastian Kleineidam <calvin@debian.org> Sat, 13 Aug 2005 18:48:20 +0200 | ||
1411 | 27 | |||
1412 | 28 | nevow (0.4.1-1) unstable; urgency=low | ||
1413 | 29 | |||
1414 | 30 | * New upstream version. | ||
1415 | 31 | * Python 2.4 support. | ||
1416 | 32 | * Not using upstream tarball as it is too broken compared to | ||
1417 | 33 | SVN tag; specifically it is missing nevow/Canvas.fla, which | ||
1418 | 34 | is considered source code. | ||
1419 | 35 | |||
1420 | 36 | -- Tommi Virtanen <tv@debian.org> Mon, 27 Jun 2005 15:35:57 +0200 | ||
1421 | 37 | |||
1422 | 38 | nevow (0.3.0-1) unstable; urgency=low | ||
1423 | 39 | |||
1424 | 40 | * New upstream version. | ||
1425 | 41 | |||
1426 | 42 | -- Tommi Virtanen <tv@debian.org> Thu, 30 Sep 2004 12:12:44 +0300 | ||
1427 | 43 | |||
1428 | 44 | nevow (0.2.0-2) unstable; urgency=low | ||
1429 | 45 | |||
1430 | 46 | * Build-depend on both python2.3-dev and python-dev, it seems that is | ||
1431 | 47 | what cdbs wants. (Closes: #257911) | ||
1432 | 48 | |||
1433 | 49 | -- Tommi Virtanen <tv@debian.org> Tue, 13 Jul 2004 16:39:17 +0300 | ||
1434 | 50 | |||
1435 | 51 | nevow (0.2.0-1) unstable; urgency=low | ||
1436 | 52 | |||
1437 | 53 | * Initial Release. | ||
1438 | 54 | |||
1439 | 55 | -- Tommi Virtanen <tv@debian.org> Tue, 29 Jun 2004 10:26:36 +0300 | ||
1440 | 56 | |||
1441 | 57 | 0 | ||
1442 | === removed file 'Nevow/debian/compat' | |||
1443 | --- Nevow/debian/compat 2005-10-14 17:36:24 +0000 | |||
1444 | +++ Nevow/debian/compat 1970-01-01 00:00:00 +0000 | |||
1445 | @@ -1,1 +0,0 @@ | |||
1446 | 1 | 4 | ||
1447 | 2 | 0 | ||
1448 | === removed file 'Nevow/debian/control' | |||
1449 | --- Nevow/debian/control 2005-11-06 18:46:50 +0000 | |||
1450 | +++ Nevow/debian/control 1970-01-01 00:00:00 +0000 | |||
1451 | @@ -1,64 +0,0 @@ | |||
1452 | 1 | Source: nevow | ||
1453 | 2 | Section: devel | ||
1454 | 3 | Priority: extra | ||
1455 | 4 | Maintainer: Tommi Virtanen <tv@debian.org> | ||
1456 | 5 | Standards-Version: 3.6.2 | ||
1457 | 6 | Build-Depends-Indep: python-dev, python2.3-dev, python2.4-dev, cdbs, debhelper (>= 4.1.68) | ||
1458 | 7 | |||
1459 | 8 | Package: python-nevow | ||
1460 | 9 | Architecture: all | ||
1461 | 10 | Depends: python (>= 2.3), python (<< 2.4), python2.3-nevow | ||
1462 | 11 | Description: Web application templating system for Python and Twisted | ||
1463 | 12 | This is a dummy package that only depends on python2.3-nevow. | ||
1464 | 13 | |||
1465 | 14 | Package: python2.3-nevow | ||
1466 | 15 | Architecture: all | ||
1467 | 16 | Depends: python2.3, python2.3-twisted | ||
1468 | 17 | Description: Web application templating system for Python and Twisted | ||
1469 | 18 | Nevow's main focus is on separating the HTML template from both the | ||
1470 | 19 | business logic and the display logic, while allowing the programmer | ||
1471 | 20 | to write pure Python code as much as possible. It separates your code | ||
1472 | 21 | into 'data' and 'render' functions, a simplified implementation of | ||
1473 | 22 | traditional MVC. It has various parts which can be used individually | ||
1474 | 23 | or as a whole, integrated web solution: | ||
1475 | 24 | . | ||
1476 | 25 | - XHTML templates: contain no programming logic, only nodes tagged | ||
1477 | 26 | with nevow attributes | ||
1478 | 27 | - data/render methods: simplified MVC | ||
1479 | 28 | - stan: An s-expression-like syntax for expressing xml in pure python | ||
1480 | 29 | - formless: For describing the types of objects which may be passed | ||
1481 | 30 | to methods of your classes, validating and coercing string input from | ||
1482 | 31 | either web or command-line sources, and calling your methods | ||
1483 | 32 | automatically once validation passes | ||
1484 | 33 | - freeform: For rendering web forms based on formless type | ||
1485 | 34 | descriptions, accepting form posts and passing them to formless | ||
1486 | 35 | validators, and rendering error forms in the event validation fails | ||
1487 | 36 | - livepage: Cross-browser JavaScript glue for sending client side | ||
1488 | 37 | events to the server and server side events to the client after the | ||
1489 | 38 | page has loaded, without causing the entire page to refresh | ||
1490 | 39 | |||
1491 | 40 | Package: python2.4-nevow | ||
1492 | 41 | Architecture: all | ||
1493 | 42 | Depends: python2.4, python2.4-twisted | ||
1494 | 43 | Description: Web application templating system for Python and Twisted | ||
1495 | 44 | Nevow's main focus is on separating the HTML template from both the | ||
1496 | 45 | business logic and the display logic, while allowing the programmer | ||
1497 | 46 | to write pure Python code as much as possible. It separates your code | ||
1498 | 47 | into 'data' and 'render' functions, a simplified implementation of | ||
1499 | 48 | traditional MVC. It has various parts which can be used individually | ||
1500 | 49 | or as a whole, integrated web solution: | ||
1501 | 50 | . | ||
1502 | 51 | - XHTML templates: contain no programming logic, only nodes tagged | ||
1503 | 52 | with nevow attributes | ||
1504 | 53 | - data/render methods: simplified MVC | ||
1505 | 54 | - stan: An s-expression-like syntax for expressing xml in pure python | ||
1506 | 55 | - formless: For describing the types of objects which may be passed | ||
1507 | 56 | to methods of your classes, validating and coercing string input from | ||
1508 | 57 | either web or command-line sources, and calling your methods | ||
1509 | 58 | automatically once validation passes | ||
1510 | 59 | - freeform: For rendering web forms based on formless type | ||
1511 | 60 | descriptions, accepting form posts and passing them to formless | ||
1512 | 61 | validators, and rendering error forms in the event validation fails | ||
1513 | 62 | - livepage: Cross-browser JavaScript glue for sending client side | ||
1514 | 63 | events to the server and server side events to the client after the | ||
1515 | 64 | page has loaded, without causing the entire page to refresh | ||
1516 | 65 | 0 | ||
1517 | === removed file 'Nevow/debian/copyright' | |||
1518 | --- Nevow/debian/copyright 2005-10-14 17:36:24 +0000 | |||
1519 | +++ Nevow/debian/copyright 1970-01-01 00:00:00 +0000 | |||
1520 | @@ -1,10 +0,0 @@ | |||
1521 | 1 | This package was debianized by Tommi Virtanen tv@debian.org on | ||
1522 | 2 | Sun, 28 Mar 2004 16:44:10 +0300. | ||
1523 | 3 | |||
1524 | 4 | It was originally downloaded from http://www.divmod.org/Home/Projects/Nevow/ | ||
1525 | 5 | |||
1526 | 6 | Upstream Author: Donovan Preston et al <dp@divmod.org> | ||
1527 | 7 | |||
1528 | 8 | Copyright: | ||
1529 | 9 | |||
1530 | 10 | # See the file LICENSE at the top of the source tree. | ||
1531 | 11 | 0 | ||
1532 | === removed file 'Nevow/debian/python2.3-nevow.manpages' | |||
1533 | --- Nevow/debian/python2.3-nevow.manpages 2005-10-14 17:36:24 +0000 | |||
1534 | +++ Nevow/debian/python2.3-nevow.manpages 1970-01-01 00:00:00 +0000 | |||
1535 | @@ -1,1 +0,0 @@ | |||
1536 | 1 | doc/man/nevow-xmlgettext.1 | ||
1537 | 2 | 0 | ||
1538 | === removed file 'Nevow/debian/python2.4-nevow.manpages' | |||
1539 | --- Nevow/debian/python2.4-nevow.manpages 2005-10-14 17:36:24 +0000 | |||
1540 | +++ Nevow/debian/python2.4-nevow.manpages 1970-01-01 00:00:00 +0000 | |||
1541 | @@ -1,1 +0,0 @@ | |||
1542 | 1 | doc/man/nevow-xmlgettext.1 | ||
1543 | 2 | 0 | ||
1544 | === removed file 'Nevow/debian/rules' | |||
1545 | --- Nevow/debian/rules 2005-11-06 18:46:50 +0000 | |||
1546 | +++ Nevow/debian/rules 1970-01-01 00:00:00 +0000 | |||
1547 | @@ -1,71 +0,0 @@ | |||
1548 | 1 | #!/usr/bin/make -f | ||
1549 | 2 | # -*- mode: makefile; coding: utf-8 -*- | ||
1550 | 3 | # Copyright © 2002,2003 Colin Walters <walters@debian.org> | ||
1551 | 4 | |||
1552 | 5 | include /usr/share/cdbs/1/rules/debhelper.mk | ||
1553 | 6 | include /usr/share/cdbs/1/class/python-distutils.mk | ||
1554 | 7 | |||
1555 | 8 | DEB_INSTALL_DOCS_python2.3-nevow := doc/* | ||
1556 | 9 | DEB_INSTALL_EXAMPLES_python2.3-nevow := examples/* | ||
1557 | 10 | DEB_INSTALL_DOCS_python2.4-nevow := doc/* | ||
1558 | 11 | DEB_INSTALL_EXAMPLES_python2.4-nevow := examples/* | ||
1559 | 12 | DEB_DH_ALWAYS_EXCLUDE := .svn | ||
1560 | 13 | |||
1561 | 14 | docdir = debian/$(1)/usr/share/doc/$(1) | ||
1562 | 15 | binary-post-install/%:: | ||
1563 | 16 | grep -v '^# See the file LICENSE' \ | ||
1564 | 17 | '$(call docdir,$*)/copyright' \ | ||
1565 | 18 | >'$(call docdir,$*)/copyright.tmp' | ||
1566 | 19 | cat LICENSE \ | ||
1567 | 20 | >>'$(call docdir,$*)/copyright.tmp' | ||
1568 | 21 | mv \ | ||
1569 | 22 | '$(call docdir,$*)/copyright.tmp' \ | ||
1570 | 23 | '$(call docdir,$*)/copyright' | ||
1571 | 24 | |||
1572 | 25 | # see http://bugs.debian.org/295906 | ||
1573 | 26 | cdbs_python_ver = $(filter-out -%,$(subst -, -,$(patsubst python%,%,$(cdbs_curpkg)))) | ||
1574 | 27 | |||
1575 | 28 | $(patsubst %,binary-post-install/%,$(DEB_PYTHON_REAL_LIB_PACKAGES)):: binary-post-install/%: | ||
1576 | 29 | set -e; for file in debian/$(cdbs_curpkg)/usr/bin/*; do \ | ||
1577 | 30 | sed '1s|.*|#!/usr/bin/python$(cdbs_python_ver)|' $$file >\ | ||
1578 | 31 | "$${file}$(cdbs_python_ver)";\ | ||
1579 | 32 | rm -- "$$file";\ | ||
1580 | 33 | chmod 755 "$${file}$(cdbs_python_ver)";\ | ||
1581 | 34 | mv "debian/$(cdbs_curpkg)/usr/share/man/man1/$$(basename "$$file").1" \ | ||
1582 | 35 | "debian/$(cdbs_curpkg)/usr/share/man/man1/$$(basename "$$file")$(cdbs_python_ver).1";\ | ||
1583 | 36 | done | ||
1584 | 37 | |||
1585 | 38 | binary-post-install/python2.3-nevow:: | ||
1586 | 39 | set -e; for file in debian/$(cdbs_curpkg)/usr/bin/*;\ | ||
1587 | 40 | do target="$$(echo "$$file" | sed 's/$(cdbs_python_ver)$$//')";\ | ||
1588 | 41 | ln -s "$$(basename "$$file")" "$$target";\ | ||
1589 | 42 | manname="$$(basename "$$target").1.gz";\ | ||
1590 | 43 | ln -s "$$(basename "$$file").1.gz" \ | ||
1591 | 44 | "debian/$(cdbs_curpkg)/usr/share/man/man1/$$manname";\ | ||
1592 | 45 | done | ||
1593 | 46 | |||
1594 | 47 | clean:: | ||
1595 | 48 | rm -f setupcommon.pyc | ||
1596 | 49 | |||
1597 | 50 | |||
1598 | 51 | ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) | ||
1599 | 52 | TRIAL=trial$(cdbs_python_ver) | ||
1600 | 53 | TOPMODULES:=nevow formless | ||
1601 | 54 | $(patsubst %,binary-post-install/%,$(DEB_PYTHON_REAL_LIB_PACKAGES)):: binary-post-install/%: | ||
1602 | 55 | PYTHONPATH='debian/$(cdbs_curpkg)/usr/lib/python$(cdbs_python_ver)/site-packages/' \ | ||
1603 | 56 | '$(TRIAL)' --bwverbose -R $(TOPMODULES) | ||
1604 | 57 | |||
1605 | 58 | # Importing the modules generates .pyc files, and dh_python (which | ||
1606 | 59 | # normally cleans them) has already been run. Remove them manually. | ||
1607 | 60 | find 'debian/$(cdbs_curpkg)' -name '*.py[co]' -print0 \ | ||
1608 | 61 | | xargs -0 rm -f -- | ||
1609 | 62 | endif | ||
1610 | 63 | |||
1611 | 64 | clean:: | ||
1612 | 65 | rm -rf _trial_temp | ||
1613 | 66 | |||
1614 | 67 | |||
1615 | 68 | # distutils is sloppy and only cleans with the default python version, | ||
1616 | 69 | # leaving all the other stuff still in build | ||
1617 | 70 | clean:: | ||
1618 | 71 | rm -rf build | ||
1619 | 72 | 0 | ||
1620 | === removed directory 'Nevow/doc' | |||
1621 | === removed file 'Nevow/doc/README' | |||
1622 | --- Nevow/doc/README 2010-04-06 11:05:45 +0000 | |||
1623 | +++ Nevow/doc/README 1970-01-01 00:00:00 +0000 | |||
1624 | @@ -1,5 +0,0 @@ | |||
1625 | 1 | This is Nevow's documentation. The preferred format is Lore XHTML. You can | ||
1626 | 2 | generate the pretty version of the documentation with by running the | ||
1627 | 3 | following command in the howto directory: | ||
1628 | 4 | |||
1629 | 5 | lore | ||
1630 | 6 | 0 | ||
1631 | === removed directory 'Nevow/doc/howto' | |||
1632 | === removed directory 'Nevow/doc/howto/chattutorial' | |||
1633 | === removed file 'Nevow/doc/howto/chattutorial/concepts.xhtml' | |||
1634 | --- Nevow/doc/howto/chattutorial/concepts.xhtml 2008-09-03 20:03:47 +0000 | |||
1635 | +++ Nevow/doc/howto/chattutorial/concepts.xhtml 1970-01-01 00:00:00 +0000 | |||
1636 | @@ -1,136 +0,0 @@ | |||
1637 | 1 | <?xml version="1.0"?> | ||
1638 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
1639 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
1640 | 4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
1641 | 5 | <head> | ||
1642 | 6 | <title>Concepts</title> | ||
1643 | 7 | </head> | ||
1644 | 8 | <body> | ||
1645 | 9 | |||
1646 | 10 | <h1>Concepts</h1> | ||
1647 | 11 | |||
1648 | 12 | <h2>Servers and Clients</h2> | ||
1649 | 13 | |||
1650 | 14 | <p>COMET applications can seem an almost impenetrable mess when one is first | ||
1651 | 15 | learning about them, much like when writing event-based desktop | ||
1652 | 16 | applications. However, there are some basic concepts that we can emphasize now | ||
1653 | 17 | to circumvent or alleviate most of the confusion.</p> | ||
1654 | 18 | |||
1655 | 19 | <p>In principle, the problem is very simple:</p> | ||
1656 | 20 | <ul> | ||
1657 | 21 | <li>We want out users to interact with a <q>web page</q> with out having to refresh | ||
1658 | 22 | the page, and we want new data and/or views to be rendered in response to our | ||
1659 | 23 | users' actions;</li> | ||
1660 | 24 | <li>We want the ability to push updates to user pages from the server to the | ||
1661 | 25 | browser, when the server has new data or views that are ready.</li> | ||
1662 | 26 | </ul> | ||
1663 | 27 | |||
1664 | 28 | <p>As usual, the implementation of the solution is much more complicated than | ||
1665 | 29 | the statement of the problem, but hopefully the way that we have designed | ||
1666 | 30 | Athena will hide those implementation details while providing powerful tools to | ||
1667 | 31 | build the applications you envision. So, let's take a look at what you need to | ||
1668 | 32 | know about servers and clients when building Athena web applications.</p> | ||
1669 | 33 | |||
1670 | 34 | <p>It is crucial that one understands that when we write Athena applications, | ||
1671 | 35 | we are doing a few different things:</p> | ||
1672 | 36 | <ul> | ||
1673 | 37 | <li>Writing server code in Python that performs server actions</li> | ||
1674 | 38 | <li>Writing server code in Python that makes remote calls to the browser</li> | ||
1675 | 39 | <li>Writing browser code in JavaScript that performs browser actions</li> | ||
1676 | 40 | <li>Writing browser code in JavaScript that makes remote calls to the server</li> | ||
1677 | 41 | </ul> | ||
1678 | 42 | |||
1679 | 43 | <p>Since server-on-server and client-on-client are rather common place and | ||
1680 | 44 | generally well understood, we will ignore those for now. As the other two | ||
1681 | 45 | are the focus of AJAX/COMET and thus also the primary domain of Athena, that is | ||
1682 | 46 | what we will discuss below.</p> | ||
1683 | 47 | |||
1684 | 48 | <p>Browser-to-server calls are made by Athena via the now-famous | ||
1685 | 49 | XMLHttpRequest. Server-to-browser calls are opened by the browser ahead of | ||
1686 | 50 | time, and when the server is ready, the data is sent to the browser via that | ||
1687 | 51 | connection.</p> | ||
1688 | 52 | |||
1689 | 53 | <h2>JavaScript: Making Calls to the Server</h2> | ||
1690 | 54 | |||
1691 | 55 | <p>When creating the JavaScript portion of our applications, we subclass | ||
1692 | 56 | an Athena JavaScript widget, which has a method named | ||
1693 | 57 | <code>callRemote()</code>. By utilizing this method, we can send messages from | ||
1694 | 58 | our JavaScript client to the server (as long as the method we call exists in | ||
1695 | 59 | the server code).</p> | ||
1696 | 60 | |||
1697 | 61 | <p>For example, in the chat application we will be building in this series | ||
1698 | 62 | of tutorials, we will have a JavaScript class called <code>ChatterBox</code> with a | ||
1699 | 63 | <code>say()</code> method, like the following:</p> | ||
1700 | 64 | <pre> | ||
1701 | 65 | function say(self, msg) { | ||
1702 | 66 | self.callRemote("say", msg); | ||
1703 | 67 | // Now show the text to the user somehow... | ||
1704 | 68 | } | ||
1705 | 69 | </pre> | ||
1706 | 70 | <p>This will make a remote call to the Python server code, executing the | ||
1707 | 71 | <code>say()</code> method and passing the <code>msg</code> variable as a | ||
1708 | 72 | parameter.</p> | ||
1709 | 73 | |||
1710 | 74 | <p>In Athena, the relationship between the browser code and the server code is | ||
1711 | 75 | established by declaring the JavaScript module in the Python server code, in | ||
1712 | 76 | the following manner:</p> | ||
1713 | 77 | <pre class="python"> | ||
1714 | 78 | class ChatterBox(LiveElement): | ||
1715 | 79 | jsClass = u'ChatThing.ChatterBox' | ||
1716 | 80 | </pre> | ||
1717 | 81 | <p>Additionally, in order for the JS to be able to make a call to remote Python | ||
1718 | 82 | code, the Python method has to be exposed. This is a security feature, | ||
1719 | 83 | implemented to ensure the JavaScript code can only call Python methods that | ||
1720 | 84 | have been specifically marked as safe. Appropriately enough, this is done in | ||
1721 | 85 | your Python class with the <code>expose</code> decorator:</p> | ||
1722 | 86 | |||
1723 | 87 | <pre class="python"> | ||
1724 | 88 | def say(self, text): | ||
1725 | 89 | for chatter in chatRoom: | ||
1726 | 90 | chatter.youHeardSomething(text) | ||
1727 | 91 | say = expose(say) | ||
1728 | 92 | </pre> | ||
1729 | 93 | |||
1730 | 94 | <h2>Python: Making Calls to the Browser</h2> | ||
1731 | 95 | |||
1732 | 96 | <p>Now what about the COMET side of the equation? If we want our server to | ||
1733 | 97 | update data in the browser, we need to be able to call JavaScript code from our | ||
1734 | 98 | Python server. We use a similar Python method as the JavaScript one (when | ||
1735 | 99 | making calls from the browser to the server), acquired when our Python class | ||
1736 | 100 | inherited from <code>nevow.athena.LiveElement</code>:</p> | ||
1737 | 101 | |||
1738 | 102 | <pre class="python"> | ||
1739 | 103 | def hear(self, sayer, text): | ||
1740 | 104 | self.callRemote("hear", sayer, text) | ||
1741 | 105 | </pre> | ||
1742 | 106 | |||
1743 | 107 | <p>In order for this call to work, we need to have the <code>hear()</code> | ||
1744 | 108 | method defined in our <code>ChatterBox</code> JavaScript class, and that will | ||
1745 | 109 | look like this:</p> | ||
1746 | 110 | <pre> | ||
1747 | 111 | function hear(self, avatarName, text) { | ||
1748 | 112 | // Here, you'd show the user some text. | ||
1749 | 113 | } | ||
1750 | 114 | </pre> | ||
1751 | 115 | |||
1752 | 116 | <p>Unlike on our Python classes, no special annotations need to be made on the | ||
1753 | 117 | JavaScript side: all JavaScript methods on browser-side Widget objects are | ||
1754 | 118 | allowed to be called by the server. If you've sent code to the browser, you've | ||
1755 | 119 | already forfeited the ability to control when it's called. There wouldn't be a | ||
1756 | 120 | point to limiting the server's rights to run its code when the user can freely | ||
1757 | 121 | run it herself.</p> | ||
1758 | 122 | |||
1759 | 123 | <h2>Summary</h2> | ||
1760 | 124 | |||
1761 | 125 | <p>With the samples above, you should have a growing sense of how Python and | ||
1762 | 126 | JavaScript interact as servers and clients in the world of Athena. In | ||
1763 | 127 | particular, you should be getting a sense of how JavaScript and Python will be | ||
1764 | 128 | interacting in your Athena applications.</p> | ||
1765 | 129 | |||
1766 | 130 | <p>This has just been a taste of Athena with a few peeks into the code we | ||
1767 | 131 | will be writing. We will cover these topics in greater detail in the following | ||
1768 | 132 | pages, within the context of creating a functional Athena application, | ||
1769 | 133 | complete with step-by-step instructions and rationale.</p> | ||
1770 | 134 | |||
1771 | 135 | </body> | ||
1772 | 136 | </html> | ||
1773 | 137 | 0 | ||
1774 | === removed file 'Nevow/doc/howto/chattutorial/env.xhtml' | |||
1775 | --- Nevow/doc/howto/chattutorial/env.xhtml 2008-09-03 20:03:47 +0000 | |||
1776 | +++ Nevow/doc/howto/chattutorial/env.xhtml 1970-01-01 00:00:00 +0000 | |||
1777 | @@ -1,122 +0,0 @@ | |||
1778 | 1 | <?xml version="1.0"?> | ||
1779 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
1780 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
1781 | 4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
1782 | 5 | <head> | ||
1783 | 6 | <title>Setting up an Environment</title> | ||
1784 | 7 | </head> | ||
1785 | 8 | <body> | ||
1786 | 9 | |||
1787 | 10 | <h1>Setting up an Environment</h1> | ||
1788 | 11 | |||
1789 | 12 | <h2>Install</h2> | ||
1790 | 13 | |||
1791 | 14 | To run this tutorial, you need to have nevow available to python and | ||
1792 | 15 | you'll need the files in the doc/howto tree. You don't even have to | ||
1793 | 16 | install nevow; the examples will run within the source tree. | ||
1794 | 17 | |||
1795 | 18 | |||
1796 | 19 | <h3>Combinator: The Divmod Way</h3> | ||
1797 | 20 | |||
1798 | 21 | <p>Using SVN | ||
1799 | 22 | with <a href="http://divmod.org/trac/wiki/DivmodCombinator">Combinator</a> | ||
1800 | 23 | is the best way to try out the example code in-place (and hop between | ||
1801 | 24 | other SVN branches in the future). This is how we develop and test our | ||
1802 | 25 | applications at Divmod. If you have a system installation of Twisted | ||
1803 | 26 | that you don't want to update or interfere with, you can use this | ||
1804 | 27 | method without installing anything. | ||
1805 | 28 | </p> | ||
1806 | 29 | |||
1807 | 30 | <ol> | ||
1808 | 31 | |||
1809 | 32 | <li>Create a projects directory or change to some other test directory | ||
1810 | 33 | of your choice: | ||
1811 | 34 | <pre class="shell"> | ||
1812 | 35 | $ mkdir ~/Projects | ||
1813 | 36 | $ cd ~/Projects | ||
1814 | 37 | </pre> | ||
1815 | 38 | </li> | ||
1816 | 39 | |||
1817 | 40 | |||
1818 | 41 | <li>If you don't have the | ||
1819 | 42 | <a href="http://twistedmatrix.com/trac/">twisted library</a>, check it out now: | ||
1820 | 43 | <pre class="shell"> | ||
1821 | 44 | $ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted/trunk | ||
1822 | 45 | </pre> | ||
1823 | 46 | </li> | ||
1824 | 47 | |||
1825 | 48 | <li>Then get Combinator and Nevow (and the rest of Divmod). See the | ||
1826 | 49 | <a href="http://divmod.org/trac/wiki/CombinatorTutorial">Combinator | ||
1827 | 50 | Tutorial</a> for more about these special checkout paths. | ||
1828 | 51 | <pre class="shell"> | ||
1829 | 52 | $ svn co http://divmod.org/svn/Divmod/trunk Divmod/trunk | ||
1830 | 53 | </pre> | ||
1831 | 54 | </li> | ||
1832 | 55 | |||
1833 | 56 | <li>Set up the Combinator environment in this shell. You'll need this | ||
1834 | 57 | step in any future test shells since it adjusts PATH and PYTHONPATH: | ||
1835 | 58 | <pre class="shell"> | ||
1836 | 59 | $ eval `python Divmod/trunk/Combinator/environment.py` | ||
1837 | 60 | (some "link:" lines are normal) | ||
1838 | 61 | </pre> | ||
1839 | 62 | </li> | ||
1840 | 63 | |||
1841 | 64 | <li>Register both the Twisted and Divmod (and thus Nevow+Athena) codebases with | ||
1842 | 65 | Combinator: | ||
1843 | 66 | <pre class="shell"> | ||
1844 | 67 | $ chbranch Twisted trunk | ||
1845 | 68 | $ chbranch Divmod trunk | ||
1846 | 69 | </pre> | ||
1847 | 70 | </li> | ||
1848 | 71 | |||
1849 | 72 | <li>You can check to see if your environment is ready to go by running the | ||
1850 | 73 | tutorial tests (from any directory, after executing the previous command): | ||
1851 | 74 | <pre class="shell"> | ||
1852 | 75 | $ trial nevow.test.test_howtolistings | ||
1853 | 76 | </pre> | ||
1854 | 77 | If they all pass, you're ready to begin the tutorial. | ||
1855 | 78 | </li> | ||
1856 | 79 | </ol> | ||
1857 | 80 | |||
1858 | 81 | |||
1859 | 82 | |||
1860 | 83 | <h3>Standard distutils Installation</h3> | ||
1861 | 84 | |||
1862 | 85 | <p>If you don't want to manage branches and environments with | ||
1863 | 86 | Combinator, you can install our code in the | ||
1864 | 87 | standard <code>site-packages</code> directory. You'll still need the | ||
1865 | 88 | source tree so you can use the files in doc/howto.</p> | ||
1866 | 89 | |||
1867 | 90 | <p>For those that would prefer the <q>old way,</q> here's how you do it:</p> | ||
1868 | 91 | |||
1869 | 92 | <ol> | ||
1870 | 93 | |||
1871 | 94 | <li>Create a projects directory: | ||
1872 | 95 | <pre class="shell"> | ||
1873 | 96 | $ mkdir ~/Projects | ||
1874 | 97 | $ cd ~/Projects | ||
1875 | 98 | </pre> | ||
1876 | 99 | </li> | ||
1877 | 100 | |||
1878 | 101 | <li>Checkout and install the latest Twisted: | ||
1879 | 102 | <pre class="shell"> | ||
1880 | 103 | $ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted | ||
1881 | 104 | $ cd Twisted | ||
1882 | 105 | $ sudo python setup.py install | ||
1883 | 106 | $ cd ../ | ||
1884 | 107 | </pre> | ||
1885 | 108 | </li> | ||
1886 | 109 | |||
1887 | 110 | <li>Checkout and install Nevow: | ||
1888 | 111 | <pre class="shell"> | ||
1889 | 112 | $ svn co http://divmod.org/svn/Divmod/trunk/Nevow Nevow | ||
1890 | 113 | $ cd Nevow | ||
1891 | 114 | $ sudo python setup.py install | ||
1892 | 115 | $ cd ../ | ||
1893 | 116 | </pre> | ||
1894 | 117 | </li> | ||
1895 | 118 | |||
1896 | 119 | </ol> | ||
1897 | 120 | |||
1898 | 121 | </body> | ||
1899 | 122 | </html> | ||
1900 | 123 | 0 | ||
1901 | === removed file 'Nevow/doc/howto/chattutorial/index.xhtml' | |||
1902 | --- Nevow/doc/howto/chattutorial/index.xhtml 2008-09-03 20:03:47 +0000 | |||
1903 | +++ Nevow/doc/howto/chattutorial/index.xhtml 1970-01-01 00:00:00 +0000 | |||
1904 | @@ -1,56 +0,0 @@ | |||
1905 | 1 | <?xml version="1.0"?> | ||
1906 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
1907 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
1908 | 4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
1909 | 5 | <head> | ||
1910 | 6 | <title>Nevow Athena from Scratch, or The Evolution of a Chat Application</title> | ||
1911 | 7 | </head> | ||
1912 | 8 | <body> | ||
1913 | 9 | |||
1914 | 10 | <h1>Nevow Athena from Scratch, or The Evolution of a Chat Application</h1> | ||
1915 | 11 | |||
1916 | 12 | <h2>The <q>Chat Tutorial</q> Series</h2> | ||
1917 | 13 | |||
1918 | 14 | <p> | ||
1919 | 15 | Athena is the JavaScript engine behind Nevow, providing a great deal of | ||
1920 | 16 | resources and power to the developer of asynchronous web applications. To | ||
1921 | 17 | demonstrate this, we are using a web chat application as our primary example | ||
1922 | 18 | in this tutorial. The tutorial is split into several parts: a few introductory | ||
1923 | 19 | pages and then independent (but related) tutorials of increasing complexity. | ||
1924 | 20 | </p> | ||
1925 | 21 | |||
1926 | 22 | <ol> | ||
1927 | 23 | <li>Basics | ||
1928 | 24 | <ul> | ||
1929 | 25 | <li><a href="intro.xhtml">Introduction</a></li> | ||
1930 | 26 | <li><a href="concepts.xhtml">Concepts of Athena: AJAX, COMET, and Python</a></li> | ||
1931 | 27 | <li><a href="env.xhtml">Setting Up the Tutorial Environment and Running Tutorial Source Code</a></li> | ||
1932 | 28 | </ul> | ||
1933 | 29 | </li> | ||
1934 | 30 | <li><a href="part00/index.xhtml">Toy <q>Echo</q> Application </a></li> | ||
1935 | 31 | <li><a href="part01/index.xhtml">Simple Chat and Two-Way Communications</a></li> | ||
1936 | 32 | </ol> | ||
1937 | 33 | |||
1938 | 34 | <h2>History</h2> | ||
1939 | 35 | <p> | ||
1940 | 36 | Nevow's predecessor was Woven (and prior to that, WebMVC). Woven had something | ||
1941 | 37 | called <code>LivePage</code> that was doing DOM manipulation as far back as | ||
1942 | 38 | 2002. In early 2003, Woven event handlers supported sending JavaScript back to | ||
1943 | 39 | the user's browser, allowing pages to be updated in response to user-generated | ||
1944 | 40 | events. The earliest publicly visible revisions of Nevow made use of XHR | ||
1945 | 41 | (XMLHttpRequest) in early 2004. These facts are notable because Nevow was using | ||
1946 | 42 | AJAX a year before the term was coined in 2005 and had working code in 2002 and | ||
1947 | 43 | 2003 that predated Netscape publishing articles on what they called <q>Inner | ||
1948 | 44 | Browsing</q> where all navigation takes place withing a single page. | ||
1949 | 45 | </p> | ||
1950 | 46 | |||
1951 | 47 | <p> | ||
1952 | 48 | Again taking the lead, Athena offers features which developers cannot find | ||
1953 | 49 | elsewhere. In this series, we attempt to expose these excellent qualities | ||
1954 | 50 | to the world of application | ||
1955 | 51 | developers. | ||
1956 | 52 | </p> | ||
1957 | 53 | |||
1958 | 54 | </body> | ||
1959 | 55 | </html> | ||
1960 | 56 | |||
1961 | 57 | 0 | ||
1962 | === removed file 'Nevow/doc/howto/chattutorial/intro.xhtml' | |||
1963 | --- Nevow/doc/howto/chattutorial/intro.xhtml 2008-09-03 20:03:47 +0000 | |||
1964 | +++ Nevow/doc/howto/chattutorial/intro.xhtml 1970-01-01 00:00:00 +0000 | |||
1965 | @@ -1,106 +0,0 @@ | |||
1966 | 1 | <?xml version="1.0"?> | ||
1967 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
1968 | 3 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
1969 | 4 | <head> | ||
1970 | 5 | <title>Introduction</title> | ||
1971 | 6 | </head> | ||
1972 | 7 | <body> | ||
1973 | 8 | |||
1974 | 9 | <h1>Introduction</h1> | ||
1975 | 10 | |||
1976 | 11 | <h2>Who is this tutorial for?</h2> | ||
1977 | 12 | |||
1978 | 13 | <p>This tutorial is for people who want to build interactive client-server | ||
1979 | 14 | functionality where a web-browser is the client. It will show you how to build | ||
1980 | 15 | a live, interactive chat application that requires nothing more than a web | ||
1981 | 16 | browser that supports JavaScript.</p> | ||
1982 | 17 | |||
1983 | 18 | <p>The interesting thing about a chat application, which shows why Nevow Athena | ||
1984 | 19 | is special, is that it involves two-way communication. In other words, it | ||
1985 | 20 | involves not only the recently-popular AJAX (the web browser sending commands | ||
1986 | 21 | to the server without loading a new page) but also the trickier and, in our | ||
1987 | 22 | opinion, somewhat cooler technique known as COMET (the web server | ||
1988 | 23 | sending commands to the <em>browser</em>).</p> | ||
1989 | 24 | |||
1990 | 25 | <h2>Who is this tutorial <em>not</em> for?</h2> | ||
1991 | 26 | |||
1992 | 27 | <p>Nevow Athena is <em>not</em> for people who want a normal web application | ||
1993 | 28 | framework. If you want one of those, you should use | ||
1994 | 29 | non-Athena-<a href="http://divmod.org/trac/wiki/DivmodNevow">Nevow</a>, | ||
1995 | 30 | <a href="http://www.djangoproject.com/">Django</a>, | ||
1996 | 31 | <a href="http://turbogears.org/">TurboGears</a>, or maybe even | ||
1997 | 32 | <a href="http://rubyonrails.org/">Ruby On Rails</a>. Athena doesn't work in terms | ||
1998 | 33 | of pages, links, or HTTP requests and responses; it is a client-server | ||
1999 | 34 | framework that works in terms of widgets, JavaScript objects, and symmetric | ||
2000 | 35 | asynchronous message queues.</p> | ||
2001 | 36 | |||
2002 | 37 | <p>However, as alluded to above, Athena is part of a larger framework, Nevow, | ||
2003 | 38 | which can be used to build more general-purpose and <q>traditional</q> | ||
2004 | 39 | web applications.</p> | ||
2005 | 40 | |||
2006 | 41 | <h2>AJAX</h2> | ||
2007 | 42 | |||
2008 | 43 | <p>AJAX isn't a technology in and of itself, bur rather an amalgam of | ||
2009 | 44 | technologies used together in order to accomplish the goal of making web | ||
2010 | 45 | applications more responsive than traditional delivery and interactive | ||
2011 | 46 | mechanisms, such as HTML forms submitted to a server.</p> | ||
2012 | 47 | |||
2013 | 48 | <p>In particular, AJAX consists of the following:</p> | ||
2014 | 49 | <ul> | ||
2015 | 50 | <li>Asynchronous communications from a user's browser to a server</li> | ||
2016 | 51 | <li>JavaScript</li> | ||
2017 | 52 | <li>Exchanged data (usually XML or JSON)</li> | ||
2018 | 53 | </ul> | ||
2019 | 54 | |||
2020 | 55 | <h2>COMET</h2> | ||
2021 | 56 | |||
2022 | 57 | <p>Historically, the focus of AJAX technologies was user-event driven. However, | ||
2023 | 58 | with the need to update the user's browser with events generated at the server, | ||
2024 | 59 | a solution more sophisticated than AJAX was needed; this has been dubbed COMET. | ||
2025 | 60 | Athena is implemented using both AJAX and COMET techniques, and therefore | ||
2026 | 61 | allows two-way browser <-> server communications.</p> | ||
2027 | 62 | |||
2028 | 63 | <h2>Athena Basics</h2> | ||
2029 | 64 | |||
2030 | 65 | <p>We've provided brief background information on AJAX/COMET, but what is the | ||
2031 | 66 | purpose of Athena? What makes Athena different than other solutions? Here are a | ||
2032 | 67 | few key points that should help with these questions::</p> | ||
2033 | 68 | <ul> | ||
2034 | 69 | <li>Athena exists to make writing COMET web applications easy.</li> | ||
2035 | 70 | <li>Athena is written in Python and JavaScript</li> | ||
2036 | 71 | <li>It is written to be used with Nevow, a <a | ||
2037 | 72 | href="http://twistedmatrix.com/">Twisted</a>-based web framework.</li> | ||
2038 | 73 | <li>Similar to Twisted's <a | ||
2039 | 74 | href="http://twistedmatrix.com/projects/core/documentation/howto/pb-intro.html">Perspective | ||
2040 | 75 | Broker</a>, Athena employs remote calls.</li> | ||
2041 | 76 | </ul> | ||
2042 | 77 | |||
2043 | 78 | <p>Athena was written by Twisted and Divmod developers (in addition to | ||
2044 | 79 | contributing members of the community) in order to bring the outdated and | ||
2045 | 80 | Nevow-incompatible Woven LivePage technology to Nevow. In addition, it was an | ||
2046 | 81 | opportunity to improve upon the original design and incorporate new features to | ||
2047 | 82 | address the growing needs of developers.</p> | ||
2048 | 83 | |||
2049 | 84 | <h2>Target Applications</h2> | ||
2050 | 85 | |||
2051 | 86 | <p>Good candidates for Athena web applications would include those where the | ||
2052 | 87 | application needs to respond to user input and/or updates from servers, such | ||
2053 | 88 | as the following:</p> | ||
2054 | 89 | <ul> | ||
2055 | 90 | <li>conference software (e.g. whiteboard, shared text, chat, etc.)</li> | ||
2056 | 91 | <li>mail clients</li> | ||
2057 | 92 | <li>interactive, multi-player games</li> | ||
2058 | 93 | <li>social networking tools</li> | ||
2059 | 94 | <li>office applications (e.g., spreadsheets, word processors, etc.)</li> | ||
2060 | 95 | </ul> | ||
2061 | 96 | |||
2062 | 97 | <h2>Target Developers</h2> | ||
2063 | 98 | |||
2064 | 99 | <p>Anyone who wants to create interactive, web-based applications is a | ||
2065 | 100 | potential Nevow/Athena user. It's best to have some background in writing web | ||
2066 | 101 | applications, and in addition, to know how to use Nevow. However, we hope that | ||
2067 | 102 | this tutorial will be just as useful for beginners as experienced | ||
2068 | 103 | developers.</p> | ||
2069 | 104 | |||
2070 | 105 | </body> | ||
2071 | 106 | </html> | ||
2072 | 107 | 0 | ||
2073 | === removed directory 'Nevow/doc/howto/chattutorial/part00' | |||
2074 | === removed file 'Nevow/doc/howto/chattutorial/part00/index.xhtml' | |||
2075 | --- Nevow/doc/howto/chattutorial/part00/index.xhtml 2008-09-03 20:03:47 +0000 | |||
2076 | +++ Nevow/doc/howto/chattutorial/part00/index.xhtml 1970-01-01 00:00:00 +0000 | |||
2077 | @@ -1,230 +0,0 @@ | |||
2078 | 1 | <?xml version="1.0"?> | ||
2079 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
2080 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
2081 | 4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
2082 | 5 | <head> | ||
2083 | 6 | <title>Nevow Athena from Scratch: Echo Application</title> | ||
2084 | 7 | </head> | ||
2085 | 8 | <body> | ||
2086 | 9 | |||
2087 | 10 | <h2>What is an "Echo Application?"</h2> | ||
2088 | 11 | |||
2089 | 12 | <p> | ||
2090 | 13 | Our first foray into building an Athena application will be an easy venture: | ||
2091 | 14 | we want to type something in an input box and have it echoed back to us on | ||
2092 | 15 | the same page, without having to reload anything. Why? Well, our eventual | ||
2093 | 16 | goal is to have a working chat server, with all sorts of technical bells | ||
2094 | 17 | and whistles (persistent storage, authentication, | ||
2095 | 18 | etc.), but that's a bit heady for right now. Many of the same principles | ||
2096 | 19 | which we will eventually employ in our chat application exist for a simple | ||
2097 | 20 | case of sending textual messages between a web browser and a server. This | ||
2098 | 21 | is the essence of our "Echo" application. | ||
2099 | 22 | </p> | ||
2100 | 23 | |||
2101 | 24 | <h2>Mental Preparation</h2> | ||
2102 | 25 | |||
2103 | 26 | <p>In the | ||
2104 | 27 | <a href="../intro.html">Introduction</a> and the | ||
2105 | 28 | <a href="../concepts.html">Concepts</a> pages, we had a refresher on AJAX and | ||
2106 | 29 | COMET and we learned a little bit about what that looks like for Athena. But | ||
2107 | 30 | as we sit down to actually write an Athena application, what do we need to | ||
2108 | 31 | wrap our heads around? | ||
2109 | 32 | </p> | ||
2110 | 33 | |||
2111 | 34 | <p>Given the introductory knowledge we have, we know that we will need to | ||
2112 | 35 | write some JavaScript, some Python, and if our past experience in developing | ||
2113 | 36 | web applications is any guide, some form of template. This indeed is the | ||
2114 | 37 | case, but here's something big: we're not working with pages and page | ||
2115 | 38 | templates; we're working with "elements", or parts of the DOM tree. We will | ||
2116 | 39 | not be creating page resources; we will be creating just the parts of a | ||
2117 | 40 | "traditional" page that will be dynamic and interactive. | ||
2118 | 41 | </p> | ||
2119 | 42 | |||
2120 | 43 | <h2>Architecture</h2> | ||
2121 | 44 | |||
2122 | 45 | <p>Now that we've pumped ourselves up and before we start clacking away at the | ||
2123 | 46 | keyboard, we need to get pointed in the right direction. We need a | ||
2124 | 47 | plan. Here's what we know:</p> | ||
2125 | 48 | |||
2126 | 49 | <ol> | ||
2127 | 50 | <li>We will have a server that: | ||
2128 | 51 | <ul> | ||
2129 | 52 | <li>serves dynamic elements in a resource accessible via a URL;</li> | ||
2130 | 53 | <li>communicates with a client.</li> | ||
2131 | 54 | </ul> | ||
2132 | 55 | </li> | ||
2133 | 56 | <li>We will have a client that: | ||
2134 | 57 | <ul> | ||
2135 | 58 | <li>communicates with the server;</li> | ||
2136 | 59 | <li>updates its DOM tree.</li> | ||
2137 | 60 | </ul> | ||
2138 | 61 | </li> | ||
2139 | 62 | </ol> | ||
2140 | 63 | |||
2141 | 64 | <p>The user experience of this application will be the following:</p> | ||
2142 | 65 | <ol> | ||
2143 | 66 | <li>they will type text in an input on a form; and</li> | ||
2144 | 67 | <li>the typed text will be rendered to a different part of the page upon | ||
2145 | 68 | hitting a submit button.</li> | ||
2146 | 69 | </ol> | ||
2147 | 70 | |||
2148 | 71 | <p>We will not simply write user input to a <code>div</code> with JavaScript | ||
2149 | 72 | DOM manipulation, but will instead pass data like we expect will be necessary | ||
2150 | 73 | when we write our chat application. After all, it's probably best to build | ||
2151 | 74 | towards our goal. In order to accomplish this, the application will do | ||
2152 | 75 | something like the following:</p> | ||
2153 | 76 | |||
2154 | 77 | <ol> | ||
2155 | 78 | <li>JavaScript client code will extract user input and send | ||
2156 | 79 | it to our server;</li> | ||
2157 | 80 | <li>Python code will receive messages from the client;</li> | ||
2158 | 81 | <li>Python code will send messages to the client; and</li> | ||
2159 | 82 | <li>a template file (or <code>stan</code> code) will be used for | ||
2160 | 83 | presentation.</li> | ||
2161 | 84 | </ol> | ||
2162 | 85 | |||
2163 | 86 | <p></p> | ||
2164 | 87 | |||
2165 | 88 | <h2>Let the Coding Begin</h2> | ||
2166 | 89 | |||
2167 | 90 | <p>In a future installment, we will outline the development process from | ||
2168 | 91 | the perspective of test-driven development, in order to not only show how | ||
2169 | 92 | to write unit tests for Athena (Python and JavaScript), but to encourage | ||
2170 | 93 | good programming practices while working with Athena. For now, though, we will | ||
2171 | 94 | just dive right in.</p> | ||
2172 | 95 | |||
2173 | 96 | <h3>Presentation</h3> | ||
2174 | 97 | |||
2175 | 98 | <p>Let's start with the easy bit: what our app will look like. Here is the | ||
2176 | 99 | template for our echo application:</p> | ||
2177 | 100 | |||
2178 | 101 | <a href="listings/echothing/template.html" class="html-listing" /> | ||
2179 | 102 | |||
2180 | 103 | <p>Things to note:</p> | ||
2181 | 104 | <ul> | ||
2182 | 105 | <li>This is not a complete HTML document, but is an XHTML template for an | ||
2183 | 106 | "element".</li> | ||
2184 | 107 | <li>The name space declarations in the top <code>div</code> tag are necessary | ||
2185 | 108 | for the operation of Athena.</li> | ||
2186 | 109 | <li>When we hit the "Send" button, our JavaScript class will call the | ||
2187 | 110 | <code>doSay()</code> method.</li> | ||
2188 | 111 | </ul> | ||
2189 | 112 | |||
2190 | 113 | <h3>Writing the Client</h3> | ||
2191 | 114 | |||
2192 | 115 | <p>Next up is the JavaScript. We need to send our data to the server. In a | ||
2193 | 116 | full chat application, it would be necessary to send the data to the server | ||
2194 | 117 | so that we could propagate the message to all connected clients. In this | ||
2195 | 118 | case, with the simple echo, we're not going to do anything with the data | ||
2196 | 119 | that gets sent to the server, except send it back, of course.</p> | ||
2197 | 120 | |||
2198 | 121 | <p>Our JavaScript will need to do several things:</p> | ||
2199 | 122 | <ol> | ||
2200 | 123 | <li>import required modules;</li> | ||
2201 | 124 | <li>inherit <code>callRemote</code> functionality from the base | ||
2202 | 125 | <code>Widget</code> class;</li> | ||
2203 | 126 | <li>setup convenience attributes;</li> | ||
2204 | 127 | <li>implement the <code>doSay()</code> method we put in our template above; | ||
2205 | 128 | and</li> | ||
2206 | 129 | <li>implement a method for updating the DOM with data it receives from | ||
2207 | 130 | the server</li> | ||
2208 | 131 | </ol> | ||
2209 | 132 | |||
2210 | 133 | <a href="listings/echothing/js/EchoThing.js" class="py-listing" /> | ||
2211 | 134 | |||
2212 | 135 | <p>Points to note:</p> | ||
2213 | 136 | <ul> | ||
2214 | 137 | <li>Those import statements aren't just pretty: they are necessary! In Athena, | ||
2215 | 138 | you need to treat those like you treat the import statements in Python. | ||
2216 | 139 | </li> | ||
2217 | 140 | <li>The attributes set in the <code>__init__()</code> method are for | ||
2218 | 141 | convenience when we reference them in other methods.</li> | ||
2219 | 142 | <li>Note the <code>callRemote()</code> method in <code>doSay()</code>, | ||
2220 | 143 | As mentioned in the <a href="../concepts.html">Concepts</a> section, this | ||
2221 | 144 | is how JavaScript is communicating with our Python server.</li> | ||
2222 | 145 | <li>Another thing about <code>doSay</code>: this is the submit handler. As | ||
2223 | 146 | such, it needs to return false so that the browser is prevented from doing a | ||
2224 | 147 | normal form submission.</li> | ||
2225 | 148 | <li><code>addText()</code> is the method that will be updating the browser | ||
2226 | 149 | DOM once the server sends the data back.</li> | ||
2227 | 150 | </ul> | ||
2228 | 151 | |||
2229 | 152 | <p>There's not much to say about the next one. This is what sets up the | ||
2230 | 153 | relationship between our module name and the actual file itself (so that | ||
2231 | 154 | the JavaScript can be loaded):</p> | ||
2232 | 155 | |||
2233 | 156 | <a href="listings/nevow/plugins/echothing_package.py" class="py-listing" /> | ||
2234 | 157 | |||
2235 | 158 | <h3>Writing the Server</h3> | ||
2236 | 159 | |||
2237 | 160 | <p>Despite what one might think, writing the server may be the easiest | ||
2238 | 161 | part! If you've created Nevow applications before, then this will look | ||
2239 | 162 | very familiar. The only method we need is one that will send data back to | ||
2240 | 163 | the client. Besides importing the necessary modules and creating a class | ||
2241 | 164 | with some boilerplate, that's about it. | ||
2242 | 165 | </p> | ||
2243 | 166 | |||
2244 | 167 | <p>Let's take a look at the code:</p> | ||
2245 | 168 | |||
2246 | 169 | <a href="listings/echothing/echobox.py" class="py-listing" /> | ||
2247 | 170 | |||
2248 | 171 | <p>As promised, simple as can be. We do make use of a Twisted utility that | ||
2249 | 172 | simplifies typing the path to our template. Some very important points:</p> | ||
2250 | 173 | <ul> | ||
2251 | 174 | <li>The <code>jsClass</code> assignment is what connects this code to your | ||
2252 | 175 | JavaScript code.</li> | ||
2253 | 176 | <li>As discussed in the <a href="../concepts.html">Concepts</a> section, | ||
2254 | 177 | the <code>expose</code> decorator is required if our JavaScript is going | ||
2255 | 178 | to be able to call the <code>say()</code> method.</li> | ||
2256 | 179 | </ul> | ||
2257 | 180 | |||
2258 | 181 | <h3>Putting it All Together</h3> | ||
2259 | 182 | |||
2260 | 183 | <p>Now that we've got all the code in front of us, we can trace out exactly | ||
2261 | 184 | what happens:</p> | ||
2262 | 185 | <ol> | ||
2263 | 186 | <li>the user loads the resource in their browser, and the template is | ||
2264 | 187 | rendered;</li> | ||
2265 | 188 | <li>after typing a message in the input box, the user hits submit;</li> | ||
2266 | 189 | <li>upon hitting submit, the client code <code>doSay()</code> method is | ||
2267 | 190 | called;</li> | ||
2268 | 191 | <li><code>doSay()</code> makes a remote call to the Python server method | ||
2269 | 192 | <code>say()</code>;</li> | ||
2270 | 193 | <li>the Python server receives the data when <code>say()</code> is called, and | ||
2271 | 194 | then it passes that data to the client code's <code>addText()</code> method;</li> | ||
2272 | 195 | <li>with control back in the client code and data fresh from the server, | ||
2273 | 196 | JavaScript can now update the page's DOM with the new data, and this is | ||
2274 | 197 | what the <code>addText()</code> method does;</li> | ||
2275 | 198 | <li>when <code>addText()</code> finishes, the cycle has completed and the | ||
2276 | 199 | browser now displays the latest data input by the user.</li> | ||
2277 | 200 | </ol> | ||
2278 | 201 | |||
2279 | 202 | <h3>The Fruits of Our Labor</h3> | ||
2280 | 203 | |||
2281 | 204 | <p>Now we get to run it! This is a little different than what you may be | ||
2282 | 205 | used to, if you have written Twisted applications in the past. We are using | ||
2283 | 206 | the plugin architecture of Twisted and Nevow such that <code>twistd</code> | ||
2284 | 207 | will publish our element in an HTTP service. To do this, we will use | ||
2285 | 208 | <code>twistd</code>'s <code>athena-widget</code> command:</p> | ||
2286 | 209 | |||
2287 | 210 | <pre class="shell"> | ||
2288 | 211 | cd Nevow/doc/howto/chattutorial/part00/listings | ||
2289 | 212 | twistd -n athena-widget --element=echothing.echobox.EchoElement | ||
2290 | 213 | </pre> | ||
2291 | 214 | |||
2292 | 215 | <p>If you executed this against the tutorial code on your local machine, | ||
2293 | 216 | you can now visit <a href="http://localhost:8080">localhost:8080</a> and start | ||
2294 | 217 | echoing to your heart's content.</p> | ||
2295 | 218 | |||
2296 | 219 | <h2>Summary</h2> | ||
2297 | 220 | |||
2298 | 221 | <p>As you can see, our echo application is a toy app that doesn't do | ||
2299 | 222 | anything very useful. However, it has provided us with a basis for learning how | ||
2300 | 223 | to write working Athena code that lets a browser and server communicate | ||
2301 | 224 | with each other, both sending and receiving data. As such, we now have a | ||
2302 | 225 | solid foundation upon which we can build a functional, useful <i>and</i> | ||
2303 | 226 | instructional chat application.</p> | ||
2304 | 227 | |||
2305 | 228 | </body> | ||
2306 | 229 | </html> | ||
2307 | 230 | |||
2308 | 231 | 0 | ||
2309 | === removed directory 'Nevow/doc/howto/chattutorial/part00/listings' | |||
2310 | === removed directory 'Nevow/doc/howto/chattutorial/part00/listings/echothing' | |||
2311 | === removed file 'Nevow/doc/howto/chattutorial/part00/listings/echothing/__init__.py' | |||
2312 | --- Nevow/doc/howto/chattutorial/part00/listings/echothing/__init__.py 2008-09-03 20:03:47 +0000 | |||
2313 | +++ Nevow/doc/howto/chattutorial/part00/listings/echothing/__init__.py 1970-01-01 00:00:00 +0000 | |||
2314 | @@ -1,1 +0,0 @@ | |||
2315 | 1 | |||
2316 | 2 | 0 | ||
2317 | === removed file 'Nevow/doc/howto/chattutorial/part00/listings/echothing/echobox.py' | |||
2318 | --- Nevow/doc/howto/chattutorial/part00/listings/echothing/echobox.py 2008-09-03 20:03:47 +0000 | |||
2319 | +++ Nevow/doc/howto/chattutorial/part00/listings/echothing/echobox.py 1970-01-01 00:00:00 +0000 | |||
2320 | @@ -1,12 +0,0 @@ | |||
2321 | 1 | from twisted.python.util import sibpath | ||
2322 | 2 | from nevow.athena import LiveElement, expose | ||
2323 | 3 | from nevow.loaders import xmlfile | ||
2324 | 4 | |||
2325 | 5 | class EchoElement(LiveElement): | ||
2326 | 6 | |||
2327 | 7 | docFactory = xmlfile(sibpath(__file__, 'template.html')) | ||
2328 | 8 | jsClass = u'EchoThing.EchoWidget' | ||
2329 | 9 | |||
2330 | 10 | def say(self, message): | ||
2331 | 11 | self.callRemote('addText', message) | ||
2332 | 12 | say = expose(say) | ||
2333 | 13 | 0 | ||
2334 | === removed directory 'Nevow/doc/howto/chattutorial/part00/listings/echothing/js' | |||
2335 | === removed file 'Nevow/doc/howto/chattutorial/part00/listings/echothing/js/EchoThing.js' | |||
2336 | --- Nevow/doc/howto/chattutorial/part00/listings/echothing/js/EchoThing.js 2009-01-21 22:58:03 +0000 | |||
2337 | +++ Nevow/doc/howto/chattutorial/part00/listings/echothing/js/EchoThing.js 1970-01-01 00:00:00 +0000 | |||
2338 | @@ -1,22 +0,0 @@ | |||
2339 | 1 | // import Nevow.Athena | ||
2340 | 2 | |||
2341 | 3 | Nevow.Athena.Widget.subclass(EchoThing, 'EchoWidget').methods( | ||
2342 | 4 | function __init__(self, node) { | ||
2343 | 5 | EchoThing.EchoWidget.upcall(self, "__init__", node); | ||
2344 | 6 | self.echoWidget = self.nodeByAttribute('name', 'echoElement'); | ||
2345 | 7 | self.scrollArea = self.nodeByAttribute('name', 'scrollArea'); | ||
2346 | 8 | self.message = self.nodeByAttribute('name', 'message'); | ||
2347 | 9 | }, | ||
2348 | 10 | |||
2349 | 11 | function doSay(self) { | ||
2350 | 12 | self.callRemote("say", self.message.value); | ||
2351 | 13 | self.message.value = ""; | ||
2352 | 14 | return false; | ||
2353 | 15 | }, | ||
2354 | 16 | |||
2355 | 17 | function addText(self, text) { | ||
2356 | 18 | var newNode = document.createElement('div'); | ||
2357 | 19 | newNode.appendChild(document.createTextNode(text)); | ||
2358 | 20 | self.scrollArea.appendChild(newNode); | ||
2359 | 21 | document.body.scrollTop = document.body.scrollHeight; | ||
2360 | 22 | }); | ||
2361 | 23 | 0 | ||
2362 | === removed file 'Nevow/doc/howto/chattutorial/part00/listings/echothing/template.html' | |||
2363 | --- Nevow/doc/howto/chattutorial/part00/listings/echothing/template.html 2008-09-03 20:03:47 +0000 | |||
2364 | +++ Nevow/doc/howto/chattutorial/part00/listings/echothing/template.html 1970-01-01 00:00:00 +0000 | |||
2365 | @@ -1,11 +0,0 @@ | |||
2366 | 1 | <div xmlns:nevow="http://nevow.com/ns/nevow/0.1" | ||
2367 | 2 | xmlns:athena="http://divmod.org/ns/athena/0.7" | ||
2368 | 3 | nevow:render="liveElement"> | ||
2369 | 4 | <h2>Echo Element</h2> | ||
2370 | 5 | <form name="echoElement"> | ||
2371 | 6 | <athena:handler event="onsubmit" handler="doSay" /> | ||
2372 | 7 | <div name="scrollArea"> | ||
2373 | 8 | </div> | ||
2374 | 9 | <input name="message" /><input type="submit" value="Send" /> | ||
2375 | 10 | </form> | ||
2376 | 11 | </div> | ||
2377 | 12 | 0 | ||
2378 | === removed directory 'Nevow/doc/howto/chattutorial/part00/listings/nevow' | |||
2379 | === removed directory 'Nevow/doc/howto/chattutorial/part00/listings/nevow/plugins' | |||
2380 | === removed file 'Nevow/doc/howto/chattutorial/part00/listings/nevow/plugins/echothing_package.py' | |||
2381 | --- Nevow/doc/howto/chattutorial/part00/listings/nevow/plugins/echothing_package.py 2008-09-03 20:03:47 +0000 | |||
2382 | +++ Nevow/doc/howto/chattutorial/part00/listings/nevow/plugins/echothing_package.py 1970-01-01 00:00:00 +0000 | |||
2383 | @@ -1,8 +0,0 @@ | |||
2384 | 1 | |||
2385 | 2 | from twisted.python import util | ||
2386 | 3 | |||
2387 | 4 | from nevow import athena | ||
2388 | 5 | |||
2389 | 6 | import echothing | ||
2390 | 7 | |||
2391 | 8 | chatthingPkg = athena.AutoJSPackage(util.sibpath(echothing.__file__, 'js')) | ||
2392 | 9 | 0 | ||
2393 | === removed directory 'Nevow/doc/howto/chattutorial/part01' | |||
2394 | === removed file 'Nevow/doc/howto/chattutorial/part01/index.xhtml' | |||
2395 | --- Nevow/doc/howto/chattutorial/part01/index.xhtml 2008-09-03 20:03:47 +0000 | |||
2396 | +++ Nevow/doc/howto/chattutorial/part01/index.xhtml 1970-01-01 00:00:00 +0000 | |||
2397 | @@ -1,236 +0,0 @@ | |||
2398 | 1 | <?xml version="1.0"?> | ||
2399 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
2400 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
2401 | 4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
2402 | 5 | <head> | ||
2403 | 6 | <title>Nevow Athena from Scratch: Chat Application</title> | ||
2404 | 7 | </head> | ||
2405 | 8 | <body> | ||
2406 | 9 | |||
2407 | 10 | <h2>Architecture</h2> | ||
2408 | 11 | |||
2409 | 12 | <p>We'll assume that you've read all the preceding sections of this tutorial | ||
2410 | 13 | and have just finished the "Echo" application example. As such, we don't need | ||
2411 | 14 | to do any more "mental preparation" and can skip straight to a description of | ||
2412 | 15 | the architecture.</p> | ||
2413 | 16 | |||
2414 | 17 | <p>Fundamentally, this is no different than our echo application: there is | ||
2415 | 18 | a little more chatter that takes place between the client and server; | ||
2416 | 19 | there's another object involved (a <code>ChatRoom</code>); and we'll have | ||
2417 | 20 | to run the server a little differently. | ||
2418 | 21 | </p> | ||
2419 | 22 | |||
2420 | 23 | <p>Here are the new features we want to support:</p> | ||
2421 | 24 | <ul> | ||
2422 | 25 | <li>login form;</li> | ||
2423 | 26 | <li>in-memory user storage;</li> | ||
2424 | 27 | <li>the ability to send global alerts to all users; and</li> | ||
2425 | 28 | <li>the ability for all users to "hear" when another user speaks in the | ||
2426 | 29 | chat room;</li> | ||
2427 | 30 | </ul> | ||
2428 | 31 | |||
2429 | 32 | <p>A general rule we can establish about our architecture is that if something | ||
2430 | 33 | has to happen for everyone, that code needs to appear on the server side, | ||
2431 | 34 | since it's the server that is keeping track of all users. If something is | ||
2432 | 35 | going to happen irrespective of other users or if browser DOM manipulation | ||
2433 | 36 | is required, then we know the client will be the recipient of the code.</p> | ||
2434 | 37 | |||
2435 | 38 | <p>As such, in the features above, the login form will be client code. The | ||
2436 | 39 | user storage, global alerts, and "hearing" will be implemented in server | ||
2437 | 40 | code for the data; updating the DOM with that data will be implemented in | ||
2438 | 41 | client code.</p> | ||
2439 | 42 | |||
2440 | 43 | <p>The user experience of this application will be the following:</p> | ||
2441 | 44 | <ol> | ||
2442 | 45 | <li>they will be presented with a login box (no password, only username);</li> | ||
2443 | 46 | <li>upon logging in, a message will be sent to all logged in users that | ||
2444 | 47 | this person has joined, they will see a message at the bottom of the | ||
2445 | 48 | chat that states their login name, and the login form will be replaced with | ||
2446 | 49 | a chat area and a text input field;</li> | ||
2447 | 50 | <li>they will type text in the input field; and</li> | ||
2448 | 51 | <li>the typed text will appear in the browser of every person who is | ||
2449 | 52 | logged in.</li> | ||
2450 | 53 | </ol> | ||
2451 | 54 | |||
2452 | 55 | <p>Building upon our previous example, our application will do the | ||
2453 | 56 | following:</p> | ||
2454 | 57 | |||
2455 | 58 | <ol> | ||
2456 | 59 | <li>JavaScript client code will extract user input and send | ||
2457 | 60 | it to our server;</li> | ||
2458 | 61 | <li>Python code will receive messages from the client;</li> | ||
2459 | 62 | <li>Python code will process these messages;</li> | ||
2460 | 63 | <li>Python code will send messages to the all clients; and</li> | ||
2461 | 64 | <li>a template file (or <code>stan</code> code) will be used for | ||
2462 | 65 | presentation.</li> | ||
2463 | 66 | </ol> | ||
2464 | 67 | |||
2465 | 68 | <h2>More Coding</h2> | ||
2466 | 69 | |||
2467 | 70 | <h3>Presentation</h3> | ||
2468 | 71 | |||
2469 | 72 | <p>The template is very similar as it was in the previous example, with | ||
2470 | 73 | the differences being a new login box, a "logged in as" | ||
2471 | 74 | area, and some name changes:</p> | ||
2472 | 75 | |||
2473 | 76 | <a href="listings/chatthing/template.html" class="html-listing" /> | ||
2474 | 77 | |||
2475 | 78 | <p>We've now got two JavaScript methods that need to be defined: | ||
2476 | 79 | <code>doSetUsername()</code> and <code>doSay()</code>. We can also infer | ||
2477 | 80 | from this template that elements will be hidden and shown after login | ||
2478 | 81 | (note the presence of <code>style="display:none"</code> in two places). With | ||
2479 | 82 | these observations in hand, let's proceed to the JavaScript code.</p> | ||
2480 | 83 | |||
2481 | 84 | <h3>Writing the Client</h3> | ||
2482 | 85 | |||
2483 | 86 | <p>Referring back to our thoughts in the "Architecture" section above, we | ||
2484 | 87 | can establish that the JavaScript code needs the following:</p> | ||
2485 | 88 | |||
2486 | 89 | <ul> | ||
2487 | 90 | <li>have the same basic boilerplate as in the "echo" example (imports, | ||
2488 | 91 | inheritance, attribute-setting in the constructor);</li> | ||
2489 | 92 | <li>implement the <code>doSetUsername()</code> and <code>doSay()</code> | ||
2490 | 93 | methods;</li> | ||
2491 | 94 | <li>create a method that will send a message to all users; and</li> | ||
2492 | 95 | <li>create a method that will let everyone know when someone says | ||
2493 | 96 | something. Let's see how this is done.</li> | ||
2494 | 97 | </ul> | ||
2495 | 98 | |||
2496 | 99 | <a href="listings/chatthing/js/ChatThing.js" class="py-listing" /> | ||
2497 | 100 | |||
2498 | 101 | <p>There is a little abstraction here:</p> | ||
2499 | 102 | <ul> | ||
2500 | 103 | <li>we need a general message-sending method (<code>displayMessage()</code>) for any | ||
2501 | 104 | message that gets sent to all users;</li> | ||
2502 | 105 | <li>for user chat messages, we need something that will prepend the username so | ||
2503 | 106 | that everyone knows who said what (<code>displayUserMessage()</code>), and once this method | ||
2504 | 107 | does its thing, it passes the adjusted message on to <code>displayMessage()</code>.</li> | ||
2505 | 108 | </ul> | ||
2506 | 109 | |||
2507 | 110 | <p>Other than that, this is very straight-forward code; it's pretty much | ||
2508 | 111 | the same as the "Echo" tutorial. The <code>display*()</code> methods | ||
2509 | 112 | are only responsible for updating the UI, just as we would expect.</p> | ||
2510 | 113 | |||
2511 | 114 | We also need the same glue that we demonstrated in the "Echo" example: | ||
2512 | 115 | |||
2513 | 116 | <a href="listings/nevow/plugins/chatthing_package.py" class="py-listing" /> | ||
2514 | 117 | |||
2515 | 118 | <h3>Writing the Server</h3> | ||
2516 | 119 | |||
2517 | 120 | <p>The server code is a bit more complicated. We | ||
2518 | 121 | anticipated this above in the "Architecture" section where we noted that | ||
2519 | 122 | the Python code needs to receive, process and send messages.</p> | ||
2520 | 123 | |||
2521 | 124 | <a href="listings/chatthing/chatterbox.py" class="py-listing" /> | ||
2522 | 125 | |||
2523 | 126 | <p>There is something in our "Chat" code that is not at all present in the | ||
2524 | 127 | "Echo" application: the <code>ChatRoom</code> object. We need this object for the | ||
2525 | 128 | following functionality:</p> | ||
2526 | 129 | <ul> | ||
2527 | 130 | <li>a means of instantiating new <code>ChatterElement</code> clients;</li> | ||
2528 | 131 | <li>a "singleton" instance for keeping track of all <code>ChatterElement</code> clients;</li> | ||
2529 | 132 | <li>a means sending messages to all clients;</li> | ||
2530 | 133 | </ul> | ||
2531 | 134 | |||
2532 | 135 | <p>Let's look at the second two reasons first. In our "Chat" application, | ||
2533 | 136 | a new <code>ChatterElement</code> is created whenever a user connects, | ||
2534 | 137 | so we will have potentially many of these instances. In order | ||
2535 | 138 | for our chat server to function as designed, it will need a way to | ||
2536 | 139 | communicate with each of these. If we create an object that can keep the | ||
2537 | 140 | <code>ChatterElement</code>es in a list, then it will be able to iterate that | ||
2538 | 141 | list and call methods that, in turn, make remote calls to the JavaScript. | ||
2539 | 142 | </p> | ||
2540 | 143 | |||
2541 | 144 | <p>Because we need the chat room to be a singleton object, it | ||
2542 | 145 | can only be instantiated once. But we need many instantiations of | ||
2543 | 146 | <code>ChatterElement</code> -- one for each connection, in fact. So what do | ||
2544 | 147 | we do? Well, in this case, we make one of the methods | ||
2545 | 148 | of <code>ChatRoom</code> a factory for instantiating a | ||
2546 | 149 | <code>ChatterElement</code>. Before we return the instance, though, we | ||
2547 | 150 | append it to the list of instances that the <code>ChatRoom</code> | ||
2548 | 151 | is keeping track of. | ||
2549 | 152 | </p> | ||
2550 | 153 | |||
2551 | 154 | <h3>Putting it All Together</h3> | ||
2552 | 155 | |||
2553 | 156 | |||
2554 | 157 | <p>Now that we've got all the code in front of us, we can trace out exactly what happens:</p> | ||
2555 | 158 | |||
2556 | 159 | <ol> | ||
2557 | 160 | <li>the user loads the resource in their browser, and the template is rendered;</li> | ||
2558 | 161 | <li>after typing a message in the input box, the user hits submit;</li> | ||
2559 | 162 | <li>JavaScript client code calls to the server with the text the user submitted;</li> | ||
2560 | 163 | <li>the server gets the message and shares it with all the connected | ||
2561 | 164 | <code>ChatterElement</code>s;</li> | ||
2562 | 165 | <li>each <code>ChatterElement</code> hears this message and passes it back to the JavaScript client;</li> | ||
2563 | 166 | <li>the client prepends the username to the message and then updates the display with the complete message.</li> | ||
2564 | 167 | </ol> | ||
2565 | 168 | |||
2566 | 169 | <p> | ||
2567 | 170 | Keep in mind that <code>ChatterElement</code> entails several duties: it | ||
2568 | 171 | establishes a relationship with a room object, it "registers" a user (there's a | ||
2569 | 172 | one-to-one mapping between users and <code>ChatterElement</code>), it sends | ||
2570 | 173 | messages to the browser, and it receives messages from the chat room. Being a | ||
2571 | 174 | <code>LiveElement</code> subclass, <code>ChatterElement</code> is also | ||
2572 | 175 | responsible for the view (via the document factory). | ||
2573 | 176 | </p> | ||
2574 | 177 | |||
2575 | 178 | |||
2576 | 179 | <h3>Running with <code>twistd</code></h3> | ||
2577 | 180 | |||
2578 | 181 | <p>One last bit of code that may seem odd is the <code>chat</code> | ||
2579 | 182 | variable we define right after the <code>ChatRoom</code> class. What | ||
2580 | 183 | is this? This is how we make all this cleverness work as a twisted | ||
2581 | 184 | plugin. </p> | ||
2582 | 185 | |||
2583 | 186 | <p>If you recall, in our "Echo" application, we ran the code with | ||
2584 | 187 | the following command: | ||
2585 | 188 | </p> | ||
2586 | 189 | |||
2587 | 190 | <pre class="shell"> | ||
2588 | 191 | twistd -n athena-widget --element=echothing.echobox.EchoElement | ||
2589 | 192 | </pre> | ||
2590 | 193 | |||
2591 | 194 | <p>The value we pass as the <code>--element</code> argument is the dotted | ||
2592 | 195 | name of the <code>LiveElement</code> object of which our "web page" | ||
2593 | 196 | is primarily comprised: the <code>EchoElement</code> object. In | ||
2594 | 197 | our "Chat" application, we have more moving parts: not only | ||
2595 | 198 | do we have the <code>ChatterElement</code> object, but we have the | ||
2596 | 199 | <code>ChatRoom</code> object which is responsible for keeping track of | ||
2597 | 200 | many <code>ChatterElement</code>es. By defining the <code>chat</code> | ||
2598 | 201 | variable, we are accomplishing the following all at once: | ||
2599 | 202 | </p> | ||
2600 | 203 | |||
2601 | 204 | <ul> | ||
2602 | 205 | <li>providing a variable that can be accessed as a dotted name and thus | ||
2603 | 206 | used when starting the server (<code>chatthing.chatterbox.chat</code>);</li> | ||
2604 | 207 | <li>creating a singleton of <code>ChatRoom</code> (via the "magic" | ||
2605 | 208 | of Python module-level instantiations);</li> | ||
2606 | 209 | <li>making use of a factory, that when called, will both return a | ||
2607 | 210 | new <code>ChatterElement</code> instance <i>and</i> add itself to the | ||
2608 | 211 | <code>ChatRoom</code>.</li> | ||
2609 | 212 | </ul> | ||
2610 | 213 | |||
2611 | 214 | <p>Running this version of our code is a little bit different than the | ||
2612 | 215 | "Echo" version. This is because of the <code>ChatRoom</code> code we | ||
2613 | 216 | discussed above. As such, we pass a factory as our element, like so:</p> | ||
2614 | 217 | |||
2615 | 218 | <pre class="shell"> | ||
2616 | 219 | cd Nevow/doc/howto/chattutorial/part01/listings | ||
2617 | 220 | twistd -n athena-widget --element=chatthing.chatterbox.chat | ||
2618 | 221 | </pre> | ||
2619 | 222 | |||
2620 | 223 | <p>If you executed this against the tutorial code on your local machine, | ||
2621 | 224 | you can now visit <a href="http://localhost:8080/">http://localhost:8080/</a> | ||
2622 | 225 | and start chatting to your heart's content.</p> | ||
2623 | 226 | |||
2624 | 227 | <h2>Summary</h2> | ||
2625 | 228 | <p> | ||
2626 | 229 | Unlike our echo application, the chat application has some real functionality | ||
2627 | 230 | and does some useful stuff: supporting user chats via browser/server two-way | ||
2628 | 231 | communications. It should be evident now how the echo application provided a | ||
2629 | 232 | basic conceptual and (partially) functional foundation upon which our chat work | ||
2630 | 233 | could be based. | ||
2631 | 234 | </p> | ||
2632 | 235 | </body> | ||
2633 | 236 | </html> | ||
2634 | 237 | 0 | ||
2635 | === removed directory 'Nevow/doc/howto/chattutorial/part01/listings' | |||
2636 | === removed directory 'Nevow/doc/howto/chattutorial/part01/listings/chatthing' | |||
2637 | === removed file 'Nevow/doc/howto/chattutorial/part01/listings/chatthing/__init__.py' | |||
2638 | --- Nevow/doc/howto/chattutorial/part01/listings/chatthing/__init__.py 2008-09-03 20:03:47 +0000 | |||
2639 | +++ Nevow/doc/howto/chattutorial/part01/listings/chatthing/__init__.py 1970-01-01 00:00:00 +0000 | |||
2640 | @@ -1,1 +0,0 @@ | |||
2641 | 1 | |||
2642 | 2 | 0 | ||
2643 | === removed file 'Nevow/doc/howto/chattutorial/part01/listings/chatthing/chatterbox.py' | |||
2644 | --- Nevow/doc/howto/chattutorial/part01/listings/chatthing/chatterbox.py 2008-09-03 20:03:47 +0000 | |||
2645 | +++ Nevow/doc/howto/chattutorial/part01/listings/chatthing/chatterbox.py 1970-01-01 00:00:00 +0000 | |||
2646 | @@ -1,48 +0,0 @@ | |||
2647 | 1 | from twisted.python.util import sibpath | ||
2648 | 2 | from nevow.loaders import xmlfile | ||
2649 | 3 | from nevow.athena import LiveElement, expose | ||
2650 | 4 | |||
2651 | 5 | class ChatRoom(object): | ||
2652 | 6 | |||
2653 | 7 | def __init__(self): | ||
2654 | 8 | self.chatters = [] | ||
2655 | 9 | |||
2656 | 10 | def wall(self, message): | ||
2657 | 11 | for chatter in self.chatters: | ||
2658 | 12 | chatter.wall(message) | ||
2659 | 13 | |||
2660 | 14 | def tellEverybody(self, who, what): | ||
2661 | 15 | for chatter in self.chatters: | ||
2662 | 16 | chatter.hear(who.username, what) | ||
2663 | 17 | |||
2664 | 18 | def makeChatter(self): | ||
2665 | 19 | elem = ChatterElement(self) | ||
2666 | 20 | self.chatters.append(elem) | ||
2667 | 21 | return elem | ||
2668 | 22 | |||
2669 | 23 | # element to be run with twistd | ||
2670 | 24 | chat = ChatRoom().makeChatter | ||
2671 | 25 | |||
2672 | 26 | class ChatterElement(LiveElement): | ||
2673 | 27 | |||
2674 | 28 | docFactory = xmlfile(sibpath(__file__, 'template.html')) | ||
2675 | 29 | jsClass = u'ChatThing.ChatterWidget' | ||
2676 | 30 | |||
2677 | 31 | def __init__(self, room): | ||
2678 | 32 | self.room = room | ||
2679 | 33 | |||
2680 | 34 | def setUsername(self, username): | ||
2681 | 35 | self.username = username | ||
2682 | 36 | message = ' * user '+username+' has joined the room' | ||
2683 | 37 | self.room.wall(message) | ||
2684 | 38 | setUsername = expose(setUsername) | ||
2685 | 39 | |||
2686 | 40 | def say(self, message): | ||
2687 | 41 | self.room.tellEverybody(self, message) | ||
2688 | 42 | say = expose(say) | ||
2689 | 43 | |||
2690 | 44 | def wall(self, message): | ||
2691 | 45 | self.callRemote('displayMessage', message) | ||
2692 | 46 | |||
2693 | 47 | def hear(self, username, what): | ||
2694 | 48 | self.callRemote('displayUserMessage', username, what) | ||
2695 | 49 | 0 | ||
2696 | === removed directory 'Nevow/doc/howto/chattutorial/part01/listings/chatthing/js' | |||
2697 | === removed file 'Nevow/doc/howto/chattutorial/part01/listings/chatthing/js/ChatThing.js' | |||
2698 | --- Nevow/doc/howto/chattutorial/part01/listings/chatthing/js/ChatThing.js 2009-01-21 22:58:03 +0000 | |||
2699 | +++ Nevow/doc/howto/chattutorial/part01/listings/chatthing/js/ChatThing.js 1970-01-01 00:00:00 +0000 | |||
2700 | @@ -1,42 +0,0 @@ | |||
2701 | 1 | // import Nevow.Athena | ||
2702 | 2 | |||
2703 | 3 | Nevow.Athena.Widget.subclass(ChatThing, 'ChatterWidget').methods( | ||
2704 | 4 | function __init__(self, node) { | ||
2705 | 5 | ChatThing.ChatterWidget.upcall(self, "__init__", node); | ||
2706 | 6 | self.chooseBox = self.nodeByAttribute('name', 'chooseBox'); | ||
2707 | 7 | self.scrollArea = self.nodeByAttribute('name', 'scrollArea'); | ||
2708 | 8 | self.sendLine = self.nodeByAttribute('name', 'sendLine'); | ||
2709 | 9 | self.usernameField = self.nodeByAttribute('name', 'username'); | ||
2710 | 10 | self.userMessage = self.nodeByAttribute('name', 'userMessage'); | ||
2711 | 11 | self.loggedInAs = self.nodeByAttribute('name', 'loggedInAs'); | ||
2712 | 12 | }, | ||
2713 | 13 | |||
2714 | 14 | function doSetUsername(self) { | ||
2715 | 15 | var username = self.usernameField.value; | ||
2716 | 16 | self.callRemote("setUsername", username).addCallback( | ||
2717 | 17 | function (result) { | ||
2718 | 18 | self.chooseBox.style.display = "none"; | ||
2719 | 19 | self.sendLine.style.display = "block"; | ||
2720 | 20 | self.loggedInAs.appendChild(document.createTextNode(username)); | ||
2721 | 21 | self.loggedInAs.style.display = "block"; | ||
2722 | 22 | }); | ||
2723 | 23 | return false; | ||
2724 | 24 | }, | ||
2725 | 25 | |||
2726 | 26 | function doSay(self) { | ||
2727 | 27 | self.callRemote("say", self.userMessage.value); | ||
2728 | 28 | self.nodeByAttribute('name', 'userMessage').value = ""; | ||
2729 | 29 | return false; | ||
2730 | 30 | }, | ||
2731 | 31 | |||
2732 | 32 | function displayMessage(self, message) { | ||
2733 | 33 | var newNode = document.createElement('div'); | ||
2734 | 34 | newNode.appendChild(document.createTextNode(message)); | ||
2735 | 35 | self.scrollArea.appendChild(newNode); | ||
2736 | 36 | document.body.scrollTop = document.body.scrollHeight; | ||
2737 | 37 | }, | ||
2738 | 38 | |||
2739 | 39 | function displayUserMessage(self, avatarName, text) { | ||
2740 | 40 | var msg = avatarName+': '+text; | ||
2741 | 41 | self.displayMessage(msg); | ||
2742 | 42 | }); | ||
2743 | 43 | 0 | ||
2744 | === removed file 'Nevow/doc/howto/chattutorial/part01/listings/chatthing/template.html' | |||
2745 | --- Nevow/doc/howto/chattutorial/part01/listings/chatthing/template.html 2008-09-03 20:03:47 +0000 | |||
2746 | +++ Nevow/doc/howto/chattutorial/part01/listings/chatthing/template.html 1970-01-01 00:00:00 +0000 | |||
2747 | @@ -1,20 +0,0 @@ | |||
2748 | 1 | <div xmlns:nevow="http://nevow.com/ns/nevow/0.1" | ||
2749 | 2 | xmlns:athena="http://divmod.org/ns/athena/0.7" | ||
2750 | 3 | nevow:render="liveElement"> | ||
2751 | 4 | <h2>Chatter Element</h2> | ||
2752 | 5 | <form name="chatBox"> | ||
2753 | 6 | <athena:handler event="onsubmit" handler="doSay" /> | ||
2754 | 7 | <div name="scrollArea" | ||
2755 | 8 | style="border: 1px solid gray; padding: 5; margin: 5"> | ||
2756 | 9 | </div> | ||
2757 | 10 | <div name="sendLine" style="display: none"> | ||
2758 | 11 | <input name="userMessage" /><input type="submit" value="Send" /> | ||
2759 | 12 | </div> | ||
2760 | 13 | </form> | ||
2761 | 14 | <form name="chooseBox"> | ||
2762 | 15 | <athena:handler event="onsubmit" handler="doSetUsername" /> | ||
2763 | 16 | Choose your username: <input name="username" /> | ||
2764 | 17 | <input type="submit" name="GO" value="Enter"/> | ||
2765 | 18 | </form> | ||
2766 | 19 | <div name="loggedInAs" style="display:none"><span>Logged in as </span></div> | ||
2767 | 20 | </div> | ||
2768 | 21 | 0 | ||
2769 | === removed directory 'Nevow/doc/howto/chattutorial/part01/listings/nevow' | |||
2770 | === removed directory 'Nevow/doc/howto/chattutorial/part01/listings/nevow/plugins' | |||
2771 | === removed file 'Nevow/doc/howto/chattutorial/part01/listings/nevow/plugins/chatthing_package.py' | |||
2772 | --- Nevow/doc/howto/chattutorial/part01/listings/nevow/plugins/chatthing_package.py 2008-09-03 20:03:47 +0000 | |||
2773 | +++ Nevow/doc/howto/chattutorial/part01/listings/nevow/plugins/chatthing_package.py 1970-01-01 00:00:00 +0000 | |||
2774 | @@ -1,8 +0,0 @@ | |||
2775 | 1 | |||
2776 | 2 | from twisted.python import util | ||
2777 | 3 | |||
2778 | 4 | from nevow import athena | ||
2779 | 5 | |||
2780 | 6 | import chatthing | ||
2781 | 7 | |||
2782 | 8 | chatthingPkg = athena.AutoJSPackage(util.sibpath(chatthing.__file__, 'js')) | ||
2783 | 9 | 0 | ||
2784 | === removed file 'Nevow/doc/howto/deployment.xhtml' | |||
2785 | --- Nevow/doc/howto/deployment.xhtml 2008-08-26 13:45:59 +0000 | |||
2786 | +++ Nevow/doc/howto/deployment.xhtml 1970-01-01 00:00:00 +0000 | |||
2787 | @@ -1,300 +0,0 @@ | |||
2788 | 1 | <?xml version="1.0"?> | ||
2789 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
2790 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
2791 | 4 | |||
2792 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
2793 | 6 | <head> | ||
2794 | 7 | <title> | ||
2795 | 8 | Deployment | ||
2796 | 9 | </title> | ||
2797 | 10 | </head> | ||
2798 | 11 | <body> | ||
2799 | 12 | <h1>Deployment</h1> | ||
2800 | 13 | |||
2801 | 14 | <p> | ||
2802 | 15 | Nevow includes two major phases for deciding what HTML to render. <a | ||
2803 | 16 | href="traversal.xhtml">Object Traversal</a> is the procedure by which a | ||
2804 | 17 | URL is mapped to a Python object which will perform the HTML | ||
2805 | 18 | generation. <a href="glossary.xhtml">Page Rendering</a> is the process by | ||
2806 | 19 | which data objects are combined with an HTML template to produce the | ||
2807 | 20 | final output. | ||
2808 | 21 | </p> | ||
2809 | 22 | |||
2810 | 23 | <p> | ||
2811 | 24 | Before any of this can take place, however, we must have an environment | ||
2812 | 25 | in which our Python code can run in response to an HTTP request, and HTML | ||
2813 | 26 | can be returned to the browser for rendering. This is called the | ||
2814 | 27 | <a href="glossary.xhtml">Deployment Environment</a>. | ||
2815 | 28 | </p> | ||
2816 | 29 | |||
2817 | 30 | <p> | ||
2818 | 31 | There are various deployment options for Nevow page code: | ||
2819 | 32 | </p> | ||
2820 | 33 | |||
2821 | 34 | <ul> | ||
2822 | 35 | <li> | ||
2823 | 36 | CGI: Simple deployment in almost any HTTP server | ||
2824 | 37 | </li> | ||
2825 | 38 | <li> | ||
2826 | 39 | WSGI: A more complete and flexible way for deploying on many HTTP | ||
2827 | 40 | servers | ||
2828 | 41 | </li> | ||
2829 | 42 | <li> | ||
2830 | 43 | Twisted.Web: A standalone application server process which includes a | ||
2831 | 44 | built-in HTTP server | ||
2832 | 45 | </li> | ||
2833 | 46 | <li> | ||
2834 | 47 | Zomne: A small CGI which hands off HTTP requests to a long-running | ||
2835 | 48 | application server process, similar to FastCGI or SCGI | ||
2836 | 49 | </li> | ||
2837 | 50 | </ul> | ||
2838 | 51 | |||
2839 | 52 | <h2>CGI</h2> | ||
2840 | 53 | |||
2841 | 54 | <p> | ||
2842 | 55 | You can deploy Nevow on any webserver which uses the Common Gateway | ||
2843 | 56 | Interface. Using this method, your code is responsible for properly | ||
2844 | 57 | formatting and outputting the HTTP response headers, and Nevow is used | ||
2845 | 58 | only to generate the HTML body of your page. Here is the simplest | ||
2846 | 59 | possible CGI: | ||
2847 | 60 | </p> | ||
2848 | 61 | |||
2849 | 62 | <pre class="python"> | ||
2850 | 63 | #!/usr/bin/env python | ||
2851 | 64 | |||
2852 | 65 | print "Content-type: text/plain\r\n\r\n", | ||
2853 | 66 | |||
2854 | 67 | from nevow import rend, loaders | ||
2855 | 68 | |||
2856 | 69 | class HelloWorld(rend.Page): | ||
2857 | 70 | docFactory = loaders.stan("Hello, world!") | ||
2858 | 71 | |||
2859 | 72 | print HelloWorld().renderSynchronously() | ||
2860 | 73 | </pre> | ||
2861 | 74 | |||
2862 | 75 | <p> | ||
2863 | 76 | With this simple CGI you can use the Nevow template loaders and standard | ||
2864 | 77 | nevow template interpolation techniques in your CGIs. However, you do not | ||
2865 | 78 | get any <a href="traversal.xhtml">Object Traversal</a> features, and you | ||
2866 | 79 | have to generate HTTP headers yourself. WSGI is a slightly higher-level | ||
2867 | 80 | deployment option which does not suffer these problems. | ||
2868 | 81 | </p> | ||
2869 | 82 | |||
2870 | 83 | <h2>WSGI</h2> | ||
2871 | 84 | |||
2872 | 85 | <p> | ||
2873 | 86 | WSGI is a python interface for plugging web applications into various | ||
2874 | 87 | HTTP server architectures. It is described in <a | ||
2875 | 88 | href="http://www.python.org/peps/pep-0333.html">PEP 333</a>, the Python | ||
2876 | 89 | Web Services Gateway Interface Python Enhancement Proposal. Nevow | ||
2877 | 90 | includes the <code class="API">nevow.wsgi</code> module, which includes a | ||
2878 | 91 | <code>createWSGIApplication</code> function which takes a Page and | ||
2879 | 92 | returns a standard WSGI application callable. With the help of the | ||
2880 | 93 | <code>run_with_cgi</code> example gateway from the PEP (which I will omit | ||
2881 | 94 | here), our CGI example becomes shorter: | ||
2882 | 95 | </p> | ||
2883 | 96 | |||
2884 | 97 | <pre class="python"> | ||
2885 | 98 | #!/usr/bin/env python | ||
2886 | 99 | |||
2887 | 100 | from nevow import rend, loaders, wsgi | ||
2888 | 101 | |||
2889 | 102 | class HelloWorld(rend.Page): | ||
2890 | 103 | docFactory = loaders.stan("Hello, world!") | ||
2891 | 104 | |||
2892 | 105 | run_with_cgi(wsgi.createWSGIApplication(HelloWorld())) | ||
2893 | 106 | </pre> | ||
2894 | 107 | |||
2895 | 108 | <p> | ||
2896 | 109 | Of course, you can use any available WSGI gateway to publish your | ||
2897 | 110 | application object, such as one of the gateways which comes with the <a | ||
2898 | 111 | href="http://peak.telecommunity.com/">PEAK</a> toolkit. For example, here | ||
2899 | 112 | is a simple python module which creates a WSGI application which we will | ||
2900 | 113 | then deploy with PEAK's SimpleHTTPServer gateway:: | ||
2901 | 114 | </p> | ||
2902 | 115 | |||
2903 | 116 | <pre class="python"> | ||
2904 | 117 | ## helloworld.py | ||
2905 | 118 | |||
2906 | 119 | from nevow import rend, loaders, wsgi | ||
2907 | 120 | |||
2908 | 121 | class HelloWorld(rend.Page): | ||
2909 | 122 | docFactory = loaders.stan("Hello, world!") | ||
2910 | 123 | |||
2911 | 124 | application = wsgi.createWSGIApplication(HelloWorld()) | ||
2912 | 125 | </pre> | ||
2913 | 126 | |||
2914 | 127 | <p> | ||
2915 | 128 | Save this file as "helloworld.py" somewhere on your PYTHONPATH and then | ||
2916 | 129 | run the following command: | ||
2917 | 130 | </p> | ||
2918 | 131 | |||
2919 | 132 | <pre>peak launch WSGI import:helloworld.application</pre> | ||
2920 | 133 | |||
2921 | 134 | <p> | ||
2922 | 135 | This will bring up a SimpleHTTPServer running your Nevow code and launch | ||
2923 | 136 | a web browser to view the output. (TODO: I couldn't get this working | ||
2924 | 137 | immediately but I will seek assistance with PEAK and update the | ||
2925 | 138 | instructions once I do.) | ||
2926 | 139 | </p> | ||
2927 | 140 | |||
2928 | 141 | <h2>Twisted.Web</h2> | ||
2929 | 142 | |||
2930 | 143 | <p> | ||
2931 | 144 | A convenient and powerful way to deploy Nevow applications is inside a | ||
2932 | 145 | process running the twisted.web HTTP server. With Python, Twisted, and | ||
2933 | 146 | Nevow installed, you have all you need to run a Web Application, with no | ||
2934 | 147 | other dependencies or external HTTP servers such as Apache | ||
2935 | 148 | required. Running your Nevow applications under twisted.web also gives | ||
2936 | 149 | you access to some of the more advanced "Live" features of Nevow, such as | ||
2937 | 150 | <code>nevow.livepage</code> and <code>nevow.canvas</code>. Currently, | ||
2938 | 151 | these modules require more control over the HTTP socket than CGI or WSGI | ||
2939 | 152 | can provide. (This may change in the future.) | ||
2940 | 153 | </p> | ||
2941 | 154 | |||
2942 | 155 | <p> | ||
2943 | 156 | Deploying a Nevow application under twisted.web requires a little more | ||
2944 | 157 | boilerplate, but can be considerably easier to set up than other | ||
2945 | 158 | deployment options because there are no external dependencies. Note that | ||
2946 | 159 | normally you should declare your Page classes in modules external to the | ||
2947 | 160 | twisted configuration file, but everything is included in one file here | ||
2948 | 161 | for brevity. Here is the minimal configuration file required to use | ||
2949 | 162 | Nevow with twisted.web: | ||
2950 | 163 | </p> | ||
2951 | 164 | |||
2952 | 165 | <pre class="python"> | ||
2953 | 166 | from nevow import rend, loaders, appserver | ||
2954 | 167 | |||
2955 | 168 | class HelloWorld(rend.Page): | ||
2956 | 169 | docFactory = loaders.stan("Hello, world!") | ||
2957 | 170 | |||
2958 | 171 | from twisted.application import service, internet | ||
2959 | 172 | application = service.Application("hello-world") | ||
2960 | 173 | internet.TCPServer(8080, appserver.NevowSite(HelloWorld())).setServiceParent(application) | ||
2961 | 174 | </pre> | ||
2962 | 175 | |||
2963 | 176 | <p> | ||
2964 | 177 | Save this file as "helloworld.tac" and start the server using the | ||
2965 | 178 | command: | ||
2966 | 179 | </p> | ||
2967 | 180 | |||
2968 | 181 | <pre>twistd -noy helloworld.tac</pre> | ||
2969 | 182 | |||
2970 | 183 | <p> | ||
2971 | 184 | Then visit your twisted.web server by viewing the url | ||
2972 | 185 | "http://localhost:8080/" in your browser. See the twistd man page for | ||
2973 | 186 | more information about what twistd is capable of, including daemonizing | ||
2974 | 187 | the HTTP server. | ||
2975 | 188 | </p> | ||
2976 | 189 | |||
2977 | 190 | <h2>Zomne</h2> | ||
2978 | 191 | |||
2979 | 192 | <p> | ||
2980 | 193 | <em>Warning</em> Zomne is experimental. It may blow up your computer and | ||
2981 | 194 | require your first born son as a sacrifice. Zomne also only works in | ||
2982 | 195 | UNIX-like environments where unix domain sockets are available, and may | ||
2983 | 196 | not work on windows. | ||
2984 | 197 | </p> | ||
2985 | 198 | |||
2986 | 199 | <p> | ||
2987 | 200 | Zomne, or "Zombie Nevow", is a CGI written in C which can start up a | ||
2988 | 201 | long-running Application Server process if one is not already running. It | ||
2989 | 202 | then uses a simple custom protocol to transmit information about the HTTP | ||
2990 | 203 | request from the CGI process to the application server process. | ||
2991 | 204 | </p> | ||
2992 | 205 | |||
2993 | 206 | <p> | ||
2994 | 207 | Zomne combines the ease of deployment of the CGI environment with the | ||
2995 | 208 | speed and flexibility of the twisted.web long-running application server | ||
2996 | 209 | process model. | ||
2997 | 210 | </p> | ||
2998 | 211 | |||
2999 | 212 | <p> | ||
3000 | 213 | To use Zomne, you must first compile the CGI. cd into the directory | ||
3001 | 214 | created when unpacking the Nevow tarball, and compile the CGI: | ||
3002 | 215 | </p> | ||
3003 | 216 | |||
3004 | 217 | <pre>% gcc zomne.c</pre> | ||
3005 | 218 | |||
3006 | 219 | <p> | ||
3007 | 220 | Move it into your cgi-bin: | ||
3008 | 221 | </p> | ||
3009 | 222 | |||
3010 | 223 | <pre>% mv a.out /Library/WebServer/CGI-Executables/nevow.cgi</pre> | ||
3011 | 224 | |||
3012 | 225 | <p> | ||
3013 | 226 | Create a file which tells the cgi where to look for the application: | ||
3014 | 227 | </p> | ||
3015 | 228 | |||
3016 | 229 | <pre> | ||
3017 | 230 | % cat > /Library/WebServer/CGI-Executables/.nevow.cgi.dir | ||
3018 | 231 | /Users/dp/zomne-test | ||
3019 | 232 | ^D</pre> | ||
3020 | 233 | |||
3021 | 234 | <p> | ||
3022 | 235 | The CGI name can be anything, as long as there is a file with a prepended | ||
3023 | 236 | "." and a postfixed ".dir" in the same directory which contains the full | ||
3024 | 237 | path of a zomne application directory. Next, create the application | ||
3025 | 238 | directory: | ||
3026 | 239 | </p> | ||
3027 | 240 | |||
3028 | 241 | <pre>mkdir /Users/dp/zomne-test</pre> | ||
3029 | 242 | |||
3030 | 243 | <p> | ||
3031 | 244 | Finally, create the zomne.tac file which the zomne.cgi will execute to | ||
3032 | 245 | start the long-running application server process: | ||
3033 | 246 | </p> | ||
3034 | 247 | |||
3035 | 248 | <pre class="python"> | ||
3036 | 249 | from nevow import rend, loaders, zomnesrv | ||
3037 | 250 | |||
3038 | 251 | class HelloWorld(rend.Page): | ||
3039 | 252 | docFactory = loaders.stan("Hello, world!") | ||
3040 | 253 | |||
3041 | 254 | from twisted.application import service, internet | ||
3042 | 255 | application = service.Application('nevow-zomne-test') | ||
3043 | 256 | internet.UNIXServer('zomne.socket', zomnesrv.ZomneFactory(HelloWorld())).setServiceParent(application) | ||
3044 | 257 | </pre> | ||
3045 | 258 | |||
3046 | 259 | <p> | ||
3047 | 260 | Now, visiting the nevow.cgi URL through the web should render the Hello | ||
3048 | 261 | World page, after a pause while the server is starting up. Subsequent | ||
3049 | 262 | requests should be very fast, because the application server is already | ||
3050 | 263 | running, and the CGI merely has to forward the request to it. | ||
3051 | 264 | </p> | ||
3052 | 265 | |||
3053 | 266 | <p> | ||
3054 | 267 | Another useful capability of the zomne CGI process is the ability to | ||
3055 | 268 | control environment variables the CGI will use. Create a directory named | ||
3056 | 269 | "zomne_environ" in the application directory, and fill it with text files | ||
3057 | 270 | whose name will be the environment key and whose contents will be the | ||
3058 | 271 | environment value: | ||
3059 | 272 | </p> | ||
3060 | 273 | |||
3061 | 274 | <pre> | ||
3062 | 275 | % cd zomne-test | ||
3063 | 276 | % mkdir zomne-environ | ||
3064 | 277 | % cd zomne-environ | ||
3065 | 278 | % cat > PYTHONPATH | ||
3066 | 279 | /Users/dp/Projects/Nevow:/Users/dp/Projects/helloworld | ||
3067 | 280 | ^D</pre> | ||
3068 | 281 | |||
3069 | 282 | <h2>Conclusion</h2> | ||
3070 | 283 | |||
3071 | 284 | <p> | ||
3072 | 285 | Nevow may be deployed in a number of environments, from the most | ||
3073 | 286 | restrictive to the most permissive. Writing a CGI can be an easy way to | ||
3074 | 287 | try out the Nevow templating mechanism, but can be slow. A long-running | ||
3075 | 288 | application server process can be a good way to get good performance as | ||
3076 | 289 | well as additional features such as in-memory server-side sessions, | ||
3077 | 290 | advanced automatic form handling with formless, and live page updating | ||
3078 | 291 | features such as nevow.livepage and nevow.canvas. | ||
3079 | 292 | </p> | ||
3080 | 293 | |||
3081 | 294 | <p> | ||
3082 | 295 | Which deployment option you choose will depend on the amount of control | ||
3083 | 296 | you have over your deployment environment, and what advanced features | ||
3084 | 297 | your application will require. | ||
3085 | 298 | </p> | ||
3086 | 299 | </body> | ||
3087 | 300 | </html> | ||
3088 | 301 | 0 | ||
3089 | === removed file 'Nevow/doc/howto/gettingstarted.xhtml' | |||
3090 | --- Nevow/doc/howto/gettingstarted.xhtml 2008-08-26 13:45:59 +0000 | |||
3091 | +++ Nevow/doc/howto/gettingstarted.xhtml 1970-01-01 00:00:00 +0000 | |||
3092 | @@ -1,110 +0,0 @@ | |||
3093 | 1 | <?xml version="1.0"?> | ||
3094 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
3095 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3096 | 4 | |||
3097 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
3098 | 6 | <head> | ||
3099 | 7 | <title> | ||
3100 | 8 | Getting Started | ||
3101 | 9 | </title> | ||
3102 | 10 | </head> | ||
3103 | 11 | <body> | ||
3104 | 12 | <h1>Getting Started</h1> | ||
3105 | 13 | |||
3106 | 14 | <p> | ||
3107 | 15 | Warning: This document has only just been started. It's not going to get | ||
3108 | 16 | you very far right now. | ||
3109 | 17 | </p> | ||
3110 | 18 | |||
3111 | 19 | <p> | ||
3112 | 20 | Nevow is a reasonably large library and can be quite daunting at | ||
3113 | 21 | first. This document's aim is to guide the first time user in building a | ||
3114 | 22 | Nevow application. | ||
3115 | 23 | </p> | ||
3116 | 24 | |||
3117 | 25 | <h2>Our First Application</h2> | ||
3118 | 26 | |||
3119 | 27 | <p> | ||
3120 | 28 | Let's dive straight in, here's the code for our first (very, very simple) | ||
3121 | 29 | application. Create the following module, helloworld.py: | ||
3122 | 30 | </p> | ||
3123 | 31 | |||
3124 | 32 | <a href="listings/gettingstarted/helloworld.py" class="py-listing"> | ||
3125 | 33 | helloworld.py | ||
3126 | 34 | </a> | ||
3127 | 35 | |||
3128 | 36 | <p> | ||
3129 | 37 | It looks quite simple but let's walk through it anyway. | ||
3130 | 38 | </p> | ||
3131 | 39 | |||
3132 | 40 | <p> | ||
3133 | 41 | First, we import two Nevow modules. <code | ||
3134 | 42 | class="API">nevow.loaders</code> contains template loaders of which the | ||
3135 | 43 | two most useful are <code class="API" base="nevow.loaders">xmlfile</code> | ||
3136 | 44 | and <code class="API" base="nevow.loaders">stan</code>. | ||
3137 | 45 | <code>xmlfile</code> can load any well-formed XML (i.e. XHTML) file; | ||
3138 | 46 | <code>stan</code> loads a stan tree (more on these later). The other | ||
3139 | 47 | module, <code class="API">nevow.rend</code>, contains all Nevow's | ||
3140 | 48 | standard renders, many of which we'll meet in this document. | ||
3141 | 49 | </p> | ||
3142 | 50 | |||
3143 | 51 | <p> | ||
3144 | 52 | We then define the <code>HelloWorld</code> class that subclasses <code | ||
3145 | 53 | class="API" base="nevow">rend.Page</code>, Nevow's main resource | ||
3146 | 54 | class. <code>HelloWorld</code> has two class | ||
3147 | 55 | attributes. <code>addSlash</code> tells <code>rend.Page</code> to | ||
3148 | 56 | redirect to a version of the request URL that ends in a <code>/</code> if | ||
3149 | 57 | necessary. You generally want to set this to <code>True</code> for the | ||
3150 | 58 | root resource. <code>docFactory</code> tells the page instance where to | ||
3151 | 59 | get the template from. In this case we're providing a loader that parses | ||
3152 | 60 | an HTML file (not shown) from disk. | ||
3153 | 61 | </p> | ||
3154 | 62 | |||
3155 | 63 | <p> | ||
3156 | 64 | Hmm, ok I hear you say but how do I see it. Well, Twisted provides a good | ||
3157 | 65 | web server which we can use. Twisted also includes a clever little | ||
3158 | 66 | application for starting Twisted applications. Here's the helloworld.tac | ||
3159 | 67 | file, a Twisted Application Configuration: | ||
3160 | 68 | </p> | ||
3161 | 69 | |||
3162 | 70 | <a href="listings/gettingstarted/helloworld.tac" class="py-listing"> | ||
3163 | 71 | helloworld.tac | ||
3164 | 72 | </a> | ||
3165 | 73 | |||
3166 | 74 | <p> | ||
3167 | 75 | Give it a go, run the following and connect to <a | ||
3168 | 76 | href="http://localhost:8080/">http://localhost:8080/</a> to see your | ||
3169 | 77 | application: | ||
3170 | 78 | </p> | ||
3171 | 79 | |||
3172 | 80 | <pre>twistd -ny helloworld.tac</pre> | ||
3173 | 81 | |||
3174 | 82 | <p> | ||
3175 | 83 | You'll probably notice that you get log output on the console. This is | ||
3176 | 84 | just one of the good things that twistd does. It can also daemonize the | ||
3177 | 85 | application, shed privileges if run as root, etc. | ||
3178 | 86 | </p> | ||
3179 | 87 | |||
3180 | 88 | <p> | ||
3181 | 89 | TAC files are covered in more detail in the Twisted documentation but | ||
3182 | 90 | let's quickly explain what all this does anyway. | ||
3183 | 91 | </p> | ||
3184 | 92 | |||
3185 | 93 | <p> | ||
3186 | 94 | When <code class="shell">twistd</code> starts up it loads the | ||
3187 | 95 | <code>.tac</code> file (it's just Python) and looks for the attribute | ||
3188 | 96 | called <code>application</code>. When <code class="shell">twistd</code> | ||
3189 | 97 | is all ready to go it starts the <code>application</code>. | ||
3190 | 98 | </p> | ||
3191 | 99 | |||
3192 | 100 | <p> | ||
3193 | 101 | The application is not much use unless it actually does something so the | ||
3194 | 102 | next thing we do is create a <code class="API" | ||
3195 | 103 | base="nevow.appserver">NevowSite</code> instance, <code>site</code>, and | ||
3196 | 104 | pass it a root resource, a <code>HelloWorld</code> instance. Finally, we | ||
3197 | 105 | create a TCP server that makes the site available on port 8080 and bind | ||
3198 | 106 | the server to the application to ensure the server is started when the | ||
3199 | 107 | application is started. | ||
3200 | 108 | </p> | ||
3201 | 109 | </body> | ||
3202 | 110 | </html> | ||
3203 | 111 | 0 | ||
3204 | === removed file 'Nevow/doc/howto/glossary.xhtml' | |||
3205 | --- Nevow/doc/howto/glossary.xhtml 2010-04-06 11:05:45 +0000 | |||
3206 | +++ Nevow/doc/howto/glossary.xhtml 1970-01-01 00:00:00 +0000 | |||
3207 | @@ -1,82 +0,0 @@ | |||
3208 | 1 | <?xml version="1.0"?> | ||
3209 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
3210 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3211 | 4 | |||
3212 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
3213 | 6 | <head> | ||
3214 | 7 | <title> | ||
3215 | 8 | Glossary | ||
3216 | 9 | </title> | ||
3217 | 10 | </head> | ||
3218 | 11 | <body> | ||
3219 | 12 | <h1>Glossary</h1> | ||
3220 | 13 | |||
3221 | 14 | <h2>Object Traversal</h2> | ||
3222 | 15 | |||
3223 | 16 | <p> | ||
3224 | 17 | The process by which a Python object is located to render HTML for a | ||
3225 | 18 | given HTTP URL. For example, given the URL http://example.com/foo/bar, | ||
3226 | 19 | Object Traversal will begin at the "Root Resource" object by asking it | ||
3227 | 20 | for an object which is capable of rendering the page at ('foo', | ||
3228 | 21 | 'bar'). The "Root Resource" returns an object and a list of unhandled | ||
3229 | 22 | path segments, and the traversal continues across this new Resource | ||
3230 | 23 | object until all path segments have been consumed. | ||
3231 | 24 | </p> | ||
3232 | 25 | |||
3233 | 26 | <h2>Page Rendering</h2> | ||
3234 | 27 | |||
3235 | 28 | <p> | ||
3236 | 29 | The process by which a Python object, usually a rend.Page subclass, turns | ||
3237 | 30 | itself into HTML. Page Rendering involves locating some page data, | ||
3238 | 31 | loading a template document, and applying the template to the data, in | ||
3239 | 32 | the process generating HTML. | ||
3240 | 33 | </p> | ||
3241 | 34 | |||
3242 | 35 | <h2>Deployment Environment</h2> | ||
3243 | 36 | |||
3244 | 37 | <p> | ||
3245 | 38 | The environment in which a Nevow application is deployed. Generally | ||
3246 | 39 | involves an HTTP server which is configured to route certain (or all) | ||
3247 | 40 | HTTP requests through the Nevow Object Traversal and Page Rendering | ||
3248 | 41 | process. Deployment environments include CGI, WSGI, and twisted.web. | ||
3249 | 42 | </p> | ||
3250 | 43 | |||
3251 | 44 | <h2>DOM</h2> | ||
3252 | 45 | |||
3253 | 46 | <p> | ||
3254 | 47 | Document Object Model. A tree of objects which represent the structure of | ||
3255 | 48 | an XHTML document in memory. Nevow uses a nonstandard DOM named "stan", | ||
3256 | 49 | which is made up of simple Python lists, dicts, strings, and | ||
3257 | 50 | nevow.stan.Tag instances. | ||
3258 | 51 | </p> | ||
3259 | 52 | |||
3260 | 53 | <h2>Flattener</h2> | ||
3261 | 54 | |||
3262 | 55 | <p> | ||
3263 | 56 | A Python function which knows how to translate from a rich type to a | ||
3264 | 57 | string containing HTML. For example, the integer flattener calls str() on | ||
3265 | 58 | the integer. The string flattener escapes characters which are unsafe in | ||
3266 | 59 | HTML, such as <, >, and &. | ||
3267 | 60 | </p> | ||
3268 | 61 | |||
3269 | 62 | <h2>Tag</h2> | ||
3270 | 63 | |||
3271 | 64 | <p> | ||
3272 | 65 | A class, defined at nevow.stan.Tag, which holds information about a | ||
3273 | 66 | single HTML tag in a DOM. Tag instances have three attributes: tagName, | ||
3274 | 67 | attributes, and children. tagName is a string indicating the tag | ||
3275 | 68 | name. attributes is a dict indicating the HTML attributes of that | ||
3276 | 69 | node. children is a list indicating the child nodes of that node. | ||
3277 | 70 | </p> | ||
3278 | 71 | |||
3279 | 72 | <h2>Tag Specials</h2> | ||
3280 | 73 | |||
3281 | 74 | <p> | ||
3282 | 75 | A Tag attribute which is "special" to nevow. Tag specials include data, | ||
3283 | 76 | render, pattern, slot, and macro. Tag Specials will never be output as | ||
3284 | 77 | HTML attributes of tags, but will be used by the internal Nevow rendering | ||
3285 | 78 | process to influence how the Tag is rendered. | ||
3286 | 79 | </p> | ||
3287 | 80 | |||
3288 | 81 | </body> | ||
3289 | 82 | </html> | ||
3290 | 83 | 0 | ||
3291 | === removed file 'Nevow/doc/howto/index.xhtml' | |||
3292 | --- Nevow/doc/howto/index.xhtml 2008-09-20 01:06:47 +0000 | |||
3293 | +++ Nevow/doc/howto/index.xhtml 1970-01-01 00:00:00 +0000 | |||
3294 | @@ -1,49 +0,0 @@ | |||
3295 | 1 | <?xml version="1.0"?> | ||
3296 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
3297 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3298 | 4 | |||
3299 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
3300 | 6 | <head> | ||
3301 | 7 | <title> | ||
3302 | 8 | Manual | ||
3303 | 9 | </title> | ||
3304 | 10 | </head> | ||
3305 | 11 | <body> | ||
3306 | 12 | <h1> | ||
3307 | 13 | Manual | ||
3308 | 14 | </h1> | ||
3309 | 15 | <ul class="toc"> | ||
3310 | 16 | <li> | ||
3311 | 17 | <a href="intro.xhtml">Introduction</a> | ||
3312 | 18 | </li> | ||
3313 | 19 | <li> | ||
3314 | 20 | <a href="gettingstarted.xhtml">Getting Started</a> | ||
3315 | 21 | <p>A basic introduction to rendering a web page in Nevow.</p> | ||
3316 | 22 | </li> | ||
3317 | 23 | <li> | ||
3318 | 24 | <a href="traversal.xhtml">Object Traversal</a> | ||
3319 | 25 | <p>Getting from an URL to a Python page object you want to render.</p> | ||
3320 | 26 | </li> | ||
3321 | 27 | <li> | ||
3322 | 28 | <a href="publishing.xhtml">Object Publishing</a> | ||
3323 | 29 | <p>Exposing Python objects as parts of a web page in Nevow.</p> | ||
3324 | 30 | </li> | ||
3325 | 31 | <li> | ||
3326 | 32 | <a href="xmltemplates.xhtml">XML Templates</a> | ||
3327 | 33 | <p>Using standard XHTML as a template for Nevow.</p> | ||
3328 | 34 | </li> | ||
3329 | 35 | <li> | ||
3330 | 36 | <a href="deployment.xhtml">Deployment</a> | ||
3331 | 37 | <p>How to get your Nevow application running on different types of | ||
3332 | 38 | servers.</p> | ||
3333 | 39 | </li> | ||
3334 | 40 | <li> | ||
3335 | 41 | <a href="chattutorial/index.xhtml">Nevow Athena</a> | ||
3336 | 42 | <p>Two-way communication with JavaScript in a web browser.</p> | ||
3337 | 43 | </li> | ||
3338 | 44 | <li> | ||
3339 | 45 | <a href="glossary.xhtml">Glossary</a> | ||
3340 | 46 | </li> | ||
3341 | 47 | </ul> | ||
3342 | 48 | </body> | ||
3343 | 49 | </html> | ||
3344 | 50 | 0 | ||
3345 | === removed file 'Nevow/doc/howto/intro.xhtml' | |||
3346 | --- Nevow/doc/howto/intro.xhtml 2008-08-26 13:45:59 +0000 | |||
3347 | +++ Nevow/doc/howto/intro.xhtml 1970-01-01 00:00:00 +0000 | |||
3348 | @@ -1,294 +0,0 @@ | |||
3349 | 1 | <?xml version="1.0"?> | ||
3350 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
3351 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3352 | 4 | |||
3353 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
3354 | 6 | <head> | ||
3355 | 7 | <title> | ||
3356 | 8 | A Web Application Construction Kit | ||
3357 | 9 | </title> | ||
3358 | 10 | </head> | ||
3359 | 11 | <body> | ||
3360 | 12 | <h1>A Web Application Construction Kit</h1> | ||
3361 | 13 | |||
3362 | 14 | <h2>Summary</h2> | ||
3363 | 15 | |||
3364 | 16 | <p> | ||
3365 | 17 | Nevow is a next-generation web application templating system, based on | ||
3366 | 18 | the ideas developed in the Twisted Woven package. Its main focus is on | ||
3367 | 19 | separating the HTML template from both the business logic and the display | ||
3368 | 20 | logic, while allowing the programmer to write pure Python code as much as | ||
3369 | 21 | possible. It separates your code into 'data' and 'render' functions, a | ||
3370 | 22 | simplified implementation of traditional MVC. It has various parts which | ||
3371 | 23 | can be used individually or as a whole, integrated web solution: | ||
3372 | 24 | </p> | ||
3373 | 25 | |||
3374 | 26 | <ul> | ||
3375 | 27 | <li> | ||
3376 | 28 | XHTML templates: contain no programming logic, only nodes tagged with | ||
3377 | 29 | nevow attributes | ||
3378 | 30 | </li> | ||
3379 | 31 | <li> | ||
3380 | 32 | data/render methods: simplified MVC | ||
3381 | 33 | </li> | ||
3382 | 34 | <li> | ||
3383 | 35 | stan: An s-expression-like syntax for expressing xml in pure python | ||
3384 | 36 | </li> | ||
3385 | 37 | <li> | ||
3386 | 38 | formless: For describing the types of objects which may be passed to | ||
3387 | 39 | methods of your classes, validating and coercing string input from | ||
3388 | 40 | either web or command-line sources, and calling your methods | ||
3389 | 41 | automatically once validation passes | ||
3390 | 42 | </li> | ||
3391 | 43 | <li> | ||
3392 | 44 | formless.webform: For rendering web forms based on formless type | ||
3393 | 45 | descriptions, accepting form posts and passing them to formless | ||
3394 | 46 | validators, and rendering error forms in the event validation fails | ||
3395 | 47 | </li> | ||
3396 | 48 | <li> | ||
3397 | 49 | livepage: Cross-browser JavaScript glue for sending client side events | ||
3398 | 50 | to the server and server side events to the client after the page has | ||
3399 | 51 | loaded, without causing the entire page to refresh | ||
3400 | 52 | </li> | ||
3401 | 53 | </ul> | ||
3402 | 54 | |||
3403 | 55 | <h2>Disk based templates</h2> | ||
3404 | 56 | |||
3405 | 57 | <p> | ||
3406 | 58 | Nevow includes the ability to load templates off disk. These templates | ||
3407 | 59 | may have processing directives which cause the execution of python | ||
3408 | 60 | methods at render time. The attribute technique was inspired by the | ||
3409 | 61 | attributes used by ZPT. However, no actual code may be embedded in the | ||
3410 | 62 | HTML template: | ||
3411 | 63 | </p> | ||
3412 | 64 | |||
3413 | 65 | <pre> | ||
3414 | 66 | <html xmlns:nevow="http://nevow.com/ns/nevow/0.1"> | ||
3415 | 67 | <head> | ||
3416 | 68 | <title>Greetings!</title> | ||
3417 | 69 | </head> | ||
3418 | 70 | <body> | ||
3419 | 71 | <h1 style="font-size: large">Now I will greet you:</h1> | ||
3420 | 72 | <span nevow:render="greet" /> | ||
3421 | 73 | </body> | ||
3422 | 74 | </html></pre> | ||
3423 | 75 | |||
3424 | 76 | <p> | ||
3425 | 77 | This template can then be loaded and rendered like so: | ||
3426 | 78 | </p> | ||
3427 | 79 | |||
3428 | 80 | <pre class="python"> | ||
3429 | 81 | class Greeter(rend.Page): | ||
3430 | 82 | docFactory = loaders.xmlfile("Greeting.html") | ||
3431 | 83 | |||
3432 | 84 | def render_greet(self, context, data): | ||
3433 | 85 | return random.choice(["Hello", "Greetings", "Hi"]), " ", data | ||
3434 | 86 | |||
3435 | 87 | Greeter("My name is").renderString() | ||
3436 | 88 | </pre> | ||
3437 | 89 | |||
3438 | 90 | <h2>data/render methods</h2> | ||
3439 | 91 | |||
3440 | 92 | <p> | ||
3441 | 93 | To allow clean isolation between code which fetches data from a data | ||
3442 | 94 | source and code which renders the data into HTML, nevow allows you to | ||
3443 | 95 | write both 'data' methods and 'render' methods. These concepts are | ||
3444 | 96 | inspired by MVC, but simpler, since the framework can handle most of the | ||
3445 | 97 | controller aspect. An example: | ||
3446 | 98 | </p> | ||
3447 | 99 | |||
3448 | 100 | <pre> | ||
3449 | 101 | <html xmlns:nevow="http://nevow.com/ns/nevow/0.1"> | ||
3450 | 102 | <body> | ||
3451 | 103 | <span nevow:data="name" nevow:render="colorful" /> | ||
3452 | 104 | <span nevow:data="fun" nevow:render="colorful" /> | ||
3453 | 105 | </body> | ||
3454 | 106 | </html></pre> | ||
3455 | 107 | |||
3456 | 108 | <p> | ||
3457 | 109 | This template can be loaded and rendered using a class such as this: | ||
3458 | 110 | </p> | ||
3459 | 111 | |||
3460 | 112 | <pre class="python"> | ||
3461 | 113 | class Colorful(rend.Page): | ||
3462 | 114 | docFactory = loaders.xmlfile("Colorful.html") | ||
3463 | 115 | |||
3464 | 116 | def render_colorful(self, context, data): | ||
3465 | 117 | color = random.choice(['red', 'green', 'blue']) | ||
3466 | 118 | return context.tag(style="color: %s" % color) | ||
3467 | 119 | |||
3468 | 120 | def data_name(self, context, data): | ||
3469 | 121 | return "Your name here" | ||
3470 | 122 | |||
3471 | 123 | def data_fun(self, context, data): | ||
3472 | 124 | return "Are we having fun yet?" | ||
3473 | 125 | </pre> | ||
3474 | 126 | |||
3475 | 127 | <h2>Stan</h2> | ||
3476 | 128 | |||
3477 | 129 | <p> | ||
3478 | 130 | One of the most powerful things about nevow is stan, an s-expression-like | ||
3479 | 131 | syntax for producing XML fragments in pure Python syntax. Stan is not | ||
3480 | 132 | required for using nevow, but it is both a simple and powerful way to | ||
3481 | 133 | both lay out one's XHTML templates and express one's display logic. A | ||
3482 | 134 | brief example will illustrate its utility: | ||
3483 | 135 | </p> | ||
3484 | 136 | |||
3485 | 137 | <pre class="python"> | ||
3486 | 138 | import random | ||
3487 | 139 | from nevow import rend, tags | ||
3488 | 140 | |||
3489 | 141 | class Greeter(rend.Page): | ||
3490 | 142 | def greet(self, context, data): | ||
3491 | 143 | return random.choice(["Hello", "Greetings", "Hi"]), " ", data | ||
3492 | 144 | |||
3493 | 145 | docFactory = loaders.stan( | ||
3494 | 146 | tags.html[ | ||
3495 | 147 | tags.head[ tags.title[ "Greetings!" ]], | ||
3496 | 148 | tags.body[ | ||
3497 | 149 | tags.h1(style="font-size: large")[ "Now I will greet you:" ], | ||
3498 | 150 | greet | ||
3499 | 151 | ] | ||
3500 | 152 | ]) | ||
3501 | 153 | </pre> | ||
3502 | 154 | |||
3503 | 155 | <p> | ||
3504 | 156 | When the Greeter class is constructed, it is passed a Python object which | ||
3505 | 157 | will be used as that page's data: | ||
3506 | 158 | </p> | ||
3507 | 159 | |||
3508 | 160 | <pre class="python"> | ||
3509 | 161 | Greeter("Your name here").renderString() | ||
3510 | 162 | </pre> | ||
3511 | 163 | |||
3512 | 164 | <h2>Formless</h2> | ||
3513 | 165 | |||
3514 | 166 | <p> | ||
3515 | 167 | Python is dynamically typed, which means it has no built-in controls for | ||
3516 | 168 | enforcing the types of objects which are passed to one's methods. This is | ||
3517 | 169 | great for programmers, but not necessarily great if you are going to be | ||
3518 | 170 | passing user-entered input to those methods. Formless is a simple way to | ||
3519 | 171 | describe the types of objects that can be passed to one's methods, as | ||
3520 | 172 | well as coerce from string input to those types. Other code can then | ||
3521 | 173 | accept user input from a command line or from a web form, validate the | ||
3522 | 174 | input against the types described using formless, and call the method | ||
3523 | 175 | once validation has passed. A simple example: | ||
3524 | 176 | </p> | ||
3525 | 177 | |||
3526 | 178 | <pre class="python"> | ||
3527 | 179 | from zope.interface import implements | ||
3528 | 180 | from formless.annotate import TypedInterface, Integer, String | ||
3529 | 181 | |||
3530 | 182 | class ISimpleMethod(TypedInterface): | ||
3531 | 183 | def simple(self, | ||
3532 | 184 | name=String(description="Your name."), | ||
3533 | 185 | age=Integer(description="Your age.")): | ||
3534 | 186 | """ | ||
3535 | 187 | Simple | ||
3536 | 188 | |||
3537 | 189 | Please enter your name and age. | ||
3538 | 190 | """ | ||
3539 | 191 | |||
3540 | 192 | class Implementation(object): | ||
3541 | 193 | implements(ISimpleMethod) | ||
3542 | 194 | |||
3543 | 195 | def simple(self, name, age): | ||
3544 | 196 | print "Hello, %s, who is %s" % (name, age) | ||
3545 | 197 | </pre> | ||
3546 | 198 | |||
3547 | 199 | <h2>Webform</h2> | ||
3548 | 200 | |||
3549 | 201 | <p> | ||
3550 | 202 | Webform is a nevow module which will automatically render web forms and | ||
3551 | 203 | accept form posts based on types described using the classes in | ||
3552 | 204 | formless. Used in conjunction with the twisted.web HTTP server, the | ||
3553 | 205 | process is almost automatic: | ||
3554 | 206 | </p> | ||
3555 | 207 | |||
3556 | 208 | <pre class="python"> | ||
3557 | 209 | from nevow import rend, tags | ||
3558 | 210 | from formless import webform | ||
3559 | 211 | |||
3560 | 212 | class WebForm(rend.Page): | ||
3561 | 213 | document = rend.stan( | ||
3562 | 214 | tags.html[ | ||
3563 | 215 | tags.body[ | ||
3564 | 216 | h1["Here is the form:"], | ||
3565 | 217 | webform.renderForms('original') | ||
3566 | 218 | ] | ||
3567 | 219 | ]) | ||
3568 | 220 | |||
3569 | 221 | resource = WebForm(Implementation()) | ||
3570 | 222 | </pre> | ||
3571 | 223 | |||
3572 | 224 | <p> | ||
3573 | 225 | Exposing this resource instance to the web using twisted.web and visiting | ||
3574 | 226 | it will cause a form with two input boxes to be rendered. Posting the | ||
3575 | 227 | form will cause form validation to occur. Upon error, the user will be | ||
3576 | 228 | returned to the original page, with the form annotated with error | ||
3577 | 229 | messages. Upon success, the "simple" method of the Implementation | ||
3578 | 230 | instance will be called and passed a string and an integer. | ||
3579 | 231 | </p> | ||
3580 | 232 | |||
3581 | 233 | <h2>LivePage</h2> | ||
3582 | 234 | |||
3583 | 235 | <p> | ||
3584 | 236 | LivePage was a Woven technology which allowed programmers to receive | ||
3585 | 237 | server-side notification of client-side JavaScript events, and to send | ||
3586 | 238 | JavaScript to the client in response to a server-side event. New for | ||
3587 | 239 | Nevow 0.3, LivePage has been updated to support Mozilla, Firefox, IE6 | ||
3588 | 240 | Win, and Safari. Using LivePage is very easy: | ||
3589 | 241 | </p> | ||
3590 | 242 | |||
3591 | 243 | <pre class="python"> | ||
3592 | 244 | from nevow.liveevil import handler | ||
3593 | 245 | |||
3594 | 246 | def greeter(client, nodeName): | ||
3595 | 247 | client.alert("Greetings. You clicked the %s node." % nodeName) | ||
3596 | 248 | |||
3597 | 249 | # Any string arguments after the event handler function will be evaluated | ||
3598 | 250 | # as JavaScript in the context of the web browser and results passed to the | ||
3599 | 251 | # Python event handler | ||
3600 | 252 | handler = handler(greeter, 'node.name') | ||
3601 | 253 | |||
3602 | 254 | class Live(rend.Page): | ||
3603 | 255 | docFactory = loaders.stan( | ||
3604 | 256 | tags.html[ | ||
3605 | 257 | tags.body[ | ||
3606 | 258 | ol[ | ||
3607 | 259 | li(onclick=handler, name="one")["One"] | ||
3608 | 260 | li(onclick=handler, name="two")["Two"] | ||
3609 | 261 | li(onclick=handler, name="three")["Three"] | ||
3610 | 262 | ] | ||
3611 | 263 | ] | ||
3612 | 264 | ]) | ||
3613 | 265 | </pre> | ||
3614 | 266 | |||
3615 | 267 | <h2>More Information</h2> | ||
3616 | 268 | |||
3617 | 269 | <p> | ||
3618 | 270 | The <a href="http://divmod.org/trac/wiki/DivmodNevow">Nevow website</a> | ||
3619 | 271 | has more information. Starting with 0.3, it contains a simple WSGI | ||
3620 | 272 | implementation and can also be used to render CGIs. However, the | ||
3621 | 273 | recommended mode of operation is using the <a | ||
3622 | 274 | href="http://twistedmatrix.com/trac/wiki/TwistedWeb">Twisted web</a> | ||
3623 | 275 | server. Nevow is an active project, and many new bugfixes and features | ||
3624 | 276 | are committed to the Nevow SVN repository. Information about Nevow | ||
3625 | 277 | commits is available by subscribing to the <a | ||
3626 | 278 | href="http://divmod.net/users/mailman.twistd/listinfo/divmod-commits"> | ||
3627 | 279 | Divmod commits</a> mailing list. The Nevow SVN repository can be checked | ||
3628 | 280 | out using: | ||
3629 | 281 | </p> | ||
3630 | 282 | |||
3631 | 283 | <pre>svn co svn://divmod.org/svn/Nevow/trunk Nevow</pre> | ||
3632 | 284 | |||
3633 | 285 | <p> | ||
3634 | 286 | Discussion of Nevow occurs on the <a | ||
3635 | 287 | href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web"> | ||
3636 | 288 | twisted.web mailing list</a>. The Nevow developers are also often | ||
3637 | 289 | available for real-time help on the <a | ||
3638 | 290 | href="irc://irc.freenode.net/#twisted.web">#twisted.web channel</a> on | ||
3639 | 291 | irc.freenode.net. | ||
3640 | 292 | </p> | ||
3641 | 293 | </body> | ||
3642 | 294 | </html> | ||
3643 | 295 | 0 | ||
3644 | === removed directory 'Nevow/doc/howto/listings' | |||
3645 | === removed directory 'Nevow/doc/howto/listings/gettingstarted' | |||
3646 | === removed file 'Nevow/doc/howto/listings/gettingstarted/helloworld.py' | |||
3647 | --- Nevow/doc/howto/listings/gettingstarted/helloworld.py 2008-08-26 13:45:59 +0000 | |||
3648 | +++ Nevow/doc/howto/listings/gettingstarted/helloworld.py 1970-01-01 00:00:00 +0000 | |||
3649 | @@ -1,6 +0,0 @@ | |||
3650 | 1 | from nevow import loaders, rend | ||
3651 | 2 | |||
3652 | 3 | class HelloWorld(rend.Page): | ||
3653 | 4 | addSlash = True | ||
3654 | 5 | docFactory = loaders.xmlfile('helloworld.html') | ||
3655 | 6 | |||
3656 | 7 | 0 | ||
3657 | === removed file 'Nevow/doc/howto/listings/gettingstarted/helloworld.tac' | |||
3658 | --- Nevow/doc/howto/listings/gettingstarted/helloworld.tac 2008-08-26 13:45:59 +0000 | |||
3659 | +++ Nevow/doc/howto/listings/gettingstarted/helloworld.tac 1970-01-01 00:00:00 +0000 | |||
3660 | @@ -1,10 +0,0 @@ | |||
3661 | 1 | from twisted.application import internet | ||
3662 | 2 | from twisted.application import service | ||
3663 | 3 | from nevow import appserver | ||
3664 | 4 | import helloworld | ||
3665 | 5 | |||
3666 | 6 | application = service.Application('helloworld') | ||
3667 | 7 | site = appserver.NevowSite(helloworld.HelloWorld()) | ||
3668 | 8 | webServer = internet.TCPServer(8080, site) | ||
3669 | 9 | webServer.setServiceParent(application) | ||
3670 | 10 | |||
3671 | 11 | 0 | ||
3672 | === removed file 'Nevow/doc/howto/publishing.xhtml' | |||
3673 | --- Nevow/doc/howto/publishing.xhtml 2008-08-26 13:45:59 +0000 | |||
3674 | +++ Nevow/doc/howto/publishing.xhtml 1970-01-01 00:00:00 +0000 | |||
3675 | @@ -1,658 +0,0 @@ | |||
3676 | 1 | <?xml version="1.0"?> | ||
3677 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
3678 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3679 | 4 | |||
3680 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
3681 | 6 | <head> | ||
3682 | 7 | <title> | ||
3683 | 8 | Object Publishing | ||
3684 | 9 | </title> | ||
3685 | 10 | </head> | ||
3686 | 11 | <body> | ||
3687 | 12 | <h1> | ||
3688 | 13 | Object Publishing | ||
3689 | 14 | </h1> | ||
3690 | 15 | |||
3691 | 16 | <p> | ||
3692 | 17 | In <a href="traversal.xhtml">Object Traversal</a>, we learned about the | ||
3693 | 18 | <code class="API">nevow.inevow.IResource.renderHTTP</code> method, which | ||
3694 | 19 | is the most basic way to send HTML to a browser when using | ||
3695 | 20 | Nevow. However, it is not very convenient (or clean) to generate HTML | ||
3696 | 21 | tags by concatenating strings in Python code. In the <a | ||
3697 | 22 | href="deployment.xhtml">Deployment</a> documentation, we saw that it was | ||
3698 | 23 | possible to render a <em>Hello World</em> page using a <code class="API"> | ||
3699 | 24 | nevow.rend.Page</code> subclass and providing a <code>docFactory</code>: | ||
3700 | 25 | </p> | ||
3701 | 26 | |||
3702 | 27 | <pre class="python-interpreter"> | ||
3703 | 28 | >>> from nevow import rend, loaders | ||
3704 | 29 | >>> class HelloWorld(rend.Page): | ||
3705 | 30 | ... docFactory = loaders.stan("Hello, world!") | ||
3706 | 31 | ... | ||
3707 | 32 | >>> HelloWorld().renderSynchronously() | ||
3708 | 33 | 'Hello, world!'</pre> | ||
3709 | 34 | |||
3710 | 35 | <p> | ||
3711 | 36 | This example does nothing interesting, but the concept of a loader is | ||
3712 | 37 | important in Nevow. The <code>rend.Page.renderHTTP</code> implementation | ||
3713 | 38 | always starts rendering HTML by loading a template from the | ||
3714 | 39 | <code>docFactory</code>. | ||
3715 | 40 | </p> | ||
3716 | 41 | |||
3717 | 42 | |||
3718 | 43 | <h2>The stan DOM</h2> | ||
3719 | 44 | |||
3720 | 45 | <p> | ||
3721 | 46 | Nevow uses a DOM-based approach to rendering HTML. A tree of objects is | ||
3722 | 47 | first constructed in memory by the template loader. This tree is then | ||
3723 | 48 | processed one node at a time, applying functions which transform from | ||
3724 | 49 | various Python types to HTML strings. | ||
3725 | 50 | </p> | ||
3726 | 51 | |||
3727 | 52 | <p> | ||
3728 | 53 | Nevow uses a nonstandard DOM named "stan". Unlike the W3C DOM, stan is | ||
3729 | 54 | made up of simple python lists, strings, and instances of the | ||
3730 | 55 | nevow.stan.Tag class. During the rendering process, "Flattener" | ||
3731 | 56 | functions convert from rich types to HTML strings. For example, we can | ||
3732 | 57 | load a template made up of some nested lists and Python types, render it, | ||
3733 | 58 | and see what happens: | ||
3734 | 59 | </p> | ||
3735 | 60 | |||
3736 | 61 | <pre class="python-interpreter"> | ||
3737 | 62 | >>> class PythonTypes(rend.Page): | ||
3738 | 63 | ... docFactory = loaders.stan(["Hello", 1, 1.5, True, ["Goodbye", 3]]) | ||
3739 | 64 | ... | ||
3740 | 65 | >>> PythonTypes().renderSynchronously() | ||
3741 | 66 | 'Hello11.5TrueGoodbye3'</pre> | ||
3742 | 67 | |||
3743 | 68 | <h2>Tag instances</h2> | ||
3744 | 69 | |||
3745 | 70 | <p> | ||
3746 | 71 | So far, we have only rendered simple strings as output. However, the main | ||
3747 | 72 | purpose of Nevow is HTML generation. In the stan DOM, HTML tags are | ||
3748 | 73 | represented by instances of the <code class="API">nevow.stan.Tag</code> | ||
3749 | 74 | class. <code>Tag</code> is a very simple class, whose instances have an | ||
3750 | 75 | <code>attributes</code> dictionary and a <code>children</code> list. The | ||
3751 | 76 | <code>Tag</code> flattener knows how to recursively flatten attributes | ||
3752 | 77 | and children of the tag. To show you how <code>Tag</code>s really work | ||
3753 | 78 | before you layer Nevow's convenience syntax on top, try this horrible | ||
3754 | 79 | example: | ||
3755 | 80 | </p> | ||
3756 | 81 | |||
3757 | 82 | <pre class="python-interpreter"> | ||
3758 | 83 | >>> from nevow import stan | ||
3759 | 84 | >>> h = stan.Tag('html') | ||
3760 | 85 | >>> d = stan.Tag('div') | ||
3761 | 86 | >>> d.attributes['style'] = 'border: 1px solid black' | ||
3762 | 87 | >>> h.children.append(d) | ||
3763 | 88 | >>> class Tags(rend.Page): | ||
3764 | 89 | ... docFactory = loaders.stan(h) | ||
3765 | 90 | ... | ||
3766 | 91 | >>> Tags().renderSynchronously() | ||
3767 | 92 | '<html><div style="border: 1px solid black"></div></html>'</pre> | ||
3768 | 93 | |||
3769 | 94 | <p> | ||
3770 | 95 | So, we see how it is possible to programatically generate HTML by | ||
3771 | 96 | constructing and nesting stan <code>Tag</code> instances. However, it is | ||
3772 | 97 | far more convenient to use the overloaded operators <code>Tag</code> | ||
3773 | 98 | provides to manipulate them. <code>Tag</code> implements a | ||
3774 | 99 | <code>__call__</code> method which takes any keyword arguments and values | ||
3775 | 100 | and updates the attributes dictionary; it also implements a | ||
3776 | 101 | <code>__getitem__</code> method which takes whatever is between the square | ||
3777 | 102 | brackets and appends them to the children list. A simple example should | ||
3778 | 103 | clarify things: | ||
3779 | 104 | </p> | ||
3780 | 105 | |||
3781 | 106 | <pre class="python-interpreter"> | ||
3782 | 107 | >>> class Tags2(rend.Page): | ||
3783 | 108 | ... docFactory = loaders.stan(stan.Tag('html')[stan.Tag('div')(style="border: 1px solid black")]) | ||
3784 | 109 | ... | ||
3785 | 110 | >>> Tags2().renderSynchronously() | ||
3786 | 111 | '<html><div style="border: 1px solid black"></div></html>'</pre> | ||
3787 | 112 | |||
3788 | 113 | <p> | ||
3789 | 114 | This isn't very easy to read, but luckily we can simplify the example | ||
3790 | 115 | even further by using the nevow.tags module, which is full of "Tag | ||
3791 | 116 | prototypes" for every tag type described by the XHTML 1.0 specification: | ||
3792 | 117 | </p> | ||
3793 | 118 | |||
3794 | 119 | <pre class="python-interpreter"> | ||
3795 | 120 | >>> class Tags3(rend.Page): | ||
3796 | 121 | ... docFactory = loaders.stan(tags.html[tags.div(style="border: 1px solid black")]) | ||
3797 | 122 | ... | ||
3798 | 123 | >>> Tags3().renderSynchronously() | ||
3799 | 124 | '<html><div style="border: 1px solid black"></div></html>'</pre> | ||
3800 | 125 | |||
3801 | 126 | <p> | ||
3802 | 127 | Using stan syntax is not the only way to construct template DOM for use | ||
3803 | 128 | by the Nevow rendering process. Nevow also includes <code class="API" | ||
3804 | 129 | base="nevow">loaders.xmlfile</code> which implements a simple tag | ||
3805 | 130 | attribute language similar to the Zope Page Templates (ZPT) Tag Attribute | ||
3806 | 131 | Language (TAL). However, experience with the stan DOM should give you | ||
3807 | 132 | insight into how the Nevow rendering process really works. Rendering a | ||
3808 | 133 | template into HTML in Nevow is really nothing more than iterating a tree | ||
3809 | 134 | of objects and recursively applying "Flattener" functions to objects in | ||
3810 | 135 | this tree, until all HTML has been generated. | ||
3811 | 136 | </p> | ||
3812 | 137 | |||
3813 | 138 | <h2>Functions in the DOM</h2> | ||
3814 | 139 | |||
3815 | 140 | <p> | ||
3816 | 141 | So far, all of our examples have generated static HTML pages, which is | ||
3817 | 142 | not terribly interesting when discussing dynamic web applications. Nevow | ||
3818 | 143 | takes a very simple approach to dynamic HTML generation. If you put a | ||
3819 | 144 | Python function reference in the DOM, Nevow will call it when the page is | ||
3820 | 145 | rendered. The return value of the function replaces the function itself | ||
3821 | 146 | in the DOM, and the results are flattened further. This makes it easy to | ||
3822 | 147 | express looping and branching structures in Nevow, because normal Python | ||
3823 | 148 | looping and branching constructs are used to do the job: | ||
3824 | 149 | </p> | ||
3825 | 150 | |||
3826 | 151 | <pre class="python-interpreter"> | ||
3827 | 152 | >>> def repeat(ctx, data): | ||
3828 | 153 | ... return [tags.div(style="color: %s" % (color, )) | ||
3829 | 154 | ... for color in ['red', 'blue', 'green']] | ||
3830 | 155 | ... | ||
3831 | 156 | >>> class Repeat(rend.Page): | ||
3832 | 157 | ... docFactory = loaders.stan(tags.html[repeat]) | ||
3833 | 158 | ... | ||
3834 | 159 | >>> Repeat().renderSynchronously() | ||
3835 | 160 | '<html><div style="color: red"></div><div style="color: blue"></div><div style="color: green"></div></html>'</pre> | ||
3836 | 161 | |||
3837 | 162 | <p> | ||
3838 | 163 | However, in the example above, the repeat function isn't even necessary, | ||
3839 | 164 | because we could have inlined the list comprehension right where we | ||
3840 | 165 | placed the function reference in the DOM. Things only really become | ||
3841 | 166 | interesting when we begin writing parameterized render functions which | ||
3842 | 167 | cause templates to render differently depending on the input to the web | ||
3843 | 168 | application. | ||
3844 | 169 | </p> | ||
3845 | 170 | |||
3846 | 171 | <p> | ||
3847 | 172 | The required signature of functions which we can place in the DOM is | ||
3848 | 173 | (ctx, data). The "context" object is essentially opaque for now, and we | ||
3849 | 174 | will learn how to extract useful information out of it later. The "data" | ||
3850 | 175 | object is anything we want it to be, and can change during the rendering | ||
3851 | 176 | of the page. By default, the data object is whatever we pass as the first | ||
3852 | 177 | argument to the Page constructor, <em>or</em> the Page instance itself if | ||
3853 | 178 | nothing is passed. Armed with this knowledge, we can create a Page which | ||
3854 | 179 | renders differently depending on the data we pass to the Page | ||
3855 | 180 | constructor: | ||
3856 | 181 | </p> | ||
3857 | 182 | |||
3858 | 183 | <pre class="python"> | ||
3859 | 184 | class Root(rend.Page): | ||
3860 | 185 | docFactory = loaders.stan(tags.html[ | ||
3861 | 186 | tags.h1["Welcome."], | ||
3862 | 187 | tags.a(href="foo")["Foo"], | ||
3863 | 188 | tags.a(href="bar")["Bar"], | ||
3864 | 189 | tags.a(href="baz")["Baz"]]) | ||
3865 | 190 | |||
3866 | 191 | def childFactory(self, ctx, name): | ||
3867 | 192 | return Leaf(name) | ||
3868 | 193 | |||
3869 | 194 | def greet(ctx, name): | ||
3870 | 195 | return "Hello. You are visiting the ", name, " page." | ||
3871 | 196 | |||
3872 | 197 | class Leaf(rend.Page): | ||
3873 | 198 | docFactory = loaders.stan(tags.html[greet]) | ||
3874 | 199 | </pre> | ||
3875 | 200 | |||
3876 | 201 | <p> | ||
3877 | 202 | Armed with this knowledge and the information in the <a | ||
3878 | 203 | href="traversal.xhtml">Object Traversal</a> documentation, we now have | ||
3879 | 204 | enough information to create dynamic websites with arbitrary URL | ||
3880 | 205 | hierarchies whose pages render dynamically depending on which URL was | ||
3881 | 206 | used to access them. | ||
3882 | 207 | </p> | ||
3883 | 208 | |||
3884 | 209 | <h2>Accessing query parameters and form post data</h2> | ||
3885 | 210 | |||
3886 | 211 | <p> | ||
3887 | 212 | Before we move on to more advanced rendering techniques, let us first | ||
3888 | 213 | examine how one could further customize the rendering of a Page based on | ||
3889 | 214 | the URL query parameters and form post information provided to us by a | ||
3890 | 215 | browser. Recall that URL parameters are expressed in the form: | ||
3891 | 216 | </p> | ||
3892 | 217 | |||
3893 | 218 | <pre>http://example.com/foo/bar?baz=1&quux=2</pre> | ||
3894 | 219 | |||
3895 | 220 | <p> | ||
3896 | 221 | And form post data can be generated by providing a form to a browser: | ||
3897 | 222 | </p> | ||
3898 | 223 | |||
3899 | 224 | <pre> | ||
3900 | 225 | <form action="" method="POST"> | ||
3901 | 226 | <input type="text" name="baz" /> | ||
3902 | 227 | <input type="text" name="quux" /> | ||
3903 | 228 | <input type="submit" /> | ||
3904 | 229 | </form></pre> | ||
3905 | 230 | |||
3906 | 231 | <p> | ||
3907 | 232 | Accessing this information is such a common procedure that Nevow provides | ||
3908 | 233 | a convenience method on the context to do it. Let's examine a simple page | ||
3909 | 234 | whose output can be influenced by the query parameters in the URL used to | ||
3910 | 235 | access it: | ||
3911 | 236 | </p> | ||
3912 | 237 | |||
3913 | 238 | <pre class="python"> | ||
3914 | 239 | def showChoice(ctx, data): | ||
3915 | 240 | choice = ctx.arg('choice') | ||
3916 | 241 | if choice is None: | ||
3917 | 242 | return '' | ||
3918 | 243 | return "You chose ", choice, "." | ||
3919 | 244 | |||
3920 | 245 | class Custom(rend.Page): | ||
3921 | 246 | docFactory = loaders.stan(tags.html[ | ||
3922 | 247 | tags.a(href="?choice=baz")["Baz"], | ||
3923 | 248 | tags.a(href="?choice=quux")["Quux"], | ||
3924 | 249 | tags.p[showChoice]]) | ||
3925 | 250 | </pre> | ||
3926 | 251 | |||
3927 | 252 | <p> | ||
3928 | 253 | The procedure is exactly the same for simple form post information: | ||
3929 | 254 | </p> | ||
3930 | 255 | |||
3931 | 256 | <pre class="python"> | ||
3932 | 257 | def greet(ctx, data): | ||
3933 | 258 | name = ctx.arg('name') | ||
3934 | 259 | if name is None: | ||
3935 | 260 | return '' | ||
3936 | 261 | return "Greetings, ", name, "!" | ||
3937 | 262 | |||
3938 | 263 | class Form(rend.Page): | ||
3939 | 264 | docFactory = loaders.stan(tags.html[ | ||
3940 | 265 | tags.form(action="", method="POST")[ | ||
3941 | 266 | tags.input(name="name"), | ||
3942 | 267 | tags.input(type="submit")], | ||
3943 | 268 | greet]) | ||
3944 | 269 | </pre> | ||
3945 | 270 | |||
3946 | 271 | <p> | ||
3947 | 272 | Note that <code>ctx.arg</code> returns only the first argument with the | ||
3948 | 273 | given name. For complex cases where multiple arguments and lists of | ||
3949 | 274 | argument values are required, you can access the request argument | ||
3950 | 275 | dictionary directly using the syntax: | ||
3951 | 276 | </p> | ||
3952 | 277 | |||
3953 | 278 | <pre class="python"> | ||
3954 | 279 | def arguments(ctx, data): | ||
3955 | 280 | args = inevow.IRequest(ctx).args | ||
3956 | 281 | return "Request arguments are: ", str(args) | ||
3957 | 282 | </pre> | ||
3958 | 283 | |||
3959 | 284 | <h2>Generators in the DOM</h2> | ||
3960 | 285 | |||
3961 | 286 | <p> | ||
3962 | 287 | One common operation when building dynamic pages is iterating a list of | ||
3963 | 288 | data and emitting some HTML for each item. Python generators are well | ||
3964 | 289 | suited for expressing this sort of logic, and code which is written as a | ||
3965 | 290 | python generator can perform tests (<code>if</code>) and loops of various | ||
3966 | 291 | kinds (<code>while</code>, <code>for</code>) and emit a row of html | ||
3967 | 292 | whenever it has enough data to do so. Nevow can handle generators in the | ||
3968 | 293 | DOM just as gracefully as it can handle anything else: | ||
3969 | 294 | </p> | ||
3970 | 295 | |||
3971 | 296 | <pre class="python-interpreter"> | ||
3972 | 297 | >>> from nevow import rend, loaders, tags | ||
3973 | 298 | >>> def generate(ctx, items): | ||
3974 | 299 | ... for item in items: | ||
3975 | 300 | ... yield tags.div[ item ] | ||
3976 | 301 | ... | ||
3977 | 302 | >>> class List(rend.Page): | ||
3978 | 303 | ... docFactory = loaders.stan(tags.html[ generate ]) | ||
3979 | 304 | ... | ||
3980 | 305 | >>> List(['one', 'two', 'three']).renderSynchronously() | ||
3981 | 306 | '<html><div>one</div><div>two</div><div>three</div></html>'</pre> | ||
3982 | 307 | |||
3983 | 308 | <p> | ||
3984 | 309 | As you can see, generating HTML inside of functions or generators can be | ||
3985 | 310 | very convenient, and can lead to very rapid application | ||
3986 | 311 | development. However, it is also what I would call a "template | ||
3987 | 312 | abstraction violation", and we will learn how we can keep knowledge of | ||
3988 | 313 | HTML out of our python code when we learn about patterns and slots. | ||
3989 | 314 | </p> | ||
3990 | 315 | |||
3991 | 316 | <h2>Methods in the DOM</h2> | ||
3992 | 317 | |||
3993 | 318 | <p> | ||
3994 | 319 | Up until now, we have been placing our template manipulation logic inside | ||
3995 | 320 | of simple Python functions and generators. However, it is often | ||
3996 | 321 | appropriate to use a method instead of a function. Nevow makes it just as | ||
3997 | 322 | easy to use a method to render HTML: | ||
3998 | 323 | </p> | ||
3999 | 324 | |||
4000 | 325 | <pre class="python"> | ||
4001 | 326 | class MethodRender(rend.Page): | ||
4002 | 327 | def __init__(self, foo): | ||
4003 | 328 | self.foo = foo | ||
4004 | 329 | |||
4005 | 330 | def render_foo(self, ctx, data): | ||
4006 | 331 | return self.foo | ||
4007 | 332 | |||
4008 | 333 | docFactory = loaders.stan(tags.html[ render_foo ]) | ||
4009 | 334 | </pre> | ||
4010 | 335 | |||
4011 | 336 | <p> | ||
4012 | 337 | Using render methods makes it possible to parameterize your Page class | ||
4013 | 338 | with more parameters. With render methods, you can also use the Page | ||
4014 | 339 | instance as a state machine to keep track of the state of the | ||
4015 | 340 | render. While Nevow is designed to allow you to render the same Page | ||
4016 | 341 | instance repeatedly, it can also be convenient to know that a Page | ||
4017 | 342 | instance will only be used one time, and that the Page instance can be | ||
4018 | 343 | used as a scratch pad to manage information about the render. | ||
4019 | 344 | </p> | ||
4020 | 345 | |||
4021 | 346 | <h2>Data specials</h2> | ||
4022 | 347 | |||
4023 | 348 | <p> | ||
4024 | 349 | Previously we saw how passing a parameter to the default Page constructor | ||
4025 | 350 | makes it available as the "data" parameter to all of our render | ||
4026 | 351 | methods. This "data" parameter can change as the page render proceeds, | ||
4027 | 352 | and is a useful way to ensure that render functions are isolated and only | ||
4028 | 353 | act upon the data which is available to them. Render functions which do | ||
4029 | 354 | not pull information from sources other than the "data" parameter are | ||
4030 | 355 | more easily reusable and can be composed into larger parts more easily. | ||
4031 | 356 | </p> | ||
4032 | 357 | |||
4033 | 358 | <p> | ||
4034 | 359 | Deciding which data gets passed as the data parameter is as simple as | ||
4035 | 360 | changing the "Data special" for a Tag. See the <a | ||
4036 | 361 | href="glossary.xhtml">Glossary</a> under "Tag Specials" for more | ||
4037 | 362 | information about specials. Assigning to the data special is as simple as | ||
4038 | 363 | assigning to a tag attribute: | ||
4039 | 364 | </p> | ||
4040 | 365 | |||
4041 | 366 | <pre class="python-interpreter"> | ||
4042 | 367 | >>> def hello(ctx, name): | ||
4043 | 368 | ... return "Hello, ", name | ||
4044 | 369 | ... | ||
4045 | 370 | >>> class DataSpecial(rend.Page): | ||
4046 | 371 | ... docFactory = loaders.stan(tags.html[ | ||
4047 | 372 | ... tags.div(data="foo")[ hello ], | ||
4048 | 373 | ... tags.div(data="bar")[ hello ]]) | ||
4049 | 374 | ... | ||
4050 | 375 | >>> DataSpecial().renderSynchronously() | ||
4051 | 376 | '<html><div>Hello, foo</div><div>Hello, bar</div></html>'</pre> | ||
4052 | 377 | |||
4053 | 378 | <p> | ||
4054 | 379 | Data specials may be assigned any python value. Data specials are only in | ||
4055 | 380 | scope during the rendering of the tag they are assigned to, so if the | ||
4056 | 381 | "hello" renderer were placed in the DOM inside the html node directly, | ||
4057 | 382 | "Hello, None" would be output. | ||
4058 | 383 | </p> | ||
4059 | 384 | |||
4060 | 385 | <p> | ||
4061 | 386 | Before data is passed to a render function, Nevow first checks to see if | ||
4062 | 387 | there is an <code class="API">IGettable</code> adapter for it. If there | ||
4063 | 388 | is, it calls <code>IGettable.get()</code>, and passes the result of this | ||
4064 | 389 | as the data parameter instead. Nevow includes an <code>IGettable</code> | ||
4065 | 390 | adapter for python functions, which means you can set a Tag data special | ||
4066 | 391 | to a function reference and Nevow will call it to obtain the data when | ||
4067 | 392 | the Tag is rendered. The signature for data methods is similar to that of | ||
4068 | 393 | render methods, (ctx, data). For example: | ||
4069 | 394 | </p> | ||
4070 | 395 | |||
4071 | 396 | <pre class="python"> | ||
4072 | 397 | def getName(ctx, data): | ||
4073 | 398 | return ctx.arg('name') | ||
4074 | 399 | |||
4075 | 400 | def greet(ctx, name): | ||
4076 | 401 | return "Greetings, ", name | ||
4077 | 402 | |||
4078 | 403 | class GreetName(rend.Page): | ||
4079 | 404 | docFactory = loaders.stan(tags.html[ | ||
4080 | 405 | tags.form(action="")[ | ||
4081 | 406 | tags.input(name="name"), | ||
4082 | 407 | tags.input(type="submit")], | ||
4083 | 408 | tags.div(data=getName)[ greet ]]) | ||
4084 | 409 | </pre> | ||
4085 | 410 | |||
4086 | 411 | <p> | ||
4087 | 412 | Data specials exist mainly to allow you to construct and enforce a | ||
4088 | 413 | Model-View-Controller style separation of the Model code from the | ||
4089 | 414 | View. Here we see that the greet function is capable of rendering a | ||
4090 | 415 | greeting view for a name model, and that the implementation of getName | ||
4091 | 416 | may change without the view code changing. | ||
4092 | 417 | </p> | ||
4093 | 418 | |||
4094 | 419 | <h2>Render specials</h2> | ||
4095 | 420 | |||
4096 | 421 | <p> | ||
4097 | 422 | Previously, we have seen how render functions can be placed directly in | ||
4098 | 423 | the DOM, and the return value replaces the render function in the | ||
4099 | 424 | DOM. However, these free functions and methods are devoid of any | ||
4100 | 425 | contextual information about the template they are living in. The | ||
4101 | 426 | render special is a way to associate a render function or method with a | ||
4102 | 427 | particular Tag instance, which the render function can then examine to | ||
4103 | 428 | decide how to render: | ||
4104 | 429 | </p> | ||
4105 | 430 | |||
4106 | 431 | <pre class="python-interpreter"> | ||
4107 | 432 | >>> def alignment(ctx, data): | ||
4108 | 433 | ... align = ctx.tag.attributes.get('align') | ||
4109 | 434 | ... if align == 'right': | ||
4110 | 435 | ... return ctx.tag["Aligned right"] | ||
4111 | 436 | ... elif align == 'center': | ||
4112 | 437 | ... return ctx.tag["Aligned center"] | ||
4113 | 438 | ... else: | ||
4114 | 439 | ... return ctx.tag["Aligned left"] | ||
4115 | 440 | ... | ||
4116 | 441 | >>> class AlignmentPage(rend.Page): | ||
4117 | 442 | ... docFactory = loaders.stan(tags.html[ | ||
4118 | 443 | ... tags.p(render=alignment), | ||
4119 | 444 | ... tags.p(render=alignment, align="center"), | ||
4120 | 445 | ... tags.p(render=alignment, align="right")]) | ||
4121 | 446 | ... | ||
4122 | 447 | >>> AlignmentPage().renderSynchronously() | ||
4123 | 448 | '<html><p>Aligned left</p><p align="center">Aligned center</p><p align="right">Aligned right</p></html>'</pre> | ||
4124 | 449 | |||
4125 | 450 | <p> | ||
4126 | 451 | Note how the alignment renderer has access to the template node as | ||
4127 | 452 | <code>ctx.tag</code>. It can examine and change this node, and the return value of | ||
4128 | 453 | the render function replaces the original node in the DOM. Note that | ||
4129 | 454 | here we are returning the template node after changing it. We will see | ||
4130 | 455 | later how we can instead mutate the context and use slots so that the | ||
4131 | 456 | knowledge the renderer requires about the structure of the template is | ||
4132 | 457 | reduced even more. | ||
4133 | 458 | </p> | ||
4134 | 459 | |||
4135 | 460 | <h2>Pattern specials</h2> | ||
4136 | 461 | |||
4137 | 462 | <p> | ||
4138 | 463 | When writing render methods, it is easy to inline the construction of | ||
4139 | 464 | Tag instances to generate HTML programatically. However, this creates a | ||
4140 | 465 | template abstraction violation, where part of the HTML which will show | ||
4141 | 466 | up in the final page output is hidden away inside of render methods | ||
4142 | 467 | instead of inside the template. Pattern specials are designed to avoid | ||
4143 | 468 | this problem. A node which has been tagged with a pattern special can | ||
4144 | 469 | then be located and copied by a render method. The render method does | ||
4145 | 470 | not need to know anything about the structure or location of the | ||
4146 | 471 | pattern, only it's name. | ||
4147 | 472 | </p> | ||
4148 | 473 | |||
4149 | 474 | <p> | ||
4150 | 475 | We can rewrite our previous generator example so that the generator | ||
4151 | 476 | does not have to know what type of tag the template designer would like | ||
4152 | 477 | repeated for each item in the list: | ||
4153 | 478 | </p> | ||
4154 | 479 | |||
4155 | 480 | <pre class="python-interpreter"> | ||
4156 | 481 | >>> from nevow import rend, loaders, tags, inevow | ||
4157 | 482 | >>> def generate(ctx, items): | ||
4158 | 483 | ... pat = inevow.IQ(ctx).patternGenerator('item') | ||
4159 | 484 | ... for item in items: | ||
4160 | 485 | ... ctx.tag[ pat(data=item) ] | ||
4161 | 486 | ... return ctx.tag | ||
4162 | 487 | ... | ||
4163 | 488 | >>> def string(ctx, item): | ||
4164 | 489 | ... return ctx.tag[ str(item) ] | ||
4165 | 490 | ... | ||
4166 | 491 | >>> class List(rend.Page): | ||
4167 | 492 | ... docFactory = loaders.stan(tags.html[ | ||
4168 | 493 | ... tags.ul(render=generate)[ | ||
4169 | 494 | ... tags.li(pattern="item", render=string)]]) | ||
4170 | 495 | ... | ||
4171 | 496 | >>> List([1, 2, 3]).renderSynchronously() | ||
4172 | 497 | '<html><ol><li>1</li><li>2</li><li>3</li></ol></html>'</pre> | ||
4173 | 498 | |||
4174 | 499 | <p> | ||
4175 | 500 | Note that we have to mutate the tag in place and repeatedly copy the | ||
4176 | 501 | item pattern, applying the item as the data special to the resulting | ||
4177 | 502 | Tag. It turns out that this is such a common operation that nevow comes | ||
4178 | 503 | out of the box with these two render functions: | ||
4179 | 504 | </p> | ||
4180 | 505 | |||
4181 | 506 | <pre class="python-interpreter"> | ||
4182 | 507 | >>> class List(rend.Page): | ||
4183 | 508 | ... docFactory = loaders.stan(tags.html[ | ||
4184 | 509 | ... tags.ul(render=rend.sequence)[ | ||
4185 | 510 | ... tags.li(pattern="item", render=rend.data)]]) | ||
4186 | 511 | ... | ||
4187 | 512 | >>> List([1, 2, 3]).renderSynchronously() | ||
4188 | 513 | '<html><ul><li>1</li><li>2</li><li>3</li></ul></html>'</pre> | ||
4189 | 514 | |||
4190 | 515 | <h2>Slot specials</h2> | ||
4191 | 516 | |||
4192 | 517 | <p> | ||
4193 | 518 | The problem with render methods is that they are only capable of making | ||
4194 | 519 | changes to their direct children. Because of the architecture of Nevow, | ||
4195 | 520 | they should not attempt to change grandchildren or parent nodes. It is | ||
4196 | 521 | possible to write one render method for every node you wish to change, | ||
4197 | 522 | but there is a better way. A node with a slot special can be "filled" | ||
4198 | 523 | with content by any renderer above the slot. Creating a slot special is | ||
4199 | 524 | such a frequent task that there is a prototype in <code>nevow.tags</code> | ||
4200 | 525 | which is usually used. | ||
4201 | 526 | </p> | ||
4202 | 527 | |||
4203 | 528 | <p> | ||
4204 | 529 | Let us examine a renderer which fills a template with information about | ||
4205 | 530 | a person: | ||
4206 | 531 | </p> | ||
4207 | 532 | |||
4208 | 533 | <pre class="python-interpreter"> | ||
4209 | 534 | >>> from nevow import loaders, rend, tags | ||
4210 | 535 | ... | ||
4211 | 536 | >>> person = ('Donovan', 'Preston', 'Male', 'California') | ||
4212 | 537 | ... | ||
4213 | 538 | >>> def render_person(ctx, person): | ||
4214 | 539 | ... firstName, lastName, sex, location = person | ||
4215 | 540 | ... ctx.fillSlots('firstName', firstName) | ||
4216 | 541 | ... ctx.fillSlots('lastName', lastName) | ||
4217 | 542 | ... ctx.fillSlots('sex', sex) | ||
4218 | 543 | ... ctx.fillSlots('location', location) | ||
4219 | 544 | ... return ctx.tag | ||
4220 | 545 | ... | ||
4221 | 546 | >>> class PersonPage(rend.Page): | ||
4222 | 547 | ... docFactory = loaders.stan(tags.html(render=render_person)[ | ||
4223 | 548 | ... tags.table[ | ||
4224 | 549 | ... tags.tr[ | ||
4225 | 550 | ... tags.td[tags.slot('firstName')], | ||
4226 | 551 | ... tags.td[tags.slot('lastName')], | ||
4227 | 552 | ... tags.td[tags.slot('sex')], | ||
4228 | 553 | ... tags.td[tags.slot('location')]]]]) | ||
4229 | 554 | ... | ||
4230 | 555 | >>> PersonPage(person).renderSynchronously() | ||
4231 | 556 | '<html><table><tr><td>Donovan</td><td>Preston</td><td>Male</td><td>California</td></tr></table></html>'</pre> | ||
4232 | 557 | |||
4233 | 558 | <p> | ||
4234 | 559 | Using patterns in combination with slots can lead to very powerful | ||
4235 | 560 | template abstraction. Nevow also includes another standard renderer | ||
4236 | 561 | called "mapping" which takes any data which responds to the "items()" | ||
4237 | 562 | message and inserts the items into appropriate slots: | ||
4238 | 563 | </p> | ||
4239 | 564 | |||
4240 | 565 | <pre class="python-interpreter"> | ||
4241 | 566 | >>> class DictPage(rend.Page): | ||
4242 | 567 | ... docFactory = loaders.stan(tags.html(render=rend.mapping)[ | ||
4243 | 568 | ... tags.span[ tags.slot('foo') ], tags.span[ tags.slot('bar') ]]) | ||
4244 | 569 | ... | ||
4245 | 570 | >>> DictPage(dict(foo=1, bar=2)).renderSynchronously() | ||
4246 | 571 | '<html><span>1</span><span>2</span></html>'</pre> | ||
4247 | 572 | |||
4248 | 573 | <h2>Data directives</h2> | ||
4249 | 574 | |||
4250 | 575 | <p> | ||
4251 | 576 | So far, we have always placed data functions directly in the Data | ||
4252 | 577 | special attribute of a Tag. Sometimes, it is preferable to look up a | ||
4253 | 578 | data method from the Page class as the Page has being rendered. For | ||
4254 | 579 | example, a base class may define a template and a subclass may provide | ||
4255 | 580 | the implementation of the data method. We can accomplish this effect by | ||
4256 | 581 | using a data directive as a Tag's data special: | ||
4257 | 582 | </p> | ||
4258 | 583 | |||
4259 | 584 | <pre class="python"> | ||
4260 | 585 | class Base(rend.Page): | ||
4261 | 586 | docFactory = loaders.stan(tags.html[ | ||
4262 | 587 | tags.div(data=tags.directive('name'), render=rend.data)]) | ||
4263 | 588 | |||
4264 | 589 | class Subclass(Base): | ||
4265 | 590 | def data_name(self, ctx, data): | ||
4266 | 591 | return "Your name" | ||
4267 | 592 | </pre> | ||
4268 | 593 | |||
4269 | 594 | <p> | ||
4270 | 595 | The data directive is resolved by searching for the | ||
4271 | 596 | <code>IContainer</code> implementation in the context. | ||
4272 | 597 | <code>rend.Page</code> implements <code>IContainer.get</code> by | ||
4273 | 598 | performing an attribute lookup on the Page with the prefix 'data_*'. You | ||
4274 | 599 | can provide your own <code>IContainer</code> implementation if you wish, | ||
4275 | 600 | and also you should know that <code>IContainer</code> implementations for | ||
4276 | 601 | list and dict are included in the <code class="API">nevow.accessors</code> | ||
4277 | 602 | module. | ||
4278 | 603 | </p> | ||
4279 | 604 | |||
4280 | 605 | <p> | ||
4281 | 606 | A common gotcha is that the closest <code>IContainer</code> is used to | ||
4282 | 607 | resolve data directives. This means that if a list is being used as the | ||
4283 | 608 | data during the rendering process, data directives below this will be | ||
4284 | 609 | resolved against the <code>IContainer</code> implementation in | ||
4285 | 610 | <code>nevow.accessors.ListAccessor</code>. If you are expecting a data | ||
4286 | 611 | directive to invoke a Page's data_* method but instead get a | ||
4287 | 612 | <code>KeyError</code>, this is why. | ||
4288 | 613 | </p> | ||
4289 | 614 | |||
4290 | 615 | <h2>Render directives</h2> | ||
4291 | 616 | |||
4292 | 617 | <p> | ||
4293 | 618 | Render directives are almost exactly the same, except they are resolved | ||
4294 | 619 | using the closest <code>IRendererFactory</code> implementation in the | ||
4295 | 620 | context. Render directives can be used to allow subclasses to override | ||
4296 | 621 | certain render methods, and also can be used to allow Fragments to | ||
4297 | 622 | locate their own prefixed render methods. | ||
4298 | 623 | </p> | ||
4299 | 624 | |||
4300 | 625 | <h2>Flatteners</h2> | ||
4301 | 626 | |||
4302 | 627 | <p> | ||
4303 | 628 | TODO This section isn't done yet. | ||
4304 | 629 | </p> | ||
4305 | 630 | |||
4306 | 631 | <p> | ||
4307 | 632 | Nevow's flatteners use a type/function registry to determine how to | ||
4308 | 633 | render objects which Nevow encounters in the DOM during the rendering | ||
4309 | 634 | process. "Explicit is better than implicit", so in most cases, | ||
4310 | 635 | explicitly applying render methods to data will be better than | ||
4311 | 636 | registering a flattener, but in some cases it can be useful: | ||
4312 | 637 | </p> | ||
4313 | 638 | |||
4314 | 639 | <pre class="python"> | ||
4315 | 640 | class Person(object): | ||
4316 | 641 | def __init__(self, firstName, lastName): | ||
4317 | 642 | self.firstName = firstName | ||
4318 | 643 | self.lastName = lastName | ||
4319 | 644 | |||
4320 | 645 | def flattenPerson(person, ctx): | ||
4321 | 646 | return flat.partialflatten(ctx, (person.firstName, " ", person.lastName)) | ||
4322 | 647 | |||
4323 | 648 | from nevow import flat | ||
4324 | 649 | flat.registerFlattener(flattenPerson, Person) | ||
4325 | 650 | |||
4326 | 651 | def insertData(ctx, data): | ||
4327 | 652 | return data | ||
4328 | 653 | |||
4329 | 654 | class PersonPage(rend.Page): | ||
4330 | 655 | docFactory = loaders.stan(tags.html[insertData]) | ||
4331 | 656 | </pre> | ||
4332 | 657 | </body> | ||
4333 | 658 | </html> | ||
4334 | 659 | 0 | ||
4335 | === removed file 'Nevow/doc/howto/stylesheet.css' | |||
4336 | --- Nevow/doc/howto/stylesheet.css 2008-09-20 01:06:47 +0000 | |||
4337 | +++ Nevow/doc/howto/stylesheet.css 1970-01-01 00:00:00 +0000 | |||
4338 | @@ -1,129 +0,0 @@ | |||
4339 | 1 | body | ||
4340 | 2 | { | ||
4341 | 3 | margin-left: 2em; | ||
4342 | 4 | margin-right: 2em; | ||
4343 | 5 | border: 0px; | ||
4344 | 6 | padding: 0px; | ||
4345 | 7 | font-family: sans-serif; | ||
4346 | 8 | } | ||
4347 | 9 | |||
4348 | 10 | pre | ||
4349 | 11 | { | ||
4350 | 12 | padding: 1em; | ||
4351 | 13 | font-family: Monospace, Neep Alt, Courier New, Courier; | ||
4352 | 14 | font-size: 12pt; | ||
4353 | 15 | border: thin black solid; | ||
4354 | 16 | } | ||
4355 | 17 | |||
4356 | 18 | .python | ||
4357 | 19 | { | ||
4358 | 20 | background-color: #dddddd; | ||
4359 | 21 | } | ||
4360 | 22 | |||
4361 | 23 | .py-listing, .html-listing, .listing | ||
4362 | 24 | { | ||
4363 | 25 | margin: 1ex; | ||
4364 | 26 | border: thin solid black; | ||
4365 | 27 | background-color: #eee; | ||
4366 | 28 | } | ||
4367 | 29 | |||
4368 | 30 | .py-listing pre, .html-listing pre, .listing pre | ||
4369 | 31 | { | ||
4370 | 32 | margin: 0px; | ||
4371 | 33 | border: none; | ||
4372 | 34 | border-bottom: thin solid black; | ||
4373 | 35 | } | ||
4374 | 36 | |||
4375 | 37 | .py-listing .python | ||
4376 | 38 | { | ||
4377 | 39 | margin-top: 0; | ||
4378 | 40 | margin-bottom: 0; | ||
4379 | 41 | border: none; | ||
4380 | 42 | border-bottom: thin solid black; | ||
4381 | 43 | } | ||
4382 | 44 | |||
4383 | 45 | .py-src-comment | ||
4384 | 46 | { | ||
4385 | 47 | color: #1111CC | ||
4386 | 48 | } | ||
4387 | 49 | |||
4388 | 50 | .py-src-keyword | ||
4389 | 51 | { | ||
4390 | 52 | color: #3333CC; | ||
4391 | 53 | font-weight: bold; | ||
4392 | 54 | } | ||
4393 | 55 | |||
4394 | 56 | .py-src-parameter | ||
4395 | 57 | { | ||
4396 | 58 | color: #000066; | ||
4397 | 59 | font-weight: bold; | ||
4398 | 60 | } | ||
4399 | 61 | |||
4400 | 62 | .py-src-identifier | ||
4401 | 63 | { | ||
4402 | 64 | color: #CC0000 | ||
4403 | 65 | } | ||
4404 | 66 | |||
4405 | 67 | .py-src-string | ||
4406 | 68 | { | ||
4407 | 69 | color: #115511 | ||
4408 | 70 | } | ||
4409 | 71 | |||
4410 | 72 | .py-src-endmarker | ||
4411 | 73 | { | ||
4412 | 74 | display: block; /* IE hack; prevents following line from being sucked into the py-listing box. */ | ||
4413 | 75 | } | ||
4414 | 76 | |||
4415 | 77 | hr | ||
4416 | 78 | { | ||
4417 | 79 | display: inline; | ||
4418 | 80 | } | ||
4419 | 81 | |||
4420 | 82 | ul | ||
4421 | 83 | { | ||
4422 | 84 | padding: 0px; | ||
4423 | 85 | margin: 0px; | ||
4424 | 86 | margin-left: 1em; | ||
4425 | 87 | padding-left: 1em; | ||
4426 | 88 | border-left: 1em; | ||
4427 | 89 | } | ||
4428 | 90 | |||
4429 | 91 | li | ||
4430 | 92 | { | ||
4431 | 93 | padding: 2px; | ||
4432 | 94 | } | ||
4433 | 95 | |||
4434 | 96 | dt | ||
4435 | 97 | { | ||
4436 | 98 | font-weight: bold; | ||
4437 | 99 | margin-left: 1ex; | ||
4438 | 100 | } | ||
4439 | 101 | |||
4440 | 102 | dd | ||
4441 | 103 | { | ||
4442 | 104 | margin-bottom: 1em; | ||
4443 | 105 | } | ||
4444 | 106 | |||
4445 | 107 | div.note | ||
4446 | 108 | { | ||
4447 | 109 | background-color: #FFFFCC; | ||
4448 | 110 | margin-top: 1ex; | ||
4449 | 111 | margin-left: 5%; | ||
4450 | 112 | margin-right: 5%; | ||
4451 | 113 | padding-top: 1ex; | ||
4452 | 114 | padding-left: 5%; | ||
4453 | 115 | padding-right: 5%; | ||
4454 | 116 | border: thin black solid; | ||
4455 | 117 | } | ||
4456 | 118 | |||
4457 | 119 | .caption | ||
4458 | 120 | { | ||
4459 | 121 | text-align: center; | ||
4460 | 122 | padding-top: 0.5em; | ||
4461 | 123 | padding-bottom: 0.5em; | ||
4462 | 124 | } | ||
4463 | 125 | |||
4464 | 126 | .filename | ||
4465 | 127 | { | ||
4466 | 128 | font-style: italic; | ||
4467 | 129 | } | ||
4468 | 130 | 0 | ||
4469 | === removed file 'Nevow/doc/howto/template.tpl' | |||
4470 | --- Nevow/doc/howto/template.tpl 2008-08-26 13:45:59 +0000 | |||
4471 | +++ Nevow/doc/howto/template.tpl 1970-01-01 00:00:00 +0000 | |||
4472 | @@ -1,24 +0,0 @@ | |||
4473 | 1 | <?xml version="1.0"?> | ||
4474 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
4475 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
4476 | 4 | |||
4477 | 5 | <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> | ||
4478 | 6 | <head> | ||
4479 | 7 | <title> | ||
4480 | 8 | Nevow: | ||
4481 | 9 | </title> | ||
4482 | 10 | <link type="text/css" rel="stylesheet" href="stylesheet.css" /> | ||
4483 | 11 | </head> | ||
4484 | 12 | |||
4485 | 13 | <body bgcolor="white"> | ||
4486 | 14 | <h1 class="title"></h1> | ||
4487 | 15 | <div class="toc"></div> | ||
4488 | 16 | <div class="body"> | ||
4489 | 17 | |||
4490 | 18 | </div> | ||
4491 | 19 | |||
4492 | 20 | <p><a href="index.html">Index</a></p> | ||
4493 | 21 | <span class="version">Version: </span> | ||
4494 | 22 | </body> | ||
4495 | 23 | </html> | ||
4496 | 24 | |||
4497 | 25 | 0 | ||
4498 | === removed file 'Nevow/doc/howto/traversal.xhtml' | |||
4499 | --- Nevow/doc/howto/traversal.xhtml 2008-09-26 16:30:04 +0000 | |||
4500 | +++ Nevow/doc/howto/traversal.xhtml 1970-01-01 00:00:00 +0000 | |||
4501 | @@ -1,448 +0,0 @@ | |||
4502 | 1 | <?xml version="1.0"?> | ||
4503 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
4504 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
4505 | 4 | |||
4506 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
4507 | 6 | <head> | ||
4508 | 7 | <title> | ||
4509 | 8 | Object Traversal | ||
4510 | 9 | </title> | ||
4511 | 10 | </head> | ||
4512 | 11 | <body> | ||
4513 | 12 | <h1>Object Traversal</h1> | ||
4514 | 13 | |||
4515 | 14 | <p> | ||
4516 | 15 | <strong>Object traversal</strong> is the process Nevow uses to determine | ||
4517 | 16 | what object to use to render HTML for a particular URL. When an HTTP | ||
4518 | 17 | request comes in to the web server, the object publisher splits the URL | ||
4519 | 18 | into segments, and repeatedly calls methods which consume path segments | ||
4520 | 19 | and return objects which represent that path, until all segments have | ||
4521 | 20 | been consumed. At the core, the Nevow traversal API is very | ||
4522 | 21 | simple. However, it provides some higher level functionality layered on | ||
4523 | 22 | top of this to satisfy common use cases. | ||
4524 | 23 | </p> | ||
4525 | 24 | |||
4526 | 25 | <h2>Object Traversal Basics</h2> | ||
4527 | 26 | |||
4528 | 27 | <p> | ||
4529 | 28 | The <strong>root resource</strong> is the top-level object in the URL | ||
4530 | 29 | space; it conceptually represents the URI <code>/</code>. The Nevow | ||
4531 | 30 | <strong>object traversal</strong> and <strong>object publishing</strong> | ||
4532 | 31 | machinery uses only two methods to locate an object suitable for | ||
4533 | 32 | publishing and to generate the HTML from it; these methods are described | ||
4534 | 33 | in the interface <code class="API">nevow.inevow.IResource</code>: | ||
4535 | 34 | </p> | ||
4536 | 35 | |||
4537 | 36 | <pre class="python"> | ||
4538 | 37 | class IResource(Interface): | ||
4539 | 38 | def locateChild(self, ctx, segments): | ||
4540 | 39 | """Locate another object which can be adapted to IResource | ||
4541 | 40 | Return a tuple of resource, path segments | ||
4542 | 41 | """ | ||
4543 | 42 | |||
4544 | 43 | def renderHTTP(self, ctx): | ||
4545 | 44 | """Render a request | ||
4546 | 45 | """ | ||
4547 | 46 | </pre> | ||
4548 | 47 | |||
4549 | 48 | <p> | ||
4550 | 49 | <code class="API" base="nevow.inevow.IResource">renderHTTP</code> can be | ||
4551 | 50 | as simple as a method which simply returns a string of HTML. Let's | ||
4552 | 51 | examine what happens when object traversal occurs over a very simple root | ||
4553 | 52 | resource: | ||
4554 | 53 | </p> | ||
4555 | 54 | |||
4556 | 55 | <pre class="python"> | ||
4557 | 56 | from zope.interface import implements | ||
4558 | 57 | |||
4559 | 58 | class SimpleRoot(object): | ||
4560 | 59 | implements(inevow.IResource) | ||
4561 | 60 | |||
4562 | 61 | def locateChild(self, ctx, segments): | ||
4563 | 62 | return self, () | ||
4564 | 63 | |||
4565 | 64 | def renderHTTP(self, ctx): | ||
4566 | 65 | return "Hello, world!" | ||
4567 | 66 | </pre> | ||
4568 | 67 | |||
4569 | 68 | <p> | ||
4570 | 69 | This resource, when passed as the root resource to <code class="API" | ||
4571 | 70 | base="nevow">appserver.NevowSite</code> or <code class="API" | ||
4572 | 71 | base="nevow">wsgi.createWSGIApplication</code>, will immediately return | ||
4573 | 72 | itself, consuming all path segments. This means that for every URI a user | ||
4574 | 73 | visits on a web server which is serving this root resource, the text | ||
4575 | 74 | <code>"Hello, world!"</code> will be rendered. Let's examine the value of | ||
4576 | 75 | <code>segments</code> for various values of URI: | ||
4577 | 76 | </p> | ||
4578 | 77 | |||
4579 | 78 | <ul> | ||
4580 | 79 | <li><code>/</code> - <code>('',)</code></li> | ||
4581 | 80 | <li><code>/foo/bar</code> - <code>('foo', 'bar')</code></li> | ||
4582 | 81 | <li> | ||
4583 | 82 | <code>/foo/bar/baz.html</code> - | ||
4584 | 83 | <code>('foo', 'bar', 'baz.html')</code> | ||
4585 | 84 | </li> | ||
4586 | 85 | <li> | ||
4587 | 86 | <code>/foo/bar/directory/</code> - | ||
4588 | 87 | <code>('foo', 'bar', 'directory', '')</code> | ||
4589 | 88 | </li> | ||
4590 | 89 | </ul> | ||
4591 | 90 | |||
4592 | 91 | <p> | ||
4593 | 92 | So we see that Nevow does nothing more than split the URI on the string | ||
4594 | 93 | <code>/</code> and pass these path segments to our application for | ||
4595 | 94 | consumption. Armed with these two methods alone, we already have enough | ||
4596 | 95 | information to write applications which service any form of URL | ||
4597 | 96 | imaginable in any way we wish. However, there are some common URL | ||
4598 | 97 | handling patterns which Nevow provides higher level support for. | ||
4599 | 98 | </p> | ||
4600 | 99 | |||
4601 | 100 | <h2><code>locateChild</code> In Depth</h2> | ||
4602 | 101 | |||
4603 | 102 | <p> | ||
4604 | 103 | One common URL handling pattern involves parents which only know about | ||
4605 | 104 | their direct children. For example, a ``Directory`` object may only know | ||
4606 | 105 | about the contents of a single directory, but if it contains other | ||
4607 | 106 | directories, it does not know about the contents of them. Let's examine a | ||
4608 | 107 | simple ``Directory`` object which can provide directory listings and | ||
4609 | 108 | serves up objects for child directories and files: | ||
4610 | 109 | </p> | ||
4611 | 110 | |||
4612 | 111 | <pre class="python"> | ||
4613 | 112 | from zope.interface import implements | ||
4614 | 113 | |||
4615 | 114 | class Directory(object): | ||
4616 | 115 | implements(inevow.IResource) | ||
4617 | 116 | |||
4618 | 117 | def __init__(self, directory): | ||
4619 | 118 | self.directory = directory | ||
4620 | 119 | |||
4621 | 120 | def renderHTTP(self, ctx): | ||
4622 | 121 | html = ['<ul>'] | ||
4623 | 122 | for child in os.listdir(self.directory): | ||
4624 | 123 | fullpath = os.path.join(self.directory, child) | ||
4625 | 124 | if os.path.isdir(fullpath): | ||
4626 | 125 | child += '/' | ||
4627 | 126 | html.extend(['<li><a href="', child, '">', child, '</a></li>']) | ||
4628 | 127 | html.append('</ul>') | ||
4629 | 128 | return ''.join(html) | ||
4630 | 129 | |||
4631 | 130 | def locateChild(self, ctx, segments): | ||
4632 | 131 | name = segments[0] | ||
4633 | 132 | fullpath = os.path.join(self.directory, name) | ||
4634 | 133 | if not os.path.exists(fullpath): | ||
4635 | 134 | return None, () # 404 | ||
4636 | 135 | |||
4637 | 136 | if os.path.isdir(fullpath): | ||
4638 | 137 | return Directory(fullpath), segments[1:] | ||
4639 | 138 | if os.path.isfile(fullpath): | ||
4640 | 139 | return static.File(fullpath), segments[1:] | ||
4641 | 140 | </pre> | ||
4642 | 141 | |||
4643 | 142 | <p> | ||
4644 | 143 | Because this implementation of <code>locateChild</code> only consumed one | ||
4645 | 144 | segment and returned the rest of them (<code>segments[1:]</code>), the | ||
4646 | 145 | object traversal process will continue by calling | ||
4647 | 146 | <code>locateChild</code> on the returned resource and passing the | ||
4648 | 147 | partially-consumed segments. In this way, a directory structure of any | ||
4649 | 148 | depth can be traversed, and directory listings or file contents can be | ||
4650 | 149 | rendered for any existing directories and files. | ||
4651 | 150 | </p> | ||
4652 | 151 | |||
4653 | 152 | <p> | ||
4654 | 153 | So, let us examine what happens when the URI | ||
4655 | 154 | <code>"/foo/bar/baz.html"</code> is traversed, where <code>"foo"</code> | ||
4656 | 155 | and <code>"bar"</code> are directories, and <code>"baz.html"</code> is a | ||
4657 | 156 | file. | ||
4658 | 157 | </p> | ||
4659 | 158 | |||
4660 | 159 | <ol> | ||
4661 | 160 | <li> | ||
4662 | 161 | <code> | ||
4663 | 162 | Directory('/').locateChild(ctx, ('foo', 'bar', 'baz.html')) | ||
4664 | 163 | </code> | ||
4665 | 164 | returns | ||
4666 | 165 | <code>Directory('/foo'), ('bar', 'baz.html')</code> | ||
4667 | 166 | </li> | ||
4668 | 167 | <li> | ||
4669 | 168 | <code> | ||
4670 | 169 | Directory('/foo').locateChild(ctx, ('bar', 'baz.html')) | ||
4671 | 170 | </code> | ||
4672 | 171 | returns | ||
4673 | 172 | <code>Directory('/foo/bar'), ('baz.html, )</code> | ||
4674 | 173 | </li> | ||
4675 | 174 | <li> | ||
4676 | 175 | <code> | ||
4677 | 176 | Directory('/foo/bar').locateChild(ctx, ('baz.html')) | ||
4678 | 177 | </code> | ||
4679 | 178 | returns | ||
4680 | 179 | <code>File('/foo/bar/baz.html'), ()</code> | ||
4681 | 180 | </li> | ||
4682 | 181 | <li> | ||
4683 | 182 | No more segments to be consumed; | ||
4684 | 183 | <code>File('/foo/bar/baz.html').renderHTTP(ctx)</code> is called, and | ||
4685 | 184 | the result is sent to the browser. | ||
4686 | 185 | </li> | ||
4687 | 186 | </ol> | ||
4688 | 187 | |||
4689 | 188 | |||
4690 | 189 | <h2><code>childFactory</code> Method</h2> | ||
4691 | 190 | |||
4692 | 191 | <p> | ||
4693 | 192 | Consuming one URI segment at a time by checking to see if a requested | ||
4694 | 193 | resource exists and returning a new object is a very common | ||
4695 | 194 | pattern. Nevow's default implementation of <code class="API" | ||
4696 | 195 | base="nevow.inevow">IResource</code>, <code | ||
4697 | 196 | class="API">nevow.rend.Page</code>, contains an implementation of | ||
4698 | 197 | <code>locateChild</code> which provides more convenient hooks for | ||
4699 | 198 | implementing object traversal. One of these hooks is | ||
4700 | 199 | <code>childFactory</code>. Let us imagine for the sake of example that we | ||
4701 | 200 | wished to render a tree of dictionaries. Our data structure might look | ||
4702 | 201 | something like this: | ||
4703 | 202 | </p> | ||
4704 | 203 | |||
4705 | 204 | <pre class="python"> | ||
4706 | 205 | tree = dict( | ||
4707 | 206 | one=dict( | ||
4708 | 207 | foo=None, | ||
4709 | 208 | bar=None), | ||
4710 | 209 | two=dict( | ||
4711 | 210 | baz=dict( | ||
4712 | 211 | quux=None))) | ||
4713 | 212 | </pre> | ||
4714 | 213 | |||
4715 | 214 | <p> | ||
4716 | 215 | Given this data structure, the valid URIs would be: | ||
4717 | 216 | </p> | ||
4718 | 217 | |||
4719 | 218 | <ul> | ||
4720 | 219 | <li>/</li> | ||
4721 | 220 | <li>/one</li> | ||
4722 | 221 | <li>/one/foo</li> | ||
4723 | 222 | <li>/one/bar</li> | ||
4724 | 223 | <li>/two</li> | ||
4725 | 224 | <li>/two/baz</li> | ||
4726 | 225 | <li>/two/baz/quux</li> | ||
4727 | 226 | </ul> | ||
4728 | 227 | |||
4729 | 228 | <p> | ||
4730 | 229 | Let us construct a <code class="API" base="nevow">rend.Page</code> | ||
4731 | 230 | subclass which uses the default <code>locateChild</code> implementation | ||
4732 | 231 | and overrides the <code>childFactory</code> hook instead: | ||
4733 | 232 | </p> | ||
4734 | 233 | |||
4735 | 234 | <pre class="python"> | ||
4736 | 235 | class DictTree(rend.Page): | ||
4737 | 236 | def __init__(self, dataDict): | ||
4738 | 237 | self.dataDict = dataDict | ||
4739 | 238 | |||
4740 | 239 | def renderHTTP(self, ctx): | ||
4741 | 240 | if self.dataDict is None: | ||
4742 | 241 | return "Leaf" | ||
4743 | 242 | html = ['<ul>'] | ||
4744 | 243 | for key in self.dataDict.keys(): | ||
4745 | 244 | html.extend(['<li><a href="', key, '">', key, '</a></li>']) | ||
4746 | 245 | html.append('</ul>') | ||
4747 | 246 | return ''.join(html) | ||
4748 | 247 | |||
4749 | 248 | def childFactory(self, ctx, name): | ||
4750 | 249 | if name not in self.dataDict: | ||
4751 | 250 | return rend.NotFound # 404 | ||
4752 | 251 | return DictTree(self.dataDict[name]) | ||
4753 | 252 | </pre> | ||
4754 | 253 | |||
4755 | 254 | <p> | ||
4756 | 255 | As you can see, the <code>childFactory</code> implementation is | ||
4757 | 256 | considerably shorter than the equivalent <code>locateChild</code> | ||
4758 | 257 | implementation would have been. | ||
4759 | 258 | </p> | ||
4760 | 259 | |||
4761 | 260 | <h2><code>child_*</code> methods and attributes</h2> | ||
4762 | 261 | |||
4763 | 262 | <p> | ||
4764 | 263 | Often we may wish to have some hardcoded URLs which are not dynamically | ||
4765 | 264 | generated based on some data structure. For example, we might have an | ||
4766 | 265 | application which uses an external CSS stylesheet, an external JavaScript | ||
4767 | 266 | file, and a folder full of images. The <code class="API" | ||
4768 | 267 | base="nevow">rend.Page.locateChild</code> implementation provides a | ||
4769 | 268 | convenient way for us to express these relationships by using | ||
4770 | 269 | child-prefixed methods: | ||
4771 | 270 | </p> | ||
4772 | 271 | |||
4773 | 272 | <pre class="python"> | ||
4774 | 273 | class Linker(rend.Page): | ||
4775 | 274 | def renderHTTP(self, ctx): | ||
4776 | 275 | return """<html> | ||
4777 | 276 | <head> | ||
4778 | 277 | <link href="css" rel="stylesheet" /> | ||
4779 | 278 | <script type="text/javascript" src="scripts" /> | ||
4780 | 279 | <body> | ||
4781 | 280 | <img src="images/logo.png" /> | ||
4782 | 281 | </body> | ||
4783 | 282 | </html>""" | ||
4784 | 283 | |||
4785 | 284 | def child_css(self, ctx): | ||
4786 | 285 | return static.File('styles.css') | ||
4787 | 286 | |||
4788 | 287 | def child_scripts(self, ctx): | ||
4789 | 288 | return static.File('scripts.js') | ||
4790 | 289 | |||
4791 | 290 | def child_images(self, ctx): | ||
4792 | 291 | return static.File('images/') | ||
4793 | 292 | </pre> | ||
4794 | 293 | |||
4795 | 294 | <p> | ||
4796 | 295 | One thing you may have noticed is that all of the examples so far have | ||
4797 | 296 | returned new object instances whenever they were implementing a traversal | ||
4798 | 297 | API. However, there is no reason these instances cannot be shared. One | ||
4799 | 298 | could for example return a global resource instance, an instance which | ||
4800 | 299 | was previously inserted in a dict, or lazily create and cache dynamic | ||
4801 | 300 | resource instances on the fly. The <code>rend.Page.locateChild</code> | ||
4802 | 301 | implementation also provides a convenient way to express that one global | ||
4803 | 302 | resource instance should always be used for a particular URL, the | ||
4804 | 303 | child-prefixed attribute: | ||
4805 | 304 | </p> | ||
4806 | 305 | |||
4807 | 306 | <pre class="python"> | ||
4808 | 307 | class FasterLinker(Linker): | ||
4809 | 308 | child_css = static.File('styles.css') | ||
4810 | 309 | child_scripts = static.File('scripts.js') | ||
4811 | 310 | child_images = static.File('images/') | ||
4812 | 311 | </pre> | ||
4813 | 312 | |||
4814 | 313 | <h2>Dots in child names</h2> | ||
4815 | 314 | |||
4816 | 315 | <p> | ||
4817 | 316 | When a URL contains dots, which is quite common in normal URLs, it is | ||
4818 | 317 | simple enough to handle these URL segments in <code>locateChild</code> or | ||
4819 | 318 | <code>childFactory</code> -- one of the passed segments will simply be a | ||
4820 | 319 | string containing a dot. However, it is not immediately obvious how one | ||
4821 | 320 | would express a URL segment with a dot in it when using child-prefixed | ||
4822 | 321 | methods. The solution is really quite simple: | ||
4823 | 322 | </p> | ||
4824 | 323 | |||
4825 | 324 | <pre class="python"> | ||
4826 | 325 | class DotChildren(rend.Page): | ||
4827 | 326 | def renderHTTP(self, ctx): | ||
4828 | 327 | return """ | ||
4829 | 328 | <html> | ||
4830 | 329 | <head> | ||
4831 | 330 | <script type="text/javascript" src="scripts.js" /> | ||
4832 | 331 | </head> | ||
4833 | 332 | </html>""" | ||
4834 | 333 | |||
4835 | 334 | setattr(DotChildren, 'child_scripts.js', static.File('scripts.js')) | ||
4836 | 335 | </pre> | ||
4837 | 336 | |||
4838 | 337 | <p> | ||
4839 | 338 | The same technique could be used to install a child method with a dot in | ||
4840 | 339 | the name. | ||
4841 | 340 | </p> | ||
4842 | 341 | |||
4843 | 342 | |||
4844 | 343 | <h2>children dictionary</h2> | ||
4845 | 344 | |||
4846 | 345 | <p> | ||
4847 | 346 | The final hook supported by the default implementation of | ||
4848 | 347 | <code>locateChild</code> is the <code>rend.Page.children</code> | ||
4849 | 348 | dictionary: | ||
4850 | 349 | </p> | ||
4851 | 350 | |||
4852 | 351 | <pre class="python"> | ||
4853 | 352 | class Main(rend.Page): | ||
4854 | 353 | children = { | ||
4855 | 354 | 'people': People(), | ||
4856 | 355 | 'jobs': Jobs(), | ||
4857 | 356 | 'events': Events()} | ||
4858 | 357 | |||
4859 | 358 | def renderHTTP(self, ctx): | ||
4860 | 359 | return """ | ||
4861 | 360 | <html> | ||
4862 | 361 | <head> | ||
4863 | 362 | <title>Our Site</title> | ||
4864 | 363 | </head> | ||
4865 | 364 | <body> | ||
4866 | 365 | <p>bla bla bla</p> | ||
4867 | 366 | </body> | ||
4868 | 367 | </html>""" | ||
4869 | 368 | </pre> | ||
4870 | 369 | |||
4871 | 370 | <p> | ||
4872 | 371 | Hooks are checked in the following order: | ||
4873 | 372 | </p> | ||
4874 | 373 | |||
4875 | 374 | <ol> | ||
4876 | 375 | <li><code>self.children</code></li> | ||
4877 | 376 | <li><code>self.child_*</code></li> | ||
4878 | 377 | <li><code>self.childFactory</code></li> | ||
4879 | 378 | </ol> | ||
4880 | 379 | |||
4881 | 380 | <h2>The default trailing slash handler</h2> | ||
4882 | 381 | |||
4883 | 382 | <p> | ||
4884 | 383 | When a URI which is being handled ends in a slash, such as when the | ||
4885 | 384 | <code>/</code> URI is being rendered or when a directory-like URI is | ||
4886 | 385 | being rendered, the string <code>''</code> appears in the path segments | ||
4887 | 386 | which will be traversed. Again, handling this case is trivial inside | ||
4888 | 387 | either <code>locateChild</code> or <code>childFactory</code>, but it may | ||
4889 | 388 | not be immediately obvious what child-prefixed method or attribute will | ||
4890 | 389 | be looked up. The method or attribute name which will be used is simply | ||
4891 | 390 | <code>child</code> with a single trailing underscore. | ||
4892 | 391 | </p> | ||
4893 | 392 | |||
4894 | 393 | <p> | ||
4895 | 394 | The <code>rend.Page</code> class provides an implementation of this | ||
4896 | 395 | method which can work in two different ways. If the attribute | ||
4897 | 396 | <code>addSlash</code> is <code>True</code>, the default trailing slash | ||
4898 | 397 | handler will return <code>self</code>. In the case when | ||
4899 | 398 | <code>addSlash</code> is <code>True</code>, the default | ||
4900 | 399 | <code>rend.Page.renderHTTP</code> implementation will simply perform a | ||
4901 | 400 | redirect which adds the missing slash to the URL. | ||
4902 | 401 | </p> | ||
4903 | 402 | |||
4904 | 403 | <p> | ||
4905 | 404 | The default trailing slash handler also returns self if | ||
4906 | 405 | <code>addSlash</code> is <code>False</code>, but emits a warning as it | ||
4907 | 406 | does so. This warning may become an exception at some point in the | ||
4908 | 407 | future. | ||
4909 | 408 | </p> | ||
4910 | 409 | |||
4911 | 410 | <h2><code>ICurrentSegments</code> and <code>IRemainingSegments</code></h2> | ||
4912 | 411 | |||
4913 | 412 | <p> | ||
4914 | 413 | During the object traversal process, it may be useful to discover which | ||
4915 | 414 | segments have already been handled and which segments are remaining to be | ||
4916 | 415 | handled. This information may be obtained from the <code>context</code> | ||
4917 | 416 | object which is passed to all the traversal APIs. The interfaces <code | ||
4918 | 417 | class="API">nevow.inevow.ICurrentSegments</code> and <code | ||
4919 | 418 | class="API">nevow.inevow.IRemainingSegments</code> are used to retrieve | ||
4920 | 419 | this information. To retrieve a tuple of segments which have previously | ||
4921 | 420 | been consumed during object traversal, use this syntax: | ||
4922 | 421 | </p> | ||
4923 | 422 | |||
4924 | 423 | <pre class="python"> | ||
4925 | 424 | segs = ICurrentSegments(ctx) | ||
4926 | 425 | </pre> | ||
4927 | 426 | |||
4928 | 427 | <p> | ||
4929 | 428 | The same is true of <code>IRemainingSegments</code>. | ||
4930 | 429 | <code>IRemainingSegments</code> is the same value which is passed as | ||
4931 | 430 | <code>segments</code> to <code>locateChild</code>, but may also be useful | ||
4932 | 431 | in the implementations of <code>childFactory</code> or a child-prefixed | ||
4933 | 432 | method, where this information would not otherwise be available. | ||
4934 | 433 | </p> | ||
4935 | 434 | |||
4936 | 435 | <h2>Conclusion</h2> | ||
4937 | 436 | |||
4938 | 437 | <p> | ||
4939 | 438 | Nevow makes it easy to handle complex URL hierarchies. The most basic | ||
4940 | 439 | object traversal interface, <code | ||
4941 | 440 | class="API">nevow.inevow.IResource.locateChild</code>, provides powerful | ||
4942 | 441 | and flexible control over the entire object traversal process. Nevow's | ||
4943 | 442 | canonical <code>IResource</code> implementation, <code>rend.Page</code>, | ||
4944 | 443 | also includes the convenience hooks <code>childFactory</code> along with | ||
4945 | 444 | child-prefixed method and attribute semantics to simplify common use | ||
4946 | 445 | cases. | ||
4947 | 446 | </p> | ||
4948 | 447 | </body> | ||
4949 | 448 | </html> | ||
4950 | 449 | 0 | ||
4951 | === removed file 'Nevow/doc/howto/xmltemplates.xhtml' | |||
4952 | --- Nevow/doc/howto/xmltemplates.xhtml 2008-08-26 13:45:59 +0000 | |||
4953 | +++ Nevow/doc/howto/xmltemplates.xhtml 1970-01-01 00:00:00 +0000 | |||
4954 | @@ -1,407 +0,0 @@ | |||
4955 | 1 | <?xml version="1.0"?> | ||
4956 | 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | ||
4957 | 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
4958 | 4 | |||
4959 | 5 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
4960 | 6 | <head> | ||
4961 | 7 | <title> | ||
4962 | 8 | XML Templates | ||
4963 | 9 | </title> | ||
4964 | 10 | </head> | ||
4965 | 11 | <body> | ||
4966 | 12 | <h1>Nevow XML Templates</h1> | ||
4967 | 13 | |||
4968 | 14 | <p> | ||
4969 | 15 | Stan syntax is cool, but eventually you are going to want to integrate | ||
4970 | 16 | your Python code with a template designed by an HTML monkey. Nevow | ||
4971 | 17 | accomplishes this by providing an xmlfile loader which uses the built-in | ||
4972 | 18 | Python SAX libraries to generate a tree of stan behind the scenes. The | ||
4973 | 19 | general rule is anything that is possible in stan should be possible in a | ||
4974 | 20 | pure XML template; of course, the XML syntax is generally going to be | ||
4975 | 21 | much more verbose. | ||
4976 | 22 | </p> | ||
4977 | 23 | |||
4978 | 24 | <h2>loaders.xmlfile</h2> | ||
4979 | 25 | |||
4980 | 26 | <p> | ||
4981 | 27 | Wherever you have seen a loaders.stan being created in any of the example | ||
4982 | 28 | code, a <code class="API" base="nevow">loaders.xmlfile</code> can be | ||
4983 | 29 | substituted instead. At the most basic, <code>xmlfile</code> merely | ||
4984 | 30 | requires the name of an xml template: | ||
4985 | 31 | </p> | ||
4986 | 32 | |||
4987 | 33 | <pre class="python"> | ||
4988 | 34 | class HelloXML(rend.Page): | ||
4989 | 35 | docFactory = loaders.xmlfile('hello.xml') | ||
4990 | 36 | </pre> | ||
4991 | 37 | |||
4992 | 38 | <p> | ||
4993 | 39 | Placing the following xml in the <code>hello.xml</code> file will cause | ||
4994 | 40 | <code>HelloXML</code> to display a static page when it is rendered: | ||
4995 | 41 | </p> | ||
4996 | 42 | |||
4997 | 43 | <pre><html>Hello, world!</html></pre> | ||
4998 | 44 | |||
4999 | 45 | <p> | ||
5000 | 46 | The following additional keyword arguments may be given to |
The diff has been truncated for viewing.