camping 2.1.467 → 2.1.523
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{README → README.md} +45 -43
- data/Rakefile +20 -1
- data/book/{01_introduction → 01_introduction.md} +4 -4
- data/book/{02_getting_started → 02_getting_started.md} +196 -196
- data/book/{51_upgrading → 51_upgrading.md} +54 -54
- data/lib/camping-unabridged.rb +7 -7
- data/lib/camping.rb +4 -4
- data/lib/camping/mab.rb +24 -13
- data/lib/camping/reloader.rb +98 -128
- data/lib/camping/server.rb +22 -89
- data/test/app_helpers.rb +12 -1
- data/test/app_markup.rb +54 -0
- data/test/app_reloader.rb +70 -0
- data/test/app_simple.rb +12 -0
- data/test/apps/reloader.rb +7 -0
- data/test/apps/reloader/config.ru +5 -0
- data/test/apps/reloader/reload_me.rb +2 -0
- data/test/apps/reloader_indirect.rb +3 -0
- data/test/test_helper.rb +4 -5
- metadata +67 -55
data/{README → README.md}
RENAMED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
[](http://travis-ci.org/camping/camping)
|
|
2
|
+
|
|
3
|
+
#Camping, a Microframework
|
|
2
4
|
|
|
3
5
|
Camping is a web framework which consistently stays at less than 4kB of code.
|
|
4
6
|
You can probably view the complete source code on a single page. But, you
|
|
@@ -9,48 +11,48 @@ file like many small CGIs. But to organize it as a Model-View-Controller
|
|
|
9
11
|
application like Rails does. You can then easily move it to Rails once you've
|
|
10
12
|
got it going.
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
##A Camping Skeleton
|
|
13
15
|
|
|
14
16
|
A skeletal Camping blog could look like this:
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
require 'camping'
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
module Blog::Controllers
|
|
27
|
-
class Index
|
|
28
|
-
def get
|
|
29
|
-
@posts = Post.find :all
|
|
30
|
-
render :index
|
|
31
|
-
end
|
|
20
|
+
Camping.goes :Blog
|
|
21
|
+
|
|
22
|
+
module Blog::Models
|
|
23
|
+
class Post < Base; belongs_to :user; end
|
|
24
|
+
class Comment < Base; belongs_to :user; end
|
|
25
|
+
class User < Base; end
|
|
32
26
|
end
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
body do
|
|
40
|
-
h1 "My Blog"
|
|
41
|
-
self << yield
|
|
27
|
+
|
|
28
|
+
module Blog::Controllers
|
|
29
|
+
class Index
|
|
30
|
+
def get
|
|
31
|
+
@posts = Post.find :all
|
|
32
|
+
render :index
|
|
42
33
|
end
|
|
43
34
|
end
|
|
44
35
|
end
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
module Blog::Views
|
|
38
|
+
def layout
|
|
39
|
+
html do
|
|
40
|
+
head { title "My Blog" }
|
|
41
|
+
body do
|
|
42
|
+
h1 "My Blog"
|
|
43
|
+
self << yield
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def index
|
|
49
|
+
@posts.each do |post|
|
|
50
|
+
h1 post.title
|
|
51
|
+
end
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
|
-
end
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
##Installation
|
|
54
56
|
|
|
55
57
|
Interested yet? Luckily it's quite easy to install Camping. We'll be using
|
|
56
58
|
a tool called RubyGems, so if you don't have that installed yet, go grab it!
|
|
@@ -63,32 +65,32 @@ Even better, install the Camping Omnibus, a full package of recommended libs:
|
|
|
63
65
|
gem install camping-omnibus --source http://gems.judofyr.net
|
|
64
66
|
|
|
65
67
|
If not, you should be aware of that Camping itself only depends on
|
|
66
|
-
Rack
|
|
67
|
-
need to install
|
|
68
|
-
|
|
68
|
+
[Rack](http://rack.rubyforge.org), and if you're going to use the views you also
|
|
69
|
+
need to install **[markaby](http://markaby.github.com/)**, and if you're going to use the database you need
|
|
70
|
+
**activerecord** as well.
|
|
69
71
|
|
|
70
72
|
gem install markaby
|
|
71
73
|
gem install activerecord
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
##Learning
|
|
74
76
|
|
|
75
|
-
First of all, you should read
|
|
77
|
+
First of all, you should read [the first chapters](camping/blob/master/book/01_introduction.md)
|
|
76
78
|
of The Camping Book. It should hopefully get you started pretty quick. While
|
|
77
79
|
you're doing that, you should be aware of the _reference_ which contains
|
|
78
80
|
documentation for all the different parts of Camping.
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
[The wiki](http://wiki.github.com/camping/camping) is the place for all tiny,
|
|
81
83
|
useful tricks that we've collected over the years. Don't be afraid to share
|
|
82
84
|
your own discoveries; the more, the better!
|
|
83
85
|
|
|
84
86
|
And if there's anything you're wondering about, don't be shy, but rather
|
|
85
|
-
subscribe to
|
|
87
|
+
subscribe to [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list)
|
|
86
88
|
and ask there. We also have an IRC channel over at Freenode, so if you feel
|
|
87
|
-
like chatting with us, you should join
|
|
89
|
+
like chatting with us, you should join [#camping @ irc.freenode.net](http://java.freenode.net/?channel=camping).
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
##Authors
|
|
90
92
|
|
|
91
|
-
Camping was originally crafted by
|
|
93
|
+
Camping was originally crafted by [why the lucky stiff](http://en.wikipedia.org/wiki/Why_the_lucky_stiff),
|
|
92
94
|
but is now maintained by the _community_. This simply means that if we like your
|
|
93
|
-
patch, it will be applied. Everything is managed through
|
|
94
|
-
so just subscribe and you can instantly take a part in shaping Camping.
|
|
95
|
+
patch, it will be applied. Everything is managed through [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list),
|
|
96
|
+
so just subscribe and you can instantly take a part in shaping Camping.
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
$:.unshift 'extras'
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'rake/dsl_definition'
|
|
5
|
+
require 'rake/alt_system'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
else
|
|
8
|
+
begin
|
|
9
|
+
if defined?(Rake::DeprecatedObjectDSL)
|
|
10
|
+
Rake::DeprecatedObjectDSL.class_eval do
|
|
11
|
+
private_instance_methods(false).each do |meth|
|
|
12
|
+
remove_method meth
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
rescue Exception
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
2
20
|
require 'rake'
|
|
3
21
|
require 'rake/clean'
|
|
4
22
|
require 'rake/testtask'
|
|
@@ -61,7 +79,8 @@ task :diff do
|
|
|
61
79
|
m = Tempfile.new('mural')
|
|
62
80
|
|
|
63
81
|
u << Ruby2Ruby.new.process(RubyParser.new.parse(File.read("lib/camping.rb")))
|
|
64
|
-
|
|
82
|
+
mtext = Ruby2Ruby.new.process(RubyParser.new.parse(File.read("lib/camping-unabridged.rb")))
|
|
83
|
+
m << mtext.gsub(/^\s*#.*\n/, '')
|
|
65
84
|
|
|
66
85
|
u.flush
|
|
67
86
|
m.flush
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#Introduction
|
|
2
2
|
|
|
3
3
|
Camping is a small web framework, less than 4k, a little white blood cell in
|
|
4
4
|
the vein of Rails. This little book will start with a tutorial which takes
|
|
@@ -11,9 +11,9 @@ currently a very much work in progress, and we'll be very grateful if you want
|
|
|
11
11
|
to help out.)
|
|
12
12
|
|
|
13
13
|
If you at any moment need some help or have any questions or comments, we
|
|
14
|
-
highly recommend
|
|
14
|
+
highly recommend [the mailing list](http://rubyforge.org/mailman/listinfo/camping-list)
|
|
15
15
|
which got plenty of nice people willing to help. We also have an IRC-channel
|
|
16
|
-
at
|
|
16
|
+
at [#camping @ irc.freenode.net](http://java.freenode.net/?channel=camping)
|
|
17
17
|
if you're into that sort of things.
|
|
18
18
|
|
|
19
|
-
Enough talk. Ready? Let's
|
|
19
|
+
Enough talk. Ready? Let's ["get started"](02_getting_started.md).
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
#Getting Started
|
|
2
2
|
|
|
3
3
|
Start a new text file called nuts.rb. Here's what you put inside:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Camping.goes :Nuts
|
|
6
6
|
|
|
7
7
|
Save it. Then, open a command prompt in the same directory. You'll want to
|
|
8
8
|
run:
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
$ camping nuts.rb
|
|
11
11
|
|
|
12
12
|
And you should get a message which reads:
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
** Camping running on 0.0.0.0:3301.
|
|
15
15
|
|
|
16
16
|
This means that right now The Camping Server is running on port 3301 on your
|
|
17
17
|
machine. Open your browser and visit http://localhost:3301/.
|
|
18
18
|
|
|
19
19
|
Your browser window should show:
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Camping Problem!
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/ Not found
|
|
24
24
|
|
|
25
25
|
No problem with that. The Camping Server is running, but it doesn't know what
|
|
26
26
|
to show. Let's tell him.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
##Hello clock
|
|
29
29
|
|
|
30
30
|
So, you've got Camping installed and it's running. Keep it running. You can
|
|
31
31
|
edit files and The Camping Server will reload automatically. When you need to
|
|
@@ -33,53 +33,53 @@ stop the server, press Control-C.
|
|
|
33
33
|
|
|
34
34
|
Let's show something. At the bottom of nuts.rb add:
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
module Nuts::Controllers
|
|
37
|
+
class Index < R '/'
|
|
38
|
+
def get
|
|
39
|
+
Time.now.to_s
|
|
40
|
+
end
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
|
-
end
|
|
43
43
|
|
|
44
44
|
Save the file and refresh the browser window. Your browser window should show
|
|
45
45
|
the time, e.g.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Sun Jul 15 12:56:15 +0200 2007
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
##Enjoying the view
|
|
50
50
|
|
|
51
51
|
The Camping microframework allows us to separate our code using the MVC
|
|
52
52
|
(Model-View-Controller) design pattern. Let's add a view to our Nuts
|
|
53
53
|
application. Replace the <tt>module Nuts::Controllers</tt> with:
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
module Nuts::Controllers
|
|
56
|
+
class Index < R '/'
|
|
57
|
+
def get
|
|
58
|
+
@time = Time.now
|
|
59
|
+
render :sundial
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
|
-
end
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
module Nuts::Views
|
|
65
|
+
def layout
|
|
66
|
+
html do
|
|
67
|
+
head do
|
|
68
|
+
title { "Nuts And GORP" }
|
|
69
|
+
end
|
|
70
|
+
body { self << yield }
|
|
69
71
|
end
|
|
70
|
-
body { self << yield }
|
|
71
72
|
end
|
|
72
|
-
end
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
def sundial
|
|
75
|
+
p "The current time is: #{@time}"
|
|
76
|
+
end
|
|
76
77
|
end
|
|
77
|
-
end
|
|
78
78
|
|
|
79
79
|
Save the file and refresh your browser window and it should show a message
|
|
80
80
|
like:
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
The current time is: Sun Jul 15 13:05:41 +0200 2007
|
|
83
83
|
|
|
84
84
|
And the window title reads "Nuts And GORP".
|
|
85
85
|
|
|
@@ -94,7 +94,7 @@ Soon enough, you'll find that you can return anything from the controller, and
|
|
|
94
94
|
it will be sent to the browser. But let's keep that for later and start
|
|
95
95
|
investigating the routes.
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
##Routes
|
|
98
98
|
|
|
99
99
|
You probably noticed the weird <tt>R '/'</tt> syntax in the previous page.
|
|
100
100
|
This is an uncommon feature of Ruby that is used in our favorite
|
|
@@ -104,89 +104,89 @@ on.
|
|
|
104
104
|
These routes can be very powerful, but we're going to have look at the
|
|
105
105
|
simplest ones first.
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
111
112
|
end
|
|
112
|
-
end
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
class Digits < R '/nuts/(\d+)'
|
|
115
|
+
def get(number)
|
|
116
|
+
"You got here by: /nuts/#{number}"
|
|
117
|
+
end
|
|
117
118
|
end
|
|
118
|
-
end
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
123
124
|
end
|
|
124
|
-
end
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)'
|
|
127
|
+
def get(number, everything)
|
|
128
|
+
"You got here by: /nuts/#{number}/#{everything}"
|
|
129
|
+
end
|
|
129
130
|
end
|
|
130
131
|
end
|
|
131
|
-
end
|
|
132
132
|
|
|
133
|
-
Add this to nuts.rb and try if you can hit all of the controllers.
|
|
133
|
+
Add this to `nuts.rb` and try if you can hit all of the controllers.
|
|
134
134
|
|
|
135
135
|
Also notice how everything inside a parenthesis gets passed into the method,
|
|
136
136
|
and is ready at your disposal.
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
###Simpler routes
|
|
139
139
|
|
|
140
140
|
This just in:
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
module Nuts::Controllers
|
|
143
|
+
class Index
|
|
144
|
+
def get
|
|
145
|
+
"You got here by: /"
|
|
146
|
+
end
|
|
146
147
|
end
|
|
147
|
-
end
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
class WelcomeToMySite
|
|
150
|
+
def get
|
|
151
|
+
"You got here by: /welcome/to/my/site"
|
|
152
|
+
end
|
|
152
153
|
end
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
|
|
155
|
+
class NutsN
|
|
156
|
+
def get(number)
|
|
157
|
+
"You got here by: /nuts/#{number}"
|
|
158
|
+
end
|
|
158
159
|
end
|
|
159
|
-
end
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
class GorpX
|
|
162
|
+
def get(everything_else_than_a_slash)
|
|
163
|
+
"You got here by: /gorp/#{everything_else_than_a_slash}"
|
|
164
|
+
end
|
|
164
165
|
end
|
|
165
|
-
end
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
class NutsNX
|
|
168
|
+
def get(number, everything)
|
|
169
|
+
"You got here by: /nuts/#{number}/#{everything}"
|
|
170
|
+
end
|
|
170
171
|
end
|
|
171
172
|
end
|
|
172
|
-
end
|
|
173
173
|
|
|
174
174
|
Drop the <tt>< R</tt>-part and it attemps to read your mind. It won't always
|
|
175
175
|
succeed, but it can simplify your application once in a while.
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
## Modeling the world
|
|
178
178
|
|
|
179
179
|
You can get pretty far with what you've learned now, and hopefully you've been
|
|
180
180
|
playing a bit off-book, but it's time to take the next step: Storing data.
|
|
181
181
|
|
|
182
182
|
Let's start over again.
|
|
183
183
|
|
|
184
|
-
|
|
184
|
+
Camping.goes :Nuts
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
module Nuts::Models
|
|
187
|
+
class Page < Base
|
|
188
|
+
end
|
|
188
189
|
end
|
|
189
|
-
end
|
|
190
190
|
|
|
191
191
|
Obviously, this won't do anything, since we don't have any controllers, but
|
|
192
192
|
let's rather have a look at we _do_ have.
|
|
@@ -198,27 +198,27 @@ to do it.
|
|
|
198
198
|
|
|
199
199
|
However, our model is missing something essential: a skeleton.
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
Camping.goes :Nuts
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
module Nuts::Models
|
|
204
|
+
class Page < Base
|
|
205
|
+
end
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
214
215
|
end
|
|
215
|
-
end
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
def self.down
|
|
218
|
+
drop_table Page.table_name
|
|
219
|
+
end
|
|
219
220
|
end
|
|
220
221
|
end
|
|
221
|
-
end
|
|
222
222
|
|
|
223
223
|
Now we have our first version of our model. It says:
|
|
224
224
|
|
|
@@ -237,23 +237,23 @@ This is called a _migration_. Whenever you want to change or add new models you
|
|
|
237
237
|
|
|
238
238
|
Now we just need to tell Camping to use our migration. Write this at the bottom of nuts.rb
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
def Nuts.create
|
|
241
|
+
Nuts::Models.create_schema
|
|
242
|
+
end
|
|
243
243
|
|
|
244
244
|
When The Camping Server boots up, it will automatically call
|
|
245
245
|
<tt>Nuts.create</tt>. You can put all kind of startup-code here, but right now
|
|
246
246
|
we only want to create our skeleton (or upgrade if needed). Start The Camping
|
|
247
247
|
Server again and observe:
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
257
|
|
|
258
258
|
Restart it, and enjoy the silence. There's no point of re-creating the
|
|
259
259
|
skeleton this time.
|
|
@@ -261,87 +261,87 @@ skeleton this time.
|
|
|
261
261
|
Before we go on, there's one rule you must known: Always place your models
|
|
262
262
|
before your migrations.
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
## Using our model
|
|
265
265
|
|
|
266
266
|
Let's explore how our model works by going into the _console_
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
$ camping -C nuts.rb
|
|
269
|
+
** Starting console
|
|
270
|
+
>>
|
|
271
271
|
|
|
272
272
|
Now it's waiting for your input, and will give you the answer when you press
|
|
273
273
|
Enter. Here's what I did, leaving out the boring answers. You should add your
|
|
274
274
|
own pages.
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
299
|
|
|
300
300
|
Now I have two pages: One about hiking and one about fishing.
|
|
301
301
|
|
|
302
|
-
|
|
302
|
+
##Wrapping it up
|
|
303
303
|
|
|
304
304
|
Wouldn't it be nice if we could show this wonderful our pages in a browser?
|
|
305
305
|
Update nuts.rb so it also contains something like this:
|
|
306
306
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
|
313
314
|
end
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
315
|
+
|
|
316
|
+
class PageX
|
|
317
|
+
def get(title)
|
|
318
|
+
@page = Page.find_by_title(title)
|
|
319
|
+
render :view
|
|
320
|
+
end
|
|
320
321
|
end
|
|
321
322
|
end
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
323
|
+
|
|
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
|
|
331
332
|
end
|
|
332
333
|
end
|
|
333
334
|
end
|
|
334
|
-
end
|
|
335
335
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
336
|
+
def view
|
|
337
|
+
h1 @page.title
|
|
338
|
+
self << @page.content
|
|
339
|
+
end
|
|
339
340
|
end
|
|
340
|
-
end
|
|
341
341
|
|
|
342
342
|
Here we meet our first _helper_:
|
|
343
343
|
|
|
344
|
-
|
|
344
|
+
R(PageX, page.title)
|
|
345
345
|
|
|
346
346
|
This is the <em>reversed router</em> and it generates a URL based on a
|
|
347
347
|
controller. Camping ships with a few, but very useful, helpers and you can
|
|
@@ -355,40 +355,40 @@ There's a lot of improvements you could do here. Let me suggest:
|
|
|
355
355
|
* Add a layout.
|
|
356
356
|
* Jazz it up a bit.
|
|
357
357
|
|
|
358
|
-
|
|
358
|
+
##The last touch
|
|
359
359
|
|
|
360
360
|
We have one major flaw in our little application. You can't edit or add new
|
|
361
361
|
pages. Let's see if we can fix that:
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
|
370
371
|
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
372
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
|
382
383
|
end
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
384
|
+
|
|
385
|
+
class PageXEdit
|
|
386
|
+
def get(title)
|
|
387
|
+
@page = Page.find_or_initialize_by_title(title)
|
|
388
|
+
render :edit
|
|
389
|
+
end
|
|
389
390
|
end
|
|
390
391
|
end
|
|
391
|
-
end
|
|
392
392
|
|
|
393
393
|
The core of this code lies in the new <tt>post</tt> method in the PageX
|
|
394
394
|
controller. When someone types an address or follows a link, they'll end up at
|
|
@@ -406,22 +406,22 @@ forms and those in the URL (<tt>/posts?page=50</tt>).
|
|
|
406
406
|
Here's an <tt>edit</tt>-view, but you can probably do better. See if you can
|
|
407
407
|
integrate all of this with what you already have.
|
|
408
408
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
|
419
420
|
end
|
|
420
421
|
end
|
|
421
|
-
end
|
|
422
422
|
|
|
423
423
|
|
|
424
|
-
|
|
424
|
+
##Phew.
|
|
425
425
|
|
|
426
426
|
You've taken quite a few steps in the last minutes. You deserve a break. But
|
|
427
427
|
let's recap for a moment:
|