camping 2.1.532 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -53
  3. data/Rakefile +25 -20
  4. data/bin/camping +1 -0
  5. data/book/01_introduction.md +6 -6
  6. data/book/02_getting_started.md +348 -267
  7. data/book/03_more_about_controllers.md +124 -0
  8. data/book/04_more_about_views.md +118 -0
  9. data/book/05_more_about_markaby.md +173 -0
  10. data/book/06_more_about_models.md +58 -0
  11. data/book/06_rules_of_thumb.md +143 -0
  12. data/book/07_philosophy.md +23 -0
  13. data/book/08_publishing_an_app.md +118 -0
  14. data/book/09_upgrade_notes.md +96 -0
  15. data/book/10_middleware.md +69 -0
  16. data/book/11_gear.md +50 -0
  17. data/examples/blog.rb +38 -38
  18. data/lib/camping/ar.rb +20 -5
  19. data/lib/camping/commands.rb +388 -0
  20. data/lib/camping/gear/filters.rb +48 -0
  21. data/lib/camping/gear/inspection.rb +32 -0
  22. data/lib/camping/gear/kuddly.rb +178 -0
  23. data/lib/camping/gear/nancy.rb +170 -0
  24. data/lib/camping/loads.rb +15 -0
  25. data/lib/camping/mab.rb +1 -1
  26. data/lib/camping/reloader.rb +22 -17
  27. data/lib/camping/server.rb +145 -70
  28. data/lib/camping/session.rb +8 -5
  29. data/lib/camping/template.rb +1 -2
  30. data/lib/camping/tools.rb +43 -0
  31. data/lib/camping/version.rb +6 -0
  32. data/lib/camping-unabridged.rb +360 -133
  33. data/lib/camping.rb +78 -47
  34. data/lib/campingtrip.md +341 -0
  35. data/test/app_camping_gear.rb +121 -0
  36. data/test/app_camping_tools.rb +1 -0
  37. data/test/app_config.rb +30 -0
  38. data/test/app_cookies.rb +1 -1
  39. data/test/app_file.rb +3 -3
  40. data/test/app_goes_meta.rb +23 -0
  41. data/test/app_inception.rb +39 -0
  42. data/test/app_markup.rb +5 -20
  43. data/test/app_migrations.rb +16 -0
  44. data/test/app_partials.rb +1 -1
  45. data/test/app_prefixed.rb +88 -0
  46. data/test/app_reloader.rb +1 -2
  47. data/test/app_route_generating.rb +69 -2
  48. data/test/app_sessions.rb +24 -2
  49. data/test/app_simple.rb +18 -18
  50. data/test/apps/migrations.rb +82 -82
  51. data/test/apps/misc.rb +1 -1
  52. data/test/gear/gear_nancy.rb +129 -0
  53. data/test/test_helper.rb +69 -12
  54. metadata +152 -92
  55. data/CHANGELOG +0 -145
  56. data/book/51_upgrading.md +0 -110
@@ -1,85 +1,104 @@
1
- #Getting Started
1
+ # Getting Started
2
2
 
3
- Start a new text file called nuts.rb. Here's what you put inside:
4
-
5
- Camping.goes :Nuts
3
+ Start a new text file called camp.rb. Here's what you put inside:
4
+
5
+ ```ruby
6
+ require 'camping'
7
+ Camping.goes :Nuts
8
+ ```
6
9
 
7
10
  Save it. Then, open a command prompt in the same directory. You'll want to
8
11
  run:
9
12
 
10
- $ camping nuts.rb
13
+ ```ruby
14
+ $ camping
15
+ ```
11
16
 
12
17
  And you should get a message which reads:
13
18
 
14
- ** Camping running on 0.0.0.0:3301.
19
+ ```ruby
20
+ ** Camping running on 0.0.0.0:3301.
21
+ ```
15
22
 
16
23
  This means that right now The Camping Server is running on port 3301 on your
17
24
  machine. Open your browser and visit http://localhost:3301/.
18
25
 
19
26
  Your browser window should show:
20
27
 
21
- Camping Problem!
22
-
23
- / Not found
28
+ ```
29
+ Camping Problem!
30
+
31
+ / Not found
32
+ ```
24
33
 
25
34
  No problem with that. The Camping Server is running, but it doesn't know what
26
- to show. Let's tell him.
35
+ to show. Let's tell them.
27
36
 
28
- ##Hello clock
37
+ ## Hello clock
29
38
 
30
39
  So, you've got Camping installed and it's running. Keep it running. You can
31
40
  edit files and The Camping Server will reload automatically. When you need to
32
41
  stop the server, press Control-C.
33
42
 
34
- Let's show something. At the bottom of nuts.rb add:
43
+ Let's show something. At the bottom of camp.rb add:
35
44
 
36
- module Nuts::Controllers
37
- class Index < R '/'
38
- def get
39
- Time.now.to_s
40
- end
41
- end
45
+ ```ruby
46
+ module Nuts::Controllers
47
+ class Index < R '/'
48
+ def get
49
+ Time.now.to_s
42
50
  end
51
+ end
52
+ end
53
+ ```
43
54
 
44
55
  Save the file and refresh the browser window. Your browser window should show
45
56
  the time, e.g.
46
57
 
47
- Sun Jul 15 12:56:15 +0200 2007
58
+ ```
59
+ Sun Jul 15 12:56:15 +0200 2007
60
+ ```
48
61
 
49
- ##Enjoying the view
62
+ ## Enjoying the view
50
63
 
51
64
  The Camping microframework allows us to separate our code using the MVC
52
65
  (Model-View-Controller) design pattern. Let's add a view to our Nuts
53
66
  application. Replace the <tt>module Nuts::Controllers</tt> with:
54
67
 
55
- module Nuts::Controllers
56
- class Index < R '/'
57
- def get
58
- @time = Time.now
59
- render :sundial
60
- end
68
+ ```ruby
69
+ module Nuts::Controllers
70
+ class Index < R '/'
71
+ def get
72
+ @time = Time.now
73
+ render :sundial
74
+ end
75
+ end
76
+ end
77
+ ```
78
+
79
+ ```ruby
80
+ module Nuts::Views
81
+ def layout
82
+ html do
83
+ head do
84
+ title { "Nuts And GORP" }
61
85
  end
86
+ body { self << yield }
62
87
  end
88
+ end
63
89
 
64
- module Nuts::Views
65
- def layout
66
- html do
67
- head do
68
- title { "Nuts And GORP" }
69
- end
70
- body { self << yield }
71
- end
72
- end
90
+ def sundial
91
+ p "The current time is: #{@time}"
92
+ end
93
+ end
94
+ ```
73
95
 
74
- def sundial
75
- p "The current time is: #{@time}"
76
- end
77
- end
78
-
79
- Save the file and refresh your browser window and it should show a message
96
+ Save the file, refresh your browser window and it should show a message
80
97
  like:
81
98
 
82
- The current time is: Sun Jul 15 13:05:41 +0200 2007
99
+ ```
100
+ The current time is: Sun Jul 15 13:05:41 +0200 2013
101
+ ```
83
102
 
84
103
  And the window title reads "Nuts And GORP".
85
104
 
@@ -94,7 +113,7 @@ Soon enough, you'll find that you can return anything from the controller, and
94
113
  it will be sent to the browser. But let's keep that for later and start
95
114
  investigating the routes.
96
115
 
97
- ##Routes
116
+ ## Routes
98
117
 
99
118
  You probably noticed the weird <tt>R '/'</tt> syntax in the previous page.
100
119
  This is an uncommon feature of Ruby that is used in our favorite
@@ -104,73 +123,77 @@ on.
104
123
  These routes can be very powerful, but we're going to have look at the
105
124
  simplest ones first.
106
125
 
107
- module Nuts::Controllers
108
- class Words < R '/welcome/to/my/site'
109
- def get
110
- "You got here by: /welcome/to/my/site"
111
- end
112
- end
113
-
114
- class Digits < R '/nuts/(\d+)'
115
- def get(number)
116
- "You got here by: /nuts/#{number}"
117
- end
118
- end
126
+ ```ruby
127
+ module Nuts::Controllers
128
+ class Words < R '/welcome/to/my/site'
129
+ def get
130
+ "You got here by: /welcome/to/my/site"
131
+ end
132
+ end
119
133
 
120
- class Segment < R '/gorp/([^/]+)'
121
- def get(everything_else_than_a_slash)
122
- "You got here by: /gorp/#{everything_else_than_a_slash}"
123
- end
124
- end
125
-
126
- class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)'
127
- def get(number, everything)
128
- "You got here by: /nuts/#{number}/#{everything}"
129
- end
130
- end
134
+ class Digits < R '/nuts/(\d+)'
135
+ def get(number)
136
+ "You got here by: /nuts/#{number}"
131
137
  end
132
-
133
- Add this to `nuts.rb` and try if you can hit all of the controllers.
138
+ end
139
+
140
+ class Segment < R '/gorp/([^/]+)'
141
+ def get(everything_else_than_a_slash)
142
+ "You got here by: /gorp/#{everything_else_than_a_slash}"
143
+ end
144
+ end
145
+
146
+ class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)'
147
+ def get(number, everything)
148
+ "You got here by: /nuts/#{number}/#{everything}"
149
+ end
150
+ end
151
+ end
152
+ ```
153
+
154
+ Add this to `nuts.rb` and try if you can hit all of the controllers.
134
155
 
135
156
  Also notice how everything inside a parenthesis gets passed into the method,
136
157
  and is ready at your disposal.
137
158
 
138
- ###Simpler routes
159
+ ### Simpler routes
139
160
 
140
161
  This just in:
141
162
 
142
- module Nuts::Controllers
143
- class Index
144
- def get
145
- "You got here by: /"
146
- end
147
- end
148
-
149
- class WelcomeToMySite
150
- def get
151
- "You got here by: /welcome/to/my/site"
152
- end
153
- end
154
-
155
- class NutsN
156
- def get(number)
157
- "You got here by: /nuts/#{number}"
158
- end
159
- end
160
-
161
- class GorpX
162
- def get(everything_else_than_a_slash)
163
- "You got here by: /gorp/#{everything_else_than_a_slash}"
164
- end
165
- end
166
-
167
- class NutsNX
168
- def get(number, everything)
169
- "You got here by: /nuts/#{number}/#{everything}"
170
- end
171
- end
163
+ ```ruby
164
+ module Nuts::Controllers
165
+ class Index
166
+ def get
167
+ "You got here by: /"
168
+ end
169
+ end
170
+
171
+ class WelcomeToMySite
172
+ def get
173
+ "You got here by: /welcome/to/my/site"
174
+ end
175
+ end
176
+
177
+ class NutsN
178
+ def get(number)
179
+ "You got here by: /nuts/#{number}"
180
+ end
181
+ end
182
+
183
+ class GorpX
184
+ def get(everything_else_than_a_slash)
185
+ "You got here by: /gorp/#{everything_else_than_a_slash}"
172
186
  end
173
-
187
+ end
188
+
189
+ class NutsNX
190
+ def get(number, everything)
191
+ "You got here by: /nuts/#{number}/#{everything}"
192
+ end
193
+ end
194
+ end
195
+ ```
196
+
174
197
  Drop the <tt>< R</tt>-part and it attemps to read your mind. It won't always
175
198
  succeed, but it can simplify your application once in a while.
176
199
 
@@ -181,15 +204,17 @@ playing a bit off-book, but it's time to take the next step: Storing data.
181
204
 
182
205
  Let's start over again.
183
206
 
184
- Camping.goes :Nuts
207
+ ```ruby
208
+ Camping.goes :Nuts
209
+
210
+ module Nuts::Models
211
+ class Page < Base
212
+ end
213
+ end
214
+ ```
185
215
 
186
- module Nuts::Models
187
- class Page < Base
188
- end
189
- end
190
-
191
216
  Obviously, this won't do anything, since we don't have any controllers, but
192
- let's rather have a look at we _do_ have.
217
+ let's rather have a look at what we _do_ have.
193
218
 
194
219
  We have a model named Page. This means we now can store wiki pages and
195
220
  retrieve them later. In fact, we can have as many models as we want. Need one
@@ -197,199 +222,255 @@ for your users and one for your blog posts? Well, I think you already know how
197
222
  to do it.
198
223
 
199
224
  However, our model is missing something essential: a skeleton.
200
-
201
- Camping.goes :Nuts
202
-
203
- module Nuts::Models
204
- class Page < Base
205
- end
206
-
207
- class BasicFields < V 1.0
208
- def self.up
209
- create_table Page.table_name do |t|
210
- t.string :title
211
- t.text :content
212
- # This gives us created_at and updated_at
213
- t.timestamps
214
- end
215
- end
216
-
217
- def self.down
218
- drop_table Page.table_name
219
- end
225
+
226
+ ```ruby
227
+ Camping.goes :Nuts
228
+
229
+ module Nuts::Models
230
+ class Page < Base
231
+ end
232
+
233
+ class BasicFields < V 1.0
234
+ def self.up
235
+ create_table Page.table_name do |t|
236
+ t.string :title
237
+ t.text :content
238
+ # This gives us created_at and updated_at
239
+ t.timestamps
220
240
  end
221
241
  end
222
-
242
+
243
+ def self.down
244
+ drop_table Page.table_name
245
+ end
246
+ end
247
+ end
248
+ ```
249
+
223
250
  Now we have our first version of our model. It says:
224
251
 
225
- If you want to migrate up to version one,
226
- create the skeleton for the Page model,
227
- which should be able to store,
228
- "title" which is a string,
229
- "content" which is a larger text,
230
- "created_at" which is the time it was created,
231
- "updated_at" which is the previous time it was updated.
232
-
233
- If you want to migrate down from version one,
234
- remove the skeleton for the Page model.
235
-
236
- This is called a _migration_. Whenever you want to change or add new models you simply add a new migration below, where you increase the version number. All of these migrations builds upon each other like LEGO blocks.
252
+ ```
253
+ If you want to migrate up to version one,
254
+ create the skeleton for the Page model,
255
+ which should be able to store,
256
+ "title" which is a string,
257
+ "content" which is a larger text,
258
+ "created_at" which is the time it was created,
259
+ "updated_at" which is the previous time it was updated.
260
+
261
+ If you want to migrate down from version one,
262
+ remove the skeleton for the Page model.
263
+ ```
264
+
265
+ This is called a
266
+ [migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html).
267
+ Whenever you want to change or add new models you simply add a new migration
268
+ below, where you increase the version number. All of these migrations builds
269
+ upon each other like LEGO blocks. Each new Migrations must have different
270
+ class's names, is a good idea name migration's explicit. For example:
271
+
272
+ ```ruby
273
+ class AddTagColumn < V 1.1
274
+ def self.change
275
+ add_column Page.table_name, :tag, :string
276
+ Page.reset_column_information
277
+ end
278
+ end
279
+ ```
237
280
 
238
281
  Now we just need to tell Camping to use our migration. Write this at the bottom of nuts.rb
239
282
 
240
- def Nuts.create
241
- Nuts::Models.create_schema
242
- end
283
+ ```ruby
284
+ def Nuts.create
285
+ Nuts::Models.create_schema
286
+ end
287
+ ```
243
288
 
244
289
  When The Camping Server boots up, it will automatically call
245
290
  <tt>Nuts.create</tt>. You can put all kind of startup-code here, but right now
246
291
  we only want to create our skeleton (or upgrade if needed). Start The Camping
247
292
  Server again and observe:
248
-
249
- $ camping nuts.rb
250
- ** Starting Mongrel on 0.0.0.0:3301
251
- -- create_table("nuts_schema_infos")
252
- -> 0.1035s
253
- == Nuts::Models::BasicFields: migrating ===================================
254
- -- create_table(:nuts_pages)
255
- -> 0.0033s
256
- == Nuts::Models::BasicFields: migrated (0.0038s) ==========================
257
-
293
+
294
+ ```bash
295
+ $ camping nuts.rb
296
+ ** Starting Mongrel on 0.0.0.0:3301
297
+ -- create_table("nuts_schema_infos")
298
+ -> 0.1035s
299
+ == Nuts::Models::BasicFields: migrating ===================================
300
+ -- create_table(:nuts_pages)
301
+ -> 0.0033s
302
+ == Nuts::Models::BasicFields: migrated (0.0038s) ==========================
303
+ ```
304
+
258
305
  Restart it, and enjoy the silence. There's no point of re-creating the
259
306
  skeleton this time.
260
307
 
261
- Before we go on, there's one rule you must known: Always place your models
308
+ Before we go on, there's one rule you must know: Always place your models
262
309
  before your migrations.
263
310
 
264
311
  ## Using our model
265
312
 
266
- Let's explore how our model works by going into the _console_
313
+ Let's explore how our model works by going into the _console_. The console
314
+ is good way to familiarize with your models. Test your models adding some
315
+ data by bare hand before addin it to the application.
316
+
317
+ ```bash
318
+ $ camping -C nuts.rb
319
+ ** Starting console
320
+ >>
321
+ ```
267
322
 
268
- $ camping -C nuts.rb
269
- ** Starting console
270
- >>
271
-
272
323
  Now it's waiting for your input, and will give you the answer when you press
273
324
  Enter. Here's what I did, leaving out the boring answers. You should add your
274
325
  own pages.
275
326
 
276
- >> Page = Nuts::Models::Page
277
-
278
- >> hiking = Page.new(:title => "Hiking")
279
- >> hiking.content = "You can also set the values like this."
280
- >> hiking.save
281
-
282
- >> page = Page.find_by_title("Hiking")
283
- => #<Nuts::Models::Page id: 1, ... >
284
- >> page = Page.find(1)
285
- => #<Nuts::Models::Page id: 1, ... >
286
- >> page.title
287
- >> page.content
288
- >> page.created_at
289
- >> page.updated_at
290
-
291
- >> Page.find_by_title("Fishing")
292
- => nil
293
-
294
- ## Page.create automatically saves the page for you.
295
- >> Page.create(:title => "Fishing", :content => "Go fish!")
296
-
297
- >> Page.count
298
- => 2
299
-
327
+ ```bash
328
+ >> Page = Nuts::Models::Page
329
+
330
+ >> hiking = Page.new(:title => "Hiking")
331
+ >> hiking.content = "You can also set the values like this."
332
+ >> hiking.save
333
+
334
+ >> page = Page.find_by_title("Hiking")
335
+ => #<Nuts::Models::Page id: 1, ... >
336
+ >> page = Page.find(1)
337
+ => #<Nuts::Models::Page id: 1, ... >
338
+ >> page.title
339
+ >> page.content
340
+ >> page.created_at
341
+ >> page.updated_at
342
+
343
+ >> Page.find_by_title("Fishing")
344
+ => nil
345
+
346
+ ## Page.create automatically saves the page for you.
347
+ >> Page.create(:title => "Fishing", :content => "Go fish!")
348
+
349
+ >> Page.count
350
+ => 2
351
+ ```
352
+
300
353
  Now I have two pages: One about hiking and one about fishing.
301
354
 
302
- ##Wrapping it up
355
+ ## Wrapping it up
303
356
 
304
357
  Wouldn't it be nice if we could show this wonderful our pages in a browser?
305
358
  Update nuts.rb so it also contains something like this:
306
359
 
307
- module Nuts::Controllers
308
- class Pages
309
- def get
310
- # Only fetch the titles of the pages.
311
- @pages = Page.all(:select => "title")
312
- render :list
313
- end
314
- end
315
-
316
- class PageX
317
- def get(title)
318
- @page = Page.find_by_title(title)
319
- render :view
320
- end
321
- end
360
+ ```ruby
361
+ module Nuts::Controllers
362
+ class Pages
363
+ def get
364
+ # Only fetch the titles of the pages.
365
+ @pages = Page.all(:select => "title")
366
+ render :list
322
367
  end
368
+ end
323
369
 
324
- module Nuts::Views
325
- def list
326
- h1 "All pages"
327
- ul do
328
- @pages.each do |page|
329
- li do
330
- a page.title, :href => R(PageX, page.title)
331
- end
332
- end
370
+ class PageX
371
+ def get(title)
372
+ @page = Page.find_by_title(title)
373
+ render :view
374
+ end
375
+ end
376
+ end
377
+
378
+ module Nuts::Views
379
+ def list
380
+ h1 "All pages"
381
+ ul do
382
+ @pages.each do |page|
383
+ li do
384
+ a page.title, :href => R(PageX, page.title)
333
385
  end
334
386
  end
335
-
336
- def view
337
- h1 @page.title
338
- self << @page.content
339
- end
340
387
  end
341
-
388
+ end
389
+
390
+ def view
391
+ h1 @page.title
392
+ self << @page.content
393
+ end
394
+ end
395
+ ```
396
+
342
397
  Here we meet our first _helper_:
343
398
 
344
- R(PageX, page.title)
345
-
399
+ ```ruby
400
+ R(PageX, page.title)
401
+ ```
346
402
  This is the <em>reversed router</em> and it generates a URL based on a
347
- controller. Camping ships with a few, but very useful, helpers and you can
348
- easily add your owns. Have a look at Camping::Helpers for how you use these.
349
-
403
+ controller. R takes the controller you want to link to, followed by the router
404
+ parameters. . Instead of typing:
405
+
406
+ ```ruby
407
+ :href=>'/welcome/to/my/site'
408
+ ```
409
+
410
+ You can let Camping do the hard work for you.
411
+
412
+ ```ruby
413
+ :href=>R(Words)
414
+ ```
415
+
416
+ If the route would have some parameter, you shall write like this:
417
+
418
+ ```ruby
419
+ :href=>R(WordsX,'someword')
420
+ ```
421
+
422
+ Camping ships with a few, but very useful, helpers, and you can easily add your
423
+ owns. Have a look at Camping::Helpers for how you use these.
424
+
350
425
  There's a lot of improvements you could do here. Let me suggest:
351
426
 
352
427
  * Show when the page was created and last updated.
353
428
  * What happens when the page doesn't exist?
354
429
  * What should the front page show?
430
+ * Allow or disallow authenticated users.
355
431
  * Add a layout.
356
432
  * Jazz it up a bit.
357
-
358
- ##The last touch
433
+
434
+ Helpers can work generating Markaby's code. You could write a helper for show
435
+ some kind of data and call it from your views (Add a layout).
436
+
437
+ ## The last touch
359
438
 
360
439
  We have one major flaw in our little application. You can't edit or add new
361
440
  pages. Let's see if we can fix that:
362
441
 
363
- module Nuts::Controllers
364
- class PageX
365
- def get(title)
366
- if @page = Page.find_by_title(title)
367
- render :view
368
- else
369
- redirect PageXEdit, title
370
- end
371
- end
372
-
373
- def post(title)
374
- # If it doesn't exist, initialize it:
375
- @page = Page.find_or_initialize_by_title(title)
376
- # This is the same as:
377
- # @page = Page.find_by_title(title) || Page.new(:title => title)
378
-
379
- @page.content = @input.content
380
- @page.save
381
- redirect PageX, title
382
- end
383
- end
384
-
385
- class PageXEdit
386
- def get(title)
387
- @page = Page.find_or_initialize_by_title(title)
388
- render :edit
389
- end
442
+ ```ruby
443
+ module Nuts::Controllers
444
+ class PageX
445
+ def get(title)
446
+ if @page = Page.find_by_title(title)
447
+ render :view
448
+ else
449
+ redirect PageXEdit, title
390
450
  end
391
451
  end
392
-
452
+
453
+ def post(title)
454
+ # If it doesn't exist, initialize it:
455
+ @page = Page.find_or_initialize_by_title(title)
456
+ # This is the same as:
457
+ # @page = Page.find_by_title(title) || Page.new(:title => title)
458
+
459
+ @page.content = @input.content
460
+ @page.save
461
+ redirect PageX, title
462
+ end
463
+ end
464
+
465
+ class PageXEdit
466
+ def get(title)
467
+ @page = Page.find_or_initialize_by_title(title)
468
+ render :edit
469
+ end
470
+ end
471
+ end
472
+ ```
473
+
393
474
  The core of this code lies in the new <tt>post</tt> method in the PageX
394
475
  controller. When someone types an address or follows a link, they'll end up at
395
476
  the <tt>get</tt> method, but you can easily create a form which rather sends
@@ -405,23 +486,24 @@ forms and those in the URL (<tt>/posts?page=50</tt>).
405
486
 
406
487
  Here's an <tt>edit</tt>-view, but you can probably do better. See if you can
407
488
  integrate all of this with what you already have.
408
-
409
- module Nuts::Views
410
- def edit
411
- h1 @page.title
412
- form :action => R(PageX, @page.title), :method => :post do
413
- textarea @page.content, :name => :content,
414
- :rows => 10, :cols => 50
415
-
416
- br
417
-
418
- input :type => :submit, :value => "Submit!"
419
- end
420
- end
489
+
490
+ ```ruby
491
+ module Nuts::Views
492
+ def edit
493
+ h1 @page.title
494
+ form :action => R(PageX, @page.title), :method => :post do
495
+ textarea @page.content, :name => :content,
496
+ :rows => 10, :cols => 50
497
+
498
+ br
499
+
500
+ input :type => :submit, :value => "Submit!"
421
501
  end
422
-
502
+ end
503
+ end
504
+ ```
423
505
 
424
- ##Phew.
506
+ ## Phew.
425
507
 
426
508
  You've taken quite a few steps in the last minutes. You deserve a break. But
427
509
  let's recap for a moment:
@@ -437,7 +519,6 @@ let's recap for a moment:
437
519
  * Place your models before your migrations.
438
520
  * Helpers are helpful.
439
521
 
440
- Unfortunately, the book stops here for now. Come back in a few months, or join
441
- the mailing list to stay updated, and hopefully there's another chapter
442
- waiting for you.
522
+ If you wanna dive deep, stay reading for a [more extended tutorial](03_more_about_controllers.md) step by step...
443
523
 
524
+ Enjoy the code