ramaze 2009.05 → 2009.06

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/.gitignore +3 -0
  2. data/MANIFEST +11 -7
  3. data/README.md +3 -3
  4. data/Rakefile +17 -15
  5. data/bin/ramaze +7 -2
  6. data/doc/AUTHORS +6 -5
  7. data/doc/CHANGELOG +10057 -12259
  8. data/doc/meta/announcement.txt +93 -46
  9. data/doc/tutorial/todolist.html +22 -58
  10. data/doc/tutorial/todolist.txt +20 -39
  11. data/examples/app/wikore/src/model.rb +7 -1
  12. data/examples/app/wiktacular/template/html_layout.xhtml +1 -1
  13. data/lib/proto/config.ru +5 -1
  14. data/lib/ramaze/cache/localmemcache.rb +1 -1
  15. data/lib/ramaze/cache/memcache.rb +26 -1
  16. data/lib/ramaze/cache/sequel.rb +9 -6
  17. data/lib/ramaze/contrib/addressable_route.rb +55 -0
  18. data/lib/ramaze/contrib/facebook/facebook.rb +4 -4
  19. data/lib/ramaze/controller.rb +28 -18
  20. data/lib/ramaze/gestalt.rb +1 -1
  21. data/lib/ramaze/helper/auth.rb +0 -5
  22. data/lib/ramaze/helper/cache.rb +0 -9
  23. data/lib/ramaze/helper/form.rb +14 -2
  24. data/lib/ramaze/helper/formatting.rb +1 -1
  25. data/lib/ramaze/helper/layout.rb +97 -0
  26. data/lib/ramaze/helper/link.rb +6 -25
  27. data/lib/ramaze/helper/paginate.rb +2 -2
  28. data/lib/ramaze/helper/user.rb +3 -3
  29. data/lib/ramaze/helper.rb +1 -0
  30. data/lib/ramaze/setup.rb +22 -9
  31. data/lib/ramaze/spec/bacon.rb +34 -0
  32. data/lib/ramaze/spec/helper/template_examples.rb +13 -23
  33. data/lib/ramaze/spec.rb +8 -9
  34. data/lib/ramaze/tool/bin.rb +16 -6
  35. data/lib/ramaze/tool/project_creator.rb +1 -1
  36. data/lib/ramaze/version.rb +1 -1
  37. data/lib/ramaze.rb +8 -8
  38. data/lib/vendor/etag.rb +4 -2
  39. data/lib/vendor/route_exceptions.rb +6 -11
  40. data/ramaze.gemspec +52 -8
  41. data/spec/contrib/addressable_route.rb +32 -0
  42. data/spec/contrib/rest.rb +1 -1
  43. data/spec/examples/caching.rb +1 -1
  44. data/spec/examples/css.rb +1 -1
  45. data/spec/examples/element.rb +1 -1
  46. data/spec/examples/hello.rb +1 -1
  47. data/spec/examples/helpers/httpdigest.rb +1 -1
  48. data/spec/examples/linking.rb +1 -1
  49. data/spec/examples/simple.rb +1 -1
  50. data/spec/examples/templates/template_erubis.rb +3 -2
  51. data/spec/examples/templates/template_ezamar.rb +3 -2
  52. data/spec/examples/templates/template_haml.rb +3 -2
  53. data/spec/examples/templates/template_liquid.rb +3 -2
  54. data/spec/examples/templates/template_markaby.rb +5 -4
  55. data/spec/examples/templates/template_nagoro.rb +3 -3
  56. data/spec/examples/templates/template_redcloth.rb +3 -2
  57. data/spec/examples/templates/template_remarkably.rb +3 -2
  58. data/spec/examples/templates/template_tenjin.rb +3 -2
  59. data/spec/helper.rb +1 -2
  60. data/spec/ramaze/app.rb +1 -1
  61. data/spec/ramaze/bin/ramaze.rb +91 -0
  62. data/spec/ramaze/cache/memcache.rb +6 -0
  63. data/spec/ramaze/controller/actionless_templates.rb +1 -1
  64. data/spec/ramaze/controller/lonely_mapping.rb +16 -0
  65. data/spec/ramaze/controller/mapping.rb +0 -14
  66. data/spec/ramaze/controller/provide_inheritance.rb +1 -1
  67. data/spec/ramaze/controller/resolve.rb +1 -1
  68. data/spec/ramaze/controller/subclass.rb +1 -1
  69. data/spec/ramaze/controller/template_resolving.rb +1 -1
  70. data/spec/ramaze/dispatcher/directory.rb +1 -1
  71. data/spec/ramaze/dispatcher/file.rb +7 -7
  72. data/spec/ramaze/error.rb +2 -2
  73. data/spec/ramaze/files.rb +1 -1
  74. data/spec/ramaze/helper/auth.rb +1 -1
  75. data/spec/ramaze/helper/bench.rb +2 -1
  76. data/spec/ramaze/helper/cache.rb +1 -1
  77. data/spec/ramaze/helper/flash.rb +1 -1
  78. data/spec/ramaze/helper/form.rb +2 -2
  79. data/spec/ramaze/helper/httpdigest.rb +3 -3
  80. data/spec/ramaze/helper/layout/default.xhtml +5 -0
  81. data/spec/ramaze/helper/layout.rb +79 -0
  82. data/spec/ramaze/helper/link.rb +32 -11
  83. data/spec/ramaze/helper/localize.rb +1 -1
  84. data/spec/ramaze/helper/maruku.rb +1 -1
  85. data/spec/ramaze/helper/pager.rb +1 -1
  86. data/spec/ramaze/helper/paginate.rb +1 -1
  87. data/spec/ramaze/helper/request_accessor.rb +1 -1
  88. data/spec/ramaze/helper/sequel_form.rb +1 -1
  89. data/spec/ramaze/helper/simple_captcha.rb +1 -1
  90. data/spec/ramaze/helper/stack.rb +1 -1
  91. data/spec/ramaze/helper/user.rb +1 -1
  92. data/spec/ramaze/params.rb +4 -5
  93. data/spec/ramaze/session/memcache.rb +66 -0
  94. data/spec/ramaze/view/erubis.rb +1 -1
  95. data/spec/ramaze/view/ezamar.rb +1 -1
  96. data/spec/ramaze/view/gestalt.rb +1 -1
  97. data/spec/ramaze/view/haml.rb +1 -1
  98. data/spec/ramaze/view/liquid.rb +1 -1
  99. data/spec/ramaze/view/nagoro.rb +1 -1
  100. data/spec/ramaze/view/redcloth.rb +1 -1
  101. data/spec/ramaze/view/remarkably.rb +1 -1
  102. data/spec/ramaze/view/sass.rb +1 -1
  103. data/spec/ramaze/view/tagz.rb +1 -1
  104. data/spec/ramaze/view/tenjin.rb +1 -1
  105. data/spec/ramaze/view.rb +1 -1
  106. data/tasks/changelog.rake +3 -1
  107. data/tasks/{gem_installer.rake → gem_setup.rake} +45 -22
  108. data/tasks/release.rake +6 -4
  109. data/tasks/setup.rake +3 -21
  110. data/tasks/todo.rake +2 -4
  111. metadata +164 -10
  112. data/CHANGELOG +0 -16918
  113. data/doc/tutorial/todolist.mkd +0 -787
  114. data/lib/ramaze/snippets/divide.rb +0 -22
  115. data/lib/ramaze/snippets/object/acquire.rb +0 -37
  116. data/spec/snippets/kernel/constant.rb +0 -23
  117. data/tasks/install_dependencies.rake +0 -6
@@ -1,787 +0,0 @@
1
- Title: The official Ramaze todolist tutorial
2
- html_use_syntax: true
3
- uv_style: iplastic
4
-
5
- # To-do List Tutorial
6
-
7
- * Table of Contents
8
- {:toc}
9
-
10
- ## Step Zero, Introduction
11
-
12
- Welcome to our official tutorial, the mandatory to-do list.
13
- I'm writing this while doing the steps to assure it will work for you.
14
-
15
- The tutorial assumes that you have Ramaze installed already. The easiest way to
16
- do that is `gem install ramaze`, for other ways of installation please see the
17
- documentation at http://ramaze.rubyforge.org
18
-
19
- Should you encounter any problems while doing this tutorial, this might either
20
- be because Ramaze changed (which happens very often while it is still young)
21
- or I actually made some mistake while writing it.
22
-
23
- In either case it would make me (and all other poor fellows who happen to try
24
- this tutorial) very happy if you could spare some time and report the issue
25
- either on the Bug tracker at http://rubyforge.org/projects/ramaze or just
26
- drop by on IRC ( irc.freenode.org channel: #ramaze ).
27
-
28
- There is also a Mailing list available where you can keep yourself updated on
29
- what is going on with little effort, it is also located on the project-page at
30
- RubyForge.
31
-
32
- Additionally, we now have added tests for the resulting application that you
33
- can find in spec/examples/todolist.rb
34
-
35
- Date of last update: Thu Jan 31 04:37:16 JST 2008
36
-
37
- Thanks in advance.
38
- The author of the tutorial, Michael 'manveru' Fellinger
39
-
40
- ## First Step, Create
41
-
42
- We are using `ramaze --create todolist` to create a new application.
43
- Ramaze will then create the directory and fill it with a skeleton of a quite
44
- sophisticated hello-world example out of which we will create the actual
45
- to-do list.
46
-
47
- So run:
48
-
49
- ramaze --create todolist
50
- {:lang=shell-unix-generic}
51
-
52
- done.
53
-
54
-
55
- ## Second Step, M, like Model
56
-
57
- Ramaze comes at the moment only with a simple wrapper of the YAML::Store.
58
- So we are going to base this on the tools available, you can just do the same
59
- with your ORM or database-library of choice.
60
-
61
- So first, create a `model/todolist.rb` for our application:
62
-
63
- require 'ramaze/store/default'
64
- TodoList = Ramaze::Store::Default.new('todolist.yaml')
65
- {:lang=ruby}
66
-
67
- Let's add some items as well to have a base to start from.
68
-
69
- { 'Laundry' => {:done => false},
70
- 'Wash dishes' => {:done => false},
71
- }.each do |title, value|
72
- TodoList[title] = value
73
- end
74
- {:lang=ruby}
75
-
76
- ## Third Step, V, like View
77
-
78
- Now let's get our hands dirty and just edit the templates for our to-do list.
79
-
80
- Start with editing `view/index.xhtml`, it is using the default templating
81
- of Ramaze, called Ezamar.
82
-
83
- The index.xhtml currently contains a default welcome page, remove the contents.
84
-
85
- Let's put some things in there, I'll explain the syntax as we go, it's quite
86
- simple.
87
-
88
- <html>
89
- <head>
90
- <title>TodoList</title>
91
- </head>
92
- <body>
93
- <h1>TodoList</h1>
94
- <ul>
95
- <?r
96
- TodoList.each do |title, value|
97
- status = value[:done] ? 'done' : 'not done'
98
- ?>
99
- <li>#{h title}: #{status}</li>
100
- <?r end ?>
101
- </ul>
102
- </body>
103
- </html>
104
- {:lang=html}
105
-
106
- I will assume that you are familiar with basic Ruby already, so we will
107
- concentrate on the things new here.
108
-
109
- `<?r ?>` defines an area of ruby-code. Later, when the template is transformed
110
- into pure Ruby it will be evaluated. We iterate over the TodoList model and
111
- pass the title and value into a block. In that block we can just get the values
112
- of title and status (which we define based on the value) displayed on the
113
- page.
114
-
115
- The whole Template would expand to something like this (only showing the
116
- interesting part)
117
-
118
- <ul>
119
- <li>Laundry: not done</li>
120
- <li>Wash dishes: not done</li>
121
- </ul>
122
- {:lang=html}
123
-
124
- That wasn't too bad, huh?
125
-
126
- Now, so we can get our instant pleasure of seeing the result of our (hard) work,
127
- let's see how to start ramaze.
128
-
129
- In the `todolist` directory run `ramaze`.
130
-
131
- This will start an instance of Ramaze and run your application on it. You can
132
- now access it by browsing to http://localhost:7000/
133
-
134
- 7000 is the default-port Ramaze will run on, to change it you can just run
135
- `ramaze -p 7070` or similar. Also be sure to look at the output of
136
- `ramaze --help` to see some other options.
137
-
138
-
139
- ## Fourth Step, C, like Controller
140
-
141
- The last part of the MVC-paradigm is the Controller.
142
-
143
- Wouldn't it be nice to have a way to add and remove items on our to-do list?
144
- Editing the model every time would be quite tiresome and limited.
145
-
146
- Well, come along, I'll introduce you to Controller.
147
-
148
- In the way MVC is structured, the Controller provides the data in a nice way
149
- for the View, removing all of the data-preparation and most of the logic from
150
- the templates. This makes it firstly simple to change the front end of your
151
- application and secondly provides excellent ways of changing the complete
152
- Structure of the Model or View independent from each other.
153
-
154
- OK, enough of the theory, you will see the benefits in an instant. Go on and
155
- edit the file `controller/main.rb`.
156
-
157
- The contents of it are like following:
158
-
159
- class MainController < Controller
160
- def index
161
- @welcome = "Welcome to Ramaze!"
162
- end
163
-
164
- def notemplate
165
- "there is no template associated with this action"
166
- end
167
- end
168
- {:lang=ruby}
169
-
170
- The only methods right now are `#index` and `#notemplate`.
171
- The relationship between the methods on the controller and the templates is
172
- 1:1, so the method `#index` is combined with the template `index.xhtml`. This
173
- combination is called an `action`.
174
-
175
- Let's get back to editing and change the method `#index` to this:
176
-
177
- def index
178
- @tasks = TodoList.original
179
- @tasks.each do |title, value|
180
- status = value[:done] ? 'done' : 'not done'
181
- @tasks[title] = status
182
- end
183
- end
184
- {:lang=ruby}
185
-
186
- This will take care of the logic inside the template, which now should be
187
- changed to do following:
188
-
189
- <html>
190
- <head>
191
- <title>TodoList</title>
192
- </head>
193
- <body>
194
- <h1>TodoList</h1>
195
- <?r if @tasks.empty? ?>
196
- No Tasks
197
- <?r else ?>
198
- <ul>
199
- <?r @tasks.each do |title, status| ?>
200
- <li>#{h title}: #{status}</li>
201
- <?r end ?>
202
- </ul>
203
- <?r end ?>
204
- </body>
205
- </html>
206
- {:lang=ezamar}
207
-
208
- The rest of the template can stay the same.
209
-
210
- Now, if you browse to http://localhost:7000/ again you will not notice any
211
- change, which is how it should be. The only change is that if there are no
212
- Tasks it will say so.
213
-
214
- Some things you should know:
215
-
216
- * Instance-variables defined in the Controller are available in the View.
217
- * The return-value of the Controller does not matter if a template is present.
218
-
219
- ## Fifth Step, getting dynamic
220
-
221
- We set out to build the ultimate to-do list, but there are still some things
222
- missing. First off, we want to add new tasks, so let's get that done.
223
-
224
- Add a link on the `view/index.xhtml` like this:
225
-
226
- <h1>TodoList</h1>
227
- <a href="/new">New Task</a>
228
- {:lang=ezamar}
229
-
230
- Open a new file `view/new.xhtml` with a form to add a new task.
231
-
232
- <html>
233
- <head>
234
- <title>TodoList</title>
235
- </head>
236
- <body>
237
- <h1>New Task</h1>
238
- <a href="/">Back to TodoList</a>
239
- <form method="POST" action="create">
240
- Task: <input type="text" name="title" /><br />
241
- <input type="submit" />
242
- </form>
243
- </body>
244
- </html>
245
- {:lang=ezamar}
246
-
247
- We will not need a method for this on our controller, in fact, actions can
248
- consist of either method and template or only one of them. The Controller
249
- can act as a View and the View can work like you may know it from PHP.
250
-
251
- If you try to use this form you will notice that we have not yet defined a way
252
- to actually create the task.
253
-
254
- You will get the default Ramaze error-page instead. Please take your time to
255
- explore it and see how Ramaze reacted on the error.
256
-
257
- It will show you the back trace and what state the application is in at the
258
- moment, the request and response and the contents of the session. This is very
259
- useful for debugging and development, you can provide your own set of
260
- error-pages before going into production (or deactivate them fully) though.
261
-
262
- OK, let's implement the action for `#create`, all we want to do is take the
263
- requests parameters and create a new task for it, this looks like following on
264
- your MainController.
265
-
266
- def create
267
- title = request['title']
268
- TodoList[title] = {:done => false}
269
- redirect Rs()
270
- end
271
- {:lang=ruby}
272
-
273
- That's all folks!
274
-
275
- We get the title from the request-object, put it into our TodoList as undone
276
- and redirect back to the mapping of the current Controller ('/' in this case).
277
-
278
- Now you can create as many tasks as you want, please don't get overworked ;)
279
-
280
-
281
- ## Sixth Step, open and close tasks
282
-
283
- Since the nature of tasks is to be done eventually
284
- we will need some way to mark it as done or open tasks again.
285
-
286
- Jump into `view/index.xhtml` and do the following:
287
-
288
-
289
- <?r @tasks.each do |title, status, toggle| ?>
290
- <li>
291
- #{h title}: #{status} - [ #{toggle} ]
292
- </li>
293
- <?r end
294
- {:lang=ezamar}
295
-
296
- We added a new element here, `toggle`, the Controller should give us
297
- a link to change the status corresponding to the status of the task, so off
298
- we go and change the index method on the controller once again:
299
-
300
- def index
301
- @tasks = []
302
- TodoList.original.each do |title, value|
303
- if value[:done]
304
- status = 'done'
305
- toggle = A('Open Task', :href => Rs(:open, title))
306
- else
307
- status = 'not done'
308
- toggle = A('Close Task', :href => Rs(:close, title))
309
- end
310
- @tasks << [title, status, toggle]
311
- end
312
- @tasks.sort!
313
- end
314
- {:lang=ruby}
315
-
316
- Wow, quite some new stuff here. Let me explain that in detail.
317
-
318
- We first decide whether a task is done or not, then go on and provide a link to
319
- toggle the status, A and Rs are both methods that help you do that.
320
- The result will be something like:
321
-
322
- <a href="/open/Wash+dishes">Close Task</a>
323
- {:lang=ezamar}
324
-
325
- Rs actually is responsible to build the links href, for more information please
326
- take a look at the RDoc for LinkHelper.
327
-
328
- Also, you might have noticed that the tasks were changing order on every reload,
329
- which is because we were using an Hash, which are per definition unsorted, so
330
- now we use an array to hold our tasks and sort it.
331
-
332
- As usual since the links for open and close don't lead anywhere, add the
333
- corresponding methods to the Controller:
334
-
335
- def open title
336
- task_status title, false
337
- redirect Rs()
338
- end
339
-
340
- def close title
341
- task_status title, true
342
- redirect Rs()
343
- end
344
-
345
- private
346
-
347
- def task_status title, status
348
- task = TodoList[title]
349
- task[:done] = status
350
- TodoList[title] = task
351
- end
352
- {:lang=ruby}
353
-
354
- Oh, now what have we got here?
355
- `private` declares that methods from here on are only to be used within the
356
- Controller itself, we define an `#task_status` method that takes the title and
357
- status to be set so we don't have to repeat that code in `#open` and `#close`
358
- and follow the DRY (Don't repeat yourself) paradigm.
359
-
360
- Another thing we have not encountered so far is that you can define your public
361
- methods to take parameters on their own, they will be calculated from requests.
362
-
363
- '/open/Wash+dishes'
364
- {:lang=ruby}
365
-
366
- will translate into:
367
-
368
- open('Wash dishes')
369
- {:lang=ruby}
370
-
371
- Which in turn will call
372
-
373
- task_status('Wash dishes', false)
374
- {:lang=ruby}
375
-
376
- That's it, go on and try it :)
377
-
378
-
379
- ## Seventh Step, delete tasks
380
-
381
- Well, creating, opening and closing work now, one of the things you will
382
- consider is to delete a task permanently.
383
-
384
- This is just two little changes away, so let's add the link for deletion in our
385
- Controller:
386
-
387
- delete = A('Delete', :href => Rs(:delete, title))
388
- @tasks << [title, status, toggle, delete]
389
- {:lang=ruby}
390
-
391
- and an corresponding method while we're at it:
392
-
393
- def delete title
394
- TodoList.delete title
395
- redirect Rs()
396
- end
397
- {:lang=ruby}
398
-
399
- Now jumping to `view/index.xhtml` again, change it so it shows the link:
400
-
401
- <?r @tasks.each do |title, status, toggle, delete| ?>
402
- <li>
403
- #{h title}: #{status} [ #{toggle} | #{delete} ]
404
- </li>
405
- <?r end ?>
406
- {:lang=ezamar}
407
-
408
- Voilà, you now have acquired the Certificate of Ramazeness.
409
-
410
- Just kidding, but that really are the basics, in the next few steps I will
411
- explain some more advanced concepts of Ramaze and Ezamar.
412
-
413
-
414
- ## Eighth Step, Elements
415
-
416
- <Page></Page>
417
- {:lang=ezamar}
418
-
419
- This is called an Element, Ramaze will go and search for a class that matches
420
- the name Page and responds to `#render`. Then it will go and hand the content in
421
- between to that Element.
422
-
423
- Sounds weird?
424
-
425
- Let us have a look at our templates, they got some repetitive stuff, like:
426
-
427
- <html>
428
- <head>
429
- <title>TodoList</title>
430
- </head>
431
- <body>
432
- <h1>some title</h1>
433
- </body>
434
- </html>
435
- {:lang=ezamar}
436
-
437
- How about replacing that with something short and reusable:
438
-
439
- <Page title="TodoList">
440
- your other content
441
- </Page>
442
- {:lang=html}
443
-
444
- Would be nice of course, and when you start having more templates it makes an
445
- awful lot of sense being able to change the enclosing stuff in one place.
446
-
447
- So let's apply DRY here as well.
448
-
449
- Take a look at the `src/element/page.rb`
450
-
451
- class Page < Ezamar::Element
452
- def render
453
- %{
454
- <html>
455
- <head>
456
- <title>Welcome to Ramaze</title>
457
- </head>
458
- <body>
459
- #{content}
460
- </body>
461
- </html>
462
- }
463
- end
464
- end
465
- {:lang=ruby}
466
-
467
- Alright, most things we need are in place already, the most important thing
468
- is the `#content` method that we call with `#{content}` inside the string in
469
- `#render`.
470
-
471
- Just adopt it to your liking, I'll just use the things we had in our templates
472
- so far:
473
-
474
- class Page < Ezamar::Element
475
- def render
476
- %{
477
- <html>
478
- <head>
479
- <title>TodoList</title>
480
- </head>
481
- <body>
482
- <h1>#{@title}</h1>
483
- #{content}
484
- </body>
485
- </html>
486
- }
487
- end
488
- end
489
- {:lang=ruby}
490
-
491
- Please note that instance variables reflecting the parameters are set.
492
-
493
- And let's change our templates as well.
494
-
495
- First the `view/index.xhtml`
496
-
497
- <Page title="TodoList">
498
- <a href="/new">New Task</a>
499
- <?r if @tasks.empty? ?>
500
- No Tasks
501
- <?r else ?>
502
- <ul>
503
- <?r @tasks.each do |title, status, toggle, delete| ?>
504
- <li>
505
- #{h title}: #{status} [ #{toggle} | #{delete} ]
506
- </li>
507
- <?r end ?>
508
- </ul>
509
- <?r end ?>
510
- </Page>
511
- {:lang=ezamar}
512
-
513
- and the `view/new.xhtml`
514
-
515
- <Page title="New Task">
516
- <a href="/">Back to TodoList</a>
517
- <form method="POST" action="create">
518
- Task: <input type="text" name="title" /><br />
519
- <input type="submit" />
520
- </form>
521
- </Page>
522
- {:lang=ezamar}
523
-
524
- Alright, now just go and look at the result in the browser, try changing
525
- the things inside the Element and look at how it behaves.
526
-
527
-
528
- ## Ninth Step, Prettify
529
-
530
- We structure the data inside the list a little bit, in this case into a table,
531
- to get it line up properly and look actually structured.
532
-
533
- So, from what we have right now:
534
-
535
- <ul>
536
- <?r @tasks.each do |title, status, toggle, delete| ?>
537
- <li>
538
- #{h title}: #{status} [ #{toggle} | #{delete} ]
539
- </li>
540
- <?r end ?>
541
- </ul>
542
- {:lang=ezamar}
543
-
544
- To something like this:
545
-
546
- <table>
547
- <?r @tasks.each do |title, status, toggle, delete| ?>
548
- <tr>
549
- <td class="title"> #{h title} </td>
550
- <td class="status"> #{status} </td>
551
- <td class="toggle"> #{toggle} </td>
552
- <td class="delete"> #{delete} </td>
553
- </tr>
554
- <?r end ?>
555
- </table>
556
- {:lang=ezamar}
557
-
558
- And, since we have proper classes to address some style sheets, jump into the
559
- Page element and add some style sheet:
560
-
561
- <head>
562
- <title>TodoList</title>
563
- <style>
564
- table { width: 100%; }
565
- tr { background: #efe; width: 100%; }
566
- tr:hover { background: #dfd; }
567
- td.title { font-weight: bold; width: 60%; }
568
- td.status { margin: 1em; }
569
- a { color: #3a3; }
570
- </style>
571
- </head>
572
- {:lang=ezamar}
573
-
574
- That looks quite a bit nicer, right? And yes, if you don't like tables (though
575
- this is an entirely legit use in my opinion) you can just do it like you want,
576
- using nested lists or divs/spans, replacing the open/close and delete links with
577
- nice images and changing the style according to the status.
578
-
579
- I will leave this as an exercise to the reader.
580
-
581
-
582
- ## Tenth Step, Configuration
583
-
584
- To round up this tutorial a bit, let's introduce you to configuration in Ramaze.
585
- This will not go into full depth of possibilities or a total coverage of the
586
- options, since they are bound to change over time.
587
-
588
- First of all, the default port Ramaze runs on is 7000, but to make it a usual
589
- webserver it has to run on port 80. So, let's add following line in your
590
- start.rb right after the lines of require you added before:
591
-
592
- Ramaze::Global.port = 80
593
- {:lang=ruby}
594
-
595
- Alright, that wasn't that hard.
596
- Let's say now you also want to run Mongrel instead of WEBrick, to get nice a bit
597
- of performance:
598
-
599
- Ramaze::Global.adapter = :mongrel
600
- {:lang=ruby}
601
-
602
- To do this in a DRY way you could also do following:
603
-
604
- Ramaze::Global.setup do |g|
605
- g.port = 80
606
- g.adapter = :mongrel
607
- end
608
- {:lang=ruby}
609
-
610
- It seems to be quite common to put this configuration into separate files so you
611
- can just require it on demand. There are other, slightly stronger way to set
612
- options, which is either using flags on the ramaze executable, or like this:
613
-
614
- Ramaze.start :port => 80, :adapter => :mongrel
615
- {:lang=ruby}
616
-
617
- We haven't started Ramaze directly as of yet, but this allows you to ignore the
618
- ramaze executable and just run your application by `ruby start.rb`.
619
-
620
-
621
- ## Eleventh Step, Refactor with AspectHelper
622
-
623
- Now, if you take a closer look at the Controller you will see:
624
-
625
- def create
626
- title = request['title']
627
- TodoList[title] = {:done => false}
628
- redirect R(self)
629
- end
630
-
631
- def open title
632
- task_status title, false
633
- redirect R(self)
634
- end
635
-
636
- def close title
637
- task_status title, true
638
- redirect R(self)
639
- end
640
-
641
- def delete title
642
- TodoList.delete title
643
- redirect R(self)
644
- end
645
- {:lang=ruby}
646
-
647
- We did some refactoring before, by introducing `#task_status`, but here we have
648
- repetition again: `redirect Rs()` _after_ each method did its job.
649
-
650
- However, we can take advantage of one of the helpers Ramaze offers, the
651
- AspectHelper.
652
- It allows you to easily wrap actions in your controller with other methods
653
-
654
- In your Controller, replace the previous chunk with following:
655
-
656
- def create
657
- title = request['title']
658
- TodoList[title] = {:done => false}
659
- end
660
-
661
- def open title
662
- task_status title, false
663
- end
664
-
665
- def close title
666
- task_status title, true
667
- end
668
-
669
- def delete title
670
- TodoList.delete title
671
- end
672
-
673
- helper :aspect
674
- after(:create, :open, :close, :delete){ redirect(Rs()) }
675
- {:lang=ruby}
676
-
677
- Alright, that looks a lot nicer already and is definitely easier to maintain.
678
-
679
- There is a symmetrical `before` aspect that you could take advantage of as
680
- well, and in case you want to add required authentication for all actions of a
681
- Controller you could use `before_all` and `after_all` instead of a list of
682
- action-names.
683
-
684
-
685
- ## Twelfth Step, Validation and Errors
686
-
687
- Right now, all kinds of things could still go wrong when you do things like
688
- creating tasks with no title at all or try to open/close a task that does not
689
- exist. So in this step we will add some little checks for these cases.
690
-
691
- First we head over to the Controller again and take a look at `#create`:
692
-
693
- def create
694
- title = request['title']
695
- TodoList[title] = {:done => false}
696
- end
697
- {:lang=ruby}
698
-
699
- Here we just create a new task, no matter what we get. Every seasoned
700
- web-developer would advise you to be suspicious about all the input you receive
701
- from your users, so let's apply this advice.
702
-
703
- def create
704
- if title = request['title']
705
- title.strip!
706
- if title.empty?
707
- failed("Please enter a title")
708
- redirect '/new'
709
- end
710
- TodoList[title] = {:done => false}
711
- end
712
- end
713
- {:lang=ruby}
714
-
715
- First of all we check if we got a request with a value for 'title', if we get
716
- none we just let the aspect kick in that will redirect the browser to the index.
717
- Next we strip the title of all spaces around it so we can check if it is empty.
718
- We will talk about the specifics of our error-handling now.
719
-
720
- Ramaze has a helper called FlashHelper, that will keep a hash associated with
721
- the session for one request, afterwards the hash is thrown away. This is
722
- specifically useful for giving the user feedback while keeping a stateless
723
- approach.
724
-
725
- Let me show you our `#failed` method (it goes in the private section to
726
- `#task_status`):
727
-
728
- def failed(message)
729
- flash[:error] = message
730
- end
731
- {:lang=ruby}
732
-
733
- Duh, you may say, wouldn't that fit in the one line instead of the call to
734
- `#failed`?
735
- Indeed, it would, but let me remind you, we have no checks for changing the
736
- status of a task yet. We will need error-handling there as well, so we just keep
737
- our code DRY and maintainable by collecting shared behaviour in small pieces.
738
-
739
- Now on to the `#task_status`:
740
-
741
- def task_status title, status
742
- unless task = TodoList[title]
743
- failed "No such Task: `#{title}'"
744
- redirect_referer
745
- end
746
-
747
- task[:done] = status
748
- TodoList[title] = task
749
- end
750
- {:lang=ruby}
751
-
752
- That used to look like this:
753
-
754
- def task_status title, status
755
- task = TodoList[title]
756
- task[:done] = status
757
- TodoList[title] = task
758
- end
759
- {:lang=ruby}
760
-
761
- So in fact all we added is a check whether a task already exists, set an
762
- error-message in case it doesn't and redirect to wherever the browser came from.
763
-
764
- But what about actually showing the error-messages we so carefully set? Well,
765
- where do we change the view? Right, in the templates. But both templates we have
766
- so far (index and new) share this behaviour, so we head over to the Element
767
- and add in the right place:
768
-
769
- <body>
770
- <h1>#{@title}</h1>
771
- <?r if flash[:error] ?>
772
- <div class="error">
773
- \\#{flash[:error]}
774
- </div>
775
- <?r end ?>
776
- #{content}
777
- </body>
778
- {:lang=ezamar}
779
-
780
- The only thing special about it is the `\\#{flash[:error]}`, we have to escape
781
- the `#` so it won't evaluate this immediately but wait until it is really
782
- rendered.
783
- As a note, If you read this as pure markaby, the double backslash is to output
784
- properly to HTML, just use one instead.
785
- Again, you can add some nifty style for that.
786
-
787
- To be continued...