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.
@@ -1,9 +1,12 @@
1
- Rakefile
2
- README.txt
3
1
  CHANGELOG.txt
4
2
  LICENSE.txt
5
3
  Manifest.txt
6
- setup.rb
4
+ README.txt
5
+ Rakefile
6
+ examples/blog.rb
7
+ lib/restr.rb
7
8
  lib/reststop.rb
8
- test/test_helper.rb
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 essentially gives you two things:
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
- <b>Camping controllers that respond to the standard REST verbs:</b>
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() method documentation for usage info.
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 by
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 # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue 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', "reststop documentation",
27
+ RDOC_OPTS = ['--quiet', '--title', "#{NAME} #{VERS} documentation",
27
28
  "--opname", "index.html",
28
29
  "--line-numbers",
29
30
  "--main", "README",
@@ -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
+