Pistos-ramaze 2009.04.08 → 2009.06.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (227) hide show
  1. data/.gitignore +3 -0
  2. data/MANIFEST +37 -22
  3. data/{README.markdown → README.md} +5 -24
  4. data/Rakefile +33 -26
  5. data/benchmark/bench_templates/bench.rb +67 -0
  6. data/benchmark/bench_templates/view/large.erb +79 -0
  7. data/benchmark/bench_templates/view/large.haml +41 -0
  8. data/benchmark/bench_templates/view/large.xhtml +79 -0
  9. data/benchmark/bench_templates/view/small.erb +21 -0
  10. data/benchmark/bench_templates/view/small.haml +12 -0
  11. data/benchmark/bench_templates/view/small.xhtml +21 -0
  12. data/benchmark/run.rb +14 -21
  13. data/benchmark/suite/minimal.rb +3 -5
  14. data/benchmark/suite/no_informer.rb +0 -2
  15. data/benchmark/suite/no_sessions.rb +3 -4
  16. data/benchmark/suite/no_template.rb +1 -3
  17. data/benchmark/suite/simple.rb +1 -3
  18. data/benchmark/suite/template_erubis.rb +0 -2
  19. data/benchmark/suite/template_etanni.rb +8 -0
  20. data/benchmark/suite/template_ezamar.rb +1 -3
  21. data/benchmark/suite/template_haml.rb +0 -2
  22. data/benchmark/suite/template_liquid.rb +0 -2
  23. data/benchmark/suite/template_markaby.rb +0 -2
  24. data/benchmark/suite/template_nagoro.rb +1 -3
  25. data/benchmark/suite/template_redcloth.rb +0 -2
  26. data/benchmark/suite/template_tenjin.rb +0 -2
  27. data/bin/ramaze +11 -163
  28. data/doc/AUTHORS +15 -8
  29. data/doc/CHANGELOG +10094 -12262
  30. data/doc/meta/announcement.txt +93 -46
  31. data/doc/tutorial/todolist.html +22 -58
  32. data/doc/tutorial/todolist.txt +20 -39
  33. data/examples/app/auth/layout/{auth.nag → auth.xhtml} +0 -0
  34. data/examples/app/auth/view/{index.nag → index.xhtml} +0 -0
  35. data/examples/app/auth/view/{login.nag → login.xhtml} +0 -0
  36. data/examples/app/auth/view/{secret.nag → secret.xhtml} +0 -0
  37. data/examples/app/blog/app.rb +1 -1
  38. data/examples/app/blog/controller/comment.rb +1 -1
  39. data/examples/app/blog/controller/entry.rb +12 -3
  40. data/examples/app/blog/model/init.rb +5 -0
  41. data/examples/app/blog/model/tag.rb +2 -2
  42. data/examples/app/blog/view/entry/index.nag +3 -3
  43. data/examples/app/blog/view/feed.atom.nag +1 -1
  44. data/examples/app/blog/view/feed.rss.nag +1 -1
  45. data/examples/app/blog/view/index.nag +1 -1
  46. data/examples/app/blog/view/tag/index.nag +1 -1
  47. data/examples/app/chat/layout/{default.nag → default.xhtml} +0 -0
  48. data/examples/app/chat/view/{chat.nag → chat.xhtml} +0 -0
  49. data/examples/app/chat/view/{index.nag → index.xhtml} +0 -0
  50. data/examples/app/localization/locale/de.yaml +5 -0
  51. data/examples/app/localization/locale/en.yaml +5 -0
  52. data/examples/app/localization/locale/ja.yaml +5 -0
  53. data/examples/app/localization/start.rb +33 -20
  54. data/examples/app/wikore/spec/wikore.rb +4 -6
  55. data/examples/app/wikore/src/controller.rb +9 -11
  56. data/examples/app/wikore/src/model.rb +7 -10
  57. data/examples/app/wikore/start.rb +1 -1
  58. data/examples/app/wikore/{template → view}/index.xhtml +0 -0
  59. data/examples/helpers/cache.rb +6 -4
  60. data/examples/misc/css.rb +6 -12
  61. data/examples/misc/rapp.rb +28 -17
  62. data/lib/proto/app.rb +14 -0
  63. data/lib/proto/config.ru +8 -5
  64. data/lib/proto/controller/init.rb +3 -3
  65. data/lib/proto/spec/main.rb +9 -11
  66. data/lib/proto/start.rb +6 -10
  67. data/lib/ramaze.rb +8 -9
  68. data/lib/ramaze/app.rb +4 -3
  69. data/lib/ramaze/cache/localmemcache.rb +2 -2
  70. data/lib/ramaze/cache/memcache.rb +26 -1
  71. data/lib/ramaze/cache/sequel.rb +24 -9
  72. data/lib/ramaze/contrib/addressable_route.rb +56 -0
  73. data/lib/ramaze/contrib/email.rb +2 -0
  74. data/lib/ramaze/contrib/facebook/facebook.rb +4 -4
  75. data/lib/ramaze/contrib/sequel/create_join.rb +1 -0
  76. data/lib/ramaze/contrib/sequel/form_field.rb +4 -4
  77. data/lib/ramaze/contrib/sequel/image.rb +9 -11
  78. data/lib/ramaze/contrib/sequel/relation.rb +17 -3
  79. data/lib/ramaze/controller.rb +26 -18
  80. data/lib/ramaze/gestalt.rb +4 -1
  81. data/lib/ramaze/helper.rb +1 -0
  82. data/lib/ramaze/helper/auth.rb +0 -5
  83. data/lib/ramaze/helper/cache.rb +65 -25
  84. data/lib/ramaze/helper/form.rb +14 -2
  85. data/lib/ramaze/helper/formatting.rb +3 -1
  86. data/lib/ramaze/helper/gestalt.rb +32 -0
  87. data/lib/ramaze/helper/identity.rb +3 -3
  88. data/lib/ramaze/helper/layout.rb +97 -0
  89. data/lib/ramaze/helper/link.rb +6 -25
  90. data/lib/ramaze/helper/localize.rb +13 -3
  91. data/lib/ramaze/helper/paginate.rb +4 -2
  92. data/lib/ramaze/helper/ultraviolet.rb +2 -0
  93. data/lib/ramaze/helper/user.rb +3 -3
  94. data/lib/ramaze/log/rotatinginformer.rb +10 -10
  95. data/lib/ramaze/request.rb +3 -5
  96. data/lib/ramaze/setup.rb +22 -9
  97. data/lib/ramaze/spec.rb +8 -12
  98. data/lib/ramaze/spec/bacon.rb +34 -0
  99. data/lib/ramaze/spec/helper/template_examples.rb +15 -22
  100. data/lib/ramaze/tool/bin.rb +340 -0
  101. data/lib/ramaze/tool/project_creator.rb +1 -1
  102. data/lib/ramaze/version.rb +1 -1
  103. data/lib/ramaze/view.rb +6 -11
  104. data/lib/ramaze/view/erubis.rb +2 -2
  105. data/lib/ramaze/view/ezamar.rb +2 -2
  106. data/lib/ramaze/view/gestalt.rb +14 -0
  107. data/lib/ramaze/view/haml.rb +2 -1
  108. data/lib/ramaze/view/liquid.rb +45 -11
  109. data/lib/ramaze/view/maruku.rb +2 -1
  110. data/lib/ramaze/view/nagoro/render_partial.rb +9 -9
  111. data/lib/ramaze/view/sass.rb +1 -1
  112. data/lib/ramaze/view/tenjin.rb +7 -4
  113. data/lib/vendor/etag.rb +4 -2
  114. data/lib/vendor/route_exceptions.rb +9 -15
  115. data/ramaze.gemspec +56 -9
  116. data/spec/contrib/addressable_route.rb +30 -0
  117. data/spec/contrib/rest.rb +2 -2
  118. data/spec/examples/caching.rb +5 -7
  119. data/spec/examples/css.rb +3 -3
  120. data/spec/examples/element.rb +4 -3
  121. data/spec/examples/hello.rb +3 -3
  122. data/spec/examples/helpers/httpdigest.rb +44 -69
  123. data/spec/examples/linking.rb +3 -3
  124. data/spec/examples/simple.rb +3 -3
  125. data/spec/examples/templates/template_erubis.rb +8 -4
  126. data/spec/examples/templates/template_ezamar.rb +9 -4
  127. data/spec/examples/templates/template_haml.rb +8 -4
  128. data/spec/examples/templates/template_liquid.rb +8 -4
  129. data/spec/examples/templates/template_markaby.rb +9 -5
  130. data/spec/examples/templates/template_nagoro.rb +9 -5
  131. data/spec/examples/templates/template_redcloth.rb +8 -4
  132. data/spec/examples/templates/template_remarkably.rb +8 -4
  133. data/spec/examples/templates/template_tenjin.rb +8 -4
  134. data/spec/helper.rb +1 -2
  135. data/spec/ramaze/action/render.rb +4 -1
  136. data/spec/ramaze/app.rb +5 -2
  137. data/spec/ramaze/bin/ramaze.rb +96 -0
  138. data/spec/ramaze/cache/localmemcache.rb +1 -1
  139. data/spec/ramaze/cache/memcache.rb +7 -1
  140. data/spec/ramaze/cache/sequel.rb +1 -1
  141. data/spec/ramaze/controller/actionless_templates.rb +2 -2
  142. data/spec/ramaze/controller/lonely_mapping.rb +18 -0
  143. data/spec/ramaze/controller/mapping.rb +4 -15
  144. data/spec/ramaze/controller/provide_inheritance.rb +5 -2
  145. data/spec/ramaze/controller/resolve.rb +2 -2
  146. data/spec/ramaze/controller/subclass.rb +2 -2
  147. data/spec/ramaze/controller/template_resolving.rb +2 -2
  148. data/spec/ramaze/dispatcher/directory.rb +5 -2
  149. data/spec/ramaze/dispatcher/file.rb +28 -26
  150. data/spec/ramaze/error.rb +3 -3
  151. data/spec/ramaze/files.rb +5 -2
  152. data/spec/ramaze/gestalt.rb +26 -17
  153. data/spec/ramaze/helper/auth.rb +30 -41
  154. data/spec/ramaze/helper/bench.rb +6 -2
  155. data/spec/ramaze/helper/cache.rb +45 -5
  156. data/spec/ramaze/helper/flash.rb +11 -15
  157. data/spec/ramaze/helper/form.rb +6 -3
  158. data/spec/ramaze/helper/formatting.rb +4 -1
  159. data/spec/ramaze/helper/gestalt.rb +4 -1
  160. data/spec/ramaze/helper/gravatar.rb +4 -1
  161. data/spec/ramaze/helper/httpdigest.rb +51 -66
  162. data/spec/ramaze/helper/layout.rb +79 -0
  163. data/spec/ramaze/helper/layout/default.xhtml +5 -0
  164. data/spec/ramaze/helper/link.rb +33 -12
  165. data/spec/ramaze/helper/localize.rb +9 -4
  166. data/spec/ramaze/helper/maruku.rb +5 -2
  167. data/spec/ramaze/helper/pager.rb +2 -2
  168. data/spec/ramaze/helper/paginate.rb +5 -2
  169. data/spec/ramaze/helper/request_accessor.rb +5 -2
  170. data/spec/ramaze/helper/sequel_form.rb +7 -2
  171. data/spec/ramaze/helper/simple_captcha.rb +10 -14
  172. data/spec/ramaze/helper/stack.rb +32 -40
  173. data/spec/ramaze/helper/user.rb +19 -14
  174. data/spec/ramaze/helper/xhtml.rb +4 -1
  175. data/spec/ramaze/log/informer.rb +1 -1
  176. data/spec/ramaze/log/logging.rb +4 -1
  177. data/spec/ramaze/log/syslog.rb +6 -6
  178. data/spec/ramaze/params.rb +5 -6
  179. data/spec/ramaze/request.rb +16 -1
  180. data/spec/ramaze/session/memcache.rb +66 -0
  181. data/spec/ramaze/struct.rb +4 -1
  182. data/spec/ramaze/view.rb +2 -2
  183. data/spec/ramaze/view/erubis.rb +2 -2
  184. data/spec/ramaze/view/ezamar.rb +2 -2
  185. data/spec/ramaze/view/gestalt.rb +94 -0
  186. data/spec/ramaze/view/gestalt/external.ges +8 -0
  187. data/spec/ramaze/view/haml.rb +2 -2
  188. data/spec/ramaze/view/liquid.rb +2 -2
  189. data/spec/ramaze/view/nagoro.rb +2 -2
  190. data/spec/ramaze/view/redcloth.rb +2 -2
  191. data/spec/ramaze/view/remarkably.rb +2 -2
  192. data/spec/ramaze/view/sass.rb +2 -2
  193. data/spec/ramaze/view/tagz.rb +2 -2
  194. data/spec/ramaze/view/tenjin.rb +2 -2
  195. data/spec/snippets/array/put_within.rb +30 -25
  196. data/spec/snippets/binding/locals.rb +4 -1
  197. data/spec/snippets/numeric/filesize_format.rb +4 -1
  198. data/spec/snippets/numeric/time.rb +5 -2
  199. data/spec/snippets/object/__dir__.rb +4 -1
  200. data/spec/snippets/ordered_set.rb +4 -1
  201. data/spec/snippets/ramaze/acquire.rb +4 -1
  202. data/spec/snippets/ramaze/dictionary.rb +4 -1
  203. data/spec/snippets/ramaze/struct.rb +4 -1
  204. data/spec/snippets/string/camel_case.rb +4 -1
  205. data/spec/snippets/string/color.rb +4 -1
  206. data/spec/snippets/string/snake_case.rb +4 -1
  207. data/spec/snippets/string/unindent.rb +4 -1
  208. data/spec/snippets/thread/into.rb +4 -1
  209. data/tasks/bacon.rake +5 -3
  210. data/tasks/changelog.rake +3 -1
  211. data/tasks/{gem_installer.rake → gem_setup.rake} +45 -22
  212. data/tasks/release.rake +12 -27
  213. data/tasks/setup.rake +6 -0
  214. data/tasks/todo.rake +2 -4
  215. metadata +205 -31
  216. data/CHANGELOG +0 -16546
  217. data/benchmark/suite/template_builder.rb +0 -12
  218. data/doc/tutorial/todolist.mkd +0 -787
  219. data/examples/helpers/provide.rb +0 -23
  220. data/lib/proto/view/page.xhtml +0 -27
  221. data/lib/ramaze/snippets/divide.rb +0 -22
  222. data/lib/ramaze/snippets/kernel/constant.rb +0 -41
  223. data/lib/ramaze/snippets/object/acquire.rb +0 -37
  224. data/lib/ramaze/snippets/string/each.rb +0 -19
  225. data/spec/ramaze/helper/partial.rb +0 -40
  226. data/spec/snippets/kernel/constant.rb +0 -23
  227. data/tasks/install_dependencies.rake +0 -6
@@ -1,12 +0,0 @@
1
- require 'ramaze'
2
-
3
- class MainController < Ramaze::Controller
4
- engine :Builder
5
-
6
- def index
7
- @hello = "Hello, World!"
8
- %q[
9
- xml.html {|h| h.body {|b| 10.times { b.span @hello } }}
10
- ]
11
- end
12
- end
@@ -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.net .
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...