rackful 0.1.4 → 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,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