ramaze 2011.01.30 → 2011.07.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (229) hide show
  1. data/.gitignore +2 -1
  2. data/.mailmap +2 -0
  3. data/.rvmrc +1 -0
  4. data/README.md +119 -197
  5. data/Rakefile +14 -97
  6. data/bin/ramaze +6 -14
  7. data/doc/AUTHORS +8 -4
  8. data/doc/CHANGELOG +3784 -3339
  9. data/examples/app/chat/start.rb +2 -2
  10. data/lib/proto/app.rb +2 -3
  11. data/lib/proto/config.ru +4 -5
  12. data/lib/proto/controller/init.rb +11 -4
  13. data/lib/proto/controller/main.rb +12 -7
  14. data/lib/proto/layout/default.xhtml +56 -23
  15. data/lib/proto/model/init.rb +3 -1
  16. data/lib/proto/public/css/grid.css +107 -0
  17. data/lib/proto/public/css/layout.css +81 -0
  18. data/lib/proto/public/css/reset.css +123 -0
  19. data/lib/proto/public/css/text.css +109 -0
  20. data/lib/proto/public/images/bg.png +0 -0
  21. data/lib/proto/spec/main.rb +2 -2
  22. data/lib/proto/start.rb +11 -1
  23. data/lib/proto/view/index.xhtml +27 -23
  24. data/lib/ramaze.rb +0 -1
  25. data/lib/ramaze/app.rb +85 -12
  26. data/lib/ramaze/app_graph.rb +107 -0
  27. data/lib/ramaze/bin/console.rb +87 -0
  28. data/lib/ramaze/bin/create.rb +94 -0
  29. data/lib/ramaze/bin/helper.rb +107 -0
  30. data/lib/ramaze/bin/restart.rb +95 -0
  31. data/lib/ramaze/bin/runner.rb +141 -0
  32. data/lib/ramaze/bin/start.rb +206 -0
  33. data/lib/ramaze/bin/status.rb +152 -0
  34. data/lib/ramaze/bin/stop.rb +112 -0
  35. data/lib/ramaze/cache.rb +9 -4
  36. data/lib/ramaze/cache/localmemcache.rb +10 -13
  37. data/lib/ramaze/cache/lru.rb +49 -7
  38. data/lib/ramaze/cache/memcache.rb +170 -92
  39. data/lib/ramaze/cache/sequel.rb +301 -118
  40. data/lib/ramaze/controller.rb +108 -9
  41. data/lib/ramaze/controller/default.rb +15 -2
  42. data/lib/ramaze/current.rb +14 -2
  43. data/lib/ramaze/dependencies.rb +46 -0
  44. data/lib/ramaze/files.rb +38 -3
  45. data/lib/ramaze/gestalt.rb +12 -12
  46. data/lib/ramaze/helper.rb +0 -2
  47. data/lib/ramaze/helper/auth.rb +30 -23
  48. data/lib/ramaze/helper/blue_form.rb +175 -126
  49. data/lib/ramaze/helper/csrf.rb +76 -91
  50. data/lib/ramaze/helper/email.rb +105 -0
  51. data/lib/ramaze/helper/erector.rb +16 -15
  52. data/lib/ramaze/helper/gestalt.rb +2 -2
  53. data/lib/ramaze/helper/layout.rb +89 -73
  54. data/lib/ramaze/helper/link.rb +7 -6
  55. data/lib/ramaze/helper/localize.rb +6 -5
  56. data/lib/ramaze/helper/markaby.rb +25 -23
  57. data/lib/ramaze/helper/maruku.rb +3 -3
  58. data/lib/ramaze/helper/paginate.rb +19 -27
  59. data/lib/ramaze/helper/remarkably.rb +3 -3
  60. data/lib/ramaze/helper/request_accessor.rb +3 -3
  61. data/lib/ramaze/helper/send_file.rb +12 -8
  62. data/lib/ramaze/helper/simple_captcha.rb +5 -6
  63. data/lib/ramaze/helper/stack.rb +7 -4
  64. data/lib/ramaze/helper/tagz.rb +10 -11
  65. data/lib/ramaze/helper/thread.rb +19 -16
  66. data/lib/ramaze/helper/ultraviolet.rb +7 -4
  67. data/lib/ramaze/helper/user.rb +40 -21
  68. data/lib/ramaze/helper/xhtml.rb +29 -20
  69. data/lib/ramaze/log.rb +3 -11
  70. data/lib/ramaze/log/analogger.rb +5 -4
  71. data/lib/ramaze/log/growl.rb +9 -7
  72. data/lib/ramaze/log/hub.rb +3 -5
  73. data/lib/ramaze/log/informer.rb +15 -12
  74. data/lib/ramaze/log/knotify.rb +3 -5
  75. data/lib/ramaze/log/logger.rb +3 -5
  76. data/lib/ramaze/log/logging.rb +6 -8
  77. data/lib/ramaze/log/rotatinginformer.rb +27 -17
  78. data/lib/ramaze/log/syslog.rb +7 -7
  79. data/lib/ramaze/log/xosd.rb +3 -5
  80. data/lib/ramaze/middleware_compiler.rb +27 -4
  81. data/lib/ramaze/reloader.rb +50 -12
  82. data/lib/ramaze/reloader/watch_inotify.rb +4 -5
  83. data/lib/ramaze/reloader/watch_stat.rb +3 -3
  84. data/lib/ramaze/request.rb +18 -8
  85. data/lib/ramaze/response.rb +38 -7
  86. data/lib/ramaze/rest.rb +36 -0
  87. data/lib/ramaze/setup.rb +101 -31
  88. data/lib/ramaze/spec.rb +1 -1
  89. data/lib/ramaze/spec/bacon.rb +6 -3
  90. data/lib/ramaze/spec/helper/bacon.rb +0 -1
  91. data/lib/ramaze/version.rb +1 -1
  92. data/lib/ramaze/view.rb +2 -11
  93. data/lib/ramaze/view/erector.rb +46 -31
  94. data/lib/ramaze/view/erubis.rb +7 -3
  95. data/lib/ramaze/view/ezamar.rb +7 -3
  96. data/lib/ramaze/view/gestalt.rb +9 -3
  97. data/lib/ramaze/view/haml.rb +7 -3
  98. data/lib/ramaze/view/liquid.rb +3 -3
  99. data/lib/ramaze/view/lokar.rb +7 -3
  100. data/lib/ramaze/view/mustache.rb +11 -5
  101. data/lib/ramaze/view/nagoro.rb +3 -3
  102. data/lib/ramaze/view/sass.rb +1 -1
  103. data/lib/ramaze/view/slippers.rb +40 -13
  104. data/lib/ramaze/view/tagz.rb +9 -5
  105. data/ramaze.gemspec +23 -128
  106. data/spec/helper.rb +5 -0
  107. data/spec/ramaze/bin/app/config.ru +11 -0
  108. data/spec/ramaze/bin/create.rb +28 -0
  109. data/spec/ramaze/bin/runner.rb +30 -0
  110. data/spec/ramaze/bin/start.rb +38 -0
  111. data/spec/ramaze/cache/memcache.rb +10 -3
  112. data/spec/ramaze/cache/sequel.rb +7 -0
  113. data/spec/ramaze/controller/provide_inheritance.rb +0 -10
  114. data/spec/ramaze/dispatcher/file.rb +19 -15
  115. data/spec/ramaze/helper/auth.rb +10 -9
  116. data/spec/ramaze/helper/blue_form.rb +121 -68
  117. data/spec/ramaze/helper/email.rb +69 -0
  118. data/spec/ramaze/helper/layout.rb +12 -15
  119. data/spec/ramaze/helper/layout/alternative.xhtml +5 -0
  120. data/spec/ramaze/helper/user.rb +2 -0
  121. data/spec/ramaze/log/growl.rb +14 -1
  122. data/spec/{contrib → ramaze}/rest.rb +1 -1
  123. data/spec/ramaze/session/memcache.rb +2 -2
  124. data/spec/ramaze/view/sass.rb +1 -1
  125. data/tasks/bacon.rake +3 -3
  126. data/tasks/gem.rake +17 -18
  127. data/tasks/rcov.rake +2 -3
  128. data/tasks/release.rake +8 -65
  129. data/tasks/setup.rake +10 -8
  130. data/tasks/todo.rake +9 -5
  131. data/tasks/yard.rake +3 -2
  132. metadata +105 -397
  133. data/MANIFEST +0 -532
  134. data/TODO.md +0 -19
  135. data/benchmark/bench_templates/bench.rb +0 -67
  136. data/benchmark/bench_templates/view/large.erb +0 -79
  137. data/benchmark/bench_templates/view/large.haml +0 -41
  138. data/benchmark/bench_templates/view/large.lok +0 -79
  139. data/benchmark/bench_templates/view/large.xhtml +0 -79
  140. data/benchmark/bench_templates/view/small.erb +0 -21
  141. data/benchmark/bench_templates/view/small.haml +0 -12
  142. data/benchmark/bench_templates/view/small.lok +0 -21
  143. data/benchmark/bench_templates/view/small.xhtml +0 -21
  144. data/benchmark/results.txt +0 -131
  145. data/benchmark/run.rb +0 -355
  146. data/benchmark/suite/minimal.rb +0 -11
  147. data/benchmark/suite/no_informer.rb +0 -7
  148. data/benchmark/suite/no_sessions.rb +0 -9
  149. data/benchmark/suite/no_template.rb +0 -7
  150. data/benchmark/suite/simple.rb +0 -5
  151. data/benchmark/suite/template_erubis.rb +0 -8
  152. data/benchmark/suite/template_etanni.rb +0 -8
  153. data/benchmark/suite/template_ezamar.rb +0 -8
  154. data/benchmark/suite/template_haml.rb +0 -13
  155. data/benchmark/suite/template_liquid.rb +0 -11
  156. data/benchmark/suite/template_markaby.rb +0 -9
  157. data/benchmark/suite/template_nagoro.rb +0 -8
  158. data/benchmark/suite/template_redcloth.rb +0 -13
  159. data/benchmark/suite/template_tenjin.rb +0 -8
  160. data/benchmark/test.rb +0 -35
  161. data/doc/FAQ +0 -92
  162. data/doc/INSTALL +0 -92
  163. data/doc/TODO +0 -29
  164. data/doc/meta/announcement.txt +0 -119
  165. data/doc/meta/configuration.txt +0 -163
  166. data/doc/meta/internals.txt +0 -278
  167. data/doc/meta/users.kml +0 -64
  168. data/doc/tutorial/todolist.html +0 -1512
  169. data/doc/tutorial/todolist.txt +0 -920
  170. data/examples/app/sourceview/public/coderay.css +0 -104
  171. data/examples/app/sourceview/public/images/file.gif +0 -0
  172. data/examples/app/sourceview/public/images/folder.gif +0 -0
  173. data/examples/app/sourceview/public/images/tv-collapsable-last.gif +0 -0
  174. data/examples/app/sourceview/public/images/tv-collapsable.gif +0 -0
  175. data/examples/app/sourceview/public/images/tv-expandable-last.gif +0 -0
  176. data/examples/app/sourceview/public/images/tv-expandable.gif +0 -0
  177. data/examples/app/sourceview/public/images/tv-item-last.gif +0 -0
  178. data/examples/app/sourceview/public/images/tv-item.gif +0 -0
  179. data/examples/app/sourceview/public/jquery.js +0 -11
  180. data/examples/app/sourceview/public/jquery.treeview.css +0 -48
  181. data/examples/app/sourceview/public/jquery.treeview.js +0 -223
  182. data/examples/app/sourceview/public/sourceview.js +0 -52
  183. data/examples/app/sourceview/start.rb +0 -79
  184. data/examples/app/sourceview/view/index.haml +0 -59
  185. data/examples/helpers/httpdigest.rb +0 -107
  186. data/lib/proto/public/css/screen.css +0 -30
  187. data/lib/proto/public/js/jquery.js +0 -7179
  188. data/lib/ramaze/contrib/addressable_route.rb +0 -56
  189. data/lib/ramaze/contrib/app_graph.rb +0 -64
  190. data/lib/ramaze/contrib/email.rb +0 -88
  191. data/lib/ramaze/contrib/facebook.rb +0 -23
  192. data/lib/ramaze/contrib/facebook/facebook.rb +0 -171
  193. data/lib/ramaze/contrib/gettext.rb +0 -113
  194. data/lib/ramaze/contrib/gettext/mo.rb +0 -155
  195. data/lib/ramaze/contrib/gettext/parser.rb +0 -46
  196. data/lib/ramaze/contrib/gettext/po.rb +0 -109
  197. data/lib/ramaze/contrib/gzip_filter.rb +0 -1
  198. data/lib/ramaze/contrib/maruku_uv.rb +0 -59
  199. data/lib/ramaze/contrib/profiling.rb +0 -36
  200. data/lib/ramaze/contrib/rest.rb +0 -23
  201. data/lib/ramaze/contrib/sequel/create_join.rb +0 -26
  202. data/lib/ramaze/contrib/sequel/form_field.rb +0 -129
  203. data/lib/ramaze/contrib/sequel/image.rb +0 -196
  204. data/lib/ramaze/contrib/sequel/relation.rb +0 -98
  205. data/lib/ramaze/helper/httpdigest.rb +0 -96
  206. data/lib/ramaze/tool/bin.rb +0 -340
  207. data/lib/ramaze/tool/create.rb +0 -48
  208. data/lib/ramaze/tool/project_creator.rb +0 -120
  209. data/lib/ramaze/view/less.rb +0 -12
  210. data/lib/ramaze/view/maruku.rb +0 -15
  211. data/lib/ramaze/view/redcloth.rb +0 -21
  212. data/spec/contrib/addressable_route.rb +0 -30
  213. data/spec/examples/helpers/httpdigest.rb +0 -64
  214. data/spec/examples/templates/template_redcloth.rb +0 -13
  215. data/spec/ramaze/bin/ramaze.rb +0 -96
  216. data/spec/ramaze/helper/httpdigest.rb +0 -176
  217. data/spec/ramaze/view/less.rb +0 -60
  218. data/spec/ramaze/view/less/file.css.less +0 -8
  219. data/spec/ramaze/view/redcloth.rb +0 -66
  220. data/spec/ramaze/view/redcloth/external.redcloth +0 -8
  221. data/tasks/copyright.rake +0 -21
  222. data/tasks/gem_setup.rake +0 -112
  223. data/tasks/git.rake +0 -46
  224. data/tasks/grancher.rake +0 -12
  225. data/tasks/jquery.rake +0 -15
  226. data/tasks/manifest.rake +0 -4
  227. data/tasks/metric_changes.rake +0 -24
  228. data/tasks/reversion.rake +0 -8
  229. data/tasks/traits.rake +0 -21
@@ -1,920 +0,0 @@
1
- The official Ramaze Todo-list tutorial
2
- =====================================
3
- Michael 'manveru' Fellinger <m.fellinger@gmail.com>
4
- revision 2.0, March 2009
5
-
6
- == Abstract
7
-
8
- Welcome to the official tutorial for http://ramaze.net[Ramaze], the mandatory
9
- Todo-list.
10
-
11
- I also assume that you have some experience with HTML and some other basics in
12
- web-development already (you want to learn a web-framework after all).
13
-
14
- The tutorial assumes a working installation of http://ruby-lang.org[Ruby] and
15
- http://rubygems.org/[Rubygems].
16
-
17
- For more information on how to install these please read the introductory
18
- documentation of Ramaze, this is not in the scope of this tutorial.
19
-
20
- To install Ramaze you can `gem install ramaze`, other ways of installation are
21
- covered by the http://wiki.ramaze.net/[Ramaze Wiki].
22
-
23
- Should you encounter any problems while doing this tutorial, this might either
24
- be because Ramaze changed (which happens very often while it is still young)
25
- or I actually made some mistake while writing it.
26
-
27
- In either case it would make me (and all other poor fellows who happen to try
28
- this tutorial) very happy if you could spare some time and report the issue
29
- either on the http://github.com/manveru/ramaze/issues[Bug tracker] , or just
30
- drop by on IRC on `irc.freenode.org` in the channel `#ramaze`.
31
-
32
- If you have trouble with some of the terms used in this tutorial you can
33
- consult the <<glossary, Glossary>> at the end of this document.
34
-
35
- We are also working on a book that describes Ramaze in more depth, called
36
- 'Journey to Ramaze', it is still very much work in progress, but some of the
37
- contents might interest you.
38
-
39
- The repository for the book is at http://github.com/manveru/ramaze-book. Every
40
- once in a while, updates for the book will be put in HTML and PDF form at
41
- http://book.ramaze.net.
42
-
43
-
44
- == First Step, Create
45
-
46
- The last version of this tutorial assumed a generator to produce a skeleton in
47
- which we do the work. This time around we will do everything from scratch to
48
- give you a better experience of how exactly the pieces fit together.
49
-
50
- You can also skip all the boring learning-by-doing part and play around with
51
- the source of the todo-list example shipping with Ramaze.
52
-
53
- NOTE: The example and this tutorial differ in some points, it is recommended to
54
- actually work through the tutorial first and read the example afterwards, it
55
- takes the basics taught here one step further by utilizing the Model.
56
-
57
- You can find the example it in the `examples/app/todolist/` directory of your
58
- Ramaze distribution.
59
- To find out where that is located (as this differs widely between systems), you
60
- can follow these steps in `irb`:
61
-
62
- [source,ruby]
63
- --------------------------------------------------------------------------------
64
- require 'rubygems'
65
- # => true
66
- require 'ramaze'
67
- # => true
68
- File.expand_path(Ramaze::ROOT + '/../examples/app/todolist')
69
- # => "/home/manveru/c/ramaze/examples/app/todolist"
70
- --------------------------------------------------------------------------------
71
-
72
- To start things off, we will create a basic directory structure looking like this:
73
-
74
- --------------------------------------------------------------------------------
75
- .
76
- |-- controller
77
- |-- layout
78
- |-- model
79
- |-- public
80
- | |-- css
81
- `-- view
82
- --------------------------------------------------------------------------------
83
-
84
- Doing that is quite simple: `mkdir -p controller layout model public/css view`
85
-
86
- Alright, done? Let's go to the next step.
87
-
88
- == Second Step. Hello, World!
89
-
90
- To make sure Ramaze is installed, and working correctly we will follow an old
91
- tradition, we create a file at the root of your application directory called
92
- 'start.rb' with following content:
93
-
94
- [source,ruby]
95
- --------------------------------------------------------------------------------
96
- require 'rubygems'
97
- require 'ramaze'
98
-
99
- class MainController < Ramaze::Controller
100
- def index
101
- "Hello, World!"
102
- end
103
- end
104
-
105
- Ramaze.start
106
- --------------------------------------------------------------------------------
107
-
108
- Now we run it:
109
-
110
- --------------------------------------------------------------------------------
111
- delta ~/tmp/tutorial % ruby start.rb
112
- D [2009-03-30 14:15:01 $2124] DEBUG | : Using webrick
113
- I [2009-03-30 14:15:01 $2124] INFO | : WEBrick 1.3.1
114
- I [2009-03-30 14:15:01 $2124] INFO | : ruby 1.9.2 (2009-03-02) [i686-linux]
115
- D [2009-03-30 14:15:01 $2124] DEBUG | : TCPServer.new(0.0.0.0, 7000)
116
- D [2009-03-30 14:15:01 $2124] DEBUG | : Rack::Handler::WEBrick is mounted on /.
117
- I [2009-03-30 14:15:01 $2124] INFO | : WEBrick::HTTPServer#start: pid=2124 port=7000
118
- --------------------------------------------------------------------------------
119
-
120
- The logging output tells us that a server was started, listening to all
121
- connections at port 7000. If you open your browser and go to
122
- http://localhost:7000/ you should be able to see 'Hello, World!'.
123
-
124
-
125
- == Third Step. M, like Model
126
-
127
- Model is a term from the MVC paradigm, meaning the representation of data
128
- within your application.
129
- Ramaze doesn't promote a particular way for this part of your application, and
130
- how you are supposed to integrate it. Since there are quite a number of ways to
131
- represent data and none is clearly superior to another, this would be both
132
- futile and short-sighted.
133
-
134
- For the purpose of this tutorial we will use a lightweight database access
135
- toolkit for Ruby called http://sequel.rubyforge.org/[Sequel].
136
-
137
- Sequel is designed to take the hassle away from connecting to databases and
138
- manipulating them. Sequel deals with all the boring stuff like maintaining
139
- connections, formatting SQL correctly and fetching records so you can
140
- concentrate on your application.
141
-
142
- Being familiar with it is not a requirement for this tutorial, but will help
143
- you tremendously when it comes to writing your own applications.
144
-
145
- Installing Sequel is as simple as installing Ramaze: `gem install sequel`.
146
-
147
- In this tutorial we are going to use the light-weight
148
- http://www.sqlite.org/[sqlite] database.
149
- This requires the http://rubyforge.org/projects/sqlite-ruby[sqlite-ruby]
150
- bindings.
151
-
152
- You can try to `gem install sqlite`, which will complain if your system doesn't
153
- provide bindings, in which case I have to refer you to http://sqlite.org.
154
-
155
- In order to use Sequel we also need a database connection.
156
-
157
- So we create a new file at 'model/init.rb' with following content:
158
-
159
- [source,ruby]
160
- --------------------------------------------------------------------------------
161
- require 'sequel'
162
-
163
- Sequel::Model.plugin(:schema)
164
-
165
- DB = Sequel.sqlite('todolist.db')
166
- --------------------------------------------------------------------------------
167
-
168
- The `:schema` plugin is required since Sequel 3.0, if you run a version prior
169
- to 2.12 you may remove this line if it gives you any problems.
170
-
171
- Next we edit 'start.rb', remove the `Hello` class, and add a require for the
172
- file, 'start.rb' should look like this now:
173
-
174
- [source,ruby]
175
- --------------------------------------------------------------------------------
176
- require 'rubygems'
177
- require 'ramaze'
178
-
179
- require 'model/init'
180
-
181
- Ramaze.start
182
- --------------------------------------------------------------------------------
183
-
184
- This should hook us up with a database, but anyone familiar with SQL will now
185
- ask how we are going to create our schema.
186
-
187
- So our next step is to create the actual model for our data, for this we create another file at 'model/task.rb':
188
-
189
- [source,ruby]
190
- --------------------------------------------------------------------------------
191
- class Task < Sequel::Model
192
- set_schema do
193
- primary_key :id
194
-
195
- varchar :title, :unique => true, :empty => false
196
- boolean :done, :default => false
197
- end
198
-
199
- create_table unless table_exists?
200
-
201
- if empty?
202
- create :title => 'Laundry'
203
- create :title => 'Wash dishes'
204
- end
205
- end
206
- --------------------------------------------------------------------------------
207
-
208
- For this tutorial we will not bother with migrations, although Sequel does have
209
- very good support for them as well, but seriously, this is a really simple
210
- schema that probably won't change much over the next few years.
211
-
212
- Finally, add a line to your 'model/init.rb' that requires 'model/task.rb':
213
-
214
- [source,ruby]
215
- --------------------------------------------------------------------------------
216
- require 'sequel'
217
-
218
- Sequel::Model.plugin(:schema)
219
-
220
- DB = Sequel.sqlite('todolist.db')
221
-
222
- require 'model/task'
223
- --------------------------------------------------------------------------------
224
-
225
-
226
- == Fourth Step, V, like View
227
-
228
- To see anything of the data in your Model we will have to add the second
229
- element in MVC, the View.
230
-
231
- We are going to use the templating engine shipping with Ramaze, called Etanni.
232
- It has a very simple syntax compatible with SGML and XML documents.
233
-
234
- When handling a request to '/', Ramaze will automatically try to find an Action
235
- called 'index'. Don't bother too much about what Action means just yet, we will
236
- explain that in more detail later when we come to layouts.
237
-
238
- To start we put some contents into 'view/index.xhtml' (xhtml is the default
239
- filename-extension for Etanni templates)
240
-
241
- [source,html]
242
- --------------------------------------------------------------------------------
243
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
244
- <html>
245
- <head>
246
- <title>TodoList</title>
247
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
248
- </head>
249
- <body>
250
- <h1>TodoList</h1>
251
- <ul>
252
- <?r Task.each do |task| ?>
253
- <li>#{ h(task.title) }: #{ task.done }</li>
254
- <?r end ?>
255
- </ul>
256
- </body>
257
- </html>
258
- --------------------------------------------------------------------------------
259
-
260
- The '<?r ?>' and '\#{ }' elements enclose ruby code that will be executed when
261
- the template is being rendered (on every request to 'index').
262
- Code within '<?r ?>' is only executed and will not show up in the resulting
263
- document, while code within '#{ }' will be interpolated.
264
-
265
- In this template we iterate over all the data stored in the Task model,
266
- yielding a list of task titles and the respective status of the task.
267
-
268
- That wasn't too hard, right?
269
-
270
- Now, so we can get our instant pleasure of seeing the result of our (hard)
271
- work, let's see how this looks like in a browser, start your application like above with `ruby start.rb` and open http://localhost:7000/.
272
-
273
- The template expanded to something like (only showing the interesting part):
274
-
275
- [source,html]
276
- --------------------------------------------------------------------------------
277
- <ul>
278
- <li>Laundry: false</li>
279
- <li>Wash dishes: false</li>
280
- </ul>
281
- --------------------------------------------------------------------------------
282
-
283
- That wasn't too bad, huh?
284
-
285
-
286
- == Fifth Step, C, like Controller
287
-
288
- The last part of the MVC paradigm is the Controller. As the name indicates it
289
- gives you control over the interaction between Model and View.
290
-
291
- Wouldn't it be nice to have a way to add and remove items on our to-do list?
292
- Editing the model every time would be quite tiresome and problematic to do
293
- remotely.
294
-
295
- Well, come along, I'll give you a short intro to the concept of controllers.
296
-
297
- In the way MVC is structured, the Controller provides the data in a nice way
298
- for the View, removing all of the data-preparation and most of the logic from
299
- the templates. This makes it firstly simple to change the front end of your
300
- application and secondly provides excellent ways of changing the complete
301
- Structure of the Model or View independent from each other.
302
-
303
- OK, enough of the theory, you will see the benefits in an instant, first of all
304
- we will implement marking a task as done.
305
-
306
- Go on and create the file 'controller/task.rb' with following contents:
307
-
308
- [source,ruby]
309
- --------------------------------------------------------------------------------
310
- class Tasks < Ramaze::Controller
311
- map '/'
312
-
313
- def close(title)
314
- task = Task[:title => title]
315
- task.done = true
316
- task.save
317
-
318
- redirect_referrer
319
- end
320
- end
321
- --------------------------------------------------------------------------------
322
-
323
- That does following:
324
-
325
- * Define a `Tasks` class as a subclass of `Ramaze::Controller`.
326
- * Tell Ramaze that a request to '/' goes to this Controller.
327
- * Define a `#close` method that requires a `title` argument.
328
- * Query for a task that has the given title.
329
- * Set the status of the task to done and store the change to the database.
330
- * Redirect the client to where it came from.
331
-
332
- And we add a require to 'controller/task.rb' to our 'start.rb':
333
-
334
- [source,ruby]
335
- --------------------------------------------------------------------------------
336
- require 'rubygems'
337
- require 'ramaze'
338
-
339
- require 'model/init'
340
- require 'controller/task'
341
-
342
- Ramaze.start
343
- --------------------------------------------------------------------------------
344
-
345
- Next we will have to modify the 'view/index.xhtml' to contain a link that will
346
- change the status of a task:
347
-
348
- [source,html]
349
- --------------------------------------------------------------------------------
350
- <ul>
351
- <?r Task.each do |task| ?>
352
- <li>
353
- #{ h(task.title) }: #{ task.done },
354
- (#{ anchor('close', 'close', task.title) })
355
- </li>
356
- <?r end ?>
357
- </ul>
358
- --------------------------------------------------------------------------------
359
-
360
- Now we have an additional link next to each task that allows us to set it to
361
- done.
362
-
363
- An even shorter way of writing that line using default aliases, that you will
364
- encounter in other applications, is:
365
-
366
- [source,ruby]
367
- --------------------------------------------------------------------------------
368
- a('close', 'close', task.title)
369
- --------------------------------------------------------------------------------
370
-
371
- But for the purpose of this tutorial we'll try to be as explicit as possible.
372
-
373
- Now that's a lot of things at once, but I'm sure you will be able to keep up,
374
- the hardest part is behind us.
375
-
376
- Don't forget to try the new functionality in your browser, wash your dishes and
377
- do your laundry and come back for the next episode.
378
-
379
- == Sixth Step, Clean, Rinse, Repeat
380
-
381
- Now that you have closed (and hopefully done) all of your chores, it's time to
382
- open them again, so you won't be without work tomorrow.
383
-
384
- Let's add a method to our Controller that will let us open a closed task:
385
-
386
- [source,ruby]
387
- --------------------------------------------------------------------------------
388
- class Tasks < Ramaze::Controller
389
- map '/'
390
-
391
- def close(title)
392
- task = Task[:title => title]
393
- task.done = true
394
- task.save
395
-
396
- redirect_referrer
397
- end
398
-
399
- def open(title)
400
- task = Task[:title => title]
401
- task.done = false
402
- task.save
403
-
404
- redirect_referrer
405
- end
406
- end
407
- --------------------------------------------------------------------------------
408
-
409
- And add a link to that action:
410
-
411
- [source,html]
412
- --------------------------------------------------------------------------------
413
- <ul>
414
- <?r Task.each do |task| ?>
415
- <li>
416
- #{ h(task.title) }: #{ task.done },
417
- (#{ anchor('close', 'close', task.title) })
418
- (#{ anchor('open', 'open', task.title) })
419
- </li>
420
- <?r end ?>
421
- </ul>
422
- --------------------------------------------------------------------------------
423
-
424
- OK, nothing new here, move along.
425
-
426
- Oh, wait!
427
-
428
- Rumor has it that some mad Japanese scientist got screwed by his company (they
429
- produce dishwashers), so he filed a patent for the ultimate dish washing robot
430
- that will take care of that for you.
431
-
432
- Time to get rid of that task once and for all. No more dish washing yay!
433
-
434
- A little modification to Controller, using destructive force.
435
-
436
- [source,ruby]
437
- --------------------------------------------------------------------------------
438
- class Tasks < Ramaze::Controller
439
- map '/'
440
-
441
- def close(title)
442
- task = Task[:title => title]
443
- task.done = true
444
- task.save
445
-
446
- redirect_referrer
447
- end
448
-
449
- def open(title)
450
- task = Task[:title => title]
451
- task.done = false
452
- task.save
453
-
454
- redirect_referrer
455
- end
456
-
457
- def delete(title)
458
- task = Task[:title => title]
459
- task.destroy
460
-
461
- redirect_referrer
462
- end
463
- end
464
- --------------------------------------------------------------------------------
465
-
466
- And a link to the `delete` action.
467
-
468
- [source,html]
469
- --------------------------------------------------------------------------------
470
- <ul>
471
- <?r Task.each do |task| ?>
472
- <li>
473
- #{ h(task.title) }: #{ task.done },
474
- (#{ anchor('close', 'close', task.title)})
475
- (#{ anchor('open', 'open', task.title)})
476
- (#{ anchor('delete', 'delete', task.title)})
477
- </li>
478
- <?r end ?>
479
- </ul>
480
- --------------------------------------------------------------------------------
481
-
482
- And dish-washing begone!
483
-
484
-
485
- == Seventh Step, More Tasks
486
-
487
- Sure, it would be nice if life was so simple and you only have to do your
488
- laundry, but that would mean a premature end for this tutorial and an obstacle
489
- for GTD evangelists (not that they couldn't overcome it).
490
-
491
- So now you got a smart new robot that washes your dishes, but unfortunately it
492
- wasn't programmed to recharge once in a while and buy soap, no biggie, we can
493
- do that with little effort, but since reddit takes up all your time you keep
494
- forgetting about it.
495
-
496
- No problem, I say, adding following code to our 'view/index.xhtml' will give us
497
- a nice little form that we can fill out in the few seconds between proving
498
- people on the internet wrong.
499
-
500
- [source,html]
501
- --------------------------------------------------------------------------------
502
- <form method="post" action="#{ route('create') }">
503
- <fieldset>
504
- <legend>Add a task by entering a title.</legend>
505
- <label for="form-title">Task title:</label>
506
- <input id="form-title" name="title" type="text" />
507
- <input type="submit" value="Create" />
508
- </fieldset>
509
- </form>
510
- --------------------------------------------------------------------------------
511
-
512
- Unfortunately, you see, this references the `create` action, and we have none
513
- yet. Trying to create a task will result in an error.
514
-
515
- So what we have to do is adding one more method to our Controller that will
516
- take care of actually creating the Task.
517
-
518
- [source,ruby]
519
- --------------------------------------------------------------------------------
520
- def create
521
- if request.post? and title = request[:title]
522
- title.strip!
523
-
524
- unless title.empty?
525
- Task.create :title => title
526
- end
527
- end
528
-
529
- redirect route('/')
530
- end
531
- --------------------------------------------------------------------------------
532
-
533
- What is going on here?
534
-
535
- * Check whether the request was using the HTTP POST method and actually sent a
536
- title with it.
537
- * Strip all whitespace from beginning and end of the title.
538
- * If the title still has something in it we go on and create a task with that
539
- title.
540
- * Redirect back to the `index`
541
-
542
- == Eighth Step, Eep, Exceptions!
543
-
544
- So far, so good, but remember, when we defined the schema for `Task` we said we
545
- really want to have unique titles.
546
-
547
- So once you created the task 'recharge DishBot9000' and try to create another
548
- one with the same title, you will get a nice error:
549
-
550
- Sequel::DatabaseError: SQLite3::SQLException column title is not unique
551
-
552
- OK, programmers ignore warnings and hide errors, let's rescue the exception and
553
- just act as if nothing has happened.
554
-
555
- [source,ruby]
556
- --------------------------------------------------------------------------------
557
- class Tasks < Ramaze::Controller
558
- map '/'
559
-
560
- def create
561
- if request.post? and title = request[:title]
562
- title.strip!
563
-
564
- unless title.empty?
565
- Task.create :title => title
566
- end
567
- end
568
-
569
- redirect route('/')
570
- rescue Sequel::DatabaseError
571
- redirect route('/')
572
- end
573
-
574
- def close(title)
575
- # ...
576
- --------------------------------------------------------------------------------
577
-
578
- Easy as pie, we can try to create as many identical tasks as we want, all we
579
- get is the same old set.
580
-
581
-
582
- == Ninth Step, Curing your RSI
583
-
584
- Something you might notice is that every time you hit the submit button and you are redirected to `index`, the title you just input is gone.
585
- What a waste of our honest effort to create a duplicate task, we all know if we
586
- try often enough it will eventually have to work, so let's save us some typing.
587
-
588
- In our 'view/index.xhtml' we modify the form input to have a default value:
589
-
590
- [source,html]
591
- --------------------------------------------------------------------------------
592
- <form method="post" action="#{ route('create') }">
593
- <fieldset>
594
- <legend>Add a task by entering a title.</legend>
595
- <label for="form-title">Task title:</label>
596
- <input id="form-title" name="title" type="text" value="#{ @title }"/>
597
- <input type="submit" value="Create" />
598
- </fieldset>
599
- </form>
600
- --------------------------------------------------------------------------------
601
-
602
- The `@title` is an instance-variable, those are shared between the Controller
603
- and View.
604
- We didn't set any such variable in the Controller yet, so do it now:
605
-
606
- [source,ruby]
607
- --------------------------------------------------------------------------------
608
- class Tasks < Ramaze::Controller
609
- map '/'
610
-
611
- def index
612
- @title = 'recharge DishBot9000'
613
- end
614
-
615
- def create
616
- # ...
617
- --------------------------------------------------------------------------------
618
-
619
- Yes, that wasn't too bad, but is there a way to change the value of the
620
- `@title` without editing the source all the time?
621
-
622
- Turns out we have to revisit the `create` method to give us a hint in form of a
623
- GET parameter and change `index` to pick it up.
624
-
625
- [source,ruby]
626
- --------------------------------------------------------------------------------
627
- def index
628
- @title = request[:title]
629
- end
630
-
631
- def create
632
- if request.post? and title = request[:title]
633
- title.strip!
634
-
635
- unless title.empty?
636
- Task.create :title => title
637
- end
638
- end
639
-
640
- redirect route('/', :title => title)
641
- rescue Sequel::DatabaseError
642
- redirect route('/', :title => title)
643
- end
644
- --------------------------------------------------------------------------------
645
-
646
- And that's it.
647
- Endless hours of fun hitting the submit button lie before us!
648
-
649
-
650
- == Tenth Step, Laying out a different View of things
651
-
652
- We have one template, it's a nice one, but unfortunately we've got ourselves
653
- into quite a mess here after creating hundreds of tasks.
654
-
655
- Our way out of this is to provide some visual feedback -- when a task is done,
656
- it's gone.
657
- Not forever, but at least it will not show up anymore on the `index` action.
658
-
659
- So we filter out all tasks that haven't been done yet in the
660
- 'view/index.xhtml':
661
-
662
- [source,html]
663
- --------------------------------------------------------------------------------
664
- <ul>
665
- <?r Task.filter(:done => false).each do |task| ?>
666
- <li>
667
- # ...
668
- --------------------------------------------------------------------------------
669
-
670
-
671
- So off we go and add a new template at 'view/done.xhtml'.
672
-
673
- [source,html]
674
- --------------------------------------------------------------------------------
675
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
676
- <html>
677
- <head>
678
- <title>TodoList</title>
679
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
680
- </head>
681
- <body>
682
- <h1>TodoList</h1>
683
-
684
- <form method="post" action="#{ route('create') }">
685
- <fieldset>
686
- <legend>Add a task by entering a title.</legend>
687
- <label for="form-title">Task title:</label>
688
- <input id="form-title" name="title" type="text" value="#{ @title }"/>
689
- <input type="submit" value="Create" />
690
- </fieldset>
691
- </form>
692
-
693
- <h2>Tasks done</h2>
694
-
695
- <ul>
696
- <?r Task.filter(:done => true).each do |task| ?>
697
- <li>
698
- #{ h(task.title) }: #{ task.done },
699
- (#{ anchor('open', 'open', task.title) })
700
- (#{ anchor('delete', 'delete', task.title) })
701
- </li>
702
- <?r end ?>
703
- </ul>
704
- </body>
705
- </html>
706
- --------------------------------------------------------------------------------
707
-
708
- Having a déjà vu?
709
-
710
- Yes, me too, must be an error in the matrix.
711
-
712
- If we want one thing from a web-framework, it's to spare us writing repetitive
713
- code like this (I hope you did copy&paste).
714
-
715
- What we actually wanted to do is _sharing_ the boilerplate around our listing
716
- of tasks, that's what we call 'layout'.
717
-
718
- Every action can have a layout associated with it, remember that empty 'layout'
719
- directory in your application? That's exactly where we will put it.
720
-
721
- [source,html]
722
- --------------------------------------------------------------------------------
723
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
724
- <html>
725
- <head>
726
- <title>TodoList</title>
727
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
728
- </head>
729
- <body>
730
- <h1>TodoList</h1>
731
- <form method="post" action="#{ route('create') }">
732
- <fieldset>
733
- <legend>Add a task by entering a title.</legend>
734
- <label for="form-title">Task title:</label>
735
- <input id="form-title" name="title" type="text" value="#{ @title }"/>
736
- <input type="submit" value="Create" />
737
- </fieldset>
738
- </form>
739
- #{ @content }
740
- </body>
741
- </html>
742
- --------------------------------------------------------------------------------
743
-
744
- And to tell Ramaze which layout to use for our `Tasks` we'll have to add a line
745
- to the Controller.
746
-
747
- [source,ruby]
748
- --------------------------------------------------------------------------------
749
- class Tasks < Ramaze::Controller
750
- map '/'
751
- layout 'default'
752
- end
753
- --------------------------------------------------------------------------------
754
-
755
- And finally, since we are fond of valid HTML and just love to get rid of boring
756
- boilerplate we can delete the slack from our templates.
757
-
758
- 'view/index.xhtml' becomes:
759
-
760
- [source,html]
761
- --------------------------------------------------------------------------------
762
- <h2>Pending Tasks</h2>
763
-
764
- #{ anchor('Done tasks', 'done') }
765
-
766
- <ul>
767
- <?r Task.filter(:done => false).each do |task| ?>
768
- <li>
769
- #{ h(task.title) },
770
- (#{ anchor('close', 'close', task.title) })
771
- (#{ anchor('delete', 'delete', task.title) })
772
- </li>
773
- <?r end ?>
774
- </ul>
775
- --------------------------------------------------------------------------------
776
-
777
- 'view/done.xhtml' becomes:
778
-
779
- [source,html]
780
- --------------------------------------------------------------------------------
781
- <h2>Done Tasks</h2>
782
-
783
- #{ anchor('Pending tasks', '') }
784
-
785
- <ul>
786
- <?r Task.filter(:done => true).each do |task| ?>
787
- <li>
788
- #{ h(task.title) },
789
- (#{ anchor('open', 'open', task.title) })
790
- (#{ anchor('delete', 'delete', task.title) })
791
- </li>
792
- <?r end ?>
793
- </ul>
794
- --------------------------------------------------------------------------------
795
-
796
- Well, that's so much better, we even included links between the actions.
797
-
798
-
799
- == Eleventh Step, not all that is gold glitters...
800
-
801
- You have to admit, it's a lot of fun having such a sophisticated application,
802
- but what good is it if it's too ugly to show it even to your closest friends?
803
- They will never become addicted enough to your fancy todo-list to actually do
804
- all the work for you.
805
-
806
- Let's do things with style, with a style-sheet.
807
-
808
- Now is the time to fire up your editor, point it at 'public/css/screen.css' and
809
- churn out something of your liking.
810
-
811
- We will not cover this part in the tutorial, an example style-sheet is located
812
- in the example todo-list.
813
-
814
- What we do cover is adding it to your application, or the `<head>` in
815
- 'layout/default.xhtml' to be exact:
816
-
817
- [source,html]
818
- --------------------------------------------------------------------------------
819
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
820
- <html>
821
- <head>
822
- <title>TodoList</title>
823
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
824
- <link rel="stylesheet" type="text/css" href="/css/screen.css" />
825
- </head>
826
- --------------------------------------------------------------------------------
827
-
828
- Voilà, you now have acquired the Certificate of Ramazeness and all your friends
829
- and enemies envy you.
830
-
831
-
832
- == Twelfth Step, configuring configurable configurability
833
-
834
- To round up this tutorial a bit, let's introduce you to configuration in Ramaze.
835
- There are a number of ways to configure Ramaze, but here we'll just see the
836
- most common ones with some options you'll most likely want to change.
837
-
838
- First of all, you have been running your ramaze application always on the same
839
- port, `7000`, which prevents you from starting more than one instance or other
840
- applications.
841
-
842
- To change the port, you can, for example:
843
-
844
- [source,ruby]
845
- --------------------------------------------------------------------------------
846
- Ramaze.options.adapter.port = 80
847
- --------------------------------------------------------------------------------
848
-
849
- NOTE: Running a server on a port below 1024 will require root privileges and is
850
- generally not advised for applications that don't drop their privileges
851
- after establishing a connection.
852
- Please have a look at http://wiki.ramaze.net/Deployment for better ways
853
- to deploy your site using a reverse proxy like apache, lighttpd, or
854
- nginx.
855
-
856
- OK, a different port is fine, but how about some speed-boost? For this we will
857
- need a faster server like http://mongrel.rubyforge.org[Mongrel] or
858
- http://thin.rubyforge.org[Thin].
859
-
860
- You can install either one via:
861
-
862
- --------------------------------------------------------------------------------
863
- gem install thin
864
- gem install mongrel
865
- --------------------------------------------------------------------------------
866
-
867
- Now to the configuration:
868
-
869
- [source,ruby]
870
- --------------------------------------------------------------------------------
871
- # The default is WEBrick
872
- Ramaze.options.adapter.adapter = :webrick
873
-
874
- # How about using Mongrel instead?
875
- Ramaze.options.adapter.adapter = :mongrel
876
-
877
- # Or maybe Thin?
878
- Ramaze.options.adapter.adapter = :thin
879
- --------------------------------------------------------------------------------
880
-
881
- For the full performance, switch Ramaze into `:live` mode:
882
-
883
- [source,ruby]
884
- --------------------------------------------------------------------------------
885
- # The default is :dev
886
- Ramaze.options.mode = :live
887
-
888
- # And here comes :live
889
- Ramaze.options.mode = :live
890
- --------------------------------------------------------------------------------
891
-
892
- The major differences between `:dev` and `:live` are that in `:live` mode your
893
- code won't be automatically reloaded if it has changed and we don't run every
894
- request through `Rack::Lint`, which helps you to stay within the
895
- request/response specifications required by Rack.
896
-
897
-
898
- [[glossary]]
899
- == Glossary
900
-
901
- [glossary]
902
- RDBMS::
903
- Relational Database Management System
904
- ORM::
905
- Object Relationship Mapper: Maps data into objects and assists in querying
906
- and manipulation
907
- MVC::
908
- Model, View, Controller: one of the patterns traditionally used for GUIs in Smalltalk.
909
- Etanni::
910
- Innate spelled backwards.
911
- Innate::
912
- Core of Ramaze.
913
- Rack::
914
- HTTP abstraction layer and interface used by the majority of Ruby web-frameworks.
915
- Templating engine::
916
- Used to process so-called templates with inlined source code or instructions
917
- to produce dynamic resulting documents. Examples for traditional templating
918
- engines are XSLT, SSI, ERB.
919
- RSI::
920
- Repetive Strain Injury, prevalent among the members of the church of Emacs.