ramaze 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Rakefile +42 -0
  2. data/doc/allison/LICENSE +184 -0
  3. data/doc/allison/README +37 -0
  4. data/doc/allison/allison.css +300 -0
  5. data/doc/allison/allison.gif +0 -0
  6. data/doc/allison/allison.js +307 -0
  7. data/doc/allison/allison.rb +287 -0
  8. data/doc/allison/cache/BODY +588 -0
  9. data/doc/allison/cache/CLASS_INDEX +4 -0
  10. data/doc/allison/cache/CLASS_PAGE +1 -0
  11. data/doc/allison/cache/FILE_INDEX +4 -0
  12. data/doc/allison/cache/FILE_PAGE +1 -0
  13. data/doc/allison/cache/FONTS +1 -0
  14. data/doc/allison/cache/FR_INDEX_BODY +1 -0
  15. data/doc/allison/cache/IMGPATH +1 -0
  16. data/doc/allison/cache/INDEX +1 -0
  17. data/doc/allison/cache/JAVASCRIPT +307 -0
  18. data/doc/allison/cache/METHOD_INDEX +4 -0
  19. data/doc/allison/cache/METHOD_LIST +1 -0
  20. data/doc/allison/cache/SRC_PAGE +1 -0
  21. data/doc/allison/cache/STYLE +322 -0
  22. data/doc/allison/cache/URL +1 -0
  23. data/doc/readme_chunks/principles.txt +33 -18
  24. data/doc/tutorial/todolist.html +599 -0
  25. data/doc/tutorial/todolist.txt +230 -230
  26. data/examples/identity.rb +21 -0
  27. data/examples/nitro_form.rb +22 -0
  28. data/lib/ramaze/controller.rb +1 -1
  29. data/lib/ramaze/dispatcher.rb +10 -4
  30. data/lib/ramaze/helper/{openid.rb → identity.rb} +15 -6
  31. data/lib/ramaze/helper/nitroform.rb +10 -0
  32. data/lib/ramaze/helper/stack.rb +1 -1
  33. data/lib/ramaze/inform.rb +18 -8
  34. data/lib/ramaze/snippets/kernel/aquire.rb +3 -3
  35. data/lib/ramaze/snippets/object/traits.rb +1 -1
  36. data/lib/ramaze/snippets/ramaze/caller_info.rb +17 -1
  37. data/lib/ramaze/snippets/ramaze/caller_lines.rb +1 -1
  38. data/lib/ramaze/store/yaml.rb +10 -1
  39. data/lib/ramaze/template/ezamar.rb +10 -5
  40. data/lib/ramaze/trinity/request.rb +12 -2
  41. data/lib/ramaze/version.rb +1 -1
  42. data/lib/ramaze.rb +5 -3
  43. data/spec/public/error404.xhtml +1 -0
  44. data/spec/spec_all.rb +21 -19
  45. data/spec/spec_helper.rb +1 -1
  46. data/spec/tc_error.rb +18 -4
  47. data/spec/tc_helper_cache.rb +1 -1
  48. data/spec/tc_helper_flash.rb +1 -2
  49. metadata +32 -4
@@ -0,0 +1 @@
1
+ http://blog.evanweaver.com/articles/2006/06/02/allison
@@ -1,41 +1,56 @@
1
1
  There are some basic principles that Ramaze tries to follow:
2
2
 
3
- * Test everything
3
+ * KISS (Keep It Super Simple)
4
4
 
5
- What use is a wonderful application if it doesn't work?
5
+ Ramaze doesn't introduce any major change of paradigm for everyone familiar
6
+ with Ruby and the basics of Web-development.
6
7
 
8
+ * POLS (Principle Of Least Surprise)
7
9
 
8
- * Document everything
9
-
10
- Documentation is the glue between the code and the programmers brain
10
+ Ramaze tries to be intuitive and easy to learn. Most functionality is built in
11
+ a way to help, not to obfuscate or confuse.
11
12
 
13
+ * Modular design
12
14
 
13
- * Keep It Super Simple (KISS)
15
+ Use what you want and how you want it.
14
16
 
15
- Most things should be understandable after reading them once
17
+ Through Ruby Ramaze provides one of the most powerful programming-languages
18
+ available, giving you full control over your system.
16
19
 
20
+ Even the most essential parts of Ramaze can easily be replaced and/or modified
21
+ without losing the advantage of the whole framework.
17
22
 
18
- * Principle Of Least Surprise (POLS)
23
+ * Minimal dependencies
19
24
 
20
- Going the way of ruby
25
+ Nothing besides Ruby is required for the basic features.
21
26
 
27
+ Of course you can take advantage of several wonderful libraries, but Ramaze is
28
+ built in a way to be run on any basic setup.
22
29
 
23
- * Modular design
30
+ * Documentation
24
31
 
25
- Making it as simple as possible to extract parts
32
+ Document everything, classes, modules, methods, configuration...
26
33
 
34
+ Through 100% documentation Ramaze gives the developer easy and solid
35
+ understanding of the underlying concepts and functionality.
27
36
 
28
- * Minimal dependencies
37
+ * Open development
29
38
 
30
- In case a dependency is not met use a simple fall-back instead
39
+ Everyone is welcome to contribute to Ramaze in the easiest way possible. The
40
+ repository is open for patches passing the Test-suite.
31
41
 
42
+ * Examples
32
43
 
33
- * Provide as many examples as possible
44
+ Everyone learns different, some only read the source, others browse
45
+ documentation, but everyone loves examples for a quick and painless start.
34
46
 
35
- Examples are a superior way of getting a quick start into everything
47
+ Ramaze addresses this need and offers a wide variety of examples of usage,
48
+ basic functionality, project-layout and more advanced applications.
36
49
 
50
+ * Fully BDD (Behaviour Driven Design)
37
51
 
38
- * Open development
52
+ Ramaze has a very complete set of so-called specifications built by RSpec.
53
+ These specs define the way Ramaze has to behave.
39
54
 
40
- I happily accept all patches or feature-requests that you may have,
41
- as long as they comply with these principles
55
+ The specs are checked every time a new patch is pushed into the repository,
56
+ deciding whether the changes the patch applies are valid and don't break the framework.
@@ -0,0 +1,599 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html>
4
+ <head>
5
+ <title>Ramaze Tutorial: Todolist</title>
6
+ <style>
7
+ body {
8
+ background: #eee;
9
+ }
10
+ code {
11
+ background: #ddd;
12
+ }
13
+ pre code {
14
+ background: #ddd;
15
+ width: 70%;
16
+ display: block;
17
+ margin: 1em;
18
+ padding: 0.7em;
19
+ overflow: auto;
20
+ }
21
+ </style>
22
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
23
+ </head>
24
+ <body>
25
+ <h1>To-do List Tutorial</h1>
26
+
27
+ <p>Welcome to our official tutorial, the mandatory to-do list.
28
+ I'm writing this while doing the steps to assure it will work for you.</p>
29
+
30
+ <p>The tutorial assumes that you have Ramaze installed already. The easiest way to
31
+ do that is <code>gem install ramaze</code>, for other ways of installation please see the
32
+ documentation at http://ramaze.rubyforge.org</p>
33
+
34
+ <p>Should you encounter any problems while doing this tutorial, this might either
35
+ be because Ramaze changed (which happens very often while it is still young)
36
+ or I actually made some mistake while writing it.</p>
37
+
38
+ <p>In either case it would make me (and all other poor fellows who happen to try
39
+ this tutorial) very happy if you could spare some time and report the issue
40
+ either on the Bug tracker at http://rubyforge.org/projects/ramaze or just
41
+ drop by on IRC ( irc.freenode.org channel: #ramaze ).</p>
42
+
43
+ <p>There is also a Mailing list available where you can keep yourself updated on
44
+ what is going on with little effort, it is also located on the project-page at
45
+ RubyForge.</p>
46
+
47
+ <p>Thanks in advance.
48
+ The author of the tutorial, Michael 'manveru' Fellinger</p>
49
+
50
+ <h2>First Step, Create</h2>
51
+
52
+ <p>We are using <code>ramaze --create todolist</code> to create a new application.
53
+ Ramaze will then create the directory and fill it with a skeleton of a quite
54
+ sophisticated hello-world example out of which we will create the actual
55
+ to-do list.</p>
56
+
57
+ <p>So run:</p>
58
+
59
+ <pre><code>$ ramaze --create todolist
60
+ </code></pre>
61
+
62
+ <p>done.</p>
63
+
64
+ <h2>Second Step, M, like Model</h2>
65
+
66
+ <p>Ramaze comes at the moment only with a simple wrapper of the YAML::Store.
67
+ So we are going to base this on the tools available, you can just do the same
68
+ with your ORM or database of choice.</p>
69
+
70
+ <p>So first, edit the <code>src/model.rb</code>, it is filled with the definition of a simple
71
+ YAML::Store already, so we are just gonna modify it a bit to use our wrapper.</p>
72
+
73
+ <p>Instead of 'yaml/store' use:</p>
74
+
75
+ <pre><code>require 'ramaze/store/default'
76
+ </code></pre>
77
+
78
+ <p>And further:</p>
79
+
80
+ <pre><code>TodoList = Store::Default.new 'todolist.yaml'
81
+ </code></pre>
82
+
83
+ <p>To have a base to start off of, let's add some items as well.</p>
84
+
85
+ <pre><code>{
86
+ 'Laundry' =&gt; {:done =&gt; false},
87
+ 'Wash dishes' =&gt; {:done =&gt; false},
88
+
89
+ }.each do |title, parameters|
90
+ TodoList[title] = parameters
91
+ end
92
+ </code></pre>
93
+
94
+ <h2>Third Step, V, like View</h2>
95
+
96
+ <p>Now let's get our hands dirty and just edit the templates for our to-do list.</p>
97
+
98
+ <p>Start with editing <code>template/index.xhtml</code>, it is using the default templating
99
+ of Ramaze, called Ezamar.</p>
100
+
101
+ <p>Let's put some things in there, I'll explain the syntax as we go, it's quite
102
+ simple.</p>
103
+
104
+ <pre><code>&lt;html&gt;
105
+ &lt;head&gt;
106
+ &lt;title&gt;TodoList&lt;/title&gt;
107
+ &lt;/head&gt;
108
+ &lt;body&gt;
109
+ &lt;h1&gt;TodoList&lt;/h1&gt;
110
+ &lt;ul&gt;
111
+ &lt;?r
112
+ TodoList.each do |title, parameters|
113
+ status = parameters[:done] ? 'done' : 'not done'
114
+ ?&gt;
115
+ &lt;li&gt;#{title}: #{status}&lt;/li&gt;
116
+ &lt;?r end ?&gt;
117
+ &lt;/ul&gt;
118
+ &lt;/body&gt;
119
+ &lt;/html&gt;
120
+ </code></pre>
121
+
122
+ <p>I will assume that you are familiar with basic Ruby already, so let's
123
+ concentrate on the things new here.</p>
124
+
125
+ <p><?r ?> defines an area of ruby-code. Late when the template is transformed into
126
+ pure Ruby it will be evaluated. We iterate over the TodoList model and pass the
127
+ title and parameters into a block. In that block we can just get the values
128
+ of title and status (which we define based on the parameters) displayed on the
129
+ page.</p>
130
+
131
+ <p>The whole Template would expand to something like this (only showing the
132
+ interesting part)</p>
133
+
134
+ <pre><code>&lt;ul&gt;
135
+ &lt;li&gt;Laundry: not done&lt;/li&gt;
136
+ &lt;li&gt;Wash dishes: not done&lt;/li&gt;
137
+ &lt;/ul&gt;
138
+ </code></pre>
139
+
140
+ <p>That wasn't too bad, huh?</p>
141
+
142
+ <p>Now, so we can get our instant pleasure of seeing the result of our (hard) work,
143
+ let's see how to start ramaze.</p>
144
+
145
+ <p>In the <code>todolist</code> directory run <code>ramaze</code>.</p>
146
+
147
+ <p>This will start an instance of Ramaze and run your application on it. You can
148
+ now access it by browsing to http://localhost:7000/</p>
149
+
150
+ <p>7000 is the default-port Ramaze will run on, to change it you can just run
151
+ <code>ramaze -p 7070</code> or similar. Also be sure to look at the output of
152
+ <code>ramaze --help</code> to see some other options.</p>
153
+
154
+ <h2>Fourth Step, C, like Controller</h2>
155
+
156
+ <p>The last part of the MVC-paradigm is the Controller.</p>
157
+
158
+ <p>Wouldn't it be nice to have a way to add and remove items on our to-do list?
159
+ Editing the model every time would be quite tiresome and limited.</p>
160
+
161
+ <p>Well, come along, I'll introduce you to Controller.</p>
162
+
163
+ <p>In the way MVC is structured, the Controller provides the data in a nice way
164
+ for the View, removing all of the data-preparation and most of the logic from
165
+ the templates. This makes it firstly simple to change the fronted of your
166
+ application and secondly provides excellent ways of changing the complete
167
+ Structure of the Model or View independent from each other.</p>
168
+
169
+ <p>OK, enough of the theory, you will see the benefits in an instant. Go on and
170
+ edit the file <code>src/controller/main.rb</code>.</p>
171
+
172
+ <p>The contents of it are like following:</p>
173
+
174
+ <pre><code>class MainController &lt; Controller
175
+ def index
176
+ "Hello, World"
177
+ end
178
+ end
179
+ </code></pre>
180
+
181
+ <p>The only method right now is #index, with a simple and for the moment quite
182
+ useless "Hello, World". The relationship between the methods on the controller
183
+ and the templates is 1:1, so the method #index is combined with the template
184
+ <code>index.xhtml</code>. This combination is called an <code>action</code>.</p>
185
+
186
+ <p>Let's get back to editing and change the index-method to this:</p>
187
+
188
+ <pre><code>def index
189
+ @tasks = TodoList.content
190
+ @tasks.each do |title, parameters|
191
+ status = parameters[:done] ? 'done' : 'not done'
192
+ @tasks[title] = status
193
+ end
194
+ end
195
+ </code></pre>
196
+
197
+ <p>This will take care of the logic inside the template, which now should be
198
+ changed to do following:</p>
199
+
200
+ <pre><code>&lt;html&gt;
201
+ &lt;head&gt;
202
+ &lt;title&gt;TodoList&lt;/title&gt;
203
+ &lt;/head&gt;
204
+ &lt;body&gt;
205
+ &lt;h1&gt;TodoList&lt;/h1&gt;
206
+ &lt;a href="/new"&gt;New Task&lt;/a&gt;
207
+ &lt;?r if @tasks.empty? ?&gt;
208
+ No Tasks
209
+ &lt;?r else ?&gt;
210
+ &lt;ul&gt;
211
+ &lt;?r @tasks.each do |title, status| ?&gt;
212
+ &lt;li&gt;#{title}: #{status}&lt;/li&gt;
213
+ &lt;?r end ?&gt;
214
+ &lt;/ul&gt;
215
+ &lt;?r end ?&gt;
216
+ &lt;/body&gt;
217
+ &lt;/html&gt;
218
+ </code></pre>
219
+
220
+ <p>The rest of the template can stay the same.</p>
221
+
222
+ <p>Now, if you browse to http://localhost:7000/ again you will not notice any
223
+ change, which is how it should be. The only change is that if there are no
224
+ Tasks it will say so.</p>
225
+
226
+ <p>Some things you should know:</p>
227
+
228
+ <ul>
229
+ <li>Instance-variables defined in the Controller are available in the View.</li>
230
+ <li>The return-value of the Controller does not matter (in this case).</li>
231
+ </ul>
232
+
233
+ <h2>Fifth Step, getting dynamic</h2>
234
+
235
+ <p>We set out to build the ultimate to-do list, but there are still some things
236
+ missing. First off, we want to add new tasks, so let's get that done.</p>
237
+
238
+ <p>Add a link on the <code>template/index.xhtml</code> like this:</p>
239
+
240
+ <pre><code>&lt;h1&gt;TodoList&lt;/h1&gt;
241
+ &lt;a href="/new"&gt;New Task&lt;/a&gt;
242
+ </code></pre>
243
+
244
+ <p>Open a new file <code>template/new.xhtml</code> with a form to add a new task.</p>
245
+
246
+ <pre><code>&lt;html&gt;
247
+ &lt;head&gt;
248
+ &lt;title&gt;TodoList&lt;/title&gt;
249
+ &lt;/head&gt;
250
+ &lt;body&gt;
251
+ &lt;h1&gt;New Task&lt;/h1&gt;
252
+ &lt;a href="/"&gt;Back to TodoList&lt;/a&gt;
253
+ &lt;form method="POST" action="create"&gt;
254
+ Task: &lt;input type="text" name="title" /&gt;&lt;br /&gt;
255
+ &lt;inpyt type="submit" /&gt;
256
+ &lt;/form&gt;
257
+ &lt;/body&gt;
258
+ &lt;/html&gt;
259
+ </code></pre>
260
+
261
+ <p>We will not need a method for this on our controller, in fact, actions can
262
+ consist of either method and template or only one of them. The Controller
263
+ can act as a View and the View as Controller (if it returns a String and there
264
+ is no template).</p>
265
+
266
+ <p>If you try to use this form you will notice that we have not yet defined a way
267
+ to actually create the task.</p>
268
+
269
+ <p>You will get the default Ramaze error-page instead. Please take your time to
270
+ explore it and see how Ramaze reacted on the error.</p>
271
+
272
+ <p>It will show you the back trace and what state the application is in at the
273
+ moment, the request and response and the contents of the session. This is very
274
+ useful for debugging and development, you can provide your own set of
275
+ error-pages before going into production (or deactivate them fully) though.</p>
276
+
277
+ <p>OK, let's implement the action for #create, all we want to do is take the
278
+ requests parameters and create a new task for it, this looks like following on
279
+ your MainController.</p>
280
+
281
+ <pre><code>def create
282
+ title = request['title']
283
+ TodoList[title] = {:done =&gt; false}
284
+ redirect R(self)
285
+ end
286
+ </code></pre>
287
+
288
+ <p>That's all folks!</p>
289
+
290
+ <p>we get the title from the request-object, put it into our TodoList as undone
291
+ and redirect back to the mapping of the current Controller ('/' in this case).</p>
292
+
293
+ <p>Now you can create as many tasks as you want, please don't get overworked ;)</p>
294
+
295
+ <h2>Sixth Step, open and close tasks</h2>
296
+
297
+ <p>Since the nature of tasks is to be done eventually
298
+ we will need some way to mark it as done or open tasks again.</p>
299
+
300
+ <p>Jump into <code>template/index.xhtml</code> and do the following:</p>
301
+
302
+ <pre><code>&lt;?r @tasks.each do |title, status, toggle| ?&gt;
303
+ &lt;li&gt;
304
+ #{title}: #{status} - #{toggle}
305
+ &lt;/li&gt;
306
+ &lt;?r end ?&gt;
307
+ </code></pre>
308
+
309
+ <p>We added a new element here, <code>toggle</code>, the Controller should give us
310
+ a link to change the status corresponding to the status of the task, so off
311
+ we go and change the index method on the controller once again:</p>
312
+
313
+ <pre><code>def index
314
+ @tasks = []
315
+ TodoList.original.each do |title, parameters|
316
+ if parameters[:done]
317
+ status = 'done'
318
+ toggle = link( R( self, :open, CGI.escape(title) ), :title =&gt; 'Open Task' )
319
+ else
320
+ status = 'not done'
321
+ toggle = link( R( self, :close, CGI.escape(title) ), :title =&gt; 'Close Task' )
322
+ end
323
+ @tasks &lt;&lt; [title, status, toggle]
324
+ end
325
+ @tasks.sort!
326
+ end
327
+ </code></pre>
328
+
329
+ <p>Wow, quite some new stuff here. Let me explain that in detail.</p>
330
+
331
+ <p>We first decide whether a task is done or not, then go on and provide a link to
332
+ toggle the status, link and R are both methods that help you do that.
333
+ the result will be something like:</p>
334
+
335
+ <pre><code>&lt;a href="/open/Wash+dishes"&gt;Close Task&lt;/a&gt;
336
+ </code></pre>
337
+
338
+ <p>R actually is responsible to build the links href, for more information please
339
+ take a look at the RDoc for LinkHelper.</p>
340
+
341
+ <p>Also, you might have noticed that the tasks were changing order on every reload,
342
+ which is because we were using an Hash, which are per definition unsorted, so
343
+ now we use an array to hold our tasks and sort it.</p>
344
+
345
+ <p>Now back again to <code>template/index.xhtml</code> and change it as follows:</p>
346
+
347
+ <pre><code>&lt;?r @tasks.each do |title, status, toggle| ?&gt;
348
+ &lt;li&gt;
349
+ #{title}: #{status} [ #{toggle} ]
350
+ &lt;/li&gt;
351
+ &lt;?r end ?&gt;
352
+ </code></pre>
353
+
354
+ <p>As usual, the things not changed are omitted for terseness.</p>
355
+
356
+ <p>And as usual since the links for open and close don't lead anywhere, add the
357
+ corresponding methods to the Controller:</p>
358
+
359
+ <pre><code>def open title
360
+ task_status title, false
361
+ redirect R(self)
362
+ end
363
+
364
+ def close title
365
+ task_status title, true
366
+ redirect R(self)
367
+ end
368
+
369
+ private
370
+
371
+ def task_status title, status
372
+ task = TodoList[title]
373
+ task[:done] = status
374
+ TodoList[title] = task
375
+ end
376
+ </code></pre>
377
+
378
+ <p>Oh, now what have we got here?
379
+ private declares that methods from here on are only to be used within the
380
+ Controller itself, we define an #task_status method that takes the title and
381
+ status to be set so we don't have to repeat that code in <em>#open</em> and <em>#close</em>
382
+ and follow the DRY (Don't repeat yourself) paradigm.</p>
383
+
384
+ <p>Another thing we have not encountered so far is that you can define your public
385
+ methods to take parameters on their own, they will be calculated from requests.</p>
386
+
387
+ <pre><code>'/open/Wash+dishes'
388
+ </code></pre>
389
+
390
+ <p>will translate into:</p>
391
+
392
+ <pre><code>open('Wash dishes')
393
+ </code></pre>
394
+
395
+ <p>Which in turn will call task_status('Wash dishes', false)</p>
396
+
397
+ <p>That's it, go on and try it :)</p>
398
+
399
+ <h2>Seventh Step, delete tasks</h2>
400
+
401
+ <p>Well, creating, opening and closing work now, one of the things you will
402
+ consider is to delete a task permanently.</p>
403
+
404
+ <p>This is just two little changes away, so let's add the link for deletion in our
405
+ Controller:</p>
406
+
407
+ <pre><code>delete = link( R( self, :delete, CGI.escape(title) ), :title =&gt; 'Delete' )
408
+ @tasks &lt;&lt; [title, status, toggle, delete]
409
+ </code></pre>
410
+
411
+ <p>and an corresponding method while we're at it:</p>
412
+
413
+ <pre><code>def delete title
414
+ TodoList.delete title
415
+ redirect R(self)
416
+ end
417
+ </code></pre>
418
+
419
+ <p>Now jumping to <code>template/index.xhtml</code> again, change it so it shows the link:</p>
420
+
421
+ <pre><code>&lt;?r @tasks.each do |title, status, toggle, delete| ?&gt;
422
+ &lt;li&gt;
423
+ #{title}: #{status} [ #{toggle} | #{delete} ]
424
+ &lt;/li&gt;
425
+ &lt;?r end ?&gt;
426
+ </code></pre>
427
+
428
+ <p>Voilà, you now have acquired the Certificate of Ramazeness, our accounting-
429
+ section will contact you within the next few days.</p>
430
+
431
+ <p>Just kidding, but that really are the basics, in the next few steps I will
432
+ explain some more advanced concepts of Ramaze and the templating.</p>
433
+
434
+ <h2>Eighth Step, Elements</h2>
435
+
436
+ <pre><code>&lt;Page&gt;&lt;/Page&gt;
437
+ </code></pre>
438
+
439
+ <p>This is called an Element, Ramaze will go and search for a class that matches
440
+ the name Page and responds to <em>#render</em>. Then it will go and hand the content in
441
+ between to that Element.</p>
442
+
443
+ <p>Sounds weird?</p>
444
+
445
+ <p>Let us have a look at our templates, they all got some repetitive stuff, like:</p>
446
+
447
+ <pre><code>&lt;html&gt;
448
+ &lt;head&gt;
449
+ &lt;title&gt;TodoList&lt;/title&gt;
450
+ &lt;/head&gt;
451
+ &lt;body&gt;
452
+ &lt;h1&gt;some title&lt;/h1&gt;
453
+ &lt;/body&gt;
454
+ &lt;/html&gt;
455
+ </code></pre>
456
+
457
+ <p>How about replacing that with something short and nice:</p>
458
+
459
+ <pre><code>&lt;Page title="TodoList"&gt;
460
+ your other content
461
+ &lt;/Page&gt;
462
+ </code></pre>
463
+
464
+ <p>Would be nice of course, and when you start having more templates it makes an
465
+ awful lot of sense to change the enclosing stuff in one place.</p>
466
+
467
+ <p>So let's apply DRY here as well.</p>
468
+
469
+ <p>Take a look at the <code>src/element/page.rb</code></p>
470
+
471
+ <pre><code>class Page &lt; Element
472
+ def render
473
+ %{
474
+ &lt;html&gt;
475
+ &lt;head&gt;
476
+ &lt;title&gt;Welcome to Ramaze&lt;/title&gt;
477
+ &lt;/head&gt;
478
+ &lt;body&gt;
479
+ #{content}
480
+ &lt;/body&gt;
481
+ &lt;/html&gt;
482
+ }
483
+ end
484
+ end
485
+ </code></pre>
486
+
487
+ <p>Alright, most things we need are in place already, the most important thing
488
+ is the <em>#content</em> method that we call with <em>#{content}</em> inside the string in
489
+ <em>#render</em>.</p>
490
+
491
+ <p>Just adopt it to your liking, I'll just use the things we had in our templates
492
+ so far:</p>
493
+
494
+ <pre><code>class Page &lt; Element
495
+ def render
496
+ %{
497
+ &lt;html&gt;
498
+ &lt;head&gt;
499
+ &lt;title&gt;TodoList&lt;/title&gt;
500
+ &lt;/head&gt;
501
+ &lt;body&gt;
502
+ &lt;h1&gt;#{@hash['title']}&lt;/h1&gt;
503
+ #{content}
504
+ &lt;/body&gt;
505
+ &lt;/html&gt;
506
+ }
507
+ end
508
+ end
509
+ </code></pre>
510
+
511
+ <p>Please note that the @hash is filled with the things you pass as parameters
512
+ to tye Page-tag.</p>
513
+
514
+ <p>And let's change our templates as well.</p>
515
+
516
+ <p>First the <code>template/index.xhtml</code></p>
517
+
518
+ <pre><code>&lt;Page title="TodoList"&gt;
519
+ &lt;a href="/new"&gt;New Task&lt;/a&gt;
520
+ &lt;?r if @tasks.empty? ?&gt;
521
+ No Tasks
522
+ &lt;?r else ?&gt;
523
+ &lt;ul&gt;
524
+ &lt;?r @tasks.each do |title, status, toggle, delete| ?&gt;
525
+ &lt;li&gt;
526
+ #{title}: #{status} [ #{toggle} | #{delete} ]
527
+ &lt;/li&gt;
528
+ &lt;?r end ?&gt;
529
+ &lt;/ul&gt;
530
+ &lt;?r end ?&gt;
531
+ &lt;/Page&gt;
532
+ </code></pre>
533
+
534
+ <p>and the <code>template/new.xhtml</code></p>
535
+
536
+ <pre><code>&lt;Page title="New Task"&gt;
537
+ &lt;a href="/"&gt;Back to TodoList&lt;/a&gt;
538
+ &lt;form method="POST" action="create"&gt;
539
+ Task: &lt;input type="text" name="title" /&gt;&lt;br /&gt;
540
+ &lt;input type="submit" /&gt;
541
+ &lt;/form&gt;
542
+ &lt;/Page&gt;
543
+ </code></pre>
544
+
545
+ <p>Alright, now just go and look at the result in the browser, try changing
546
+ the things inside the Element and look at how it behaves.</p>
547
+
548
+ <h2>Ninth Step, Prettify</h2>
549
+
550
+ <p>Let's structure the data inside the list a little bit, in this case into a table to get it line up properly and look actually structured.</p>
551
+
552
+ <p>So, from what we have right now:</p>
553
+
554
+ <pre><code>&lt;ul&gt;
555
+ &lt;?r @tasks.each do |title, status, toggle, delete| ?&gt;
556
+ &lt;li&gt;
557
+ #{title}: #{status} [ #{toggle} | #{delete} ]
558
+ &lt;/li&gt;
559
+ &lt;?r end ?&gt;
560
+ &lt;/ul&gt;
561
+ </code></pre>
562
+
563
+ <p>To something like this:</p>
564
+
565
+ <pre><code>&lt;table&gt;
566
+ &lt;?r @tasks.each do |title, status, toggle, delete| ?&gt;
567
+ &lt;tr&gt;
568
+ &lt;td class="title"&gt; #{title} &lt;/td&gt;
569
+ &lt;td class="status"&gt; #{status} &lt;/td&gt;
570
+ &lt;td class="toggle"&gt; #{toggle} &lt;/td&gt;
571
+ &lt;td class="delete"&gt; #{delete} &lt;/td&gt;
572
+ &lt;/tr&gt;
573
+ &lt;?r end ?&gt;
574
+ &lt;/table&gt;
575
+ </code></pre>
576
+
577
+ <p>And, since we have proper classes to address some style sheets, jump into the Page element and add some style sheet like that:</p>
578
+
579
+ <pre><code>&lt;head&gt;
580
+ &lt;title&gt;TodoList&lt;/title&gt;
581
+ &lt;style&gt;
582
+ table { width: 100%; }
583
+ tr { background: #efe; width: 100%; }
584
+ tr:hover { background: #dfd; }
585
+ td.title { font-weight: bold; width: 60%; }
586
+ td.status { margin: 1em; }
587
+ a { color: #3a3; }
588
+ &lt;/style&gt;
589
+ &lt;/head&gt;
590
+ </code></pre>
591
+
592
+ <p>That looks quite a bit nicer, right?
593
+ And yes, if you don't like tables (though this is an entirely legit use in my opinion, you can just do it like you want, using nested lists or divs/spans, replacing the open/close and delete links with nice images and changing the style according to the status.</p>
594
+
595
+ <p>I will leave this as an exercise to the reader.</p>
596
+
597
+ <p>To be continued...</p>
598
+ </body>
599
+ </html>