wants 1.0.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.
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