rack-accept 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +3 -0
- data/README +70 -0
- data/Rakefile +78 -0
- data/lib/rack/accept.rb +24 -0
- data/lib/rack/accept/charset.rb +63 -0
- data/lib/rack/accept/context.rb +21 -0
- data/lib/rack/accept/encoding.rb +63 -0
- data/lib/rack/accept/header.rb +96 -0
- data/lib/rack/accept/language.rb +61 -0
- data/lib/rack/accept/media_type.rb +67 -0
- data/lib/rack/accept/request.rb +96 -0
- data/rack-accept.gemspec +29 -0
- data/test/charset_test.rb +34 -0
- data/test/encoding_test.rb +24 -0
- data/test/header_test.rb +49 -0
- data/test/helper.rb +17 -0
- data/test/language_test.rb +29 -0
- data/test/media_type_test.rb +29 -0
- data/test/request_test.rb +93 -0
- metadata +114 -0
data/CHANGES
ADDED
data/README
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Rack::Accept
|
2
|
+
============
|
3
|
+
|
4
|
+
rack-accept is a suite of tools for Rack/Ruby applications that eases the
|
5
|
+
complexity of reading and writing Accept, Accept-Charset, Accept-Encoding, and
|
6
|
+
Accept-Language HTTP request headers.
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
Using RubyGems:
|
12
|
+
|
13
|
+
$ sudo gem install rack-accept
|
14
|
+
|
15
|
+
From a local copy:
|
16
|
+
|
17
|
+
$ git clone git://github.com/mjijackson/rack-accept.git
|
18
|
+
$ rake package && sudo rake install
|
19
|
+
|
20
|
+
Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
rack-accept implements the Rack middleware interface and may be used with any
|
24
|
+
Rack-based application. Simply insert the Rack::Accept module in your Rack
|
25
|
+
middleware pipeline and access the Rack::Accept::Request object in the
|
26
|
+
"rack-accept.request" environment key, as in the following example:
|
27
|
+
|
28
|
+
require 'rack/accept'
|
29
|
+
|
30
|
+
use Rack::Accept
|
31
|
+
|
32
|
+
app = lambda {|env|
|
33
|
+
accept = env['rack-accept.request']
|
34
|
+
response = Rack::Response.new
|
35
|
+
|
36
|
+
if accept.media_type?('text/html')
|
37
|
+
response['Content-Type'] = 'text/html'
|
38
|
+
response.write "<p>Hello. You accept text/html!</p>"
|
39
|
+
else
|
40
|
+
response['Content-Type'] = 'text/plain'
|
41
|
+
response.write "Apparently you don't accept text/html. Too bad."
|
42
|
+
end
|
43
|
+
|
44
|
+
response.finish
|
45
|
+
}
|
46
|
+
|
47
|
+
run app
|
48
|
+
|
49
|
+
License
|
50
|
+
-------
|
51
|
+
|
52
|
+
Copyright 2010 Michael J. I. Jackson
|
53
|
+
|
54
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
55
|
+
of this software and associated documentation files (the "Software"), to deal
|
56
|
+
in the Software without restriction, including without limitation the rights
|
57
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
58
|
+
copies of the Software, and to permit persons to whom the Software is
|
59
|
+
furnished to do so, subject to the following conditions:
|
60
|
+
|
61
|
+
The above copyright notice and this permission notice shall be included in
|
62
|
+
all copies or substantial portions of the Software.
|
63
|
+
|
64
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
65
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
66
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
67
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
68
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
69
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
70
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
task :default => :test
|
5
|
+
|
6
|
+
CLEAN.include %w< doc/api >
|
7
|
+
CLOBBER.include %w< dist >
|
8
|
+
|
9
|
+
# TESTS #######################################################################
|
10
|
+
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.test_files = FileList['test/*_test.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
# DOCS ########################################################################
|
16
|
+
|
17
|
+
desc "Generate all documentation"
|
18
|
+
task :doc => %w< doc:api doc:etc >
|
19
|
+
|
20
|
+
namespace :doc do
|
21
|
+
|
22
|
+
desc "Generate API documentation (in doc/api)"
|
23
|
+
task :api => FileList['lib/**/*.rb'] do |t|
|
24
|
+
rm_rf 'doc/api'
|
25
|
+
sh((<<-SH).gsub(/[\s\n]+/, ' ').strip)
|
26
|
+
hanna
|
27
|
+
--op doc/api
|
28
|
+
--promiscuous
|
29
|
+
--charset utf8
|
30
|
+
--fmt html
|
31
|
+
--inline-source
|
32
|
+
--line-numbers
|
33
|
+
--accessor option_accessor=RW
|
34
|
+
--main Rack::Accept
|
35
|
+
--title 'Rack::Accept API Documentation'
|
36
|
+
#{t.prerequisites.join(' ')}
|
37
|
+
SH
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Generate extra documentation"
|
41
|
+
task :etc do
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# PACKAGING ###################################################################
|
47
|
+
|
48
|
+
if defined?(Gem)
|
49
|
+
$spec = eval("#{File.read('rack-accept.gemspec')}")
|
50
|
+
|
51
|
+
def package(ext='')
|
52
|
+
"dist/rack-accept-#{$spec.version}" + ext
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Build packages"
|
56
|
+
task :package => %w< .gem .tar.gz >.map {|e| package(e) }
|
57
|
+
|
58
|
+
desc "Build and install as local gem"
|
59
|
+
task :install => package('.gem') do
|
60
|
+
sh "gem install #{package('.gem')}"
|
61
|
+
end
|
62
|
+
|
63
|
+
directory 'dist/'
|
64
|
+
|
65
|
+
file package('.gem') => %w< dist/ rack-accept.gemspec > + $spec.files do |f|
|
66
|
+
sh "gem build rack-accept.gemspec"
|
67
|
+
mv File.basename(f.name), f.name
|
68
|
+
end
|
69
|
+
|
70
|
+
file package('.tar.gz') => %w< dist/ > + $spec.files do |f|
|
71
|
+
sh "git archive --format=tar HEAD | gzip > #{f.name}"
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "Upload gem to rubygems.org"
|
75
|
+
task :release => package('.gem') do |t|
|
76
|
+
sh "gem push #{package('.gem')}"
|
77
|
+
end
|
78
|
+
end
|
data/lib/rack/accept.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# The current version of rack-accept.
|
4
|
+
VERSION = [0, 1]
|
5
|
+
|
6
|
+
# Returns the current version of rack-accept as a string.
|
7
|
+
def self.version
|
8
|
+
VERSION.join('.')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Enables Rack::Accept to be used as a Rack middleware.
|
12
|
+
def self.new(app, &block)
|
13
|
+
Context.new(app, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
autoload :Charset, 'rack/accept/charset'
|
17
|
+
autoload :Context, 'rack/accept/context'
|
18
|
+
autoload :Encoding, 'rack/accept/encoding'
|
19
|
+
autoload :Header, 'rack/accept/header'
|
20
|
+
autoload :Language, 'rack/accept/language'
|
21
|
+
autoload :MediaType, 'rack/accept/media_type'
|
22
|
+
autoload :Request, 'rack/accept/request'
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Represents an HTTP Accept-Charset header according to the HTTP 1.1
|
4
|
+
# specification, and provides several convenience methods for determining
|
5
|
+
# acceptable character sets.
|
6
|
+
#
|
7
|
+
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more
|
8
|
+
# information.
|
9
|
+
class Charset
|
10
|
+
|
11
|
+
include Header
|
12
|
+
|
13
|
+
attr_reader :qvalues
|
14
|
+
|
15
|
+
def initialize(header)
|
16
|
+
@qvalues = parse(header)
|
17
|
+
end
|
18
|
+
|
19
|
+
# The name of this header.
|
20
|
+
def name
|
21
|
+
'Accept-Charset'
|
22
|
+
end
|
23
|
+
|
24
|
+
# The value of this header, built from its internal representation.
|
25
|
+
def value
|
26
|
+
join(@qvalues)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array of all character set values that were specified in the
|
30
|
+
# original header, in no particular order.
|
31
|
+
def values
|
32
|
+
@qvalues.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines the quality factor (qvalue) of the given +charset+,
|
36
|
+
# according to the specifications of the original header.
|
37
|
+
def qvalue(charset)
|
38
|
+
m = matches(charset)
|
39
|
+
if m.empty?
|
40
|
+
charset == 'iso-8859-1' ? 1 : 0
|
41
|
+
else
|
42
|
+
@qvalues[m.first]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns an array of character sets from the original header that match
|
47
|
+
# the given +charset+, ordered by precedence.
|
48
|
+
def matches(charset)
|
49
|
+
values.select {|v|
|
50
|
+
v == charset || v == '*'
|
51
|
+
}.sort {|a, b|
|
52
|
+
# "*" gets least precedence, any others should be equal.
|
53
|
+
a == '*' ? 1 : (b == '*' ? -1 : 0)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a string representation of this header.
|
58
|
+
def to_s
|
59
|
+
[name, value].join(': ')
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Implements the Rack middleware interface.
|
4
|
+
class Context
|
5
|
+
|
6
|
+
attr_reader :app
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
yield self if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
# Inserts a new Rack::Accept::Request object into the environment before
|
14
|
+
# handing the request to the app immediately downstream.
|
15
|
+
def call(env)
|
16
|
+
env['rack-accept.request'] ||= Request.new(env)
|
17
|
+
@app.call(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Represents an HTTP Accept-Encoding header according to the HTTP 1.1
|
4
|
+
# specification, and provides several convenience methods for determining
|
5
|
+
# acceptable content encodings.
|
6
|
+
#
|
7
|
+
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more
|
8
|
+
# information.
|
9
|
+
class Encoding
|
10
|
+
|
11
|
+
include Header
|
12
|
+
|
13
|
+
attr_reader :qvalues
|
14
|
+
|
15
|
+
def initialize(header)
|
16
|
+
@qvalues = parse(header)
|
17
|
+
end
|
18
|
+
|
19
|
+
# The name of this header.
|
20
|
+
def name
|
21
|
+
'Accept-Encoding'
|
22
|
+
end
|
23
|
+
|
24
|
+
# The value of this header, built from its internal representation.
|
25
|
+
def value
|
26
|
+
join(@qvalues)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array of all encoding values that were specified in the
|
30
|
+
# original header, in no particular order.
|
31
|
+
def values
|
32
|
+
@qvalues.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines the quality factor (qvalue) of the given +encoding+,
|
36
|
+
# according to the specifications of the original header.
|
37
|
+
def qvalue(encoding)
|
38
|
+
m = matches(encoding)
|
39
|
+
if m.empty?
|
40
|
+
encoding == 'identity' ? 1 : 0
|
41
|
+
else
|
42
|
+
@qvalues[m.first]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns an array of encodings from the original header that match
|
47
|
+
# the given +encoding+, ordered by precedence.
|
48
|
+
def matches(encoding)
|
49
|
+
values.select {|v|
|
50
|
+
v == encoding || v == '*'
|
51
|
+
}.sort {|a, b|
|
52
|
+
# "*" gets least precedence, any others should be equal.
|
53
|
+
a == '*' ? 1 : (b == '*' ? -1 : 0)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a string representation of this header.
|
58
|
+
def to_s
|
59
|
+
[name, value].join(': ')
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Contains methods that are useful for working with Accept-style HTTP
|
4
|
+
# headers. The MediaType, Charset, Encoding, and Language classes all mixin
|
5
|
+
# this module.
|
6
|
+
module Header
|
7
|
+
|
8
|
+
# Parses the value of an Accept-style request header into a hash of
|
9
|
+
# acceptable values and their respective quality factors (qvalues). The
|
10
|
+
# +join+ method may be used on the resulting hash to obtain a header
|
11
|
+
# string that is the semantic equivalent of the one provided.
|
12
|
+
def parse(header)
|
13
|
+
qvalues = {}
|
14
|
+
|
15
|
+
header.to_s.split(/,\s*/).map do |part|
|
16
|
+
m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
|
17
|
+
|
18
|
+
if m
|
19
|
+
qvalues[m[1]] = (m[2] || 1).to_f
|
20
|
+
else
|
21
|
+
raise "Invalid header value: #{part.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
qvalues
|
26
|
+
end
|
27
|
+
module_function :parse
|
28
|
+
|
29
|
+
# Returns a string suitable for use as the value of an Accept-style HTTP
|
30
|
+
# header from the map of acceptable values to their respective quality
|
31
|
+
# factors (qvalues). The +parse+ method may be used on the resulting string
|
32
|
+
# to obtain a hash that is the equivalent of the one provided.
|
33
|
+
def join(qvalues)
|
34
|
+
qvalues.map {|k, v| k + (v == 1 ? '' : ";q=#{v}") }.join(', ')
|
35
|
+
end
|
36
|
+
module_function :join
|
37
|
+
|
38
|
+
# Parses a media type string into its relevant pieces. The return value
|
39
|
+
# will be an array with three values: 1) the content type, 2) the content
|
40
|
+
# subtype, and 3) the media type parameters. An empty array is returned if
|
41
|
+
# no match can be made.
|
42
|
+
def parse_media_type(media_type)
|
43
|
+
m = media_type.to_s.match(/^([a-z*]+)\/([a-z*-]+)(?:;([a-z0-9=]+))?$/)
|
44
|
+
m ? [m[1], m[2], m[3] || ''] : []
|
45
|
+
end
|
46
|
+
module_function :parse_media_type
|
47
|
+
|
48
|
+
module PublicInstanceMethods
|
49
|
+
# Returns the quality factor (qvalue) of the given +value+. This method
|
50
|
+
# is the only method that must be overridden in child classes in order
|
51
|
+
# for them to be able to use all other methods of this module.
|
52
|
+
def qvalue(value)
|
53
|
+
1
|
54
|
+
end
|
55
|
+
|
56
|
+
# Determines if the given +value+ is acceptable (does not have a qvalue
|
57
|
+
# of 0) according to this header.
|
58
|
+
def accept?(value)
|
59
|
+
qvalue(value) != 0
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a copy of the given +values+ array, sorted by quality factor
|
63
|
+
# (qvalue). Each element of the returned array is itself an array
|
64
|
+
# containing two objects: 1) the value's qvalue and 2) the original
|
65
|
+
# value.
|
66
|
+
#
|
67
|
+
# It is important to note that when performing this sort the order of
|
68
|
+
# the original values is preserved so long as the qvalue for each input
|
69
|
+
# value is the same. This expectation can be useful for example when
|
70
|
+
# trying to determine which of a variety of options has the highest
|
71
|
+
# qvalue. If the user prefers using one option over another (for any
|
72
|
+
# number of reasons), he should put it first in +values+. He may then
|
73
|
+
# use the first result with confidence that it is both most acceptable
|
74
|
+
# to the user and most convenient for him as well.
|
75
|
+
def sort(values)
|
76
|
+
values.map {|v| [ qvalue(v), v ] }.sort.reverse
|
77
|
+
end
|
78
|
+
|
79
|
+
# Determines the most preferred value to use of those provided in
|
80
|
+
# +values+. See the documentation for #sort for more information on
|
81
|
+
# exactly how the sorting is done.
|
82
|
+
#
|
83
|
+
# If +keep_unacceptables+ is false (the default) and no values are
|
84
|
+
# acceptable the return value will be +nil+. Otherwise, the most
|
85
|
+
# acceptable value will be returned.
|
86
|
+
def best_of(values, keep_unacceptables=false)
|
87
|
+
s = sort(values)
|
88
|
+
s.reject! {|q, v| q == 0 } unless keep_unacceptables
|
89
|
+
s.first && s.first[1]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
include PublicInstanceMethods
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Represents an HTTP Accept-Language header according to the HTTP 1.1
|
4
|
+
# specification, and provides several convenience methods for determining
|
5
|
+
# acceptable content languages.
|
6
|
+
#
|
7
|
+
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more
|
8
|
+
# information.
|
9
|
+
class Language
|
10
|
+
|
11
|
+
include Header
|
12
|
+
|
13
|
+
attr_reader :qvalues
|
14
|
+
|
15
|
+
def initialize(header)
|
16
|
+
@qvalues = parse(header)
|
17
|
+
end
|
18
|
+
|
19
|
+
# The name of this header.
|
20
|
+
def name
|
21
|
+
'Accept-Language'
|
22
|
+
end
|
23
|
+
|
24
|
+
# The value of this header, built from its internal representation.
|
25
|
+
def value
|
26
|
+
join(@qvalues)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array of all language values that were specified in the
|
30
|
+
# original header, in no particular order.
|
31
|
+
def values
|
32
|
+
@qvalues.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines the quality factor (qvalue) of the given +language+,
|
36
|
+
# according to the specifications of the original header.
|
37
|
+
def qvalue(language)
|
38
|
+
return 1 if @qvalues.empty?
|
39
|
+
m = matches(language)
|
40
|
+
return 0 if m.empty?
|
41
|
+
@qvalues[m.first]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns an array of languages from the original header that match
|
45
|
+
# the given +language+, ordered by precedence.
|
46
|
+
def matches(language)
|
47
|
+
values.select {|v|
|
48
|
+
v == language || v == '*' || (language.match(/^(.+?)-/) && v == $1)
|
49
|
+
}.sort {|a, b|
|
50
|
+
# "*" gets least precedence, any others are compared based on length.
|
51
|
+
a == '*' ? -1 : (b == '*' ? 1 : a.length <=> b.length)
|
52
|
+
}.reverse
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a string representation of this header.
|
56
|
+
def to_s
|
57
|
+
[name, value].join(': ')
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# Represents an HTTP Accept header according to the HTTP 1.1 specification,
|
4
|
+
# and provides several convenience methods for determining acceptable media
|
5
|
+
# types.
|
6
|
+
#
|
7
|
+
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more
|
8
|
+
# information.
|
9
|
+
class MediaType
|
10
|
+
|
11
|
+
include Header
|
12
|
+
|
13
|
+
attr_reader :qvalues
|
14
|
+
|
15
|
+
def initialize(header)
|
16
|
+
@qvalues = parse(header)
|
17
|
+
end
|
18
|
+
|
19
|
+
# The name of this header.
|
20
|
+
def name
|
21
|
+
'Accept'
|
22
|
+
end
|
23
|
+
|
24
|
+
# The value of this header, built from its internal representation.
|
25
|
+
def value
|
26
|
+
join(@qvalues)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array of all media type values that were specified in the
|
30
|
+
# original header, in no particular order.
|
31
|
+
def values
|
32
|
+
@qvalues.keys
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines the quality factor (qvalue) of the given +media_type+,
|
36
|
+
# according to the specifications of the original header.
|
37
|
+
def qvalue(media_type)
|
38
|
+
return 1 if @qvalues.empty?
|
39
|
+
m = matches(media_type)
|
40
|
+
return 0 if m.empty?
|
41
|
+
@qvalues[m.first]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns an array of media types from the original header that match
|
45
|
+
# the given +media_type+, ordered by precedence.
|
46
|
+
def matches(media_type)
|
47
|
+
type, subtype, params = parse_media_type(media_type)
|
48
|
+
values.select {|v|
|
49
|
+
if v == media_type || v == '*/*'
|
50
|
+
true
|
51
|
+
else
|
52
|
+
t, s, p = parse_media_type(v)
|
53
|
+
t == type && (s == subtype || s == '*') && (p == params || p == '')
|
54
|
+
end
|
55
|
+
}.sort_by {|v|
|
56
|
+
# Most specific gets precedence.
|
57
|
+
v.length
|
58
|
+
}.reverse
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a string representation of this header.
|
62
|
+
def to_s
|
63
|
+
[name, value].join(': ')
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rack::Accept
|
2
|
+
|
3
|
+
# A container class for convenience methods when rack-accept is used on the
|
4
|
+
# request level (as a Rack middleware, for example). This class manages a
|
5
|
+
# lightweight cache of various header instances to speed up execution.
|
6
|
+
#
|
7
|
+
# This class currently does not extend Rack::Request because that class
|
8
|
+
# currently provides some limited functionality when it comes to determining
|
9
|
+
# the proper encoding to use with a request and I didn't want to confuse the
|
10
|
+
# user.
|
11
|
+
class Request
|
12
|
+
|
13
|
+
attr_reader :env
|
14
|
+
|
15
|
+
def initialize(env)
|
16
|
+
@env = env
|
17
|
+
end
|
18
|
+
|
19
|
+
# Provides access to the Rack::Accept::MediaType instance for this request.
|
20
|
+
def media_type
|
21
|
+
@media_type ||= MediaType.new(env['HTTP_ACCEPT'])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Provides access to the Rack::Accept::Charset instance for this request.
|
25
|
+
def charset
|
26
|
+
@charset ||= Charset.new(env['HTTP_ACCEPT_CHARSET'])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Provides access to the Rack::Accept::Encoding instance for this request.
|
30
|
+
def encoding
|
31
|
+
@encoding ||= Encoding.new(env['HTTP_ACCEPT_ENCODING'])
|
32
|
+
end
|
33
|
+
|
34
|
+
# Provides access to the Rack::Accept::Language instance for this request.
|
35
|
+
def language
|
36
|
+
@language ||= Language.new(env['HTTP_ACCEPT_LANGUAGE'])
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns true if the +Accept+ request header indicates the given media
|
40
|
+
# type is acceptable, false otherwise.
|
41
|
+
def media_type?(value)
|
42
|
+
media_type.accept?(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if the +Accept-Charset+ request header indicates the given
|
46
|
+
# character set is acceptable, false otherwise.
|
47
|
+
def charset?(value)
|
48
|
+
charset.accept?(value)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns true if the +Accept-Encoding+ request header indicates the given
|
52
|
+
# encoding is acceptable, false otherwise.
|
53
|
+
def encoding?(value)
|
54
|
+
encoding.accept?(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if the +Accept-Language+ request header indicates the given
|
58
|
+
# language is acceptable, false otherwise.
|
59
|
+
def language?(value)
|
60
|
+
language.accept?(value)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Determines the best media type to use in a response from the given media
|
64
|
+
# types, if any is acceptable. For more information on how this value is
|
65
|
+
# determined, see the documentation for
|
66
|
+
# Rack::Accept::Header::PublicInstanceMethods#sort.
|
67
|
+
def best_media_type(values)
|
68
|
+
media_type.best_of(values)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Determines the best character set to use in a response from the given
|
72
|
+
# character sets, if any is acceptable. For more information on how this
|
73
|
+
# value is determined, see the documentation for
|
74
|
+
# Rack::Accept::Header::PublicInstanceMethods#sort.
|
75
|
+
def best_charset(values)
|
76
|
+
charset.best_of(values)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Determines the best encoding to use in a response from the given
|
80
|
+
# encodings, if any is acceptable. For more information on how this value
|
81
|
+
# is determined, see the documentation for
|
82
|
+
# Rack::Accept::Header::PublicInstanceMethods#sort.
|
83
|
+
def best_encoding(values)
|
84
|
+
encoding.best_of(values)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Determines the best language to use in a response from the given
|
88
|
+
# languages, if any is acceptable. For more information on how this value
|
89
|
+
# is determined, see the documentation for
|
90
|
+
# Rack::Accept::Header::PublicInstanceMethods#sort.
|
91
|
+
def best_language(values)
|
92
|
+
language.best_of(values)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
data/rack-accept.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'rack-accept'
|
3
|
+
s.version = '0.1'
|
4
|
+
s.date = '2010-04-01'
|
5
|
+
|
6
|
+
s.summary = 'HTTP Accept* tools for Rack'
|
7
|
+
s.description = 'HTTP Accept, Accept-Charset, Accept-Encoding, and Accept-Language tools for Rack'
|
8
|
+
|
9
|
+
s.author = 'Michael J. I. Jackson'
|
10
|
+
s.email = 'mjijackson@gmail.com'
|
11
|
+
|
12
|
+
s.require_paths = %w< lib >
|
13
|
+
|
14
|
+
s.files = Dir['lib/**/*.rb'] +
|
15
|
+
Dir['test/*.rb'] +
|
16
|
+
%w< CHANGES rack-accept.gemspec Rakefile README >
|
17
|
+
|
18
|
+
s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/ }
|
19
|
+
|
20
|
+
s.add_dependency('rack', '>= 0.4')
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
|
23
|
+
s.has_rdoc = true
|
24
|
+
s.rdoc_options = %w< --line-numbers --inline-source --title Rack::Accept --main Rack::Accept >
|
25
|
+
s.extra_rdoc_files = %w< CHANGES README >
|
26
|
+
|
27
|
+
s.homepage = 'http://github.com/mjijackson/rack-accept'
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class CharsetTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
C = Rack::Accept::Charset
|
6
|
+
|
7
|
+
def test_qvalue
|
8
|
+
c = C.new('')
|
9
|
+
assert_equal(0, c.qvalue('unicode-1-1'))
|
10
|
+
assert_equal(1, c.qvalue('iso-8859-1'))
|
11
|
+
|
12
|
+
c = C.new('unicode-1-1')
|
13
|
+
assert_equal(1, c.qvalue('unicode-1-1'))
|
14
|
+
assert_equal(0, c.qvalue('iso-8859-5'))
|
15
|
+
assert_equal(1, c.qvalue('iso-8859-1'))
|
16
|
+
|
17
|
+
c = C.new('unicode-1-1, *;q=0.5')
|
18
|
+
assert_equal(1, c.qvalue('unicode-1-1'))
|
19
|
+
assert_equal(0.5, c.qvalue('iso-8859-5'))
|
20
|
+
assert_equal(0.5, c.qvalue('iso-8859-1'))
|
21
|
+
|
22
|
+
c = C.new('iso-8859-1;q=0, *;q=0.5')
|
23
|
+
assert_equal(0.5, c.qvalue('iso-8859-5'))
|
24
|
+
assert_equal(0, c.qvalue('iso-8859-1'))
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_matches
|
28
|
+
c = C.new('iso-8859-1, iso-8859-5, *')
|
29
|
+
assert_equal(%w{*}, c.matches(''))
|
30
|
+
assert_equal(%w{iso-8859-1 *}, c.matches('iso-8859-1'))
|
31
|
+
assert_equal(%w{*}, c.matches('unicode-1-1'))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class EncodingTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
E = Rack::Accept::Encoding
|
6
|
+
|
7
|
+
def test_qvalue
|
8
|
+
e = E.new('')
|
9
|
+
assert_equal(0, e.qvalue('gzip'))
|
10
|
+
assert_equal(1, e.qvalue('identity'))
|
11
|
+
|
12
|
+
e = E.new('gzip, *;q=0.5')
|
13
|
+
assert_equal(1, e.qvalue('gzip'))
|
14
|
+
assert_equal(0.5, e.qvalue('identity'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_matches
|
18
|
+
e = E.new('gzip, identity, *')
|
19
|
+
assert_equal(%w{*}, e.matches(''))
|
20
|
+
assert_equal(%w{gzip *}, e.matches('gzip'))
|
21
|
+
assert_equal(%w{*}, e.matches('compress'))
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/header_test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class HeaderTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
H = Rack::Accept::Header
|
6
|
+
|
7
|
+
def test_parse_and_join
|
8
|
+
# Accept
|
9
|
+
header = 'text/plain; q=0.5, text/html, text/html;level=2, text/html;level=1;q=0.3, text/x-c, image/*; q=0.2'
|
10
|
+
expect = {
|
11
|
+
'text/plain' => 0.5,
|
12
|
+
'text/html' => 1,
|
13
|
+
'text/html;level=2' => 1,
|
14
|
+
'text/html;level=1' => 0.3,
|
15
|
+
'text/x-c' => 1,
|
16
|
+
'image/*' => 0.2
|
17
|
+
}
|
18
|
+
assert_equal(expect, H.parse(header))
|
19
|
+
assert_equal(expect, H.parse(H.join(expect)))
|
20
|
+
|
21
|
+
# Accept-Charset
|
22
|
+
header = 'iso-8859-5, unicode-1-1;q=0.8'
|
23
|
+
expect = { 'iso-8859-5' => 1, 'unicode-1-1' => 0.8 }
|
24
|
+
assert_equal(expect, H.parse(header))
|
25
|
+
assert_equal(expect, H.parse(H.join(expect)))
|
26
|
+
|
27
|
+
# Accept-Encoding
|
28
|
+
header = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
29
|
+
expect = { 'gzip' => 1, 'identity' => 0.5, '*' => 0 }
|
30
|
+
assert_equal(expect, H.parse(header))
|
31
|
+
assert_equal(expect, H.parse(H.join(expect)))
|
32
|
+
|
33
|
+
# Accept-Language
|
34
|
+
header = 'da, en-gb;q=0.8, en;q=0.7'
|
35
|
+
expect = { 'da' => 1, 'en-gb' => 0.8, 'en' => 0.7 }
|
36
|
+
assert_equal(expect, H.parse(header))
|
37
|
+
assert_equal(expect, H.parse(H.join(expect)))
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_parse_media_type
|
41
|
+
assert_equal([], H.parse_media_type(''))
|
42
|
+
assert_equal(['*', '*', ''], H.parse_media_type('*/*'))
|
43
|
+
assert_equal(['text', '*', ''], H.parse_media_type('text/*'))
|
44
|
+
assert_equal(['text', 'html', ''], H.parse_media_type('text/html'))
|
45
|
+
assert_equal(['text', 'html', 'level=1'], H.parse_media_type('text/html;level=1'))
|
46
|
+
assert_equal(['text', 'x-dvi', ''], H.parse_media_type('text/x-dvi'))
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rack'
|
5
|
+
rescue LoadError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rack'
|
8
|
+
end
|
9
|
+
|
10
|
+
testdir = File.dirname(__FILE__)
|
11
|
+
$LOAD_PATH.unshift(testdir) unless $LOAD_PATH.include?(testdir)
|
12
|
+
|
13
|
+
libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
|
14
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
15
|
+
|
16
|
+
require 'test/unit'
|
17
|
+
require 'rack/accept'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class LanguageTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
L = Rack::Accept::Language
|
6
|
+
|
7
|
+
def test_qvalue
|
8
|
+
l = L.new('')
|
9
|
+
assert_equal(1, l.qvalue('en'))
|
10
|
+
|
11
|
+
l = L.new('en;q=0.5, en-gb')
|
12
|
+
assert_equal(0.5, l.qvalue('en'))
|
13
|
+
assert_equal(1, l.qvalue('en-gb'))
|
14
|
+
assert_equal(0, l.qvalue('da'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_matches
|
18
|
+
l = L.new('da, *, en')
|
19
|
+
assert_equal(%w{*}, l.matches(''))
|
20
|
+
assert_equal(%w{da *}, l.matches('da'))
|
21
|
+
assert_equal(%w{en *}, l.matches('en'))
|
22
|
+
assert_equal(%w{en *}, l.matches('en-gb'))
|
23
|
+
assert_equal(%w{*}, l.matches('eng'))
|
24
|
+
|
25
|
+
l = L.new('en, en-gb')
|
26
|
+
assert_equal(%w{en-gb en}, l.matches('en-gb'))
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class MediaTypeTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
M = Rack::Accept::MediaType
|
6
|
+
|
7
|
+
def test_qvalue
|
8
|
+
m = M.new('text/html, text/*;q=0.3, */*;q=0.5')
|
9
|
+
assert_equal(0.5, m.qvalue('image/png'))
|
10
|
+
assert_equal(0.3, m.qvalue('text/plain'))
|
11
|
+
assert_equal(1, m.qvalue('text/html'))
|
12
|
+
|
13
|
+
m = M.new('text/html')
|
14
|
+
assert_equal(0, m.qvalue('image/png'))
|
15
|
+
|
16
|
+
m = M.new('')
|
17
|
+
assert_equal(1, m.qvalue('text/html'))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_matches
|
21
|
+
m = M.new('text/*, text/html, text/html;level=1, */*')
|
22
|
+
assert_equal(%w{*/*}, m.matches(''))
|
23
|
+
assert_equal(%w{*/*}, m.matches('image/jpeg'))
|
24
|
+
assert_equal(%w{text/* */*}, m.matches('text/plain'))
|
25
|
+
assert_equal(%w{text/html text/* */*}, m.matches('text/html'))
|
26
|
+
assert_equal(%w{text/html;level=1 text/html text/* */*}, m.matches('text/html;level=1'))
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
R = Rack::Accept::Request
|
6
|
+
|
7
|
+
def test_media_type
|
8
|
+
request = R.new('HTTP_ACCEPT' => 'text/*;q=0, text/html')
|
9
|
+
assert(request.media_type?('text/html'))
|
10
|
+
assert(request.media_type?('text/html;level=1'))
|
11
|
+
assert(!request.media_type?('text/plain'))
|
12
|
+
assert(!request.media_type?('image/png'))
|
13
|
+
|
14
|
+
request = R.new('HTTP_ACCEPT' => '*/*')
|
15
|
+
assert(request.media_type?('image/png'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_best_media_type
|
19
|
+
request = R.new('HTTP_ACCEPT' => 'text/*;q=0.5, text/html')
|
20
|
+
assert_equal('text/html', request.best_media_type(%w< text/plain text/html >))
|
21
|
+
assert_equal('text/plain', request.best_media_type(%w< text/plain image/png >))
|
22
|
+
assert_equal('text/plain', request.best_media_type(%w< text/plain text/javascript >))
|
23
|
+
assert_equal(nil, request.best_media_type(%w< image/png >))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_charset
|
27
|
+
request = R.new('HTTP_ACCEPT_CHARSET' => 'iso-8859-5, unicode-1-1;q=0.8')
|
28
|
+
assert(request.charset?('iso-8859-5'))
|
29
|
+
assert(request.charset?('unicode-1-1'))
|
30
|
+
assert(request.charset?('iso-8859-1'))
|
31
|
+
assert(!request.charset?('utf-8'))
|
32
|
+
|
33
|
+
request = R.new('HTTP_ACCEPT_CHARSET' => 'iso-8859-1;q=0')
|
34
|
+
assert(!request.charset?('iso-8859-1'))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_best_charset
|
38
|
+
request = R.new('HTTP_ACCEPT_CHARSET' => 'iso-8859-5, unicode-1-1;q=0.8')
|
39
|
+
assert_equal('iso-8859-5', request.best_charset(%w< iso-8859-5 unicode-1-1 >))
|
40
|
+
assert_equal('iso-8859-5', request.best_charset(%w< iso-8859-5 utf-8 >))
|
41
|
+
assert_equal('iso-8859-1', request.best_charset(%w< iso-8859-1 utf-8 >))
|
42
|
+
assert_equal(nil, request.best_charset(%w< utf-8 >))
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_encoding
|
46
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => '')
|
47
|
+
assert(request.encoding?('identity'))
|
48
|
+
assert(!request.encoding?('gzip'))
|
49
|
+
|
50
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => 'gzip')
|
51
|
+
assert(request.encoding?('identity'))
|
52
|
+
assert(request.encoding?('gzip'))
|
53
|
+
assert(!request.encoding?('compress'))
|
54
|
+
|
55
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => 'gzip;q=0, *')
|
56
|
+
assert(request.encoding?('compress'))
|
57
|
+
assert(request.encoding?('identity'))
|
58
|
+
assert(!request.encoding?('gzip'))
|
59
|
+
|
60
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => 'identity;q=0')
|
61
|
+
assert(!request.encoding?('identity'))
|
62
|
+
|
63
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => '*;q=0')
|
64
|
+
assert(!request.encoding?('identity'))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_best_encoding
|
68
|
+
request = R.new('HTTP_ACCEPT_ENCODING' => 'gzip, compress')
|
69
|
+
assert_equal('gzip', request.best_encoding(%w< gzip compress >))
|
70
|
+
assert_equal('identity', request.best_encoding(%w< identity compress >))
|
71
|
+
assert_equal(nil, request.best_encoding(%w< zip >))
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_language
|
75
|
+
request = R.new({})
|
76
|
+
assert(request.language?('en'))
|
77
|
+
assert(request.language?('da'))
|
78
|
+
|
79
|
+
request = R.new('HTTP_ACCEPT_LANGUAGE' => 'en;q=0.5, en-gb')
|
80
|
+
assert(request.language?('en'))
|
81
|
+
assert(request.language?('en-gb'))
|
82
|
+
assert(!request.language?('da'))
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_best_language
|
86
|
+
request = R.new('HTTP_ACCEPT_LANGUAGE' => 'en;q=0.5, en-gb')
|
87
|
+
assert_equal('en-gb', request.best_language(%w< en en-gb >))
|
88
|
+
assert_equal('en', request.best_language(%w< en da >))
|
89
|
+
assert_equal('en-us', request.best_language(%w< en-us en-au >))
|
90
|
+
assert_equal(nil, request.best_language(%w< da >))
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-accept
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
version: "0.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Michael J. I. Jackson
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-04-01 00:00:00 -06:00
|
17
|
+
default_executable:
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: rack
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 0
|
28
|
+
- 4
|
29
|
+
version: "0.4"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: rake
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
description: HTTP Accept, Accept-Charset, Accept-Encoding, and Accept-Language tools for Rack
|
45
|
+
email: mjijackson@gmail.com
|
46
|
+
executables: []
|
47
|
+
|
48
|
+
extensions: []
|
49
|
+
|
50
|
+
extra_rdoc_files:
|
51
|
+
- CHANGES
|
52
|
+
- README
|
53
|
+
files:
|
54
|
+
- lib/rack/accept/charset.rb
|
55
|
+
- lib/rack/accept/context.rb
|
56
|
+
- lib/rack/accept/encoding.rb
|
57
|
+
- lib/rack/accept/header.rb
|
58
|
+
- lib/rack/accept/language.rb
|
59
|
+
- lib/rack/accept/media_type.rb
|
60
|
+
- lib/rack/accept/request.rb
|
61
|
+
- lib/rack/accept.rb
|
62
|
+
- test/charset_test.rb
|
63
|
+
- test/encoding_test.rb
|
64
|
+
- test/header_test.rb
|
65
|
+
- test/helper.rb
|
66
|
+
- test/language_test.rb
|
67
|
+
- test/media_type_test.rb
|
68
|
+
- test/request_test.rb
|
69
|
+
- CHANGES
|
70
|
+
- rack-accept.gemspec
|
71
|
+
- Rakefile
|
72
|
+
- README
|
73
|
+
has_rdoc: true
|
74
|
+
homepage: http://github.com/mjijackson/rack-accept
|
75
|
+
licenses: []
|
76
|
+
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --line-numbers
|
80
|
+
- --inline-source
|
81
|
+
- --title
|
82
|
+
- Rack::Accept
|
83
|
+
- --main
|
84
|
+
- Rack::Accept
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.3.6
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: HTTP Accept* tools for Rack
|
108
|
+
test_files:
|
109
|
+
- test/charset_test.rb
|
110
|
+
- test/encoding_test.rb
|
111
|
+
- test/header_test.rb
|
112
|
+
- test/language_test.rb
|
113
|
+
- test/media_type_test.rb
|
114
|
+
- test/request_test.rb
|