strelka 0.0.1pre4 → 0.0.1.pre129

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/History.rdoc +1 -1
  2. data/IDEAS.rdoc +62 -0
  3. data/Manifest.txt +38 -7
  4. data/README.rdoc +124 -5
  5. data/Rakefile +22 -6
  6. data/bin/leash +102 -157
  7. data/contrib/hoetemplate/.autotest.erb +23 -0
  8. data/contrib/hoetemplate/History.rdoc.erb +4 -0
  9. data/contrib/hoetemplate/Manifest.txt.erb +8 -0
  10. data/contrib/hoetemplate/README.rdoc.erb +17 -0
  11. data/contrib/hoetemplate/Rakefile.erb +24 -0
  12. data/contrib/hoetemplate/data/file_name/apps/file_name_app +36 -0
  13. data/contrib/hoetemplate/data/file_name/templates/layout.tmpl.erb +13 -0
  14. data/contrib/hoetemplate/data/file_name/templates/top.tmpl.erb +8 -0
  15. data/contrib/hoetemplate/lib/file_name.rb.erb +18 -0
  16. data/contrib/hoetemplate/spec/file_name_spec.rb.erb +21 -0
  17. data/data/strelka/apps/hello-world +30 -0
  18. data/lib/strelka/app/defaultrouter.rb +49 -30
  19. data/lib/strelka/app/errors.rb +121 -0
  20. data/lib/strelka/app/exclusiverouter.rb +40 -0
  21. data/lib/strelka/app/filters.rb +18 -7
  22. data/lib/strelka/app/negotiation.rb +122 -0
  23. data/lib/strelka/app/parameters.rb +171 -14
  24. data/lib/strelka/app/paramvalidator.rb +751 -0
  25. data/lib/strelka/app/plugins.rb +66 -46
  26. data/lib/strelka/app/restresources.rb +499 -0
  27. data/lib/strelka/app/router.rb +73 -0
  28. data/lib/strelka/app/routing.rb +140 -18
  29. data/lib/strelka/app/templating.rb +12 -3
  30. data/lib/strelka/app.rb +174 -24
  31. data/lib/strelka/constants.rb +0 -20
  32. data/lib/strelka/exceptions.rb +29 -0
  33. data/lib/strelka/httprequest/acceptparams.rb +377 -0
  34. data/lib/strelka/httprequest/negotiation.rb +257 -0
  35. data/lib/strelka/httprequest.rb +155 -7
  36. data/lib/strelka/httpresponse/negotiation.rb +579 -0
  37. data/lib/strelka/httpresponse.rb +140 -0
  38. data/lib/strelka/logging.rb +4 -1
  39. data/lib/strelka/mixins.rb +53 -0
  40. data/lib/strelka.rb +22 -1
  41. data/spec/data/error.tmpl +1 -0
  42. data/spec/lib/constants.rb +0 -1
  43. data/spec/lib/helpers.rb +21 -0
  44. data/spec/strelka/app/defaultrouter_spec.rb +41 -35
  45. data/spec/strelka/app/errors_spec.rb +212 -0
  46. data/spec/strelka/app/exclusiverouter_spec.rb +220 -0
  47. data/spec/strelka/app/filters_spec.rb +196 -0
  48. data/spec/strelka/app/negotiation_spec.rb +73 -0
  49. data/spec/strelka/app/parameters_spec.rb +149 -0
  50. data/spec/strelka/app/paramvalidator_spec.rb +1059 -0
  51. data/spec/strelka/app/plugins_spec.rb +26 -19
  52. data/spec/strelka/app/restresources_spec.rb +393 -0
  53. data/spec/strelka/app/router_spec.rb +63 -0
  54. data/spec/strelka/app/routing_spec.rb +183 -9
  55. data/spec/strelka/app/templating_spec.rb +1 -2
  56. data/spec/strelka/app_spec.rb +265 -32
  57. data/spec/strelka/exceptions_spec.rb +53 -0
  58. data/spec/strelka/httprequest/acceptparams_spec.rb +282 -0
  59. data/spec/strelka/httprequest/negotiation_spec.rb +246 -0
  60. data/spec/strelka/httprequest_spec.rb +204 -14
  61. data/spec/strelka/httpresponse/negotiation_spec.rb +464 -0
  62. data/spec/strelka/httpresponse_spec.rb +114 -0
  63. data/spec/strelka/mixins_spec.rb +99 -0
  64. data.tar.gz.sig +1 -0
  65. metadata +175 -79
  66. metadata.gz.sig +2 -0
  67. data/IDEAS.textile +0 -174
  68. data/data/strelka/apps/strelka-admin +0 -65
  69. data/data/strelka/apps/strelka-setup +0 -26
  70. data/data/strelka/bootstrap-config.rb +0 -34
  71. data/data/strelka/templates/admin/console.tmpl +0 -21
  72. data/data/strelka/templates/layout.tmpl +0 -30
  73. data/lib/strelka/process.rb +0 -19
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'strelka/mixins'
4
+ require 'strelka/httprequest'
5
+
6
+ class Strelka::HTTPRequest
7
+
8
+ # A parser for request Accept, Accept-encoding, Accept-charset, and Accept-language
9
+ # header values. They provide weighted and wildcard comparisions between two values
10
+ # of the same field.
11
+ #
12
+ # require 'strelka/httprequest/acceptparam'
13
+ # mediatype = Strelka::HTTPRequest::AcceptParam.parse_mediatype( "text/html;q=0.9;level=2" )
14
+ #
15
+ # ap.type #=> 'text'
16
+ # ap.subtype #=> 'html'
17
+ # ap.qvalue #=> 0.9
18
+ # ap =~ 'text/*' #=> true
19
+ #
20
+ # language = Strelka::HTTPRequest::AcceptParam.parse_language( "en-gb" )
21
+ #
22
+ # ap.type #=> :en
23
+ # ap.subtype #=> :gb
24
+ # ap.qvalue #=> 1.0
25
+ # ap =~ 'en' #=> true
26
+ #
27
+ # encoding = Strelka::HTTPRequest::AcceptParam.parse_encoding( "compress; q=0.7" )
28
+ #
29
+ # ap.type #=> :compress
30
+ # ap.subtype #=> nil
31
+ # ap.qvalue #=> 0.7
32
+ # ap =~ 'compress' #=> true
33
+ #
34
+ # charset = Strelka::HTTPRequest::AcceptParam.parse_charset( "koi8-r" )
35
+ #
36
+ # ap.type #=> 'koi8-r'
37
+ # ap.subtype #=> nil
38
+ # ap.qvalue #=> 1.0
39
+ # ap =~ 'koi8-r' #=> true
40
+ #
41
+ # == Authors
42
+ #
43
+ # * Michael Granger <ged@FaerieMUD.org>
44
+ # * Mahlon E. Smith <mahlon@martini.nu>
45
+ #
46
+ class AcceptParam
47
+ include Comparable,
48
+ Strelka::Loggable,
49
+ Strelka::AbstractClass
50
+
51
+
52
+ # The default quality value (weight) if none is specified
53
+ Q_DEFAULT = 1.0
54
+
55
+ # The maximum quality value
56
+ Q_MAX = Q_DEFAULT
57
+
58
+
59
+ #################################################################
60
+ ### I N S T A N C E M E T H O D S
61
+ #################################################################
62
+
63
+ ### Create a new Strelka::HTTPRequest::AcceptParam with the given media
64
+ ### +range+, quality value (+qval+), and extensions
65
+ def initialize( type, subtype='*', qval=Q_DEFAULT, *extensions )
66
+ type = nil if type == '*'
67
+ subtype = nil if subtype == '*'
68
+
69
+ @type = type
70
+ @subtype = subtype
71
+ @qvalue = normalize_qvalue( qval )
72
+ @extensions = extensions.flatten
73
+ end
74
+
75
+
76
+ ######
77
+ public
78
+ ######
79
+
80
+ pure_virtual :to_s
81
+
82
+
83
+ # The 'type' part of the media range
84
+ attr_reader :type
85
+
86
+ # The 'subtype' part of the media range
87
+ attr_reader :subtype
88
+
89
+ # The weight of the param
90
+ attr_reader :qvalue
91
+
92
+ # An array of any accept-extensions specified with the parameter
93
+ attr_reader :extensions
94
+
95
+
96
+ ### Match operator -- returns true if +other+ matches the receiving
97
+ ### AcceptParam.
98
+ def =~( other )
99
+ unless other.is_a?( self.class )
100
+ other = self.class.parse( other.to_s ) rescue nil
101
+ return false unless other
102
+ end
103
+
104
+ # */* returns true in either side of the comparison.
105
+ # ASSUMPTION: There will never be a case when a type is wildcarded
106
+ # and the subtype is specific. (e.g., */xml)
107
+ # We gave up trying to read RFC 2045.
108
+ return true if other.type.nil? || self.type.nil?
109
+
110
+ # text/html =~ text/html
111
+ # text/* =~ text/html
112
+ # text/html =~ text/*
113
+ if other.type == self.type
114
+ return true if other.subtype.nil? || self.subtype.nil?
115
+ return true if other.subtype == self.subtype
116
+ end
117
+
118
+ return false
119
+ end
120
+
121
+
122
+ ### Return a human-readable version of the object
123
+ def inspect
124
+ return "#<%s:0x%07x '%s/%s' q=%0.3f %p>" % [
125
+ self.class.name,
126
+ self.object_id * 2,
127
+ self.type || '*',
128
+ self.subtype || '*',
129
+ self.qvalue,
130
+ self.extensions,
131
+ ]
132
+ end
133
+
134
+
135
+ ### The weighting or "qvalue" of the parameter in the form "q=<value>"
136
+ def qvaluestring
137
+ # 3 digit precision, trim excess zeros
138
+ return sprintf( "q=%0.3f", self.qvalue ).gsub(/0{1,2}$/, '')
139
+ end
140
+
141
+
142
+ ### Return a String containing any extensions for this parameter, joined
143
+ ### with ';'
144
+ def extension_strings
145
+ return nil if self.extensions.empty?
146
+ return self.extensions.compact.join('; ')
147
+ end
148
+
149
+
150
+ ### Comparable interface. Sort parameters by weight: Returns -1 if +other+
151
+ ### is less specific than the receiver, 0 if +other+ is as specific as
152
+ ### the receiver, and +1 if +other+ is more specific than the receiver.
153
+ def <=>( other )
154
+
155
+ if rval = (other.qvalue <=> @qvalue).nonzero?
156
+ return rval
157
+ end
158
+
159
+ if self.type.nil?
160
+ return 1 if ! other.type.nil?
161
+ elsif other.type.nil?
162
+ return -1
163
+ end
164
+
165
+ if self.subtype.nil?
166
+ return 1 if ! other.subtype.nil?
167
+ elsif other.subtype.nil?
168
+ return -1
169
+ end
170
+
171
+ if rval = (self.extensions.length <=> other.extensions.length).nonzero?
172
+ return rval
173
+ end
174
+
175
+ return self.to_s <=> other.to_s
176
+ end
177
+
178
+
179
+ #######
180
+ private
181
+ #######
182
+
183
+ ### Given an input +qvalue+, return the Float equivalent.
184
+ def normalize_qvalue( qvalue )
185
+ return Q_DEFAULT unless qvalue
186
+ qvalue = Float( qvalue.to_s.sub(/q=/, '') ) unless qvalue.is_a?( Float )
187
+
188
+ if qvalue > Q_MAX
189
+ self.log.warn "Squishing invalid qvalue %p to %0.1f" %
190
+ [ qvalue, Q_DEFAULT ]
191
+ return Q_DEFAULT
192
+ end
193
+
194
+ return qvalue
195
+ end
196
+
197
+ end # class AcceptParam
198
+
199
+
200
+ # A mediatype parameter such as one you'd find in an Accept header.
201
+ class MediaType < Strelka::HTTPRequest::AcceptParam
202
+
203
+ ### Parse the given +accept_param+ as a mediatype and return a
204
+ ### Strelka::HTTPRequest::MediaType object for it.
205
+ def self::parse( accept_param )
206
+ raise ArgumentError, "Bad Accept param: no media-range in %p" % [accept_param] unless
207
+ accept_param.include?( '/' )
208
+ media_range, *stuff = accept_param.split( /\s*;\s*/ )
209
+ type, subtype = media_range.downcase.split( '/', 2 )
210
+ qval, opts = stuff.partition {|par| par =~ /^q\s*=/ }
211
+
212
+ return new( type, subtype, qval.first, *opts )
213
+ end
214
+
215
+
216
+ ### The mediatype of the parameter, consisting of the type and subtype
217
+ ### separated by '/'.
218
+ def mediatype
219
+ return "%s/%s" % [ self.type || '*', self.subtype || '*' ]
220
+ end
221
+ alias_method :mimetype, :mediatype
222
+ alias_method :content_type, :mediatype
223
+
224
+
225
+ ### Return the parameter as a String suitable for inclusion in an Accept
226
+ ### HTTP header
227
+ def to_s
228
+ return [
229
+ self.mediatype,
230
+ self.qvaluestring,
231
+ self.extension_strings
232
+ ].compact.join(';')
233
+ end
234
+
235
+ end # class MediaType
236
+
237
+
238
+ # A natural language specification parameter, such as one you'd find in an
239
+ # Accept-Language header.
240
+ class Language < Strelka::HTTPRequest::AcceptParam
241
+
242
+ ### Parse the given +accept_param+ as a language range and return a
243
+ ### Strelka::HTTPRequest::Language object for it.
244
+ def self::parse( accept_param )
245
+ language_range, *stuff = accept_param.split( /\s*;\s*/ )
246
+ type, subtype = language_range.downcase.split( '-', 2 )
247
+ qval, opts = stuff.partition {|par| par =~ /^q\s*=/ }
248
+
249
+ return new( type, subtype, qval.first, *opts )
250
+ end
251
+
252
+
253
+ ######
254
+ public
255
+ ######
256
+
257
+ alias_method :primary_tag, :type
258
+ alias_method :subtag, :subtype
259
+
260
+ ### Return the language range of the parameter as a String.
261
+ def language_range
262
+ return [ self.primary_tag, self.subtag ].compact.join( '-' )
263
+ end
264
+
265
+ ### Return the parameter as a String suitable for inclusion in an
266
+ ### Accept-language header.
267
+ def to_s
268
+ return [
269
+ self.language_range,
270
+ self.qvaluestring,
271
+ self.extension_strings,
272
+ ].compact.join( ';' )
273
+ end
274
+
275
+ end # class Language
276
+
277
+
278
+ # A content encoding parameter, such as one you'd find in an Accept-Encoding header.
279
+ class Encoding < Strelka::HTTPRequest::AcceptParam
280
+
281
+ ### Parse the given +accept_param+ as a content coding and return a
282
+ ### Strelka::HTTPRequest::Encoding object for it.
283
+ def self::parse( accept_param )
284
+ content_coding, *stuff = accept_param.split( /\s*;\s*/ )
285
+ qval, opts = stuff.partition {|par| par =~ /^q\s*=/ }
286
+
287
+ return new( content_coding, nil, qval.first, *opts )
288
+ end
289
+
290
+
291
+ ######
292
+ public
293
+ ######
294
+
295
+ alias_method :content_coding, :type
296
+
297
+
298
+ ### Return the parameter as a String suitable for inclusion in an
299
+ ### Accept-language header.
300
+ def to_s
301
+ return [
302
+ self.content_coding,
303
+ self.qvaluestring,
304
+ self.extension_strings,
305
+ ].compact.join( ';' )
306
+ end
307
+
308
+ end # class Encoding
309
+
310
+
311
+ # A content character-set parameter, such as one you'd find in an Accept-Charset header.
312
+ class Charset < Strelka::HTTPRequest::AcceptParam
313
+
314
+ ### Parse the given +accept_param+ as a charset and return a
315
+ ### Strelka::HTTPRequest::Charset object for it.
316
+ def self::parse( accept_param )
317
+ charset, *stuff = accept_param.split( /\s*;\s*/ )
318
+ qval, opts = stuff.partition {|par| par =~ /^q\s*=/ }
319
+
320
+ return new( charset, nil, qval.first, *opts )
321
+ end
322
+
323
+
324
+ ######
325
+ public
326
+ ######
327
+
328
+ alias_method :name, :type
329
+
330
+
331
+ ### Return the parameter as a String suitable for inclusion in an
332
+ ### Accept-language header.
333
+ def to_s
334
+ return [
335
+ self.name,
336
+ self.qvaluestring,
337
+ self.extension_strings,
338
+ ].compact.join( ';' )
339
+ end
340
+
341
+
342
+ ### Return the Ruby Encoding object that is associated with the parameter's charset.
343
+ def encoding_object
344
+ return ::Encoding.find( self.name )
345
+ rescue ArgumentError => err
346
+ self.log.warn( err.message )
347
+ # self.log.debug( err.backtrace.join($/) )
348
+ return nil
349
+ end
350
+
351
+
352
+ ### Match operator -- returns true if +other+ matches the receiving
353
+ ### AcceptParam.
354
+ def =~( other )
355
+ unless other.is_a?( self.class )
356
+ other = self.class.parse( other.to_s ) rescue nil
357
+ return false unless other
358
+ end
359
+
360
+ # The special value "*", if present in the Accept-Charset field,
361
+ # matches every character set (including ISO-8859-1) which is not
362
+ # mentioned elsewhere in the Accept-Charset field.
363
+ return true if other.name.nil? || self.name.nil?
364
+
365
+ # Same downcased names or different names for the same encoding should match
366
+ return true if other.name.downcase == self.name.downcase ||
367
+ other.encoding_object == self.encoding_object
368
+
369
+ return false
370
+ end
371
+
372
+ end # class Charset
373
+
374
+
375
+ end # class Strelka::HTTPRequest
376
+
377
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'strelka/constants'
4
+ require 'strelka/httprequest' unless defined?( Strelka::HTTPRequest )
5
+ require 'strelka/httprequest/acceptparams'
6
+
7
+
8
+ # The mixin that adds methods to Strelka::HTTPRequest for content-negotiation.
9
+ #
10
+ # request.accepts?( 'application/json' )
11
+ # request.explicitly_accepts?( 'application/xml+rdf' )
12
+ # request.accepts_charset?( Encoding::UTF_8 )
13
+ # request.accepts_charset?( 'iso-8859-15' )
14
+ # request.accepts_encoding?( 'compress' )
15
+ # request.accepts_language?( 'en' )
16
+ # request.explicitly_accepts_language?( 'en' )
17
+ # request.explicitly_accepts_language?( 'en-gb' )
18
+ #
19
+ module Strelka::HTTPRequest::Negotiation
20
+ include Strelka::Constants
21
+
22
+ ### Extension callback -- add instance variables to extended objects.
23
+ def initialize( * )
24
+ super
25
+ @accepted_mediatypes = nil
26
+ @accepted_charsets = nil
27
+ @accepted_encodings = nil
28
+ @accepted_languages = nil
29
+ end
30
+
31
+
32
+ ### Fetch the value of the given +header+, split apart the values, and parse
33
+ ### each one using the specified +paramclass+. If no values are parsed from
34
+ ### the header, and a block is given, the block is called and its return value
35
+ ### is appended to the empty Array before returning it.
36
+ def parse_negotiation_header( header, paramclass )
37
+ self.log.debug "Parsing %s header into %p objects" % [ header, paramclass ]
38
+ rval = []
39
+ headerval = self.headers[ header ]
40
+ self.log.debug " raw header value: %p" % [ headerval ]
41
+
42
+ # Handle the case where there's more than one of the header in question by
43
+ # forcing everything to an Array
44
+ Array( headerval ).compact.flatten.each do |paramstr|
45
+ paramstr.split( /\s*,\s*/ ).each do |param|
46
+ self.log.debug " parsing param: %p" % [ param ]
47
+ rval << paramclass.parse( param )
48
+ end
49
+ end
50
+
51
+ if rval.empty? && block_given?
52
+ self.log.debug " no parsed values; calling the fallback block"
53
+ rval << yield
54
+ end
55
+
56
+ return rval.flatten
57
+ end
58
+
59
+
60
+ #
61
+ # :section: Mediatype negotiation
62
+ #
63
+
64
+ ### Return an Array of Strelka::HTTPRequest::MediaType objects for each
65
+ ### type in the 'Accept' header.
66
+ def accepted_mediatypes
67
+ @accepted_mediatypes ||= self.parse_accept_header
68
+ return @accepted_mediatypes
69
+ end
70
+ alias_method :accepted_types, :accepted_mediatypes
71
+
72
+
73
+ ### Returns boolean true/false if the requestor can handle the given
74
+ ### +content_type+.
75
+ def accepts?( content_type )
76
+ self.log.debug "Checking to see if request accepts %p" % [ content_type ]
77
+ atype = self.accepted_types.find {|type| type =~ content_type }
78
+ self.log.debug " find returned: %p" % [ atype ]
79
+ return atype ? true : false
80
+ end
81
+ alias_method :accept?, :accepts?
82
+
83
+
84
+ ### Returns boolean true/false if the requestor can handle the given
85
+ ### +content_type+, not including mime wildcards.
86
+ def explicitly_accepts?( content_type )
87
+ non_wildcard_types = self.accepted_types.reject {|param| param.subtype.nil? }
88
+ return non_wildcard_types.find {|type| type =~ content_type } ? true : false
89
+ end
90
+ alias_method :explicitly_accept?, :explicitly_accepts?
91
+
92
+
93
+ ### Parse the receiver's 'Accept' header and return it as an Array of
94
+ ### Strelka::HTTPRequest::MediaType objects.
95
+ def parse_accept_header
96
+ return self.parse_negotiation_header( :accept, Strelka::HTTPRequest::MediaType ) do
97
+ Strelka::HTTPRequest::MediaType.new( '*', '*' )
98
+ end
99
+ end
100
+
101
+
102
+ #
103
+ # :section: Charset negotiation
104
+ #
105
+
106
+ ### Return an Array of Strelka::HTTPRequest::Charset objects for each
107
+ ### type in the 'Accept-Charset' header.
108
+ def accepted_charsets
109
+ @accepted_charsets ||= self.parse_accept_charset_header
110
+ return @accepted_charsets
111
+ end
112
+
113
+
114
+ ### Returns boolean true/false if the requestor can handle the given
115
+ ### +charset+.
116
+ def accepts_charset?( charset )
117
+ self.log.debug "Checking to see if request accepts charset: %p" % [ charset ]
118
+ aset = self.accepted_charsets.find {|cs| cs =~ charset }
119
+ self.log.debug " find returned: %p" % [ aset ]
120
+ return aset ? true : false
121
+ end
122
+ alias_method :accept_charset?, :accepts_charset?
123
+
124
+
125
+ ### Returns boolean true/false if the requestor can handle the given
126
+ ### +charset+, not including the wildcard tag if present.
127
+ def explicitly_accepts_charset?( charset )
128
+ non_wildcard_charsets = self.accepted_charsets.reject {|param| param.charset.nil? }
129
+ return non_wildcard_charsets.find {|cs| cs =~ charset } ? true : false
130
+ end
131
+ alias_method :explicitly_accept_charset?, :explicitly_accepts_charset?
132
+
133
+
134
+ ### Parse the receiver's 'Accept-Charset' header and return it as an Array of
135
+ ### Strelka::HTTPRequest::Charset objects.
136
+ def parse_accept_charset_header
137
+ return self.parse_negotiation_header( :accept_charset, Strelka::HTTPRequest::Charset ) do
138
+ Strelka::HTTPRequest::Charset.new( '*' )
139
+ end
140
+ end
141
+
142
+
143
+ #
144
+ # :section: Encoding negotiation
145
+ #
146
+
147
+ ### Return an Array of Strelka::HTTPRequest::Encoding objects for each
148
+ ### type in the 'Accept-Encoding' header.
149
+ def accepted_encodings
150
+ @accepted_encodings ||= self.parse_accept_encoding_header
151
+ return @accepted_encodings
152
+ end
153
+
154
+
155
+ ### Returns boolean true/false if the requestor can handle the given
156
+ ### +encoding+.
157
+ def accepts_encoding?( encoding )
158
+ self.log.debug "Checking to see if request accepts encoding: %p" % [ encoding ]
159
+ return true if self.accepted_encodings.empty?
160
+ found_encoding = self.accepted_encodings.find {|enc| enc =~ encoding }
161
+ self.log.debug " find returned: %p" % [ found_encoding ]
162
+
163
+ # If there was no match, then it's not accepted, unless it's the 'identity'
164
+ # encoding, which is accepted unless it's disabled.
165
+ return encoding == 'identity' if !found_encoding
166
+
167
+ return found_encoding.qvalue.nonzero?
168
+ end
169
+ alias_method :accept_encoding?, :accepts_encoding?
170
+
171
+
172
+ ### Returns boolean true/false if the requestor can handle the given
173
+ ### +encoding+, not including the wildcard encoding if present.
174
+ def explicitly_accepts_encoding?( encoding )
175
+ non_wildcard_encodings = self.accepted_encodings.reject {|enc| enc.content_coding.nil? }
176
+ found_encoding = non_wildcard_encodings.find {|enc| enc =~ encoding } or
177
+ return false
178
+ return found_encoding.qvalue.nonzero?
179
+ end
180
+ alias_method :explicitly_accept_encoding?, :explicitly_accepts_encoding?
181
+
182
+
183
+ ### Parse the receiver's 'Accept-Encoding' header and return it as an Array of
184
+ ### Strelka::HTTPRequest::Encoding objects.
185
+ def parse_accept_encoding_header
186
+ return self.parse_negotiation_header( :accept_encoding, Strelka::HTTPRequest::Encoding ) do
187
+ # If the Accept-Encoding field-value is empty, then only the "identity"
188
+ # encoding is acceptable.
189
+ if self.headers.include?( :accept_encoding )
190
+ self.log.debug "Empty accept-encoding header: identity-only"
191
+ [ Strelka::HTTPRequest::Encoding.new('identity') ]
192
+
193
+ # I have no idea how this is different than an empty accept-encoding header
194
+ # for any practical case, but RFC2616 says:
195
+ # If no Accept-Encoding field is present in a request, the server MAY
196
+ # assume that the client will accept any content coding. In this
197
+ # case, if "identity" is one of the available content-codings, then
198
+ # the server SHOULD use the "identity" content-coding, unless it has
199
+ # additional information that a different content-coding is meaningful
200
+ # to the client.
201
+ else
202
+ self.log.debug "No accept-encoding header: identity + any encoding"
203
+ [
204
+ Strelka::HTTPRequest::Encoding.new( 'identity' ),
205
+ Strelka::HTTPRequest::Encoding.new( '*', nil, 0.9 )
206
+ ]
207
+ end
208
+ end
209
+ end
210
+
211
+
212
+
213
+ #
214
+ # :section: Language negotiation
215
+ #
216
+
217
+ ### Return an Array of Strelka::HTTPRequest::Language objects for each
218
+ ### type in the 'Accept-Language' header.
219
+ def accepted_languages
220
+ @accepted_languages ||= self.parse_accept_language_header
221
+ return @accepted_languages
222
+ end
223
+
224
+
225
+ ### Returns boolean true/false if the requestor can handle the given
226
+ ### +language+.
227
+ def accepts_language?( language )
228
+ self.log.debug "Checking to see if request accepts language: %p" % [ language ]
229
+ found_language = self.accepted_languages.find {|langcode| langcode =~ language }
230
+ self.log.debug " find returned: %p" % [ found_language ]
231
+ return found_language && found_language.qvalue.nonzero?
232
+ end
233
+ alias_method :accept_language?, :accepts_language?
234
+
235
+
236
+ ### Returns boolean true/false if the requestor can handle the given
237
+ ### +language+, not including the wildcard language if present.
238
+ def explicitly_accepts_language?( language )
239
+ non_wildcard_languages = self.accepted_languages.reject {|enc| enc.content_coding.nil? }
240
+ found_language = non_wildcard_languages.find {|enc| enc =~ language }
241
+ return found_language.qvalue.nonzero?
242
+ end
243
+ alias_method :explicitly_accept_language?, :explicitly_accepts_language?
244
+
245
+
246
+ ### Parse the receiver's 'Accept-Language' header and return it as an Array of
247
+ ### Strelka::HTTPRequest::Language objects.
248
+ def parse_accept_language_header
249
+ return self.parse_negotiation_header( :accept_language, Strelka::HTTPRequest::Language ) do
250
+ Strelka::HTTPRequest::Language.new( '*' )
251
+ end
252
+ end
253
+
254
+
255
+ end # module RequestMethods
256
+
257
+