wants 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ ## Wants
2
+
3
+ This library provides support for choosing the proper MIME type for a
4
+ response.
5
+
6
+ ### Installation
7
+
8
+ With Rubygems:
9
+
10
+ ```ruby
11
+ gem install wants
12
+ ```
13
+
14
+ With Bundler:
15
+
16
+ ```ruby
17
+ gem 'wants'
18
+ ```
19
+
20
+ ### Loading
21
+
22
+ Simply require the gem name:
23
+
24
+ ```ruby
25
+ require 'wants'
26
+ ```
27
+
28
+ ### Usage
29
+
30
+ #### `Wants.new`
31
+
32
+ ```ruby
33
+ wants = Wants.new( accept, [ :html, :json, :atom ] )
34
+ ```
35
+
36
+ The `Wants` constructor takes two arguments:
37
+
38
+ * The value of an HTTP Accept header. cf
39
+ [RFC 2616, §14.1](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1)
40
+ * an `Array` of all the supported MIME types, each of which can be a full
41
+ type (e.g. `"application/json"`) or, if `Rack` is loaded, a key in
42
+ [`Rack::Mime::MIME_TYPES`](https://github.com/rack/rack/blob/master/lib/rack/mime.rb).
43
+ (If you want to use a different lookup table, you can set `Wants.mime_lookup_table`.)
44
+
45
+ `Wants.new` will return to you a `Wants::MatchResult` object that represents the
46
+ single best MIME type from the available options. It supports a variety of
47
+ introspection methods:
48
+
49
+ #### `MatchResult#not_acceptable?`
50
+
51
+ This predicate tell you whether there was no acceptable match. For example,
52
+
53
+ ```ruby
54
+ wants = Wants.new( { 'application/json' }, [ :html ] )
55
+ wants.not_acceptable? # true
56
+ ```
57
+
58
+ This method is aliased as `#blank?` and its inverse is available as `#present?`.
59
+
60
+ #### `MatchResult#{mime}?`
61
+
62
+ You can use a MIME abbreviation as a query method on the matcher. For example,
63
+
64
+ ```ruby
65
+ acceptable = 'text/html,application/xhtml+xml;q=0.9'
66
+ offered = [ :html, :json ]
67
+ wants = Wants.new( acceptable, offered )
68
+ wants.html? # true
69
+ wants.xhtml? # false
70
+ wants.json? # false
71
+ ```
72
+
73
+ #### `MatchResult#[{mime}]`
74
+
75
+ To query a full MIME type, use `#[]`. For example,
76
+
77
+ ```ruby
78
+ acceptable = 'text/html,application/xhtml+xml;q=0.9'
79
+ offered = [ :html, :json ]
80
+ wants = Wants.new( acceptable, offered )
81
+ wants[:html] # true
82
+ wants['text/html'] # true
83
+ wants['application/xhtml_xml'] # false
84
+ wants['application/json'] # false
85
+ ```
86
+
87
+ #### `MatchResult#{mime}`
88
+
89
+ Lastly, you can use the matcher as DSL. For example,
90
+
91
+ ```ruby
92
+ acceptable = 'application/json,application/javascript;q=0.8'
93
+ offered = [ :html, :json ]
94
+ wants = Wants.new( acceptable, offered )
95
+
96
+ wants.atom { build_an_atom_response }
97
+ wants.json { build_a_json_response }
98
+ wants.html { build_an_html_response }
99
+ wants.not_acceptable { build_a_406_unacceptable_response }
100
+ wants.any { build_a_generic_response }
101
+ ```
102
+
103
+ In this example, only `build_a_json_response` will be evaluated. `wants.json`
104
+ and all subsequent `wants.{mime}` calls, including `wants.not_acceptable` and
105
+ `wants.any`, will return whatever `build_a_json_response` returned.
106
+ More formally, each `wants.{mime}` call behaves as follows:
107
+
108
+ 1. if `@response_value` is not `nil`, return it
109
+ 1. if [method name] is the abbreviation for the desired MIME type,
110
+ evaluate the block and set the result to `@response_value`
111
+
112
+ `wants.not_acceptable` will match if `wants.not_acceptable?` returns `true`.
113
+ `wants.any` will match if `wants.not_acceptable?` returns `false`. Thus,
114
+ `wants.any` should be placed after all other matchers.
115
+
116
+ #### `Wants::ValidateAcceptsMiddleware`
117
+
118
+ Usage in `config.ru`:
119
+
120
+ ```ruby
121
+ require 'wants/validate_accepts_middleware'
122
+
123
+ use Wants::ValidateAcceptsMiddleware, :mime_types => [ :html, :json ]
124
+ run MyApp
125
+ ```
126
+
127
+ This will pass HTML and JSON requests down to `MyApp` and return a
128
+ 406 Not Acceptable response for all others. You can configure the
129
+ failure case with the `:on_not_acceptable` option:
130
+
131
+ ```ruby
132
+ use Wants::ValidateAcceptsMiddleware,
133
+ :mime_types => [ ... ],
134
+ :on_not_acceptable => lambda { |env| ... }
135
+ ```
@@ -0,0 +1,70 @@
1
+ require 'wants/mimeparse'
2
+
3
+ module Wants
4
+ class MatchResult
5
+
6
+ def initialize(accept_header, acceptable)
7
+ @accept = accept_header || ''
8
+ @acceptable = acceptable.map { |mime| parse_mime(mime) }
9
+ @best_match = MIMEParse.best_match(@acceptable, @accept)
10
+ end
11
+
12
+ def not_acceptable?
13
+ @best_match.nil?
14
+ end
15
+
16
+ def blank?
17
+ not_acceptable?
18
+ end
19
+
20
+ def present?
21
+ !blank?
22
+ end
23
+
24
+ def [](mime)
25
+ @best_match == parse_mime(mime)
26
+ end
27
+
28
+ def any(&block)
29
+ @response_value ||= block.call if present?
30
+ end
31
+
32
+ def not_acceptable(&block)
33
+ @response_value ||= block.call if blank?
34
+ end
35
+
36
+ def method_missing(method, *args, &block)
37
+ if mime = mime_abbreviation_from_method(method)
38
+ if args.length > 0
39
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0)"
40
+ end
41
+ if method =~ /\?$/
42
+ self[mime]
43
+ elsif self[mime]
44
+ @response_value ||= block.call
45
+ else
46
+ @response_value
47
+ end
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ def respond_to?(method)
54
+ return true if mime_abbreviation_from_method(method)
55
+ super
56
+ end
57
+
58
+ private
59
+
60
+ def parse_mime(mime)
61
+ Wants.mime_lookup_table[".#{mime}"] || mime.to_s
62
+ end
63
+
64
+ def mime_abbreviation_from_method(method)
65
+ md = /([^\?]+)\??$/.match(method)
66
+ md && Wants.mime_lookup_table[".#{md[1]}"]
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,217 @@
1
+ module Wants
2
+ # From http://code.google.com/p/mimeparse
3
+ #
4
+ # This module provides basic functions for handling mime-types. It can
5
+ # handle matching mime-types against a list of media-ranges. See section
6
+ # 14.1 of the HTTP specification [RFC 2616] for a complete explanation.
7
+ #
8
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
9
+ #
10
+ # ---------
11
+ #
12
+ # This is a port of Joe Gregario's mimeparse.py, which can be found at
13
+ # <http://code.google.com/p/mimeparse/>.
14
+ #
15
+ # ported from version 0.1.2
16
+ #
17
+ # Comments are mostly excerpted from the original.
18
+ module MIMEParse
19
+ module_function
20
+
21
+ # Carves up a mime-type and returns an Array of the
22
+ # [type, subtype, params] where "params" is a Hash of all
23
+ # the parameters for the media range.
24
+ #
25
+ # For example, the media range "application/xhtml;q=0.5" would
26
+ # get parsed into:
27
+ #
28
+ # ["application", "xhtml", { "q" => "0.5" }]
29
+ def parse_mime_type(mime_type)
30
+ parts = mime_type.split(";")
31
+
32
+ params = {}
33
+
34
+ parts[1..-1].map do |param|
35
+ k,v = param.split("=").map { |s| s.strip }
36
+ params[k] = v
37
+ end
38
+
39
+ full_type = parts[0].strip
40
+ # Java URLConnection class sends an Accept header that includes a single "*"
41
+ # Turn it into a legal wildcard.
42
+ full_type = "*/*" if full_type == "*"
43
+ type, subtype = full_type.split("/")
44
+ raise "malformed mime type" unless subtype
45
+
46
+ [type.strip, subtype.strip, params]
47
+ end
48
+
49
+ # Carves up a media range and returns an Array of the
50
+ # [type, subtype, params] where "params" is a Hash of all
51
+ # the parameters for the media range.
52
+ #
53
+ # For example, the media range "application/*;q=0.5" would
54
+ # get parsed into:
55
+ #
56
+ # ["application", "*", { "q", "0.5" }]
57
+ #
58
+ # In addition this function also guarantees that there
59
+ # is a value for "q" in the params dictionary, filling it
60
+ # in with a proper default if necessary.
61
+ def parse_media_range(range)
62
+ type, subtype, params = parse_mime_type(range)
63
+ unless params.has_key?("q") and params["q"] and params["q"].to_f and params["q"].to_f <= 1 and params["q"].to_f >= 0
64
+ params["q"] = "1"
65
+ end
66
+
67
+ [type, subtype, params]
68
+ end
69
+
70
+ # Find the best match for a given mime-type against a list of
71
+ # media_ranges that have already been parsed by #parse_media_range
72
+ #
73
+ # Returns the fitness and the "q" quality parameter of the best match,
74
+ # or [-1, 0] if no match was found. Just as for #quality_parsed,
75
+ # "parsed_ranges" must be an Enumerable of parsed media ranges.
76
+ def fitness_and_quality_parsed(mime_type, parsed_ranges)
77
+ best_fitness = -1
78
+ best_fit_q = 0
79
+ target_type, target_subtype, target_params = parse_media_range(mime_type)
80
+
81
+ parsed_ranges.each do |type,subtype,params|
82
+ if (type == target_type or type == "*" or target_type == "*") and
83
+ (subtype == target_subtype or subtype == "*" or target_subtype == "*")
84
+ param_matches = target_params.find_all { |k,v| k != "q" and params.has_key?(k) and v == params[k] }.length
85
+
86
+ fitness = (type == target_type) ? 100 : 0
87
+ fitness += (subtype == target_subtype) ? 10 : 0
88
+ fitness += param_matches
89
+
90
+ if fitness > best_fitness
91
+ best_fitness = fitness
92
+ best_fit_q = params["q"]
93
+ end
94
+ end
95
+ end
96
+
97
+ [best_fitness, best_fit_q.to_f]
98
+ end
99
+
100
+ # Find the best match for a given mime-type against a list of
101
+ # media_ranges that have already been parsed by #parse_media_range
102
+ #
103
+ # Returns the "q" quality parameter of the best match, 0 if no match
104
+ # was found. This function behaves the same as #quality except that
105
+ # "parsed_ranges" must be an Enumerable of parsed media ranges.
106
+ def quality_parsed(mime_type, parsed_ranges)
107
+ fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
108
+ end
109
+
110
+ # Returns the quality "q" of a mime_type when compared against
111
+ # the media-ranges in ranges. For example:
112
+ #
113
+ # irb> quality("text/html", "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5")
114
+ # => 0.7
115
+ def quality(mime_type, ranges)
116
+ parsed_ranges = ranges.split(",").map { |r| parse_media_range(r) }
117
+ quality_parsed(mime_type, parsed_ranges)
118
+ end
119
+
120
+ # Takes a list of supported mime-types and finds the best match
121
+ # for all the media-ranges listed in header. The value of header
122
+ # must be a string that conforms to the format of the HTTP Accept:
123
+ # header. The value of supported is an Enumerable of mime-types
124
+ #
125
+ # irb> best_match(["application/xbel+xml", "text/xml"], "text/*;q=0.5,*/*; q=0.1")
126
+ # => "text/xml"
127
+ def best_match(supported, header)
128
+ parsed_header = header.split(",").map { |r| parse_media_range(r) }
129
+
130
+ weighted_matches = supported.map do |mime_type|
131
+ [fitness_and_quality_parsed(mime_type, parsed_header), mime_type]
132
+ end
133
+
134
+ weighted_matches.sort!
135
+
136
+ weighted_matches.last[0][1].zero? ? nil : weighted_matches.last[1]
137
+ end
138
+ end
139
+
140
+ if __FILE__ == $0
141
+ require "test/unit"
142
+
143
+ class TestMimeParsing < Test::Unit::TestCase
144
+ include MIMEParse
145
+
146
+ def test_parse_media_range
147
+ assert_equal [ "application", "xml", { "q" => "1" } ],
148
+ parse_media_range("application/xml;q=1")
149
+
150
+ assert_equal [ "application", "xml", { "q" => "1" } ],
151
+ parse_media_range("application/xml")
152
+
153
+ assert_equal [ "application", "xml", { "q" => "1" } ],
154
+ parse_media_range("application/xml;q=")
155
+
156
+ assert_equal [ "application", "xml", { "q" => "1", "b" => "other" } ],
157
+ parse_media_range("application/xml ; q=1;b=other")
158
+
159
+ assert_equal [ "application", "xml", { "q" => "1", "b" => "other" } ],
160
+ parse_media_range("application/xml ; q=2;b=other")
161
+
162
+ # Java URLConnection class sends an Accept header that includes a single "*"
163
+ assert_equal [ "*", "*", { "q" => ".2" } ],
164
+ parse_media_range(" *; q=.2")
165
+ end
166
+
167
+ def test_rfc_2616_example
168
+ accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
169
+
170
+ assert_equal 1, quality("text/html;level=1", accept)
171
+ assert_equal 0.7, quality("text/html", accept)
172
+ assert_equal 0.3, quality("text/plain", accept)
173
+ assert_equal 0.5, quality("image/jpeg", accept)
174
+ assert_equal 0.4, quality("text/html;level=2", accept)
175
+ assert_equal 0.7, quality("text/html;level=3", accept)
176
+ end
177
+
178
+ def test_best_match
179
+ @supported_mime_types = [ "application/xbel+xml", "application/xml" ]
180
+
181
+ # direct match
182
+ assert_best_match "application/xbel+xml", "application/xbel+xml"
183
+ # direct match with a q parameter
184
+ assert_best_match "application/xbel+xml", "application/xbel+xml; q=1"
185
+ # direct match of our second choice with a q parameter
186
+ assert_best_match "application/xml", "application/xml; q=1"
187
+ # match using a subtype wildcard
188
+ assert_best_match "application/xml", "application/*; q=1"
189
+ # match using a type wildcard
190
+ assert_best_match "application/xml", "*/*"
191
+
192
+ @supported_mime_types = [ "application/xbel+xml", "text/xml" ]
193
+ # match using a type versus a lower weighted subtype
194
+ assert_best_match "text/xml", "text/*;q=0.5,*/*;q=0.1"
195
+ # fail to match anything
196
+ assert_best_match nil, "text/html,application/atom+xml; q=0.9"
197
+ # common AJAX scenario
198
+ @supported_mime_types = [ "application/json", "text/html" ]
199
+ assert_best_match "application/json", "application/json, text/javascript, */*"
200
+ # verify fitness sorting
201
+ assert_best_match "application/json", "application/json, text/html;q=0.9"
202
+ end
203
+
204
+ def test_support_wildcards
205
+ @supported_mime_types = ['image/*', 'application/xml']
206
+ # match using a type wildcard
207
+ assert_best_match 'image/*', 'image/png'
208
+ # match using a wildcard for both requested and supported
209
+ assert_best_match 'image/*', 'image/*'
210
+ end
211
+
212
+ def assert_best_match(expected, header)
213
+ assert_equal(expected, best_match(@supported_mime_types, header))
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,35 @@
1
+ require 'wants/match_result'
2
+
3
+ module Wants
4
+ class ValidateAcceptsMiddleware
5
+
6
+ DEFAULT_ON_NOT_ACCEPTABLE = lambda do |env|
7
+ [
8
+ 406,
9
+ {
10
+ 'Content-Type' => 'text/plain',
11
+ 'Content-Length' => '14'
12
+ },
13
+ [ 'Not Acceptable' ]
14
+ ]
15
+ end
16
+
17
+ def initialize(app, options)
18
+ @app = app
19
+
20
+ @mime_types = options[:mime_types]
21
+ raise ArgumentError.new("#{self.class} requires option :mime_types") unless @mime_types
22
+
23
+ @on_not_acceptable = options[:on_not_acceptable] || DEFAULT_ON_NOT_ACCEPTABLE
24
+ end
25
+
26
+ def call(env)
27
+ if MatchResult.new(env['HTTP_ACCEPT'], @mime_types).present?
28
+ @app.call(env)
29
+ else
30
+ @on_not_acceptable.call(env)
31
+ end
32
+ end
33
+
34
+ end
35
+ end
data/lib/wants.rb ADDED
@@ -0,0 +1,23 @@
1
+ module Wants
2
+
3
+ class <<self
4
+
5
+ def new(env, mime_types)
6
+ MatchResult.new(env, mime_types)
7
+ end
8
+
9
+ def mime_lookup_table
10
+ @mime_lookup_table ||= begin
11
+ require 'rack/mime'
12
+ Rack::Mime::MIME_TYPES
13
+ rescue LoadError
14
+ {}
15
+ end
16
+ end
17
+
18
+ attr_writer :mime_lookup_table
19
+ end
20
+
21
+ end
22
+
23
+ require 'wants/match_result'
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+ require 'wants/match_result'
3
+
4
+ describe Wants do
5
+
6
+ let(:accept) { 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' }
7
+ let(:available) { [ :html, :xhtml, :json ] }
8
+
9
+ subject { Wants::MatchResult.new(accept, available) }
10
+
11
+ describe 'when there are no acceptable MIME types' do
12
+ let(:accept) { 'application/atom+xml' }
13
+
14
+ it 'should be not_acceptable' do
15
+ subject.should be_not_acceptable
16
+ end
17
+
18
+ it 'should be blank' do
19
+ subject.should be_blank
20
+ end
21
+
22
+ it 'should not be present' do
23
+ subject.should_not be_present
24
+ end
25
+
26
+ describe 'any' do
27
+ it 'acts like a non-best-match MIME block method' do
28
+ evaluated = 0
29
+
30
+ subject.any { evaluated += 1; 'not acceptable' }.should == nil
31
+ evaluated.should == 0
32
+ end
33
+ end
34
+
35
+ describe 'not_acceptable' do
36
+ it 'acts like a best-match MIME block method' do
37
+ evaluated = 0
38
+
39
+ subject.not_acceptable { evaluated += 1; 'not_acceptable' }.should == 'not_acceptable'
40
+ evaluated.should == 1
41
+
42
+ subject.json { evaluated += 1; 'json' }.should == 'not_acceptable'
43
+ evaluated.should == 1
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'when there are acceptable MIME types' do
49
+ it 'should not be not_acceptable' do
50
+ subject.should_not be_not_acceptable
51
+ end
52
+
53
+ it 'should not be blank' do
54
+ subject.should_not be_blank
55
+ end
56
+
57
+ it 'should be present' do
58
+ subject.should be_present
59
+ end
60
+
61
+ describe '[]' do
62
+ it 'returns true for the best match' do
63
+ subject['text/html'].should be_true
64
+ end
65
+
66
+ it 'returns true for the best match as an abbreviation' do
67
+ subject[:html].should be_true
68
+ end
69
+
70
+ it 'returns false for non-best matches' do
71
+ subject['application/xhtml+xml'].should be_false
72
+ end
73
+
74
+ it 'returns false for non-matches' do
75
+ subject['application/atom+xml'].should be_false
76
+ end
77
+ end
78
+
79
+ describe '#respond_to?' do
80
+ it 'returns true for MIME-like query methods' do
81
+ subject.respond_to?(:html?).should be_true
82
+ end
83
+
84
+ it 'returns true for MIME block methods' do
85
+ subject.respond_to?(:json).should be_true
86
+ end
87
+ end
88
+
89
+ describe 'MIME query methods' do
90
+ it 'returns true for the best match as an abbreviation' do
91
+ subject.should be_html
92
+ end
93
+
94
+ it 'returns false for non-best matches' do
95
+ subject.should_not be_xhtml
96
+ end
97
+
98
+ it 'returns false for non-matches' do
99
+ subject.should_not be_atom
100
+ end
101
+
102
+ it 'throws an exception if passed arguments' do
103
+ expect {
104
+ subject.html? :anything
105
+ }.to raise_error(ArgumentError)
106
+ end
107
+ end
108
+
109
+ describe 'MIME block methods' do
110
+ it 'evaluates the block and returns its result only if the best match' do
111
+ evaluated = 0
112
+
113
+ subject.atom { evaluated += 1; 'atom' }.should be_nil
114
+ evaluated.should == 0
115
+
116
+ subject.html { evaluated += 1; 'html' }.should == 'html'
117
+ evaluated.should == 1
118
+
119
+ subject.json { evaluated += 1; 'json' }.should == 'html'
120
+ evaluated.should == 1
121
+ end
122
+ end
123
+
124
+ describe 'any' do
125
+ it 'acts like a best-match MIME block method' do
126
+ evaluated = 0
127
+
128
+ subject.any { evaluated += 1; 'any' }.should == 'any'
129
+ evaluated.should == 1
130
+
131
+ subject.json { evaluated += 1; 'json' }.should == 'any'
132
+ evaluated.should == 1
133
+ end
134
+ end
135
+
136
+ describe 'not_acceptable' do
137
+ it 'acts like a non-best-match MIME block method' do
138
+ evaluated = 0
139
+
140
+ subject.not_acceptable { evaluated += 1; 'not acceptable' }.should == nil
141
+ evaluated.should == 0
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH <<
2
+ File.expand_path('../../lib', __FILE__) <<
3
+ File.expand_path('..', __FILE__)
4
+
5
+ require 'rspec'
6
+
7
+ RSpec.configure do |config|
8
+ config.mock_with :rspec
9
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ require 'wants/validate_accepts_middleware'
3
+
4
+ describe Wants do
5
+
6
+ let(:upstream) { double('App') }
7
+ let(:accept) { nil }
8
+ let(:env) { { 'HTTP_ACCEPT' => accept } }
9
+
10
+ subject { Wants::ValidateAcceptsMiddleware.new(upstream, :mime_types => [ :html, :json ]) }
11
+
12
+ describe 'when there is an acceptable MIME type' do
13
+ let(:accept) { 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.1' }
14
+
15
+ it 'passes the request upstream' do
16
+ upstream.should_receive(:call).with(env)
17
+ subject.call(env)
18
+ end
19
+
20
+ it 'returns the upstream response' do
21
+ result = double('Result')
22
+ upstream.stub(:call) { result }
23
+ subject.call(env).should == result
24
+ end
25
+ end
26
+
27
+ describe 'when there is no acceptable MIME type' do
28
+ let(:accept) { 'application/atom+xml' }
29
+
30
+ it "doesn't pass the request upstream" do
31
+ upstream.should_not_receive(:call)
32
+ subject.call(env)
33
+ end
34
+
35
+ it 'returns a 406' do
36
+ status, headers, body = *subject.call(env)
37
+ status.should == 406
38
+ headers['Content-Type'].should == 'text/plain'
39
+ headers['Content-Length'].should == body.first.length.to_s
40
+ body.should == [ 'Not Acceptable' ]
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'wants'
3
+
4
+ describe Wants do
5
+
6
+ describe '.new' do
7
+ it 'delegates to Wants::MatchResult.new' do
8
+ env = Object.new
9
+ mime_types = [ :html, :json ]
10
+ result = Object.new
11
+ Wants::MatchResult.should_receive(:new).with(env, mime_types) { result }
12
+ Wants.new(env, mime_types).should == result
13
+ end
14
+ end
15
+
16
+ describe '.mime_lookup_table' do
17
+ subject { Wants.mime_lookup_table }
18
+
19
+ before do
20
+ require 'rack/mime'
21
+ Wants.instance_eval do
22
+ @mime_lookup_table = nil
23
+ end
24
+ end
25
+
26
+ describe 'when Rack::Mime::MIME_TYPES is available' do
27
+ it 'defaults to that' do
28
+ subject.should == Rack::Mime::MIME_TYPES
29
+ end
30
+ end
31
+
32
+ describe 'when Rack::Mime::MIME_TYPES is unavailable' do
33
+ before do
34
+ Wants.stub(:require) { raise LoadError.new('Rack unavailable') }
35
+ end
36
+
37
+ it 'defaults to an empty Hash' do
38
+ subject.should == {}
39
+ end
40
+ end
41
+
42
+ it 'can be set' do
43
+ table = Object.new
44
+ Wants.mime_lookup_table = table
45
+ subject.should == table
46
+ end
47
+
48
+ end
49
+
50
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wants
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James A. Rosen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Parse and query the HTTP Accept header
63
+ email: james.a.rosen@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/wants/match_result.rb
69
+ - lib/wants/mimeparse.rb
70
+ - lib/wants/validate_accepts_middleware.rb
71
+ - lib/wants.rb
72
+ - README.md
73
+ - spec/match_result_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/validate_accepts_middleware_spec.rb
76
+ - spec/wants_spec.rb
77
+ homepage: http://github.com/jamesarosen/wants
78
+ licenses: []
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 1.8.24
98
+ signing_key:
99
+ specification_version: 2
100
+ summary: HTTP Accept header support
101
+ test_files:
102
+ - spec/match_result_spec.rb
103
+ - spec/spec_helper.rb
104
+ - spec/validate_accepts_middleware_spec.rb
105
+ - spec/wants_spec.rb