rackful 0.1.2 → 0.1.3
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/example/config.ru +6 -3
- data/lib/rackful.rb +6 -6
- data/lib/{rackful_http_status.rb → rackful/http_status.rb} +39 -42
- data/lib/rackful/middleware.rb +3 -0
- data/lib/rackful/{header_spoofing.rb → middleware/header_spoofing.rb} +4 -5
- data/lib/rackful/{method_spoofing.rb → middleware/method_spoofing.rb} +0 -1
- data/lib/rackful/{relative_location.rb → middleware/relative_location.rb} +0 -1
- data/lib/rackful/path.rb +179 -0
- data/lib/{rackful_request.rb → rackful/request.rb} +6 -15
- data/lib/{rackful_resource.rb → rackful/resource.rb} +125 -101
- data/lib/{rackful_serializer.rb → rackful/serializer.rb} +159 -80
- data/lib/{rackful_server.rb → rackful/server.rb} +3 -8
- data/rackful.gemspec +3 -3
- metadata +57 -55
- data/example/config2.ru +0 -41
- data/lib/rackful_path.rb +0 -119
@@ -8,7 +8,6 @@ module Rackful
|
|
8
8
|
|
9
9
|
=begin markdown
|
10
10
|
Subclass of {Rack::Request}, augmented for Rackful requests.
|
11
|
-
@since 0.0.1
|
12
11
|
=end
|
13
12
|
class Request < Rack::Request
|
14
13
|
|
@@ -17,7 +16,6 @@ class Request < Rack::Request
|
|
17
16
|
The resource factory for the current request.
|
18
17
|
@return [#[]]
|
19
18
|
@see Server#initialize
|
20
|
-
@since 0.0.1
|
21
19
|
=end
|
22
20
|
def resource_factory; self.env['rackful.resource_factory']; end
|
23
21
|
def base_path
|
@@ -31,18 +29,15 @@ The resource factory for the current request.
|
|
31
29
|
Similar to the HTTP/1.1 `Content-Location:` header. Contains the canonical path
|
32
30
|
to the requested resource, which may differ from {#path}
|
33
31
|
@return [Path]
|
34
|
-
@since 0.1.0
|
35
32
|
=end
|
36
33
|
def content_path; self.env['rackful.content_path'] ||= self.path; end
|
37
34
|
=begin markdown
|
38
35
|
Set by {Rackful::Server#call!}
|
39
36
|
@return [Path]
|
40
|
-
@since 0.1.0
|
41
37
|
=end
|
42
38
|
def content_path= bp; self.env['rackful.content_path'] = bp.to_path; end
|
43
39
|
=begin markdown
|
44
40
|
@return [Path]
|
45
|
-
@since 0.1.0
|
46
41
|
=end
|
47
42
|
def path; super.to_path; end
|
48
43
|
|
@@ -60,7 +55,6 @@ In a multi-threaded server, multiple requests can be handled at one time.
|
|
60
55
|
This method returns the request object, created (and registered) by
|
61
56
|
{Server#call!}
|
62
57
|
@return [Request]
|
63
|
-
@since 0.0.1
|
64
58
|
=end
|
65
59
|
def self.current
|
66
60
|
Thread.current[:rackful_request]
|
@@ -82,7 +76,6 @@ Assert all <tt>If-*</tt> request headers.
|
|
82
76
|
@see http://tools.ietf.org/html/rfc2616#section-13.3.3 RFC2616, section 13.3.3
|
83
77
|
for details about weak and strong validator comparison.
|
84
78
|
@todo Implement support for the `If-Range:` header.
|
85
|
-
@since 0.0.1
|
86
79
|
=end
|
87
80
|
def assert_if_headers resource
|
88
81
|
#raise HTTP501NotImplemented, 'If-Range: request header is not supported.' \
|
@@ -113,11 +106,15 @@ Assert all <tt>If-*</tt> request headers.
|
|
113
106
|
elsif cond[:unmodified_since]
|
114
107
|
raise HTTP412PreconditionFailed, 'If-Unmodified-Since'
|
115
108
|
elsif cond[:modified_since]
|
116
|
-
raise HTTP404NotFound
|
109
|
+
raise HTTP404NotFound, resource.path
|
117
110
|
end
|
118
111
|
else
|
119
112
|
if cond[:none_match] && self.validate_etag( etag, cond[:none_match] )
|
120
|
-
|
113
|
+
if allow_weak
|
114
|
+
raise HTTP304NotModified
|
115
|
+
else
|
116
|
+
raise HTTP412PreconditionFailed, 'If-None-Match'
|
117
|
+
end
|
121
118
|
elsif cond[:match] && ! self.validate_etag( etag, cond[:match] )
|
122
119
|
raise HTTP412PreconditionFailed, 'If-Match'
|
123
120
|
elsif cond[:unmodified_since]
|
@@ -145,7 +142,6 @@ Hash of acceptable media types and their qualities.
|
|
145
142
|
This method parses the HTTP/1.1 `Accept:` header. If no acceptable media
|
146
143
|
types are provided, an empty Hash is returned.
|
147
144
|
@return [Hash{media_type => quality}]
|
148
|
-
@since 0.0.1
|
149
145
|
=end
|
150
146
|
def accept
|
151
147
|
@env['rackful.accept'] ||= begin
|
@@ -172,7 +168,6 @@ Parses the HTTP/1.1 `If-Match:` header.
|
|
172
168
|
@return [nil, Array<String>]
|
173
169
|
@see http://tools.ietf.org/html/rfc2616#section-14.24 RFC2616, section 14.24
|
174
170
|
@see #if_none_match
|
175
|
-
@since 0.0.1
|
176
171
|
=end
|
177
172
|
def if_match none = false
|
178
173
|
header = @env["HTTP_IF_#{ none ? 'NONE_' : '' }MATCH"]
|
@@ -192,7 +187,6 @@ Parses the HTTP/1.1 `If-None-Match:` header.
|
|
192
187
|
@return [nil, Array<String>]
|
193
188
|
@see http://tools.ietf.org/html/rfc2616#section-14.26 RFC2616, section 14.26
|
194
189
|
@see #if_match
|
195
|
-
@since 0.0.1
|
196
190
|
=end
|
197
191
|
def if_none_match
|
198
192
|
self.if_match true
|
@@ -204,7 +198,6 @@ Parses the HTTP/1.1 `If-None-Match:` header.
|
|
204
198
|
@return [nil, Time]
|
205
199
|
@see http://tools.ietf.org/html/rfc2616#section-14.25 RFC2616, section 14.25
|
206
200
|
@see #if_unmodified_since
|
207
|
-
@since 0.0.1
|
208
201
|
=end
|
209
202
|
def if_modified_since unmodified = false
|
210
203
|
header = @env["HTTP_IF_#{ unmodified ? 'UN' : '' }MODIFIED_SINCE"]
|
@@ -222,7 +215,6 @@ Parses the HTTP/1.1 `If-None-Match:` header.
|
|
222
215
|
@return [nil, Time]
|
223
216
|
@see http://tools.ietf.org/html/rfc2616#section-14.28 RFC2616, section 14.28
|
224
217
|
@see #if_modified_since
|
225
|
-
@since 0.0.1
|
226
218
|
=end
|
227
219
|
def if_unmodified_since
|
228
220
|
self.if_modified_since true
|
@@ -241,7 +233,6 @@ Does any of the tags in `etags` match `etag`?
|
|
241
233
|
@return [Boolean]
|
242
234
|
@see http://tools.ietf.org/html/rfc2616#section-13.3.3 RFC2616 section 13.3.3
|
243
235
|
for details about weak and strong validator comparison.
|
244
|
-
@since 0.0.1
|
245
236
|
=end
|
246
237
|
def validate_etag etag, etags
|
247
238
|
etag = etag.to_s
|
@@ -15,7 +15,6 @@ Classes that include this module may implement a method `content_types`
|
|
15
15
|
for content negotiation. This method must return a Hash of
|
16
16
|
`media-type => quality` pairs.
|
17
17
|
@see Server, ResourceFactory
|
18
|
-
@since 0.0.1
|
19
18
|
=end
|
20
19
|
module Resource
|
21
20
|
|
@@ -23,6 +22,14 @@ module Resource
|
|
23
22
|
include Rack::Utils
|
24
23
|
|
25
24
|
|
25
|
+
=begin
|
26
|
+
Normally, when a module is included, all the instance methods of the included
|
27
|
+
module become available as instance methods to the including module/class. But
|
28
|
+
class methods of the included module don't become available as class methods to
|
29
|
+
the including class.
|
30
|
+
|
31
|
+
|
32
|
+
=end
|
26
33
|
def self.included(base)
|
27
34
|
base.extend ClassMethods
|
28
35
|
end
|
@@ -30,21 +37,21 @@ module Resource
|
|
30
37
|
|
31
38
|
module ClassMethods
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
=begin
|
41
|
+
Meta-programmer method.
|
42
|
+
@example Have your resource rendered in XML and JSON
|
43
|
+
class MyResource
|
44
|
+
add_serializer MyResource2XML
|
45
|
+
add_serializer MyResource2JSON, 0.5
|
46
|
+
end
|
47
|
+
@param serializer [Serializer]
|
48
|
+
@param quality [Float]
|
49
|
+
@return [self]
|
50
|
+
=end
|
43
51
|
def add_serializer serializer, quality = 1.0
|
44
52
|
quality = quality.to_f
|
45
53
|
quality = 1.0 if quality > 1.0
|
46
54
|
quality = 0.0 if quality < 0.0
|
47
|
-
# The single '@' on the following line is on purpose!
|
48
55
|
s = [serializer, quality]
|
49
56
|
serializer::CONTENT_TYPES.each {
|
50
57
|
|content_type|
|
@@ -55,11 +62,13 @@ module Resource
|
|
55
62
|
|
56
63
|
|
57
64
|
def serializers
|
65
|
+
# The single '@' on the following line is on purpose!
|
58
66
|
@rackful_resource_serializers ||= {}
|
59
67
|
end
|
60
68
|
|
61
69
|
|
62
70
|
def all_serializers
|
71
|
+
# The single '@' on the following line is on purpose!
|
63
72
|
@rackful_resource_all_serializers ||=
|
64
73
|
if self.superclass.respond_to?(:all_serializers)
|
65
74
|
self.superclass.all_serializers.merge( self.serializers ) do
|
@@ -72,28 +81,36 @@ module Resource
|
|
72
81
|
end
|
73
82
|
|
74
83
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
=begin
|
85
|
+
Meta-programmer method.
|
86
|
+
@example Have your resource accept XML and JSON in `PUT` requests
|
87
|
+
class MyResource
|
88
|
+
add_media_type 'text/xml', :PUT
|
89
|
+
add_media_type 'application/json', :PUT
|
90
|
+
end
|
91
|
+
@param [#to_s] media_type
|
92
|
+
@param [#to_sym] method
|
93
|
+
@return [self]
|
94
|
+
=end
|
84
95
|
def add_media_type media_type, method = :PUT
|
85
96
|
method = method.to_sym
|
86
97
|
self.media_types[method] ||= []
|
87
|
-
self.media_types[method] << media_type
|
98
|
+
self.media_types[method] << media_type.to_s
|
88
99
|
self
|
89
100
|
end
|
90
101
|
|
91
102
|
|
103
|
+
=begin
|
104
|
+
@todo Documentation
|
105
|
+
=end
|
92
106
|
def media_types
|
93
107
|
@rackful_resource_media_types ||= {}
|
94
108
|
end
|
95
109
|
|
96
110
|
|
111
|
+
=begin
|
112
|
+
@todo Documentation
|
113
|
+
=end
|
97
114
|
def all_media_types
|
98
115
|
@rackful_resource_all_media_types ||=
|
99
116
|
if self.superclass.respond_to?(:all_media_types)
|
@@ -113,7 +130,6 @@ The best media type for the response body, given the current HTTP request.
|
|
113
130
|
@param require_match [Boolean]
|
114
131
|
@return [String] content-type
|
115
132
|
@raise [HTTP406NotAcceptable] if `require_match` is `true` and no match was found.
|
116
|
-
@since 0.1.0
|
117
133
|
=end
|
118
134
|
def best_content_type accept, require_match = true
|
119
135
|
if accept.empty?
|
@@ -142,18 +158,6 @@ The best media type for the response body, given the current HTTP request.
|
|
142
158
|
end
|
143
159
|
|
144
160
|
|
145
|
-
#~ # @param content_type [String]
|
146
|
-
#~ # @param method [#to_s]
|
147
|
-
#~ # @return [Serializer]
|
148
|
-
#~ def parser request
|
149
|
-
#~ method = request.request_method.upcase.to_sym
|
150
|
-
#~ if !parsers[method] || !parsers[method][request.media_type]
|
151
|
-
#~ raise HTTP415UnsupportedMediaType, ( parsers[method] ? parsers[method].keys : [] )
|
152
|
-
#~ end
|
153
|
-
#~ parsers[method][request.media_type].new( request )
|
154
|
-
#~ end
|
155
|
-
|
156
|
-
|
157
161
|
end # module ClassMethods
|
158
162
|
|
159
163
|
|
@@ -167,39 +171,33 @@ The best media type for the response body, given the current HTTP request.
|
|
167
171
|
end
|
168
172
|
|
169
173
|
|
170
|
-
=begin
|
171
|
-
@!method
|
172
|
-
|
173
|
-
=end
|
174
|
-
|
174
|
+
=begin
|
175
|
+
@!method do_METHOD( Request, Rack::Response )
|
176
|
+
HTTP/1.1 method handler.
|
175
177
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
# @return [void]
|
188
|
-
# @raise [HTTPStatus, RuntimeError]
|
189
|
-
# @since 0.0.1
|
178
|
+
To handle certain HTTP/1.1 request methods, resources must implement methods
|
179
|
+
called `do_<HTTP_METHOD>`.
|
180
|
+
@example Handling `PATCH` requests
|
181
|
+
def do_PATCH request, response
|
182
|
+
response['Content-Type'] = 'text/plain'
|
183
|
+
response.body = [ 'Hello world!' ]
|
184
|
+
end
|
185
|
+
@abstract
|
186
|
+
@return [void]
|
187
|
+
@raise [HTTPStatus, RuntimeError]
|
188
|
+
=end
|
190
189
|
|
191
190
|
|
192
191
|
=begin markdown
|
193
192
|
The path of this resource.
|
194
193
|
@return [Rackful::Path]
|
195
194
|
@see #initialize
|
196
|
-
@since 0.0.1
|
197
195
|
=end
|
198
|
-
|
196
|
+
def path; @rackful_resource_path; end
|
199
197
|
|
200
198
|
|
201
199
|
def path= path
|
202
|
-
@
|
200
|
+
@rackful_resource_path = Path.new(path)
|
203
201
|
end
|
204
202
|
|
205
203
|
|
@@ -214,6 +212,7 @@ The path of this resource.
|
|
214
212
|
self.path.slashify == Request.current.path.slashify
|
215
213
|
end
|
216
214
|
|
215
|
+
|
217
216
|
=begin markdown
|
218
217
|
Does this resource _exists_?
|
219
218
|
|
@@ -223,13 +222,15 @@ produce an empty resource to to handle the `PUT` request. `HEAD` and `GET`
|
|
223
222
|
requests will still yield `404 Not Found`.
|
224
223
|
|
225
224
|
@return [Boolean] The default implementation returns `false`.
|
226
|
-
@since 0.0.1
|
227
225
|
=end
|
228
226
|
def empty?
|
229
227
|
false
|
230
228
|
end
|
231
229
|
|
232
230
|
|
231
|
+
=begin markdown
|
232
|
+
|
233
|
+
=end
|
233
234
|
def to_rackful
|
234
235
|
self
|
235
236
|
end
|
@@ -237,48 +238,46 @@ requests will still yield `404 Not Found`.
|
|
237
238
|
|
238
239
|
=begin markdown
|
239
240
|
@!attribute [r] get_etag
|
240
|
-
The ETag of this resource.
|
241
|
+
The ETag of this resource.
|
241
242
|
|
242
|
-
If your classes implement this method, then an `ETag:` response
|
243
|
-
header is generated automatically when appropriate. This allows clients to
|
244
|
-
perform conditional requests, by sending an `If-Match:` or
|
245
|
-
`If-None-Match:` request header. These conditions are then asserted
|
246
|
-
for you automatically.
|
243
|
+
If your classes implement this method, then an `ETag:` response
|
244
|
+
header is generated automatically when appropriate. This allows clients to
|
245
|
+
perform conditional requests, by sending an `If-Match:` or
|
246
|
+
`If-None-Match:` request header. These conditions are then asserted
|
247
|
+
for you automatically.
|
247
248
|
|
248
|
-
Make sure your entity tag is a properly formatted string. In ABNF:
|
249
|
+
Make sure your entity tag is a properly formatted string. In ABNF:
|
249
250
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
251
|
+
entity-tag = [ "W/" ] quoted-string
|
252
|
+
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
253
|
+
qdtext = <any TEXT except <">>
|
254
|
+
quoted-pair = "\" CHAR
|
254
255
|
|
255
|
-
@abstract
|
256
|
-
@return [String]
|
257
|
-
@see http://tools.ietf.org/html/rfc2616#section-14.19 RFC2616 section 14.19
|
258
|
-
@since 0.0.1
|
256
|
+
@abstract
|
257
|
+
@return [String]
|
258
|
+
@see http://tools.ietf.org/html/rfc2616#section-14.19 RFC2616 section 14.19
|
259
259
|
=end
|
260
260
|
|
261
261
|
|
262
262
|
=begin markdown
|
263
263
|
@!attribute [r] get_last_modified
|
264
|
-
Last modification of this resource.
|
265
|
-
|
266
|
-
If your classes implement this method, then a `Last-Modified:` response
|
267
|
-
header is generated automatically when appropriate. This allows clients to
|
268
|
-
perform conditional requests, by sending an `If-Modified-Since:` or
|
269
|
-
`If-Unmodified-Since:` request header. These conditions are then asserted
|
270
|
-
for you automatically.
|
271
|
-
@abstract
|
272
|
-
@return [Array<(Time, Boolean)>] The timestamp, and a flag indicating if the
|
273
|
-
|
274
|
-
@see http://tools.ietf.org/html/rfc2616#section-14.29 RFC2616 section 14.29
|
275
|
-
@since 0.0.1
|
264
|
+
Last modification of this resource.
|
265
|
+
|
266
|
+
If your classes implement this method, then a `Last-Modified:` response
|
267
|
+
header is generated automatically when appropriate. This allows clients to
|
268
|
+
perform conditional requests, by sending an `If-Modified-Since:` or
|
269
|
+
`If-Unmodified-Since:` request header. These conditions are then asserted
|
270
|
+
for you automatically.
|
271
|
+
@abstract
|
272
|
+
@return [Array<(Time, Boolean)>] The timestamp, and a flag indicating if the
|
273
|
+
timestamp is a strong validator.
|
274
|
+
@see http://tools.ietf.org/html/rfc2616#section-14.29 RFC2616 section 14.29
|
276
275
|
=end
|
277
276
|
|
278
277
|
|
279
278
|
=begin markdown
|
280
279
|
@!method destroy()
|
281
|
-
@return [Hash, nil] an optional header hash.
|
280
|
+
@return [Hash, nil] an optional header hash.
|
282
281
|
=end
|
283
282
|
|
284
283
|
|
@@ -287,7 +286,6 @@ List of all HTTP/1.1 methods implemented by this resource.
|
|
287
286
|
|
288
287
|
This works by inspecting all the {#do_METHOD} methods this object implements.
|
289
288
|
@return [Array<Symbol>]
|
290
|
-
@since 0.0.1
|
291
289
|
@private
|
292
290
|
=end
|
293
291
|
def http_methods
|
@@ -295,10 +293,10 @@ This works by inspecting all the {#do_METHOD} methods this object implements.
|
|
295
293
|
if self.empty?
|
296
294
|
self.class.all_media_types
|
297
295
|
else
|
298
|
-
r.
|
296
|
+
r.push( :OPTIONS, :HEAD, :GET )
|
299
297
|
r << :DELETE if self.respond_to?( :destroy )
|
300
298
|
end
|
301
|
-
self.public_instance_methods.each do
|
299
|
+
self.class.public_instance_methods.each do
|
302
300
|
|instance_method|
|
303
301
|
if /\Ado_([A-Z])+\z/ === instance_method
|
304
302
|
r << $1.to_sym
|
@@ -319,10 +317,9 @@ returned (without an entity body).
|
|
319
317
|
Feel free to override this method at will.
|
320
318
|
@return [void]
|
321
319
|
@raise [HTTP404NotFound] `404 Not Found` if this resource is empty.
|
322
|
-
@since 0.0.1
|
323
320
|
=end
|
324
321
|
def http_OPTIONS request, response
|
325
|
-
raise HTTP404NotFound if self.empty?
|
322
|
+
raise HTTP404NotFound, path if self.empty?
|
326
323
|
response.status = status_code :no_content
|
327
324
|
response.header['Allow'] = self.http_methods.join ', '
|
328
325
|
end
|
@@ -336,7 +333,6 @@ then strips off the response body.
|
|
336
333
|
|
337
334
|
Feel free to override this method at will.
|
338
335
|
@return [self]
|
339
|
-
@since 0.0.1
|
340
336
|
=end
|
341
337
|
def http_HEAD request, response
|
342
338
|
self.http_GET request, response
|
@@ -352,12 +348,13 @@ Feel free to override this method at will.
|
|
352
348
|
|
353
349
|
=begin markdown
|
354
350
|
@private
|
351
|
+
@param [Rackful::Request] request
|
352
|
+
@param [Rack::Response] response
|
355
353
|
@return [void]
|
356
354
|
@raise [HTTP404NotFound, HTTP405MethodNotAllowed]
|
357
|
-
@since 0.0.1
|
358
355
|
=end
|
359
356
|
def http_GET request, response
|
360
|
-
raise HTTP404NotFound if self.empty?
|
357
|
+
raise HTTP404NotFound, path if self.empty?
|
361
358
|
# May throw HTTP406NotAcceptable:
|
362
359
|
content_type = self.class.best_content_type( request.accept )
|
363
360
|
response['Content-Type'] = content_type
|
@@ -377,12 +374,12 @@ Wrapper around {#do_METHOD #do_GET}
|
|
377
374
|
@private
|
378
375
|
@return [void]
|
379
376
|
@raise [HTTP404NotFound, HTTP405MethodNotAllowed]
|
380
|
-
@since 0.0.1
|
381
377
|
=end
|
382
378
|
def http_DELETE request, response
|
383
|
-
raise HTTP404NotFound if self.empty?
|
379
|
+
raise HTTP404NotFound, path if self.empty?
|
380
|
+
raise HTTP405MethodNotAllowed unless self.respond_to?( :destroy )
|
384
381
|
response.status = status_code( :no_content )
|
385
|
-
if headers = self.destroy
|
382
|
+
if headers = self.destroy( request, response )
|
386
383
|
response.headers.merge! headers
|
387
384
|
end
|
388
385
|
end
|
@@ -391,8 +388,7 @@ Wrapper around {#do_METHOD #do_GET}
|
|
391
388
|
=begin markdown
|
392
389
|
@private
|
393
390
|
@return [void]
|
394
|
-
@raise [HTTP415UnsupportedMediaType]
|
395
|
-
@since 0.0.1
|
391
|
+
@raise [HTTP415UnsupportedMediaType, HTTP405MethodNotAllowed] if the resource doesn't implement the `PUT` method.
|
396
392
|
=end
|
397
393
|
def http_PUT request, response
|
398
394
|
raise HTTP405MethodNotAllowed unless self.respond_to? :do_PUT
|
@@ -411,7 +407,6 @@ Wrapper around {#do_METHOD #do_PUT}
|
|
411
407
|
@private
|
412
408
|
@return [void]
|
413
409
|
@raise [HTTPStatus] `405 Method Not Allowed` if the resource doesn't implement the `PUT` method.
|
414
|
-
@since 0.0.1
|
415
410
|
=end
|
416
411
|
def http_method request, response
|
417
412
|
method = request.request_method.to_sym
|
@@ -430,7 +425,6 @@ Wrapper around {#do_METHOD #do_PUT}
|
|
430
425
|
|
431
426
|
=begin markdown
|
432
427
|
Adds `ETag:` and `Last-Modified:` response headers.
|
433
|
-
@since 0.0.1
|
434
428
|
=end
|
435
429
|
def default_headers
|
436
430
|
r = {}
|
@@ -444,4 +438,34 @@ Adds `ETag:` and `Last-Modified:` response headers.
|
|
444
438
|
|
445
439
|
end # module Resource
|
446
440
|
|
441
|
+
|
442
|
+
=begin unused
|
443
|
+
module Collection
|
444
|
+
|
445
|
+
|
446
|
+
include Enumerable
|
447
|
+
|
448
|
+
|
449
|
+
def self.included( modul )
|
450
|
+
unless modul.kind_of? Resource
|
451
|
+
raise "module #{self} included in #{modul}, which isn't a Rackful::Resource"
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
def recurse?; false; end
|
457
|
+
|
458
|
+
|
459
|
+
def each_pair
|
460
|
+
self.each do
|
461
|
+
|path|
|
462
|
+
yield [ path, Request.current.resource_factory( path ) ]
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
|
467
|
+
end # module Collection
|
468
|
+
=end
|
469
|
+
|
470
|
+
|
447
471
|
end # module Rackful
|