rackful 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,63 +1,53 @@
1
- # Required for parsing:
2
- require 'rack'
3
-
4
- # Required for running:
1
+ # encoding: utf-8
5
2
 
6
3
 
7
4
  module Rackful
8
5
 
9
- =begin markdown
10
- Subclass of {Rack::Request}, augmented for Rackful requests.
11
- =end
6
+ # Subclass of {Rack::Request}, augmented for Rackful requests.
12
7
  class Request < Rack::Request
13
8
 
14
9
 
15
- =begin markdown
16
- The resource factory for the current request.
17
- @return [#[]]
18
- @see Server#initialize
19
- =end
20
- def resource_factory; self.env['rackful.resource_factory']; end
21
- def base_path
22
- self.env['rackful.base_path'] ||= begin
23
- r = self.content_path.dup
24
- r[%r{[^/]*\z}] = ''
25
- r
26
- end
10
+ def initialize *args
11
+ super( *args )
27
12
  end
13
+
14
+
28
15
  =begin markdown
29
- Similar to the HTTP/1.1 `Content-Location:` header. Contains the canonical path
30
- to the requested resource, which may differ from {#path}
31
- @return [Path]
32
- =end
33
- def content_path; self.env['rackful.content_path'] ||= self.path; end
34
- =begin markdown
35
- Set by {Rackful::Server#call!}
36
- @return [Path]
37
- =end
38
- def content_path= bp; self.env['rackful.content_path'] = bp.to_path; end
39
- =begin markdown
40
- @return [Path]
16
+ Shortcut to {Server#resource_at}.
17
+
18
+ (see Server#resource_at)
19
+ @return (see Server#resource_at)
20
+ @raise (see Server#resource_at)
41
21
  =end
42
- def path; super.to_path; end
22
+ def resource_at *args
23
+ env['rackful.server'].resource_at( *args )
24
+ end
43
25
 
44
26
 
45
- def initialize resource_factory, *args
46
- super( *args )
47
- self.env['rackful.resource_factory'] = resource_factory
27
+ =begin markdown
28
+ Similar to the HTTP/1.1 `Content-Location:` header. Contains the canonical url
29
+ of the requested resource, which may differ from {#url}.
30
+
31
+ If parameter +full_path+ is provided, than this is used instead of the current
32
+ request’s full path (which is the path plus optional query string).
33
+ @return [URI::Generic]
34
+ =end
35
+ def canonical_uri
36
+ env['rackful.canonical_uri'] ||= URI( self.url ).normalize
48
37
  end
49
38
 
50
39
 
51
40
  =begin markdown
52
- The request currently being processed in the current thread.
41
+ The canonical url of the requested resource. This may differ from {#url}.
53
42
 
54
- In a multi-threaded server, multiple requests can be handled at one time.
55
- This method returns the request object, created (and registered) by
56
- {Server#call!}
57
- @return [Request]
43
+ @todo Change URI::Generic into URI::HTTP
44
+ @param uri [URI::Generic, String]
45
+ @return [URI::Generic, String] `uri`
58
46
  =end
59
- def self.current
60
- Thread.current[:rackful_request]
47
+ def canonical_uri=( uri )
48
+ env['rackful.canonical_uri'] =
49
+ uri.kind_of?( URI::Generic ) ? URI(uri) : URI( uri ).normalize
50
+ uri
61
51
  end
62
52
 
63
53
 
@@ -79,7 +69,7 @@ Assert all <tt>If-*</tt> request headers.
79
69
  =end
80
70
  def assert_if_headers resource
81
71
  #raise HTTP501NotImplemented, 'If-Range: request header is not supported.' \
82
- # if @env.key? 'HTTP_IF_RANGE'
72
+ # if env.key? 'HTTP_IF_RANGE'
83
73
  empty = resource.empty?
84
74
  etag =
85
75
  if ! empty && resource.respond_to?(:get_etag)
@@ -106,7 +96,7 @@ Assert all <tt>If-*</tt> request headers.
106
96
  elsif cond[:unmodified_since]
107
97
  raise HTTP412PreconditionFailed, 'If-Unmodified-Since'
108
98
  elsif cond[:modified_since]
109
- raise HTTP404NotFound, resource.path
99
+ raise HTTP404NotFound
110
100
  end
111
101
  else
112
102
  if cond[:none_match] && self.validate_etag( etag, cond[:none_match] )
@@ -136,17 +126,77 @@ Assert all <tt>If-*</tt> request headers.
136
126
  end
137
127
 
138
128
 
139
- =begin markdown
140
- Hash of acceptable media types and their qualities.
129
+ # Shortcut to {Rack::Utils.q_values}. Well, actually, we reimplemented it
130
+ # because the implementation in {Rack::Utils} seems incomplete.
131
+ # @return [Array<Array(type, quality)>]
132
+ # @see Rack::Utils.q_values
133
+ def q_values
134
+ # This would be the “shortcut” implementation:
135
+ #env['rackful.q_values'] ||= Rack::Utils.q_values(env['HTTP_ACCEPT'])
136
+ # But here’s a full (and better) implementation:
137
+ env['rackful.q_values'] ||= env['HTTP_ACCEPT'].to_s.split(/\s*,\s*/).map do
138
+ |part|
139
+ value, *parameters = part.split(/\s*;\s*/)
140
+ quality = 1.0
141
+ parameters.each do |p|
142
+ quality = p[2..-1].to_f if p.start_with? 'q='
143
+ end
144
+ [value, quality]
145
+ end
146
+ end
147
+
141
148
 
142
- This method parses the HTTP/1.1 `Accept:` header. If no acceptable media
143
- types are provided, an empty Hash is returned.
144
- @return [Hash{media_type => quality}]
149
+ =begin markdown
150
+ The best media type to represent a resource, given the current HTTP request.
151
+ @param resource [Resource] the resource to represent
152
+ @param require_match [Boolean] this flag determines what must happen if the
153
+ client sent an `Accept:` header, and we cannot serve any of the acceptable
154
+ media types. **`TRUE`** means that an {HTTP406NotAcceptable} exception is
155
+ raised. **`FALSE`** means that the content-type with the highest quality is
156
+ returned.
157
+ @return [String] content-type
158
+ @raise [HTTP406NotAcceptable]
159
+ @api private
145
160
  =end
161
+ def best_content_type( resource, require_match = true )
162
+ q_values = self.q_values
163
+ if q_values.empty?
164
+ return resource.all_serializers.values.sort_by(&:last).last[0]::CONTENT_TYPES[0]
165
+ end
166
+ matches = []
167
+ q_values.each {
168
+ |accept_media_type, accept_quality|
169
+ resource.all_serializers.each_pair {
170
+ |content_type, v|
171
+ quality = v[1]
172
+ media_type = content_type.split(';').first.strip
173
+ if File.fnmatch( accept_media_type, media_type )
174
+ matches << [ content_type, accept_quality * quality ]
175
+ end
176
+ }
177
+ }
178
+ if matches.empty?
179
+ if require_match
180
+ raise( HTTP406NotAcceptable, resource.all_serializers.keys() )
181
+ else
182
+ return resource.all_serializers.values.sort_by(&:last).
183
+ last.first::CONTENT_TYPES.first
184
+ end
185
+ end
186
+ matches.sort_by(&:last).last.first
187
+ end
188
+
189
+
190
+ # Hash of acceptable media types and their qualities.
191
+ #
192
+ # This method parses the HTTP/1.1 `Accept:` header. If no acceptable media
193
+ # types are provided, an empty Hash is returned.
194
+ # @return [Hash{media_type => quality}]
195
+ # @deprecated Use {#q_values} instead
146
196
  def accept
147
- @env['rackful.accept'] ||= begin
197
+ env['rackful.accept'] ||= begin
148
198
  Hash[
149
- @env['HTTP_ACCEPT'].to_s.split(',').collect do
199
+ env['HTTP_ACCEPT'].to_s.split(',').collect do
150
200
  |entry|
151
201
  type, *options = entry.delete(' ').split(';')
152
202
  quality = 1
@@ -170,7 +220,7 @@ Parses the HTTP/1.1 `If-Match:` header.
170
220
  @see #if_none_match
171
221
  =end
172
222
  def if_match none = false
173
- header = @env["HTTP_IF_#{ none ? 'NONE_' : '' }MATCH"]
223
+ header = env["HTTP_IF_#{ none ? 'NONE_' : '' }MATCH"]
174
224
  return nil unless header
175
225
  envkey = "rackful.if_#{ none ? 'none_' : '' }match"
176
226
  if %r{\A\s*\*\s*\z} === header
@@ -200,7 +250,7 @@ Parses the HTTP/1.1 `If-None-Match:` header.
200
250
  @see #if_unmodified_since
201
251
  =end
202
252
  def if_modified_since unmodified = false
203
- header = @env["HTTP_IF_#{ unmodified ? 'UN' : '' }MODIFIED_SINCE"]
253
+ header = env["HTTP_IF_#{ unmodified ? 'UN' : '' }MODIFIED_SINCE"]
204
254
  return nil unless header
205
255
  begin
206
256
  header = Time.httpdate( header )
@@ -1,177 +1,169 @@
1
- # Required for parsing:
2
- require 'rack'
3
-
4
- # Required for running:
1
+ # encoding: utf-8
5
2
 
3
+ require 'forwardable'
6
4
 
7
5
  module Rackful
8
6
 
9
- =begin markdown
10
- Mixin for resources served by {Server}.
11
-
12
- {Server} helps you implement Rackful resource objects quickly in a couple
13
- of ways.
14
- Classes that include this module may implement a method `content_types`
15
- for content negotiation. This method must return a Hash of
16
- `media-type => quality` pairs.
17
- @see Server, ResourceFactory
18
- =end
19
- module Resource
7
+ # Abstract superclass for resources served by {Server}.
8
+ # @see Server
9
+ # @todo better documentation
10
+ # @abstract Realizations must implement
11
+ class Resource
12
+
13
+ class << self
14
+
15
+
16
+ # Meta-programmer method.
17
+ # @example Have your resource rendered in XML and JSON
18
+ # class MyResource
19
+ # add_serializer MyResource2XML
20
+ # add_serializer MyResource2JSON, 0.5
21
+ # end
22
+ # @param serializer [Serializer]
23
+ # @param quality [Float]
24
+ # @return [self]
25
+ def add_serializer serializer, quality = 1.0
26
+ quality = quality.to_f
27
+ quality = 1.0 if quality > 1.0
28
+ quality = 0.0 if quality < 0.0
29
+ s = [serializer, quality]
30
+ serializer::CONTENT_TYPES.each {
31
+ |content_type|
32
+ self.serializers[content_type.to_s] = s
33
+ }
34
+ self
35
+ end
20
36
 
21
37
 
22
- include Rack::Utils
38
+ # Meta-programmer method.
39
+ # @example Have your resource accept XHTML in `PUT` requests
40
+ # class MyResource
41
+ # include Rackful::Resource
42
+ # add_parser Rackful::Parser::XHTML, :PUT
43
+ # end
44
+ # @param parser [Class] an implementation (ie. subclass) of {Parser}
45
+ # @param method [#to_sym] For example: `:PUT` or `:POST`
46
+ # @return [self]
47
+ def add_parser parser, method = :PUT
48
+ method = method.to_sym
49
+ self.parsers[method] ||= []
50
+ self.parsers[method] << parser
51
+ self.parsers[method].uniq!
52
+ self
53
+ end
23
54
 
24
55
 
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.
56
+ # All parsers added to _this_ class. Ie. not including parsers added
57
+ # to parent classes.
58
+ # @return [Hash{Symbol => Array<Class>}] A hash of lists of {Parser} classes,
59
+ # indexed by HTTP method.
60
+ # @api private
61
+ def parsers
62
+ # The single '@' on the following line is on purpose!
63
+ @rackful_resource_parsers ||= {}
64
+ end
30
65
 
31
66
 
32
- =end
33
- def self.included(base)
34
- base.extend ClassMethods
67
+ # All serializers added to _this_ class. Ie. not including serializers added
68
+ # to parent classes.
69
+ # @return [Hash{Serializer => Float}]
70
+ # @api private
71
+ def serializers
72
+ # The single '@' on the following line is on purpose!
73
+ @rackful_resource_serializers ||= {}
35
74
  end
36
75
 
37
76
 
38
- module ClassMethods
39
-
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
77
+ # All parsers for this class, including parsers added to parent classes.
78
+ # The result of this method is cached, which will interfere with code reloading.
79
+ # @param method [#to_sym] For example: `:PUT` or `:POST`
80
+ # @return [Hash{Symbol => Array<Class>}]
81
+ # @api private
82
+ def all_parsers
83
+ # The single '@' on the following line is on purpose!
84
+ @rackful_resource_all_parsers ||=
85
+ if self.superclass.respond_to?(:all_parsers)
86
+ self.parsers.merge( self.superclass.all_parsers ) do
87
+ |key, oldval, newval|
88
+ ( oldval + newval ).uniq
89
+ end
90
+ else
91
+ self.parsers
92
+ end
46
93
  end
47
- @param serializer [Serializer]
48
- @param quality [Float]
49
- @return [self]
50
- =end
51
- def add_serializer serializer, quality = 1.0
52
- quality = quality.to_f
53
- quality = 1.0 if quality > 1.0
54
- quality = 0.0 if quality < 0.0
55
- s = [serializer, quality]
56
- serializer::CONTENT_TYPES.each {
57
- |content_type|
58
- self.serializers[content_type.to_s] = s
59
- }
60
- self
61
- end
62
94
 
63
95
 
64
- def serializers
65
- # The single '@' on the following line is on purpose!
66
- @rackful_resource_serializers ||= {}
67
- end
96
+ # All serializers for this class, including those added to parent classes.
97
+ # The result of this method is cached, which will interfere with code reloading.
98
+ # @return [Hash{Serializer => Float}] The float indicates the quality of each
99
+ # serializer in the interval [0,1]
100
+ # @api private
101
+ def all_serializers
102
+ # The single '@' on the following line is on purpose!
103
+ @rackful_resource_all_serializers ||=
104
+ if self.superclass.respond_to?(:all_serializers)
105
+ self.superclass.all_serializers.merge( self.serializers ) do
106
+ |key, oldval, newval|
107
+ newval[1] >= oldval[1] ? newval : oldval
108
+ end
109
+ else
110
+ self.serializers
111
+ end
112
+ end
68
113
 
69
114
 
70
- def all_serializers
71
- # The single '@' on the following line is on purpose!
72
- @rackful_resource_all_serializers ||=
73
- if self.superclass.respond_to?(:all_serializers)
74
- self.superclass.all_serializers.merge( self.serializers ) do
75
- |key, oldval, newval|
76
- newval[1] >= oldval[1] ? newval : oldval
77
- end
78
- else
79
- self.serializers
80
- end
81
- end
115
+ end # module ClassMethods
82
116
 
83
117
 
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
95
- def add_media_type media_type, method = :PUT
96
- method = method.to_sym
97
- self.media_types[method] ||= []
98
- self.media_types[method] << media_type.to_s
99
- self
100
- end
118
+ extend Forwardable
119
+ def_delegators 'self.class', :parsers, :serializers, :all_parsers, :all_serializers
101
120
 
102
121
 
103
- =begin
104
- @return [Hash(method => Array(media_types))]
105
- =end
106
- def media_types
107
- @rackful_resource_media_types ||= {}
108
- end
122
+ def initialize( uri )
123
+ @uri = uri.kind_of?( URI::Generic ) ? uri.dup : URI(uri.to_s).normalize
124
+ #self.uri = uri
125
+ end
109
126
 
110
127
 
111
- =begin
112
- @todo Documentation
128
+ =begin markdown
129
+ @param request [Request]
130
+ @param content_type [String, nil] If omitted, you get the best serializer
131
+ available, which may not be acceptable by the client.
132
+ @return [Serializer]
113
133
  =end
114
- def all_media_types
115
- @rackful_resource_all_media_types ||=
116
- if self.superclass.respond_to?(:all_media_types)
117
- self.superclass.all_media_types.merge( self.media_types ) do
118
- |key, oldval, newval|
119
- oldval + newval
120
- end
121
- else
122
- self.media_types
123
- end
124
- end
134
+ def serializer request, content_type = nil
135
+ content_type ||= request.best_content_type( self, false )
136
+ self.class.all_serializers[content_type][0].new( request, self, content_type )
137
+ end
125
138
 
126
139
 
127
140
  =begin markdown
128
141
  The best media type for the response body, given the current HTTP request.
129
- @param accept [Hash]
130
- @param require_match [Boolean]
131
- @return [String] content-type
132
- @raise [HTTP406NotAcceptable] if `require_match` is `true` and no match was found.
142
+ @param request [Rackful::Request]
143
+ @return [Parser, nil] a {Parser}, or nil if the request entity is empty
144
+ @raise [HTTP415UnsupportedMediaType] if no parser can be found for the request entity
133
145
  =end
134
- def best_content_type accept, require_match = true
135
- if accept.empty?
136
- return self.all_serializers.values.sort_by(&:last).last[0]::CONTENT_TYPES[0]
137
- end
138
- matches = []
139
- accept.each_pair {
140
- |accept_media_type, accept_quality|
141
- self.all_serializers.each_pair {
142
- |content_type, v|
143
- quality = v[1]
144
- media_type = content_type.split(';').first.strip
145
- if File.fnmatch( accept_media_type, media_type )
146
- matches << [ content_type, accept_quality * quality ]
147
- end
148
- }
149
- }
150
- if matches.empty?
151
- if require_match
152
- raise( HTTP406NotAcceptable, self.all_serializers.keys() )
153
- else
154
- return self.all_serializers.values.sort_by(&:last).last[0]::CONTENT_TYPES[0]
146
+ def parser request
147
+ unless request.content_length ||
148
+ 'chunked' == request.env['HTTP_TRANSFER_ENCODING']
149
+ raise HTTP411LengthRequired
150
+ end
151
+ request_media_type = request.media_type.to_s
152
+ supported_media_types = []
153
+ all_parsers = self.class.all_parsers[ request.request_method.to_sym ] || []
154
+ all_parsers.each do |p|
155
+ p::MEDIA_TYPES.each do |parser_media_type|
156
+ if File.fnmatch( parser_media_type, request_media_type )
157
+ return p.new( request, self )
155
158
  end
159
+ supported_media_types << parser_media_type
156
160
  end
157
- matches.sort_by(&:last).last[0]
158
161
  end
159
-
160
-
161
- end # module ClassMethods
162
-
163
-
164
- =begin markdown
165
- @return [Serializer]
166
- =end
167
- def serializer content_type
168
- @rackful_resource_serializers ||= {}
169
- @rackful_resource_serializers[content_type] ||=
170
- self.class.all_serializers[content_type][0].new( self, content_type )
162
+ raise( HTTP415UnsupportedMediaType, supported_media_types.uniq )
171
163
  end
172
164
 
173
165
 
174
- =begin
166
+ =begin markdown
175
167
  @!method do_METHOD( Request, Rack::Response )
176
168
  HTTP/1.1 method handler.
177
169
 
@@ -189,36 +181,23 @@ The best media type for the response body, given the current HTTP request.
189
181
 
190
182
 
191
183
  =begin markdown
192
- The path of this resource.
193
- @return [Rackful::Path]
194
- @see #initialize
184
+ The canonical path of this resource.
185
+ @return [URI]
195
186
  =end
196
- def path; @rackful_resource_path; end
197
-
198
-
199
- def path= path
200
- @rackful_resource_path = Path.new(path)
201
- end
187
+ attr_reader :uri
202
188
 
203
189
 
204
190
  def title
205
- ( '/' == self.path ) ?
206
- Request.current.host :
207
- File.basename(self.path).to_path.unescape
208
- end
209
-
210
-
211
- def requested?
212
- self.path.slashify == Request.current.path.slashify
191
+ self.uri.segments.last || self.class.to_s
213
192
  end
214
193
 
215
194
 
216
195
  =begin markdown
217
- Does this resource _exists_?
196
+ Does this resource _exist_?
218
197
 
219
198
  For example, a client can `PUT` to a URL that doesn't refer to a resource
220
- yet. In that case, your {Server#resource_factory resource factory} can
221
- produce an empty resource to to handle the `PUT` request. `HEAD` and `GET`
199
+ yet. In that case, your {Server#initialize resource registry} can
200
+ produce an empty resource to handle the `PUT` request. `HEAD` and `GET`
222
201
  requests will still yield `404 Not Found`.
223
202
 
224
203
  @return [Boolean] The default implementation returns `false`.
@@ -227,10 +206,7 @@ requests will still yield `404 Not Found`.
227
206
  false
228
207
  end
229
208
 
230
-
231
- =begin markdown
232
-
233
- =end
209
+ # @todo documentation
234
210
  def to_rackful
235
211
  self
236
212
  end
@@ -286,7 +262,7 @@ List of all HTTP/1.1 methods implemented by this resource.
286
262
 
287
263
  This works by inspecting all the {#do_METHOD} methods this object implements.
288
264
  @return [Array<Symbol>]
289
- @private
265
+ @api private
290
266
  =end
291
267
  def http_methods
292
268
  r = []
@@ -321,8 +297,7 @@ Feel free to override this method at will.
321
297
  @raise [HTTP404NotFound] `404 Not Found` if this resource is empty.
322
298
  =end
323
299
  def http_OPTIONS request, response
324
- raise HTTP404NotFound, path if self.empty?
325
- response.status = status_code :no_content
300
+ response.status = Rack::Utils.status_code :no_content
326
301
  response.header['Allow'] = self.http_methods.join ', '
327
302
  end
328
303
 
@@ -349,21 +324,20 @@ Feel free to override this method at will.
349
324
 
350
325
 
351
326
  =begin markdown
352
- @private
353
- @param [Rackful::Request] request
354
- @param [Rack::Response] response
327
+ @api private
328
+ @param request [Rackful::Request]
329
+ @param response [Rack::Response]
355
330
  @return [void]
356
331
  @raise [HTTP404NotFound, HTTP405MethodNotAllowed]
357
332
  =end
358
333
  def http_GET request, response
359
- raise HTTP404NotFound, path if self.empty?
334
+ raise HTTP404NotFound if self.empty?
360
335
  # May throw HTTP406NotAcceptable:
361
- content_type = self.class.best_content_type( request.accept )
336
+ content_type = request.best_content_type( self )
362
337
  response['Content-Type'] = content_type
363
- response.status = status_code( :ok )
338
+ response.status = Rack::Utils.status_code( :ok )
364
339
  response.headers.merge! self.default_headers
365
- # May throw HTTP405MethodNotAllowed:
366
- serializer = self.serializer( content_type )
340
+ serializer = self.serializer( request, content_type )
367
341
  if serializer.respond_to? :headers
368
342
  response.headers.merge!( serializer.headers )
369
343
  end
@@ -373,14 +347,15 @@ Feel free to override this method at will.
373
347
 
374
348
  =begin markdown
375
349
  Wrapper around {#do_METHOD #do_GET}
376
- @private
350
+ @api private
377
351
  @return [void]
378
352
  @raise [HTTP404NotFound, HTTP405MethodNotAllowed]
379
353
  =end
380
354
  def http_DELETE request, response
381
- raise HTTP404NotFound, path if self.empty?
382
- raise HTTP405MethodNotAllowed, self.http_methods unless self.respond_to?( :destroy )
383
- response.status = status_code( :no_content )
355
+ raise HTTP404NotFound if self.empty?
356
+ raise HTTP405MethodNotAllowed, self.http_methods unless
357
+ self.respond_to?( :destroy )
358
+ response.status = Rack::Utils.status_code( :no_content )
384
359
  if headers = self.destroy( request, response )
385
360
  response.headers.merge! headers
386
361
  end
@@ -388,39 +363,30 @@ Wrapper around {#do_METHOD #do_GET}
388
363
 
389
364
 
390
365
  =begin markdown
391
- @private
366
+ @api private
392
367
  @return [void]
393
368
  @raise [HTTP415UnsupportedMediaType, HTTP405MethodNotAllowed] if the resource doesn't implement the `PUT` method.
394
369
  =end
395
370
  def http_PUT request, response
396
371
  raise HTTP405MethodNotAllowed, self.http_methods unless self.respond_to? :do_PUT
397
- unless self.class.media_types[:PUT] &&
398
- self.class.media_types[:PUT].include?( request.media_type )
399
- raise HTTP415UnsupportedMediaType, self.class.media_types[:PUT]
400
- end
401
- response.status = status_code( self.empty? ? :created : :no_content )
372
+ response.status = Rack::Utils.status_code( self.empty? ? :created : :no_content )
402
373
  self.do_PUT( request, response )
403
374
  response.headers.merge! self.default_headers
404
375
  end
405
376
 
406
377
 
407
378
  =begin markdown
408
- Wrapper around {#do_METHOD #do_PUT}
409
- @private
379
+ Wrapper around {#do_METHOD}
380
+ @api private
410
381
  @return [void]
411
- @raise [HTTPStatus] `405 Method Not Allowed` if the resource doesn't implement the `PUT` method.
382
+ @raise [HTTPStatus] `405 Method Not Allowed` if the resource doesn't implement
383
+ the request method.
412
384
  =end
413
385
  def http_method request, response
414
386
  method = request.request_method.to_sym
415
387
  if ! self.respond_to?( :"do_#{method}" )
416
388
  raise HTTP405MethodNotAllowed, self.http_methods
417
389
  end
418
- if ( request.content_length ||
419
- 'chunked' == request.env['HTTP_TRANSFER_ENCODING'] ) and
420
- ! self.class.media_types[method] ||
421
- ! self.class.media_types[method].include?( request.media_type )
422
- raise HTTP415UnsupportedMediaType, self.class.media_types[method]
423
- end
424
390
  self.send( :"do_#{method}", request, response )
425
391
  end
426
392
 
@@ -441,33 +407,4 @@ Adds `ETag:` and `Last-Modified:` response headers.
441
407
  end # module Resource
442
408
 
443
409
 
444
- =begin unused
445
- module Collection
446
-
447
-
448
- include Enumerable
449
-
450
-
451
- def self.included( modul )
452
- unless modul.kind_of? Resource
453
- raise "module #{self} included in #{modul}, which isn't a Rackful::Resource"
454
- end
455
- end
456
-
457
-
458
- def recurse?; false; end
459
-
460
-
461
- def each_pair
462
- self.each do
463
- |path|
464
- yield [ path, Request.current.resource_factory( path ) ]
465
- end
466
- end
467
-
468
-
469
- end # module Collection
470
- =end
471
-
472
-
473
410
  end # module Rackful