camping 1.5.180 → 2.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGELOG +35 -0
  2. data/README +43 -68
  3. data/Rakefile +155 -86
  4. data/bin/camping +64 -246
  5. data/book/01_introduction +19 -0
  6. data/book/02_getting_started +443 -0
  7. data/book/51_upgrading +93 -0
  8. data/doc/api.html +1953 -0
  9. data/doc/book.html +73 -0
  10. data/doc/book/01_introduction.html +57 -0
  11. data/doc/book/02_getting_started.html +573 -0
  12. data/doc/book/51_upgrading.html +146 -0
  13. data/doc/created.rid +1 -0
  14. data/{extras → doc/images}/Camping.gif +0 -0
  15. data/doc/images/loadingAnimation.gif +0 -0
  16. data/{extras → doc/images}/permalink.gif +0 -0
  17. data/doc/index.html +148 -0
  18. data/doc/js/camping.js +79 -0
  19. data/doc/js/jquery.js +32 -0
  20. data/doc/rdoc.css +117 -0
  21. data/examples/blog.rb +280 -181
  22. data/extras/images/badge.gif +0 -0
  23. data/extras/images/boys-life.png +0 -0
  24. data/extras/images/deerputer.png +0 -0
  25. data/extras/images/diagram.png +0 -0
  26. data/extras/images/hill.png +0 -0
  27. data/extras/images/i-wish.png +0 -0
  28. data/extras/images/latl.png +0 -0
  29. data/extras/images/little-wheels.png +0 -0
  30. data/extras/images/square-badge.png +0 -0
  31. data/extras/images/uniform.png +0 -0
  32. data/extras/images/whale-bounce.png +0 -0
  33. data/extras/rdoc/generator/singledarkfish.rb +205 -0
  34. data/extras/rdoc/generator/template/flipbook/images/Camping.gif +0 -0
  35. data/extras/rdoc/generator/template/flipbook/images/loadingAnimation.gif +0 -0
  36. data/extras/rdoc/generator/template/flipbook/images/permalink.gif +0 -0
  37. data/extras/rdoc/generator/template/flipbook/js/camping.js +79 -0
  38. data/extras/rdoc/generator/template/flipbook/js/jquery.js +32 -0
  39. data/extras/rdoc/generator/template/flipbook/page.rhtml +30 -0
  40. data/extras/rdoc/generator/template/flipbook/rdoc.css +117 -0
  41. data/extras/rdoc/generator/template/flipbook/readme.rhtml +31 -0
  42. data/extras/rdoc/generator/template/flipbook/reference.rhtml +71 -0
  43. data/extras/rdoc/generator/template/flipbook/toc.rhtml +43 -0
  44. data/lib/camping-unabridged.rb +420 -481
  45. data/lib/camping.rb +40 -55
  46. data/lib/camping/{db.rb → ar.rb} +5 -8
  47. data/lib/camping/mab.rb +26 -0
  48. data/lib/camping/reloader.rb +175 -147
  49. data/lib/camping/server.rb +178 -0
  50. data/lib/camping/session.rb +34 -121
  51. data/test/apps/env_debug.rb +65 -0
  52. data/test/apps/forms.rb +95 -0
  53. data/test/apps/forward_to_other_controller.rb +60 -0
  54. data/test/apps/migrations.rb +97 -0
  55. data/test/apps/misc.rb +86 -0
  56. data/test/apps/sessions.rb +38 -0
  57. metadata +120 -80
  58. data/doc/camping.1.gz +0 -0
  59. data/examples/campsh.rb +0 -630
  60. data/examples/tepee.rb +0 -242
  61. data/extras/flipbook_rdoc.rb +0 -491
  62. data/lib/camping/fastcgi.rb +0 -244
  63. data/lib/camping/webrick.rb +0 -65
  64. data/test/test_xhtml_trans.rb +0 -55
data/doc/book.html ADDED
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Camping, a Microframework</title>
5
+ <link rel="stylesheet" href="./rdoc.css" type="text/css" media="screen" />
6
+ <script src="./js/jquery.js" type="text/javascript"></script>
7
+ <script src="js/camping.js" type="text/javascript"></script>
8
+ </head>
9
+ <body>
10
+ <div id="menu">
11
+ <ul id="links">
12
+ <li><a href="./index.html">front</a> | </li>
13
+ <li><a href="./api.html">reference</a> | </li>
14
+ <li><a href="http://wiki.github.com/camping/camping">wiki</a> | </li>
15
+ <li><a href="http://github.com/camping/camping">code</a></li>
16
+ </ul>
17
+ <p id="version">Camping 2.0.rc0</p>
18
+ </div>
19
+
20
+ <div id="fullpage">
21
+ <div class="page_shade">
22
+ <div class="page">
23
+ <p class="header">Sat Apr 03 16:22:17 +0200 2010</p>
24
+ <h1>Camping, the Book</h1>
25
+ <ol>
26
+
27
+ <li>
28
+ <a href="book/01_introduction.html">Introduction</a>
29
+
30
+ </li>
31
+
32
+ <li>
33
+ <a href="book/02_getting_started.html">Getting Started</a>
34
+
35
+ <ul>
36
+
37
+ <li><a href="book/02_getting_started.html#hello-clock">Hello clock</a></li>
38
+
39
+ <li><a href="book/02_getting_started.html#enjoying-the-view">Enjoying the view</a></li>
40
+
41
+ <li><a href="book/02_getting_started.html#routes">Routes</a></li>
42
+
43
+ <li><a href="book/02_getting_started.html#modeling-the-world">Modeling the world</a></li>
44
+
45
+ <li><a href="book/02_getting_started.html#using-our-model">Using our model</a></li>
46
+
47
+ <li><a href="book/02_getting_started.html#wrapping-it-up">Wrapping it up</a></li>
48
+
49
+ <li><a href="book/02_getting_started.html#the-last-touch">The last touch</a></li>
50
+
51
+ <li><a href="book/02_getting_started.html#phew">Phew.</a></li>
52
+
53
+ </ul>
54
+
55
+ </li>
56
+
57
+ <li>
58
+ <a href="book/51_upgrading.html">Appendix I: Upgrade Notes</a>
59
+
60
+ <ul>
61
+
62
+ <li><a href="book/51_upgrading.html#from-15-to-20">From 1.5 to 2.0</a></li>
63
+
64
+ </ul>
65
+
66
+ </li>
67
+
68
+ </ol>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </body>
73
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Camping, a Microframework</title>
5
+ <link rel="stylesheet" href="../rdoc.css" type="text/css" media="screen" />
6
+ <script src="../js/jquery.js" type="text/javascript"></script>
7
+ <script src="../js/camping.js" type="text/javascript"></script>
8
+ </head>
9
+ <body>
10
+ <div id="menu">
11
+ <ul id="links">
12
+ <li><a href="../index.html">front</a> | </li>
13
+ <li><a href="../book.html">ToC</a> | </li>
14
+ <li><a href="../api.html">reference</a> | </li>
15
+ <li><a href="http://wiki.github.com/camping/camping">wiki</a> | </li>
16
+ <li><a href="http://github.com/camping/camping">code</a></li>
17
+ </ul>
18
+ <p id="version">Camping 2.0.rc0</p>
19
+ </div>
20
+
21
+ <div id="fullpage">
22
+ <div class="page_shade">
23
+ <div class="page">
24
+ <p class="header">Sat Apr 03 16:22:17 +0200 2010</p>
25
+ <h1>Introduction</h1>
26
+ <p>
27
+ <a href="../api.html#class-Camping">Camping</a> is a small web framework,
28
+ less than 4k, a little white blood cell in the vein of Rails. This little
29
+ book will start with a tutorial which takes about fifteen minutes - by the
30
+ end you should have a little <a
31
+ href="../api.html#class-Camping">Camping</a> site up. The following
32
+ chapters will eventually go deeper into how both <a
33
+ href="../api.html#class-Camping">Camping</a>, HTTP and Rack works.
34
+ </p>
35
+ <p>
36
+ (&#8220;Eventually&#8221;, because these chapters are not written yet. This
37
+ book is currently a very much work in progress, and we&#8217;ll be very
38
+ grateful if you want to help out.)
39
+ </p>
40
+ <p>
41
+ If you at any moment need some help or have any questions or comments, we
42
+ highly recommend <a
43
+ href="http://rubyforge.org/mailman/listinfo/camping-list">the mailing
44
+ list</a> which got plenty of nice people willing to help. We also have an
45
+ IRC-channel at <a href="http://java.freenode.net/?channel=camping">#camping
46
+ @ irc.freenode.net</a> if you&#8217;re into that sort of things.
47
+ </p>
48
+ <p>
49
+ Enough talk. Ready? Let&#8217;s <a href="02_getting_started.html">"get
50
+ started"</a>.
51
+ </p>
52
+
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </body>
57
+ </html>
@@ -0,0 +1,573 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Camping, a Microframework</title>
5
+ <link rel="stylesheet" href="../rdoc.css" type="text/css" media="screen" />
6
+ <script src="../js/jquery.js" type="text/javascript"></script>
7
+ <script src="../js/camping.js" type="text/javascript"></script>
8
+ </head>
9
+ <body>
10
+ <div id="menu">
11
+ <ul id="links">
12
+ <li><a href="../index.html">front</a> | </li>
13
+ <li><a href="../book.html">ToC</a> | </li>
14
+ <li><a href="../api.html">reference</a> | </li>
15
+ <li><a href="http://wiki.github.com/camping/camping">wiki</a> | </li>
16
+ <li><a href="http://github.com/camping/camping">code</a></li>
17
+ </ul>
18
+ <p id="version">Camping 2.0.rc0</p>
19
+ </div>
20
+
21
+ <div id="fullpage">
22
+ <div class="page_shade">
23
+ <div class="page">
24
+ <p class="header">Sat Apr 03 16:22:17 +0200 2010</p>
25
+ <h1>Getting Started</h1>
26
+ <p>
27
+ Start a new text file called nuts.rb. Here&#8217;s what you put inside:
28
+ </p>
29
+ <pre>
30
+ Camping.goes :Nuts
31
+ </pre>
32
+ <p>
33
+ Save it. Then, open a command prompt in the same directory. You&#8217;ll
34
+ want to run:
35
+ </p>
36
+ <pre>
37
+ $ camping nuts.rb
38
+ </pre>
39
+ <p>
40
+ And you should get a message which reads:
41
+ </p>
42
+ <pre>
43
+ ** Camping running on 0.0.0.0:3301.
44
+ </pre>
45
+ <p>
46
+ This means that right now The <a
47
+ href="../api.html#class-Camping">Camping</a> Server is running on port 3301
48
+ on your machine. Open your browser and visit <a
49
+ href="http://localhost:3301/.">localhost:3301/.</a>
50
+ </p>
51
+ <p>
52
+ Your browser window should show:
53
+ </p>
54
+ <pre>
55
+ Camping Problem!
56
+
57
+ / Not found
58
+ </pre>
59
+ <p>
60
+ No problem with that. The <a href="../api.html#class-Camping">Camping</a>
61
+ Server is running, but it doesn&#8217;t know what to show. Let&#8217;s tell
62
+ him.
63
+ </p>
64
+ <h2 class="ruled" id="hello-clock">Hello clock</h2>
65
+ <p>
66
+ So, you&#8217;ve got <a href="../api.html#class-Camping">Camping</a>
67
+ installed and it&#8217;s running. Keep it running. You can edit files and
68
+ The <a href="../api.html#class-Camping">Camping</a> Server will reload
69
+ automatically. When you need to stop the server, press Control-C.
70
+ </p>
71
+ <p>
72
+ Let&#8217;s show something. At the bottom of nuts.rb add:
73
+ </p>
74
+ <pre>
75
+ module Nuts::Controllers
76
+ class Index &lt; R '/'
77
+ def get
78
+ Time.now.to_s
79
+ end
80
+ end
81
+ end
82
+ </pre>
83
+ <p>
84
+ Save the file and refresh the browser window. Your browser window should
85
+ show the time, e.g.
86
+ </p>
87
+ <pre>
88
+ Sun Jul 15 12:56:15 +0200 2007
89
+ </pre>
90
+ <h2 class="ruled" id="enjoying-the-view">Enjoying the view</h2>
91
+ <p>
92
+ The <a href="../api.html#class-Camping">Camping</a> microframework allows
93
+ us to separate our code using the MVC (Model-View-Controller) design
94
+ pattern. Let&#8217;s add a view to our Nuts application. Replace the
95
+ <tt>module Nuts::Controllers</tt> with:
96
+ </p>
97
+ <pre>
98
+ module Nuts::Controllers
99
+ class Index &lt; R '/'
100
+ def get
101
+ @time = Time.now
102
+ render :sundial
103
+ end
104
+ end
105
+ end
106
+
107
+ module Nuts::Views
108
+ def layout
109
+ html do
110
+ head do
111
+ title { &quot;Nuts And GORP&quot; }
112
+ end
113
+ body { self &lt;&lt; yield }
114
+ end
115
+ end
116
+
117
+ def sundial
118
+ p &quot;The current time is: #{@time}&quot;
119
+ end
120
+ end
121
+ </pre>
122
+ <p>
123
+ Save the file and refresh your browser window and it should show a message
124
+ like:
125
+ </p>
126
+ <pre>
127
+ The current time is: Sun Jul 15 13:05:41 +0200 2007
128
+ </pre>
129
+ <p>
130
+ And the window title reads &#8220;Nuts And GORP&#8221;.
131
+ </p>
132
+ <p>
133
+ Here you can see we call <tt>render :sundial</tt> from our controller. This
134
+ does exactly what it says, and renders our <tt>sundial</tt> method.
135
+ We&#8217;ve also added a special method called <tt>layout</tt> which <a
136
+ href="../api.html#class-Camping">Camping</a> will automatically wrap our
137
+ sundial output in. If you&#8217;re familiar with HTML, you&#8217;ll see
138
+ that our view contains what looks HTML tag names. This is Markaby, which is
139
+ like writing HTML using Ruby!
140
+ </p>
141
+ <p>
142
+ Soon enough, you&#8217;ll find that you can return anything from the
143
+ controller, and it will be sent to the browser. But let&#8217;s keep that
144
+ for later and start investigating the routes.
145
+ </p>
146
+ <h2 class="ruled" id="routes">Routes</h2>
147
+ <p>
148
+ You probably noticed the weird <tt>R '/'</tt> syntax in the previous page.
149
+ This is an uncommon feature of Ruby that is used in our favorite
150
+ microframework, to describe the routes which the controller can be accessed
151
+ on.
152
+ </p>
153
+ <p>
154
+ These routes can be very powerful, but we&#8217;re going to have look at
155
+ the simplest ones first.
156
+ </p>
157
+ <pre>
158
+ module Nuts::Controllers
159
+ class Words &lt; R '/welcome/to/my/site'
160
+ def get
161
+ &quot;You got here by: /welcome/to/my/site&quot;
162
+ end
163
+ end
164
+
165
+ class Digits &lt; R '/nuts/(\d+)'
166
+ def get(number)
167
+ &quot;You got here by: /nuts/#{number}&quot;
168
+ end
169
+ end
170
+
171
+ class Segment &lt; R '/gorp/([^/]+)'
172
+ def get(everything_else_than_a_slash)
173
+ &quot;You got here by: /gorp/#{everything_else_than_a_slash}&quot;
174
+ end
175
+ end
176
+
177
+ class DigitsAndEverything &lt; R '/nuts/(\d+)/([^/]+)'
178
+ def get(number, everything)
179
+ &quot;You got here by: /nuts/#{number}/#{everything}&quot;
180
+ end
181
+ end
182
+ end
183
+ </pre>
184
+ <p>
185
+ Add this to nuts.rb and try if you can hit all of the controllers.
186
+ </p>
187
+ <p>
188
+ Also notice how everything inside a parenthesis gets passed into the
189
+ method, and is ready at your disposal.
190
+ </p>
191
+ <h3>Simpler routes</h3>
192
+ <p>
193
+ This just in:
194
+ </p>
195
+ <pre>
196
+ module Nuts::Controllers
197
+ class Index
198
+ def get
199
+ &quot;You got here by: /&quot;
200
+ end
201
+ end
202
+
203
+ class WelcomeToMySite
204
+ def get
205
+ &quot;You got here by: /welcome/to/my/site&quot;
206
+ end
207
+ end
208
+
209
+ class NutsN
210
+ def get(number)
211
+ &quot;You got here by: /nuts/#{number}&quot;
212
+ end
213
+ end
214
+
215
+ class GorpX
216
+ def get(everything_else_than_a_slash)
217
+ &quot;You got here by: /gorp/#{everything_else_than_a_slash}&quot;
218
+ end
219
+ end
220
+
221
+ class NutsNX
222
+ def get(number, everything)
223
+ &quot;You got here by: /nuts/#{number}/#{everything}&quot;
224
+ end
225
+ end
226
+ end
227
+ </pre>
228
+ <p>
229
+ Drop the <tt>&lt; R</tt>-part and it attemps to read your mind. It
230
+ won&#8217;t always succeed, but it can simplify your application once in a
231
+ while.
232
+ </p>
233
+ <h2 class="ruled" id="modeling-the-world">Modeling the world</h2>
234
+ <p>
235
+ You can get pretty far with what you&#8217;ve learned now, and hopefully
236
+ you&#8217;ve been playing a bit off-book, but it&#8217;s time to take the
237
+ next step: Storing data.
238
+ </p>
239
+ <p>
240
+ Let&#8217;s start over again.
241
+ </p>
242
+ <pre>
243
+ Camping.goes :Nuts
244
+
245
+ module Nuts::Models
246
+ class Page &lt; Base
247
+ end
248
+ end
249
+ </pre>
250
+ <p>
251
+ Obviously, this won&#8217;t do anything, since we don&#8217;t have any
252
+ controllers, but let&#8217;s rather have a look at we <em>do</em> have.
253
+ </p>
254
+ <p>
255
+ We have a model named Page. This means we now can store wiki pages and
256
+ retrieve them later. In fact, we can have as many models as we want. Need
257
+ one for your users and one for your blog posts? Well, I think you already
258
+ know how to do it.
259
+ </p>
260
+ <p>
261
+ However, our model is missing something essential: a skeleton.
262
+ </p>
263
+ <pre>
264
+ Camping.goes :Nuts
265
+
266
+ module Nuts::Models
267
+ class Page &lt; Base
268
+ end
269
+
270
+ class BasicFields &lt; V 1.0
271
+ def self.up
272
+ create_table Page.table_name do |t|
273
+ t.string :title
274
+ t.text :content
275
+ # This gives us created_at and updated_at
276
+ t.timestamps
277
+ end
278
+ end
279
+
280
+ def self.down
281
+ drop_table Page.table_name
282
+ end
283
+ end
284
+ end
285
+ </pre>
286
+ <p>
287
+ Now we have our first version of our model. It says:
288
+ </p>
289
+ <pre>
290
+ If you want to migrate up to version one,
291
+ create the skeleton for the Page model,
292
+ which should be able to store,
293
+ &quot;title&quot; which is a string,
294
+ &quot;content&quot; which is a larger text,
295
+ &quot;created_at&quot; which is the time it was created,
296
+ &quot;updated_at&quot; which is the previous time it was updated.
297
+
298
+ If you want to migrate down from version one,
299
+ remove the skeleton for the Page model.
300
+ </pre>
301
+ <p>
302
+ This is called a <em>migration</em>. Whenever you want to change or add new
303
+ models you simply add a new migration below, where you increase the version
304
+ number. All of these migrations builds upon each other like LEGO blocks.
305
+ </p>
306
+ <p>
307
+ Now we just need to tell <a href="../api.html#class-Camping">Camping</a> to
308
+ use our migration. Write this at the bottom of nuts.rb
309
+ </p>
310
+ <pre>
311
+ def Nuts.create
312
+ Nuts::Models.create_schema
313
+ end
314
+ </pre>
315
+ <p>
316
+ When The <a href="../api.html#class-Camping">Camping</a> Server boots up,
317
+ it will automatically call <tt>Nuts.create</tt>. You can put all kind of
318
+ startup-code here, but right now we only want to create our skeleton (or
319
+ upgrade if needed). Start The <a
320
+ href="../api.html#class-Camping">Camping</a> Server again and observe:
321
+ </p>
322
+ <pre>
323
+ $ camping nuts.rb
324
+ ** Starting Mongrel on 0.0.0.0:3301
325
+ -- create_table(&quot;nuts_schema_infos&quot;)
326
+ -&gt; 0.1035s
327
+ == Nuts::Models::BasicFields: migrating ===================================
328
+ -- create_table(:nuts_pages)
329
+ -&gt; 0.0033s
330
+ == Nuts::Models::BasicFields: migrated (0.0038s) ==========================
331
+ </pre>
332
+ <p>
333
+ Restart it, and enjoy the silence. There&#8217;s no point of re-creating
334
+ the skeleton this time.
335
+ </p>
336
+ <p>
337
+ Before we go on, there&#8217;s one rule you must known: Always place your
338
+ models before your migrations.
339
+ </p>
340
+ <h2 class="ruled" id="using-our-model">Using our model</h2>
341
+ <p>
342
+ Let&#8217;s explore how our model works by going into the <em>console</em>
343
+ </p>
344
+ <pre>
345
+ $ camping -C nuts.rb
346
+ ** Starting console
347
+ &gt;&gt;
348
+ </pre>
349
+ <p>
350
+ Now it&#8217;s waiting for your input, and will give you the answer when
351
+ you press Enter. Here&#8217;s what I did, leaving out the boring answers.
352
+ You should add your own pages.
353
+ </p>
354
+ <pre>
355
+ &gt;&gt; Page = Nuts::Models::Page
356
+
357
+ &gt;&gt; hiking = Page.new(:title =&gt; &quot;Hiking&quot;)
358
+ &gt;&gt; hiking.content = &quot;You can also set the values like this.&quot;
359
+ &gt;&gt; hiking.save
360
+
361
+ &gt;&gt; page = Page.find_by_title(&quot;Hiking&quot;)
362
+ =&gt; #&lt;Nuts::Models::Page id: 1, ... &gt;
363
+ &gt;&gt; page = Page.find(1)
364
+ =&gt; #&lt;Nuts::Models::Page id: 1, ... &gt;
365
+ &gt;&gt; page.title
366
+ &gt;&gt; page.content
367
+ &gt;&gt; page.created_at
368
+ &gt;&gt; page.updated_at
369
+
370
+ &gt;&gt; Page.find_by_title(&quot;Fishing&quot;)
371
+ =&gt; nil
372
+
373
+ ## Page.create automatically saves the page for you.
374
+ &gt;&gt; Page.create(:title =&gt; &quot;Fishing&quot;, :content =&gt; &quot;Go fish!&quot;)
375
+
376
+ &gt;&gt; Page.count
377
+ =&gt; 2
378
+ </pre>
379
+ <p>
380
+ Now I have two pages: One about hiking and one about fishing.
381
+ </p>
382
+ <h2 class="ruled" id="wrapping-it-up">Wrapping it up</h2>
383
+ <p>
384
+ Wouldn&#8217;t it be nice if we could show this wonderful our pages in a
385
+ browser? Update nuts.rb so it also contains something like this:
386
+ </p>
387
+ <pre>
388
+ module Nuts::Controllers
389
+ class Pages
390
+ def get
391
+ # Only fetch the titles of the pages.
392
+ @pages = Page.all(:select =&gt; &quot;title&quot;)
393
+ render :list
394
+ end
395
+ end
396
+
397
+ class PageX
398
+ def get(title)
399
+ @page = Page.find_by_title(title)
400
+ render :view
401
+ end
402
+ end
403
+ end
404
+
405
+ module Nuts::Views
406
+ def list
407
+ h1 &quot;All pages&quot;
408
+ ul do
409
+ @pages.each do |page|
410
+ li do
411
+ a page.title, :href =&gt; R(PageX, page.title)
412
+ end
413
+ end
414
+ end
415
+ end
416
+
417
+ def view
418
+ h1 @page.title
419
+ self &lt;&lt; @page.content
420
+ end
421
+ end
422
+ </pre>
423
+ <p>
424
+ Here we meet our first <em>helper</em>:
425
+ </p>
426
+ <pre>
427
+ R(PageX, page.title)
428
+ </pre>
429
+ <p>
430
+ This is the <em>reversed router</em> and it generates a URL based on a
431
+ controller. <a href="../api.html#class-Camping">Camping</a> ships with a
432
+ few, but very useful, helpers and you can easily add your owns. Have a look
433
+ at <a href="../api.html#class-Camping-Helpers">Camping::Helpers</a> for how
434
+ you use these.
435
+ </p>
436
+ <p>
437
+ There&#8217;s a lot of improvements you could do here. Let me suggest:
438
+ </p>
439
+ <ul>
440
+ <li>Show when the page was created and last updated.
441
+
442
+ </li>
443
+ <li>What happens when the page doesn&#8217;t exist?
444
+
445
+ </li>
446
+ <li>What should the front page show?
447
+
448
+ </li>
449
+ <li>Add a layout.
450
+
451
+ </li>
452
+ <li>Jazz it up a bit.
453
+
454
+ </li>
455
+ </ul>
456
+ <h2 class="ruled" id="the-last-touch">The last touch</h2>
457
+ <p>
458
+ We have one major flaw in our little application. You can&#8217;t edit or
459
+ add new pages. Let&#8217;s see if we can fix that:
460
+ </p>
461
+ <pre>
462
+ module Nuts::Controllers
463
+ class PageX
464
+ def get(title)
465
+ if @page = Page.find_by_title(title)
466
+ render :view
467
+ else
468
+ redirect PageXEdit, title
469
+ end
470
+ end
471
+
472
+ def post(title)
473
+ # If it doesn't exist, initialize it:
474
+ @page = Page.find_or_initialize_by_title(title)
475
+ # This is the same as:
476
+ # @page = Page.find_by_title(title) || Page.new(:title =&gt; title)
477
+
478
+ @page.content = @input.content
479
+ @page.save
480
+ redirect PageX, title
481
+ end
482
+ end
483
+
484
+ class PageXEdit
485
+ def get(title)
486
+ @page = Page.find_or_initialize_by_title(title)
487
+ render :edit
488
+ end
489
+ end
490
+ end
491
+ </pre>
492
+ <p>
493
+ The core of this code lies in the new <tt>post</tt> method in the PageX
494
+ controller. When someone types an address or follows a link, they&#8217;ll
495
+ end up at the <tt>get</tt> method, but you can easily create a form which
496
+ rather sends you to the <tt>post</tt> when submitted.
497
+ </p>
498
+ <p>
499
+ There are other names you can use, but they won&#8217;t always work. So for
500
+ now, don&#8217;t be fancy and just stick to <tt>get</tt> and <tt>post</tt>.
501
+ We&#8217;ll show you how this really works later.
502
+ </p>
503
+ <p>
504
+ You might also notice that we use <tt>@input.content</tt>. The
505
+ <tt>@input</tt>-hash contains any extra parameters sent, like those in the
506
+ forms and those in the URL (<tt>/posts?page=50</tt>).
507
+ </p>
508
+ <p>
509
+ Here&#8217;s an <tt>edit</tt>-view, but you can probably do better. See if
510
+ you can integrate all of this with what you already have.
511
+ </p>
512
+ <pre>
513
+ module Nuts::Views
514
+ def edit
515
+ h1 @page.title
516
+ form :action =&gt; R(PageX, @page.title), :method =&gt; :post do
517
+ textarea @page.content, :name =&gt; :content,
518
+ :rows =&gt; 10, :cols =&gt; 50
519
+
520
+ br
521
+
522
+ input :type =&gt; :submit, :value =&gt; &quot;Submit!&quot;
523
+ end
524
+ end
525
+ end
526
+ </pre>
527
+ <h2 class="ruled" id="phew">Phew.</h2>
528
+ <p>
529
+ You&#8217;ve taken quite a few steps in the last minutes. You deserve a
530
+ break. But let&#8217;s recap for a moment:
531
+ </p>
532
+ <ul>
533
+ <li>Always place <tt><a href="../api.html#M000023">Camping.goes</a> :App</tt>
534
+ at the top of your file.
535
+
536
+ </li>
537
+ <li>Every route ends at a controller, but &#8230;
538
+
539
+ </li>
540
+ <li>&#8230; the controller only delegates the work.
541
+
542
+ </li>
543
+ <li><tt>@input</tt> contains the extra parameters.
544
+
545
+ </li>
546
+ <li>The views are HTML disguised as Ruby.
547
+
548
+ </li>
549
+ <li>They can access the instances variables (those that starts with a single
550
+ at-sign) from the controller.
551
+
552
+ </li>
553
+ <li>The models allows you to store all kinds of data.
554
+
555
+ </li>
556
+ <li>Place your models before your migrations.
557
+
558
+ </li>
559
+ <li>Helpers are helpful.
560
+
561
+ </li>
562
+ </ul>
563
+ <p>
564
+ Unfortunately, the book stops here for now. Come back in a few months, or
565
+ join the mailing list to stay updated, and hopefully there&#8217;s another
566
+ chapter waiting for you.
567
+ </p>
568
+
569
+ </div>
570
+ </div>
571
+ </div>
572
+ </body>
573
+ </html>