reststop 0.1.0 → 0.2.0
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/CHANGELOG.txt +21 -1
- data/LICENSE.txt +159 -668
- data/Manifest.txt +7 -4
- data/README.txt +52 -8
- data/Rakefile +3 -2
- data/examples/blog.rb +345 -0
- data/lib/restr.rb +126 -0
- data/lib/reststop.rb +226 -39
- data/lib/reststop/version.rb +9 -0
- metadata +17 -10
data/Manifest.txt
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
Rakefile
|
2
|
-
README.txt
|
3
1
|
CHANGELOG.txt
|
4
2
|
LICENSE.txt
|
5
3
|
Manifest.txt
|
6
|
-
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
examples/blog.rb
|
7
|
+
lib/restr.rb
|
7
8
|
lib/reststop.rb
|
8
|
-
|
9
|
+
lib/reststop/version.rb
|
10
|
+
setup.rb
|
9
11
|
test/reststop_test.rb
|
12
|
+
test/test_helper.rb
|
data/README.txt
CHANGED
@@ -14,9 +14,12 @@ You can contact the author at:
|
|
14
14
|
<b>Reststop makes it easy to write RESTful[http://en.wikipedia.org/wiki/Representational_State_Transfer]
|
15
15
|
applications in Camping[http://camping.rubyforge.org/files/README.html].</b>
|
16
16
|
|
17
|
-
Reststop
|
17
|
+
For an example of a complete Reststop-based Camping app, have a look at
|
18
|
+
http://reststop.rubyforge.org/svn/trunk/examples/blog.rb
|
18
19
|
|
19
|
-
|
20
|
+
Reststop essentially gives you three things:
|
21
|
+
|
22
|
+
<b>1. Camping controllers that respond to the standard REST verbs:</b>
|
20
23
|
|
21
24
|
* create (POST)
|
22
25
|
* read (GET)
|
@@ -24,9 +27,9 @@ Reststop essentially gives you two things:
|
|
24
27
|
* destroy (DELETE)
|
25
28
|
* list (GET)
|
26
29
|
|
27
|
-
See the Camping::Controllers#REST method documentation for usage info.
|
30
|
+
Custom actions are also possible. See the Camping::Controllers#REST method documentation for usage info.
|
28
31
|
|
29
|
-
<b>Camping views grouped by output format:</b>
|
32
|
+
<b>2. Camping views grouped by output format:</b>
|
30
33
|
|
31
34
|
Your views module:
|
32
35
|
|
@@ -51,14 +54,55 @@ Your render call:
|
|
51
54
|
|
52
55
|
render(:foo, :XML)
|
53
56
|
|
54
|
-
See the render
|
57
|
+
See the Camping#render method documentation for usage info.
|
58
|
+
|
59
|
+
<b>3. Nice URLs to bring it all together:</b>
|
60
|
+
|
61
|
+
For example a list of kittens in the default format (HTML) is available at:
|
62
|
+
|
63
|
+
/kittens
|
64
|
+
|
65
|
+
The list, in RSS format:
|
66
|
+
|
67
|
+
/kittens.rss
|
68
|
+
|
69
|
+
Kitten with id 1, in XML format:
|
70
|
+
|
71
|
+
/kittens/1.xml
|
72
|
+
|
73
|
+
Using custom action 'meow' on kitten with id 1:
|
74
|
+
|
75
|
+
/kittens/1/meow
|
76
|
+
|
77
|
+
In other words, say you have a "kittens" resource; you can make a GET
|
78
|
+
request to http://yourapp.com/kittens.xml and get a list of kittens
|
79
|
+
through your Kittens controller's <tt>list</tt>, formatted using your
|
80
|
+
<tt>XML</tt> view module.
|
81
|
+
|
82
|
+
|
83
|
+
<b>BONUS: A simple REST client</b>
|
84
|
+
|
85
|
+
Reststop also comes with a very simple REST client called Restr.
|
86
|
+
Restr is basically a wrapper around Ruby's Net::HTTP, offering
|
87
|
+
a more RESTfully meaningful interface.
|
88
|
+
|
89
|
+
See the Restr documentation for more info, but here's a simple
|
90
|
+
example of RESTful interaction with Restr:
|
91
|
+
|
92
|
+
require 'restr'
|
93
|
+
kitten = Restr.get('http://example.com/kittens/1.xml')
|
94
|
+
puts kitten['name']
|
95
|
+
puts kitten['colour']
|
96
|
+
|
97
|
+
kitten['colour'] = 'black'
|
98
|
+
kitten = Restr.put('http://example.com/kittens/1.xml', kitten)
|
55
99
|
|
56
100
|
|
57
101
|
----
|
58
102
|
|
59
103
|
Reststop is free software; you can redistribute it and/or modify
|
60
|
-
it under the terms of the GNU General Public License as published
|
61
|
-
the Free Software Foundation; either version 3 of the License, or
|
104
|
+
it under the terms of the GNU Lesser General Public License as published
|
105
|
+
by the Free Software Foundation; either version 3 of the License, or
|
62
106
|
(at your option) any later version.
|
63
107
|
|
64
108
|
Reststop is distributed in the hope that it will be useful,
|
@@ -66,5 +110,5 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
66
110
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
67
111
|
GNU General Public License for more details.
|
68
112
|
|
69
|
-
You should have received a copy of the GNU General Public License
|
113
|
+
You should have received a copy of the GNU Lesser General Public License
|
70
114
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
CHANGED
@@ -20,10 +20,11 @@ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
20
20
|
|
21
21
|
|
22
22
|
NAME = "reststop"
|
23
|
-
REV = nil
|
23
|
+
REV = nil
|
24
|
+
#REV = `svn info`[/Revision: (\d+)/, 1] rescue nil
|
24
25
|
VERS = ENV['VERSION'] || (Reststop::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
25
26
|
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
26
|
-
RDOC_OPTS = ['--quiet', '--title', "
|
27
|
+
RDOC_OPTS = ['--quiet', '--title', "#{NAME} #{VERS} documentation",
|
27
28
|
"--opname", "index.html",
|
28
29
|
"--line-numbers",
|
29
30
|
"--main", "README",
|
data/examples/blog.rb
ADDED
@@ -0,0 +1,345 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# This is a RESTful version of the Camping-based Blog application.
|
5
|
+
#
|
6
|
+
# The original version can be found here:
|
7
|
+
# http://code.whytheluckystiff.net/camping/browser/trunk/examples/blog.rb
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
|
12
|
+
gem 'camping', '~> 1.5'
|
13
|
+
gem 'reststop', '~> 0.2'
|
14
|
+
|
15
|
+
require 'camping'
|
16
|
+
require 'camping/db'
|
17
|
+
require 'camping/session'
|
18
|
+
|
19
|
+
begin
|
20
|
+
# try to use local copy of library
|
21
|
+
require '../lib/reststop'
|
22
|
+
rescue LoadError
|
23
|
+
# ... otherwise default to rubygem
|
24
|
+
require 'reststop'
|
25
|
+
end
|
26
|
+
|
27
|
+
Camping.goes :Blog
|
28
|
+
|
29
|
+
module Blog
|
30
|
+
include Camping::Session
|
31
|
+
end
|
32
|
+
|
33
|
+
module Blog::Models
|
34
|
+
class Post < Base
|
35
|
+
belongs_to :user
|
36
|
+
has_many :comments
|
37
|
+
end
|
38
|
+
class Comment < Base
|
39
|
+
belongs_to :user
|
40
|
+
end
|
41
|
+
class User < Base; end
|
42
|
+
|
43
|
+
class CreateTheBasics < V 1.0
|
44
|
+
def self.up
|
45
|
+
create_table :blog_posts do |t|
|
46
|
+
t.column :user_id, :integer, :null => false
|
47
|
+
t.column :title, :string, :limit => 255
|
48
|
+
t.column :body, :text
|
49
|
+
end
|
50
|
+
create_table :blog_users do |t|
|
51
|
+
t.column :username, :string
|
52
|
+
t.column :password, :string
|
53
|
+
end
|
54
|
+
create_table :blog_comments do |t|
|
55
|
+
t.column :post_id, :integer, :null => false
|
56
|
+
t.column :username, :string
|
57
|
+
t.column :body, :text
|
58
|
+
end
|
59
|
+
User.create :username => 'admin', :password => 'camping'
|
60
|
+
end
|
61
|
+
def self.down
|
62
|
+
drop_table :blog_posts
|
63
|
+
drop_table :blog_users
|
64
|
+
drop_table :blog_comments
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module Blog::Controllers
|
70
|
+
class Posts < REST 'posts'
|
71
|
+
|
72
|
+
# POST /posts
|
73
|
+
def create
|
74
|
+
unless @state.user_id.blank?
|
75
|
+
post = Post.create :title => input.post_title, :body => input.post_body,
|
76
|
+
:user_id => @state.user_id
|
77
|
+
redirect R(Posts)
|
78
|
+
else
|
79
|
+
_error("Unauthorized", 401)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# GET /posts/1
|
84
|
+
# GET /posts/1.xml
|
85
|
+
def read(post_id)
|
86
|
+
@post = Post.find post_id
|
87
|
+
@comments = Models::Comment.find :all, :conditions => ['post_id = ?', post_id]
|
88
|
+
render :view
|
89
|
+
end
|
90
|
+
|
91
|
+
# PUT /posts/1
|
92
|
+
def update(post_id)
|
93
|
+
unless @state.user_id.blank?
|
94
|
+
@post = Post.find post_id
|
95
|
+
@post.update_attributes :title => input.post_title, :body => input.post_body
|
96
|
+
redirect R(@post)
|
97
|
+
else
|
98
|
+
_error("Unauthorized", 401)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# DELETE /posts/1
|
103
|
+
def delete(post_id)
|
104
|
+
unless @state.user_id.blank?
|
105
|
+
@post = Post.find post_id
|
106
|
+
|
107
|
+
if @post.destroy
|
108
|
+
redirect R(Posts)
|
109
|
+
else
|
110
|
+
_error("Unable to delete post #{@post.id}", 500)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
_error("Unauthorized", 401)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# GET /posts
|
118
|
+
# GET /posts.xml
|
119
|
+
def list
|
120
|
+
@posts = Post.find :all
|
121
|
+
render :index
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# GET /posts/new
|
126
|
+
def new
|
127
|
+
unless @state.user_id.blank?
|
128
|
+
@user = User.find @state.user_id
|
129
|
+
@post = Post.new
|
130
|
+
end
|
131
|
+
render :add
|
132
|
+
end
|
133
|
+
|
134
|
+
# GET /posts/1/edit
|
135
|
+
def edit(post_id)
|
136
|
+
unless @state.user_id.blank?
|
137
|
+
@user = User.find @state.user_id
|
138
|
+
end
|
139
|
+
@post = Post.find post_id
|
140
|
+
render :edit
|
141
|
+
end
|
142
|
+
|
143
|
+
# GET /posts/info
|
144
|
+
def info
|
145
|
+
div do
|
146
|
+
code args.inspect; br; br
|
147
|
+
code @env.inspect; br
|
148
|
+
code "Link: #{R(Info, 1, 2)}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Comments < REST 'comments'
|
154
|
+
# POST /comments
|
155
|
+
def create
|
156
|
+
Models::Comment.create(:username => input.post_username,
|
157
|
+
:body => input.post_body, :post_id => input.post_id)
|
158
|
+
redirect R(Posts, input.post_id)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Sessions < REST 'sessions'
|
163
|
+
# POST /sessions
|
164
|
+
def create
|
165
|
+
@user = User.find :first, :conditions => ['username = ? AND password = ?', input.username, input.password]
|
166
|
+
|
167
|
+
if @user
|
168
|
+
@login = 'login success !'
|
169
|
+
@state.user_id = @user.id
|
170
|
+
else
|
171
|
+
@login = 'wrong user name or password'
|
172
|
+
end
|
173
|
+
render :login
|
174
|
+
end
|
175
|
+
|
176
|
+
# DELETE /sessions
|
177
|
+
def delete
|
178
|
+
@state.user_id = nil
|
179
|
+
render :logout
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# You can use old-fashioned Camping controllers too!
|
184
|
+
class Style < R '/styles.css'
|
185
|
+
def get
|
186
|
+
@headers["Content-Type"] = "text/css; charset=utf-8"
|
187
|
+
@body = %{
|
188
|
+
body {
|
189
|
+
font-family: Utopia, Georga, serif;
|
190
|
+
}
|
191
|
+
h1.header {
|
192
|
+
background-color: #fef;
|
193
|
+
margin: 0; padding: 10px;
|
194
|
+
}
|
195
|
+
div.content {
|
196
|
+
padding: 10px;
|
197
|
+
}
|
198
|
+
}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
Markaby::Builder.set(:indent, 2)
|
205
|
+
|
206
|
+
module Blog::Views
|
207
|
+
module HTML
|
208
|
+
include Blog::Controllers
|
209
|
+
|
210
|
+
def layout
|
211
|
+
html do
|
212
|
+
head do
|
213
|
+
title 'blog'
|
214
|
+
link :rel => 'stylesheet', :type => 'text/css',
|
215
|
+
:href => self/'/styles.css', :media => 'screen'
|
216
|
+
end
|
217
|
+
body do
|
218
|
+
h1.header { a 'blog', :href => R(Posts) }
|
219
|
+
div.content do
|
220
|
+
self << yield
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def index
|
227
|
+
if @posts.empty?
|
228
|
+
p 'No posts found.'
|
229
|
+
else
|
230
|
+
for post in @posts
|
231
|
+
_post(post)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
p { a 'Add', :href => R(Posts, 'new') }
|
235
|
+
end
|
236
|
+
|
237
|
+
def login
|
238
|
+
p { b @login }
|
239
|
+
p { a 'Continue', :href => R(Posts, 'new') }
|
240
|
+
end
|
241
|
+
|
242
|
+
def logout
|
243
|
+
p "You have been logged out."
|
244
|
+
p { a 'Continue', :href => R(Posts) }
|
245
|
+
end
|
246
|
+
|
247
|
+
def add
|
248
|
+
if @user
|
249
|
+
_form(@post, :action => R(Posts))
|
250
|
+
else
|
251
|
+
_login
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def edit
|
256
|
+
if @user
|
257
|
+
_form(@post, :action => R(@post), :method => :put)
|
258
|
+
else
|
259
|
+
_login
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def view
|
264
|
+
_post(@post)
|
265
|
+
|
266
|
+
p "Comment for this post:"
|
267
|
+
for c in @comments
|
268
|
+
h1 c.username
|
269
|
+
p c.body
|
270
|
+
end
|
271
|
+
|
272
|
+
form :action => R(Comments), :method => 'post' do
|
273
|
+
label 'Name', :for => 'post_username'; br
|
274
|
+
input :name => 'post_username', :type => 'text'; br
|
275
|
+
label 'Comment', :for => 'post_body'; br
|
276
|
+
textarea :name => 'post_body' do; end; br
|
277
|
+
input :type => 'hidden', :name => 'post_id', :value => @post.id
|
278
|
+
input :type => 'submit'
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# partials
|
283
|
+
def _login
|
284
|
+
form :action => R(Sessions), :method => 'post' do
|
285
|
+
label 'Username', :for => 'username'; br
|
286
|
+
input :name => 'username', :type => 'text'; br
|
287
|
+
|
288
|
+
label 'Password', :for => 'password'; br
|
289
|
+
input :name => 'password', :type => 'text'; br
|
290
|
+
|
291
|
+
input :type => 'submit', :name => 'login', :value => 'Login'
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def _post(post)
|
296
|
+
h1 post.title
|
297
|
+
p post.body
|
298
|
+
p do
|
299
|
+
[a("Edit", :href => R(Posts, post.id, 'edit')), a("View", :href => R(Posts, post.id, 'edit'))].join " | "
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def _form(post, opts)
|
304
|
+
form(:action => R(Sessions), :method => 'delete') do
|
305
|
+
p do
|
306
|
+
span "You are logged in as #{@user.username}"
|
307
|
+
span " | "
|
308
|
+
button(:type => 'submit') {'Logout'}
|
309
|
+
end
|
310
|
+
end
|
311
|
+
form({:method => 'post'}.merge(opts)) do
|
312
|
+
label 'Title', :for => 'post_title'; br
|
313
|
+
input :name => 'post_title', :type => 'text',
|
314
|
+
:value => post.title; br
|
315
|
+
|
316
|
+
label 'Body', :for => 'post_body'; br
|
317
|
+
textarea post.body, :name => 'post_body'; br
|
318
|
+
|
319
|
+
input :type => 'hidden', :name => 'post_id', :value => post.id
|
320
|
+
input :type => 'submit'
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
default_format :HTML
|
325
|
+
|
326
|
+
module XML
|
327
|
+
def layout
|
328
|
+
yield
|
329
|
+
end
|
330
|
+
|
331
|
+
def index
|
332
|
+
@posts.to_xml(:root => 'blog')
|
333
|
+
end
|
334
|
+
|
335
|
+
def view
|
336
|
+
@post.to_xml(:root => 'post')
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def Blog.create
|
342
|
+
Camping::Models::Session.create_schema
|
343
|
+
Blog::Models.create_schema :assume => (Blog::Models::Post.table_exists? ? 1.0 : 0.0)
|
344
|
+
end
|
345
|
+
|