reststop 0.5.1 → 0.5.2
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/History.txt +56 -52
- data/LICENSE.txt +21 -21
- data/Manifest.txt +14 -12
- data/{README.txt → README.rdoc} +125 -125
- data/Rakefile +56 -54
- data/examples/blog.rb +400 -400
- data/lib/reststop.rb +451 -450
- data/lib/reststop/version.rb +9 -9
- data/setup.rb +1585 -1585
- data/test/test.rb +31 -0
- metadata +45 -60
- data/.loadpath +0 -9
- data/.project +0 -17
data/lib/reststop.rb
CHANGED
@@ -1,450 +1,451 @@
|
|
1
|
-
# Right now you'll have to do some weird gymnastics to get this hooked in to a Camping app...
|
2
|
-
# Something like:
|
3
|
-
#
|
4
|
-
# Camping.goes :Blog
|
5
|
-
#
|
6
|
-
# module Blog
|
7
|
-
# include Camping::Session
|
8
|
-
# include Reststop
|
9
|
-
#
|
10
|
-
# Controllers.extend Reststop::Controllers
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# module Blog::Base
|
14
|
-
# alias camping_render render
|
15
|
-
# alias camping_service service
|
16
|
-
# alias camping_lookup lookup
|
17
|
-
# include Reststop::Base
|
18
|
-
# alias service reststop_service
|
19
|
-
# alias render reststop_render
|
20
|
-
#
|
21
|
-
# # Overrides the new Tilt-centric lookup method In camping
|
22
|
-
# # RESTstop needs to have a first try at looking up the view
|
23
|
-
# # located in the Views::HTML module.
|
24
|
-
# def lookup(n)
|
25
|
-
# T.fetch(n.to_sym) do |k|
|
26
|
-
# t = Blog::Views::HTML.method_defined?(k) || camping_lookup(n)
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# module Blog::Controllers
|
33
|
-
# extend Reststop::Controllers
|
34
|
-
# ...
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# module Blog::Helpers
|
38
|
-
# alias_method :_R, :R
|
39
|
-
# remove_method :R
|
40
|
-
# include Reststop::Helpers
|
41
|
-
# ...
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# module Blog::Views
|
45
|
-
# extend Reststop::Views
|
46
|
-
# ...
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# The hope is that this could all get taken care of in a
|
50
|
-
# `include Reststop` call (via overriding of #extended)
|
51
|
-
#
|
52
|
-
# See examples/blog.rb for a working example.
|
53
|
-
require 'logger'
|
54
|
-
$LOG = Logger.new(STDOUT)
|
55
|
-
|
56
|
-
module Reststop
|
57
|
-
module Base
|
58
|
-
def reststop_service(*a)
|
59
|
-
if @env['REQUEST_METHOD'] == 'POST' && (input['_method'] == 'put' || input['_method'] == 'delete')
|
60
|
-
@env['REQUEST_METHOD'] = input._method.upcase
|
61
|
-
@method = input._method
|
62
|
-
end
|
63
|
-
@a0=a[0] if !a.empty?
|
64
|
-
camping_service(*a)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Overrides Camping's render method to add the ability to specify a format
|
68
|
-
# module when rendering a view.
|
69
|
-
#
|
70
|
-
# The format can also be specified in other ways (shown in this order
|
71
|
-
# of precedence):
|
72
|
-
#
|
73
|
-
# 1. By providing a second parameter to render()
|
74
|
-
# (eg: <tt>render(:foo, :HTML)</tt>)
|
75
|
-
# 2. By setting the @format variable
|
76
|
-
# 3. By providing a 'format' parameter in the request (i.e. input[:format])
|
77
|
-
# 4. By adding a file-format extension to the url (e.g. /items.xml or
|
78
|
-
# /items/2.html).
|
79
|
-
#
|
80
|
-
# For example, you could have:
|
81
|
-
#
|
82
|
-
# module Foobar::Views
|
83
|
-
#
|
84
|
-
# module HTML
|
85
|
-
# def foo
|
86
|
-
# # ... render some HTML content
|
87
|
-
# end
|
88
|
-
# end
|
89
|
-
#
|
90
|
-
# module RSS
|
91
|
-
# def foo
|
92
|
-
# # ... render some RSS content
|
93
|
-
# end
|
94
|
-
# end
|
95
|
-
#
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# Then in your controller, you would call render() like this:
|
99
|
-
#
|
100
|
-
# render(:foo, :HTML) # render the HTML version of foo
|
101
|
-
#
|
102
|
-
# or
|
103
|
-
#
|
104
|
-
# render(:foo, :RSS) # render the RSS version of foo
|
105
|
-
#
|
106
|
-
# or
|
107
|
-
#
|
108
|
-
# @format = :RSS
|
109
|
-
# render(:foo) # render the RSS version of foo
|
110
|
-
#
|
111
|
-
# or
|
112
|
-
#
|
113
|
-
# # url is /foobar/1?format=RSS
|
114
|
-
# render(:foo) # render the RSS version of foo
|
115
|
-
#
|
116
|
-
# or
|
117
|
-
#
|
118
|
-
# # url is /foobar/1.rss
|
119
|
-
# render(:foo) # render the RSS version of foo
|
120
|
-
#
|
121
|
-
# If no format is specified, render() will behave like it normally does in
|
122
|
-
# Camping, by looking for a matching view method directly
|
123
|
-
# in the Views module.
|
124
|
-
#
|
125
|
-
# You can also specify a default format module by calling
|
126
|
-
# <tt>default_format</tt> after the format module definition.
|
127
|
-
# For example:
|
128
|
-
#
|
129
|
-
# module Foobar::Views
|
130
|
-
# module HTML
|
131
|
-
# # ... etc.
|
132
|
-
# end
|
133
|
-
# default_format :HTML
|
134
|
-
# end
|
135
|
-
#
|
136
|
-
def reststop_render(action, format = nil)
|
137
|
-
format = nil unless format.is_a? Symbol
|
138
|
-
|
139
|
-
app_name = self.class.name.split("::").first # @techarch : get the name of the app
|
140
|
-
format ||= @format
|
141
|
-
|
142
|
-
if format.nil?
|
143
|
-
begin
|
144
|
-
ct = CONTENT_TYPE
|
145
|
-
rescue NameError
|
146
|
-
ct = 'text/html'
|
147
|
-
end
|
148
|
-
|
149
|
-
@headers['Content-Type'] ||= ct
|
150
|
-
basic_render(action) # @techarch
|
151
|
-
else
|
152
|
-
mab = (app_name + '::Mab').constantize # @techarch : get the Mab class
|
153
|
-
m = mab.new({}, self) # @techarch : instantiate Mab
|
154
|
-
mod = (app_name + "::Views::#{format.to_s}").constantize # @techarch : get the right Views format class
|
155
|
-
|
156
|
-
m.extend mod
|
157
|
-
|
158
|
-
begin
|
159
|
-
ct = mod::CONTENT_TYPE
|
160
|
-
rescue NameError
|
161
|
-
ct = "text/#{format.to_s.downcase}"
|
162
|
-
end
|
163
|
-
@headers['Content-Type'] = ct
|
164
|
-
|
165
|
-
s = m.capture{m.send(action)}
|
166
|
-
s = m.capture{send(:layout){s}} if /^_/!~@method.to_s and m.respond_to?(:layout) # @techarch : replaced a[0] by @method (not 100% sure that's right though)
|
167
|
-
s
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# Performs a basic camping rendering (without use of a layout)
|
172
|
-
# This method was added since the addition of Tilt support in camping
|
173
|
-
# is assuming layout.
|
174
|
-
def basic_render(action)
|
175
|
-
app_name = self.class.name.split("::").first # @techarch : get the name of the app
|
176
|
-
|
177
|
-
m = mab.new({}, self) # @techarch : instantiate Mab
|
178
|
-
s = m.capture{m.send(action)}
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
mab.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
# R(Kittens
|
208
|
-
# R(Kittens,
|
209
|
-
# R(
|
210
|
-
# R(@kitten
|
211
|
-
# R(
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
path
|
229
|
-
path << "
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
path
|
243
|
-
path << "/#{
|
244
|
-
path << "
|
245
|
-
|
246
|
-
|
247
|
-
u=Rack::Utils
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
271
|
-
#
|
272
|
-
#
|
273
|
-
# *
|
274
|
-
# *
|
275
|
-
# *
|
276
|
-
# *
|
277
|
-
#
|
278
|
-
#
|
279
|
-
#
|
280
|
-
#
|
281
|
-
# your
|
282
|
-
#
|
283
|
-
#
|
284
|
-
#
|
285
|
-
#
|
286
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
#
|
293
|
-
#
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
# # POST/GET/PUT/DELETE /kittens/
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
#
|
326
|
-
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
330
|
-
#
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
#
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
"#{options[:prefix]}/#{r}/([
|
341
|
-
"#{options[:prefix]}/#{r}(?:\.[a-z]+)?"
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
#
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
@
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
out
|
413
|
-
out
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
end # module
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
#
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
end
|
1
|
+
# Right now you'll have to do some weird gymnastics to get this hooked in to a Camping app...
|
2
|
+
# Something like:
|
3
|
+
#
|
4
|
+
# Camping.goes :Blog
|
5
|
+
#
|
6
|
+
# module Blog
|
7
|
+
# include Camping::Session
|
8
|
+
# include Reststop
|
9
|
+
#
|
10
|
+
# Controllers.extend Reststop::Controllers
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# module Blog::Base
|
14
|
+
# alias camping_render render
|
15
|
+
# alias camping_service service
|
16
|
+
# alias camping_lookup lookup
|
17
|
+
# include Reststop::Base
|
18
|
+
# alias service reststop_service
|
19
|
+
# alias render reststop_render
|
20
|
+
#
|
21
|
+
# # Overrides the new Tilt-centric lookup method In camping
|
22
|
+
# # RESTstop needs to have a first try at looking up the view
|
23
|
+
# # located in the Views::HTML module.
|
24
|
+
# def lookup(n)
|
25
|
+
# T.fetch(n.to_sym) do |k|
|
26
|
+
# t = Blog::Views::HTML.method_defined?(k) || camping_lookup(n)
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# module Blog::Controllers
|
33
|
+
# extend Reststop::Controllers
|
34
|
+
# ...
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# module Blog::Helpers
|
38
|
+
# alias_method :_R, :R
|
39
|
+
# remove_method :R
|
40
|
+
# include Reststop::Helpers
|
41
|
+
# ...
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# module Blog::Views
|
45
|
+
# extend Reststop::Views
|
46
|
+
# ...
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# The hope is that this could all get taken care of in a
|
50
|
+
# `include Reststop` call (via overriding of #extended)
|
51
|
+
#
|
52
|
+
# See examples/blog.rb for a working example.
|
53
|
+
require 'logger'
|
54
|
+
$LOG = Logger.new(STDOUT)
|
55
|
+
|
56
|
+
module Reststop
|
57
|
+
module Base
|
58
|
+
def reststop_service(*a)
|
59
|
+
if @env['REQUEST_METHOD'] == 'POST' && (input['_method'] == 'put' || input['_method'] == 'delete')
|
60
|
+
@env['REQUEST_METHOD'] = input._method.upcase
|
61
|
+
@method = input._method
|
62
|
+
end
|
63
|
+
@a0=a[0] if !a.empty?
|
64
|
+
camping_service(*a)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Overrides Camping's render method to add the ability to specify a format
|
68
|
+
# module when rendering a view.
|
69
|
+
#
|
70
|
+
# The format can also be specified in other ways (shown in this order
|
71
|
+
# of precedence):
|
72
|
+
#
|
73
|
+
# 1. By providing a second parameter to render()
|
74
|
+
# (eg: <tt>render(:foo, :HTML)</tt>)
|
75
|
+
# 2. By setting the @format variable
|
76
|
+
# 3. By providing a 'format' parameter in the request (i.e. input[:format])
|
77
|
+
# 4. By adding a file-format extension to the url (e.g. /items.xml or
|
78
|
+
# /items/2.html).
|
79
|
+
#
|
80
|
+
# For example, you could have:
|
81
|
+
#
|
82
|
+
# module Foobar::Views
|
83
|
+
#
|
84
|
+
# module HTML
|
85
|
+
# def foo
|
86
|
+
# # ... render some HTML content
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# module RSS
|
91
|
+
# def foo
|
92
|
+
# # ... render some RSS content
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# Then in your controller, you would call render() like this:
|
99
|
+
#
|
100
|
+
# render(:foo, :HTML) # render the HTML version of foo
|
101
|
+
#
|
102
|
+
# or
|
103
|
+
#
|
104
|
+
# render(:foo, :RSS) # render the RSS version of foo
|
105
|
+
#
|
106
|
+
# or
|
107
|
+
#
|
108
|
+
# @format = :RSS
|
109
|
+
# render(:foo) # render the RSS version of foo
|
110
|
+
#
|
111
|
+
# or
|
112
|
+
#
|
113
|
+
# # url is /foobar/1?format=RSS
|
114
|
+
# render(:foo) # render the RSS version of foo
|
115
|
+
#
|
116
|
+
# or
|
117
|
+
#
|
118
|
+
# # url is /foobar/1.rss
|
119
|
+
# render(:foo) # render the RSS version of foo
|
120
|
+
#
|
121
|
+
# If no format is specified, render() will behave like it normally does in
|
122
|
+
# Camping, by looking for a matching view method directly
|
123
|
+
# in the Views module.
|
124
|
+
#
|
125
|
+
# You can also specify a default format module by calling
|
126
|
+
# <tt>default_format</tt> after the format module definition.
|
127
|
+
# For example:
|
128
|
+
#
|
129
|
+
# module Foobar::Views
|
130
|
+
# module HTML
|
131
|
+
# # ... etc.
|
132
|
+
# end
|
133
|
+
# default_format :HTML
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
def reststop_render(action, format = nil)
|
137
|
+
format = nil unless format.is_a? Symbol
|
138
|
+
|
139
|
+
app_name = self.class.name.split("::").first # @techarch : get the name of the app
|
140
|
+
format ||= @format
|
141
|
+
|
142
|
+
if format.nil?
|
143
|
+
begin
|
144
|
+
ct = CONTENT_TYPE
|
145
|
+
rescue NameError
|
146
|
+
ct = 'text/html'
|
147
|
+
end
|
148
|
+
|
149
|
+
@headers['Content-Type'] ||= ct
|
150
|
+
basic_render(action) # @techarch
|
151
|
+
else
|
152
|
+
mab = (app_name + '::Mab').constantize # @techarch : get the Mab class
|
153
|
+
m = mab.new({}, self) # @techarch : instantiate Mab
|
154
|
+
mod = (app_name + "::Views::#{format.to_s}").constantize # @techarch : get the right Views format class
|
155
|
+
|
156
|
+
m.extend mod
|
157
|
+
|
158
|
+
begin
|
159
|
+
ct = mod::CONTENT_TYPE
|
160
|
+
rescue NameError
|
161
|
+
ct = "#{format == :HTML ? 'text' : 'application'}/#{format.to_s.downcase}" #@techarch - used to be text/***
|
162
|
+
end
|
163
|
+
@headers['Content-Type'] = ct
|
164
|
+
|
165
|
+
s = m.capture{m.send(action)}
|
166
|
+
s = m.capture{send(:layout){s}} if /^_/!~@method.to_s and m.respond_to?(:layout) # @techarch : replaced a[0] by @method (not 100% sure that's right though)
|
167
|
+
s
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Performs a basic camping rendering (without use of a layout)
|
172
|
+
# This method was added since the addition of Tilt support in camping
|
173
|
+
# is assuming layout.
|
174
|
+
def basic_render(action)
|
175
|
+
app_name = self.class.name.split("::").first # @techarch : get the name of the app
|
176
|
+
mab = (app_name + '::Mab').constantize # @techarch : get the Mab class
|
177
|
+
m = mab.new({}, self) # @techarch : instantiate Mab
|
178
|
+
s = m.capture{m.send(action)}
|
179
|
+
s = m.capture{send(:layout){s}} if /^_/!~@method.to_s and m.respond_to?(:layout) # @techarch : replaced a[0] by @method (not 100% sure that's right though)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
module Views
|
185
|
+
# Call this inside your Views module to set a default format.
|
186
|
+
#
|
187
|
+
# For example:
|
188
|
+
#
|
189
|
+
# module Foobar::Views
|
190
|
+
# module HTML
|
191
|
+
# # ... etc.
|
192
|
+
# end
|
193
|
+
# default_format :XML
|
194
|
+
# end
|
195
|
+
def default_format(m)
|
196
|
+
mod = "#{self}::#{m.to_s}".constantize
|
197
|
+
mab = self.to_s.gsub('::Views','::Mab').constantize # @techarch : get the Mab class
|
198
|
+
mab.class_eval{include mod}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
module Helpers
|
203
|
+
# Overrides Camping's routing helper to make it possible to route RESTful resources.
|
204
|
+
#
|
205
|
+
# Some usage examples:
|
206
|
+
#
|
207
|
+
# R(Kittens) # /kittens
|
208
|
+
# R(Kittens, 'new') # /kittens/new
|
209
|
+
# R(Kittens, 1, 'meow') # /kittens/1/meow
|
210
|
+
# R(@kitten) # /kittens/1
|
211
|
+
# R(@kitten, 'meow') # /kittens/1/meow
|
212
|
+
# R(Kittens, 'list', :colour => 'black') # /kittens/list?colour=black
|
213
|
+
#
|
214
|
+
# The current output format is retained, so if the current <tt>@format</tt> is <tt>:XML</tt>,
|
215
|
+
# the URL will be /kittens/1.xml rather than /kittens/1.
|
216
|
+
#
|
217
|
+
# Note that your controller names might not be loaded if you're calling <tt>R</tt> inside a
|
218
|
+
# view module. In that case you should use the fully qualified name (i.e. Myapp::Controllers::Kittens)
|
219
|
+
# or include the Controllers module into your view module.
|
220
|
+
def R(c, *g)
|
221
|
+
|
222
|
+
cl = c.class.name.split("::").last.pluralize
|
223
|
+
app_name = c.class.name.split("::").first
|
224
|
+
ctrl_cl = app_name + '::Controllers' # @techarch : get to the Controllers using the current app
|
225
|
+
ctrl = (app_name != 'Class') ? ctrl_cl.constantize : Controllers
|
226
|
+
|
227
|
+
if ctrl.constants.include?(cl) #@techarch updated to use new cl variable
|
228
|
+
path = "/#{cl.underscore}/#{c.id}"
|
229
|
+
path << ".#{@format.to_s.downcase}" if @format
|
230
|
+
path << "/#{g.shift}" unless g.empty?
|
231
|
+
self / path
|
232
|
+
elsif c.respond_to?(:restful?) && c.restful?
|
233
|
+
base = c.name.split("::").last.underscore
|
234
|
+
id_or_action = g.shift
|
235
|
+
if id_or_action.to_s =~ /\d+/ #@techarch needed a to_s after id_or_action to allow pattern matching
|
236
|
+
id = id_or_action
|
237
|
+
action = g.shift
|
238
|
+
else
|
239
|
+
action = id_or_action
|
240
|
+
end
|
241
|
+
|
242
|
+
path = "/#{base}"
|
243
|
+
path << "/#{id}" if id
|
244
|
+
path << "/#{action}" if action
|
245
|
+
path << ".#{@format.to_s.downcase}" if @format
|
246
|
+
|
247
|
+
#@techarch substituted U for u=Rack::Utils
|
248
|
+
u=Rack::Utils
|
249
|
+
path << "?#{g.collect{|a|a.collect{|k,v| u.escape(k)+"="+u.escape(v)}.join("&")}.join("&")}" unless g.empty? # FIXME: undefined behaviour if there are multiple arguments left
|
250
|
+
return path
|
251
|
+
else
|
252
|
+
_R(c, *g)
|
253
|
+
end
|
254
|
+
end # def R
|
255
|
+
end # module Helpers
|
256
|
+
|
257
|
+
module Controllers
|
258
|
+
def self.determine_format(input, env) #:nodoc:
|
259
|
+
if input[:format] && !input[:format].empty?
|
260
|
+
input[:format].upcase.intern
|
261
|
+
elsif env['PATH_INFO'] =~ /\.([a-z]+)$/
|
262
|
+
$~[1].upcase.intern
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Calling <tt>REST "<resource name>"</tt> creates a controller with the
|
267
|
+
# appropriate routes and maps your REST methods to standard
|
268
|
+
# Camping controller mehods. This is meant to be used in your Controllers
|
269
|
+
# module in place of <tt>R <routes></tt>.
|
270
|
+
#
|
271
|
+
# Your REST class should define the following methods:
|
272
|
+
#
|
273
|
+
# * create
|
274
|
+
# * read(id)
|
275
|
+
# * update(id)
|
276
|
+
# * destroy(id)
|
277
|
+
# * list
|
278
|
+
#
|
279
|
+
# Routes will be automatically created based on the resource name fed to the
|
280
|
+
# REST method. <b>Your class must have the same (but CamelCaps'ed)
|
281
|
+
# name as the resource name.</b> So if your resource name is 'kittens',
|
282
|
+
# your controller class must be Kittens.
|
283
|
+
#
|
284
|
+
# For example:
|
285
|
+
#
|
286
|
+
# module Foobar::Controllers
|
287
|
+
# class Kittens < REST 'kittens'
|
288
|
+
# # POST /kittens
|
289
|
+
# def create
|
290
|
+
# end
|
291
|
+
#
|
292
|
+
# # GET /kittens/(\d+)
|
293
|
+
# def read(id)
|
294
|
+
# end
|
295
|
+
#
|
296
|
+
# # PUT /kittens/(\d+)
|
297
|
+
# def update(id)
|
298
|
+
# end
|
299
|
+
#
|
300
|
+
# # DELETE /kittens/(\d+)
|
301
|
+
# def destroy(id)
|
302
|
+
# end
|
303
|
+
#
|
304
|
+
# # GET /kittens
|
305
|
+
# def list
|
306
|
+
# end
|
307
|
+
# end
|
308
|
+
# end
|
309
|
+
#
|
310
|
+
# Custom actions are also possible. For example, to implement a 'meow'
|
311
|
+
# action simply add a 'meow' method to the above controller:
|
312
|
+
#
|
313
|
+
# # POST/GET/PUT/DELETE /kittens/meow
|
314
|
+
# # POST/GET/PUT/DELETE /kittens/(\d+)/meow
|
315
|
+
# def meow(id)
|
316
|
+
# end
|
317
|
+
#
|
318
|
+
# Note that a custom action will respond to all four HTTP methods
|
319
|
+
# (POST/GET/PUT/DELETE).
|
320
|
+
#
|
321
|
+
# Optionally, you can specify a <tt>:prefix</tt> key that will prepend the
|
322
|
+
# given string to the routes. For example, the following will create all
|
323
|
+
# of the above routes, prefixed with "/pets"
|
324
|
+
# (i.e. <tt>POST '/pets/kittens'</tt>, <tt>GET '/pets/kittens/(\d+)'</tt>,
|
325
|
+
# etc.):
|
326
|
+
#
|
327
|
+
# module Foobar::Controllers
|
328
|
+
# class Items < REST 'kittens', :prefix => '/pets'
|
329
|
+
# # ...
|
330
|
+
# end
|
331
|
+
# end
|
332
|
+
#
|
333
|
+
# Format-based routing similar to that in ActiveResource is also implemented.
|
334
|
+
# For example, to get a list of kittens in XML format, place a
|
335
|
+
# <tt>GET</tt> call to <tt>/kittens.xml</tt>.
|
336
|
+
# See the documentation for the render() method for more info.
|
337
|
+
#
|
338
|
+
def REST(r, options = {})
|
339
|
+
crud = R "#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)/([a-z_]+)(?:\.[a-z]+)?",
|
340
|
+
"#{options[:prefix]}/#{r}/([0-9a-zA-Z]+)(?:\.[a-z]+)?",
|
341
|
+
"#{options[:prefix]}/#{r}/([a-z_]+)(?:\.[a-z]+)?",
|
342
|
+
"#{options[:prefix]}/#{r}(?:\.[a-z]+)?"
|
343
|
+
|
344
|
+
crud.module_eval do
|
345
|
+
meta_def(:restful?){true}
|
346
|
+
|
347
|
+
$LOG.debug("Creating RESTful controller for #{r.inspect} using Reststop #{'pull version number here'}") if $LOG
|
348
|
+
|
349
|
+
def get(id_or_custom_action = nil, custom_action = nil) # :nodoc:
|
350
|
+
id = input['id'] if input['id']
|
351
|
+
|
352
|
+
custom_action = input['action'] if input['action']
|
353
|
+
|
354
|
+
if self.methods.include? id_or_custom_action
|
355
|
+
custom_action ||= id_or_custom_action
|
356
|
+
id ||= nil
|
357
|
+
else
|
358
|
+
id ||= id_or_custom_action
|
359
|
+
end
|
360
|
+
|
361
|
+
id = id.to_i if id && id =~ /^[0-9]+$/
|
362
|
+
|
363
|
+
@format = Reststop::Controllers.determine_format(input, @env)
|
364
|
+
|
365
|
+
begin
|
366
|
+
if id.nil? && input['id'].nil?
|
367
|
+
custom_action ? send(custom_action) : list
|
368
|
+
else
|
369
|
+
custom_action ? send(custom_action, id || input['id']) : read(id || input['id'])
|
370
|
+
end
|
371
|
+
rescue NoMethodError => e
|
372
|
+
# FIXME: this is probably not a good way to do this, but we need to somehow differentiate
|
373
|
+
# between 'no such route' vs. other NoMethodErrors
|
374
|
+
if e.message =~ /no such method/
|
375
|
+
return no_method(e)
|
376
|
+
else
|
377
|
+
raise e
|
378
|
+
end
|
379
|
+
rescue ActiveRecord::RecordNotFound => e
|
380
|
+
return not_found(e)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
|
385
|
+
def post(custom_action = nil) # :nodoc:
|
386
|
+
@format = Reststop::Controllers.determine_format(input, @env)
|
387
|
+
custom_action ? send(custom_action) : create
|
388
|
+
end
|
389
|
+
|
390
|
+
def put(id, custom_action = nil) # :nodoc:
|
391
|
+
id = id.to_i if id =~ /^[0-9]+$/
|
392
|
+
@format = Reststop::Controllers.determine_format(input, @env)
|
393
|
+
custom_action ? send(custom_action, id || input['id']) : update(id || input['id'])
|
394
|
+
end
|
395
|
+
|
396
|
+
def delete(id, custom_action = nil) # :nodoc:
|
397
|
+
id = id.to_i if id =~ /^[0-9]+$/
|
398
|
+
@format = Reststop::Controllers.determine_format(input, @env)
|
399
|
+
custom_action ? send(custom_action, id || input['id']) : destroy(id || input['id'])
|
400
|
+
end
|
401
|
+
|
402
|
+
private
|
403
|
+
def _error(message, status_code = 500, e = nil)
|
404
|
+
@status = status_code
|
405
|
+
@message = message
|
406
|
+
begin
|
407
|
+
render "error_#{status_code}".intern
|
408
|
+
rescue NoMethodError
|
409
|
+
if @format.to_s == 'XML'
|
410
|
+
"<error code='#{status_code}'>#{@message}</error>"
|
411
|
+
else
|
412
|
+
out = "<strong>#{@message}</strong>"
|
413
|
+
out += "<pre style='color: #bbb'><strong>#{e.class}: #{e}</strong>\n#{e.backtrace.join("\n")}</pre>" if e
|
414
|
+
out
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def no_method(e)
|
420
|
+
_error("No controller method responds to this route!", 501, e)
|
421
|
+
end
|
422
|
+
|
423
|
+
def not_found(e)
|
424
|
+
_error("Record not found!", 404, e)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
crud
|
428
|
+
end # def REST
|
429
|
+
end # module Controllers
|
430
|
+
end # module Reststop
|
431
|
+
|
432
|
+
module Markaby
|
433
|
+
class Builder
|
434
|
+
# Modifies Markaby's 'form' generator so that if a 'method' parameter
|
435
|
+
# is supplied, a hidden '_method' input is automatically added.
|
436
|
+
def form(*args, &block)
|
437
|
+
options = args[0] if args && args[0] && args[0].kind_of?(Hash)
|
438
|
+
inside = capture &block
|
439
|
+
|
440
|
+
if options && options.has_key?(:method)
|
441
|
+
inside = input(:type => 'hidden', :name => '_method', :value => options[:method]) +
|
442
|
+
inside
|
443
|
+
if options[:method].to_s === 'put' || options[:method].to_s == 'delete'
|
444
|
+
options[:method] = 'post'
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
tag!(:form, options || args[0]) {inside}
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|