mynyml-rack-accept-media-types 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright © 2009 Martin Aumont (mynyml)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ # --------------------------------------------------
2
+ # based on thin's Rakefile (http://github.com/macournoyer/thin)
3
+ # --------------------------------------------------
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'pathname'
7
+ require 'yaml'
8
+
9
+ RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
10
+ WIN = (RUBY_PLATFORM =~ /mswin|cygwin/)
11
+ SUDO = (WIN ? "" : "sudo")
12
+
13
+ def gem
14
+ RUBY_1_9 ? 'gem19' : 'gem'
15
+ end
16
+
17
+ def all_except(res)
18
+ Dir['**/*'].reject do |path|
19
+ Array(res).any? {|re| path.match(re) }
20
+ end
21
+ end
22
+
23
+ spec = Gem::Specification.new do |s|
24
+ s.name = 'rack-accept-media-types'
25
+ s.version = '0.5'
26
+ s.summary = "Rack convenience middleware for simplified handling of Accept header."
27
+ s.description = "Rack convenience middleware for simplified handling of Accept header."
28
+ s.author = "Martin Aumont"
29
+ s.email = 'mynyml@gmail.com'
30
+ s.homepage = ''
31
+ s.has_rdoc = true
32
+ s.require_path = "lib"
33
+ s.files = all_except([/doc/, /pkg/])
34
+ end
35
+
36
+ desc "Generate rdoc documentation."
37
+ Rake::RDocTask.new("rdoc") { |rdoc|
38
+ rdoc.rdoc_dir = 'doc/rdoc'
39
+ rdoc.title = "Rack::AcceptMediaTypes"
40
+ rdoc.options << '--line-numbers' << '--inline-source'
41
+ rdoc.options << '--charset' << 'utf-8'
42
+ rdoc.rdoc_files.include('README')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ }
45
+
46
+ Rake::GemPackageTask.new(spec) do |p|
47
+ p.gem_spec = spec
48
+ end
49
+
50
+ desc "Remove package products"
51
+ task :clean => :clobber_package
52
+
53
+ desc "Update the gemspec for GitHub's gem server"
54
+ task :gemspec do
55
+ Pathname("#{spec.name}.gemspec").open('w') {|f| f << YAML.dump(spec) }
56
+ end
57
+
58
+ desc "Install gem"
59
+ task :install => [:clobber, :package] do
60
+ sh "#{SUDO} #{gem} install pkg/#{spec.full_name}.gem"
61
+ end
62
+
63
+ desc "Uninstall gem"
64
+ task :uninstall => :clean do
65
+ sh "#{SUDO} #{gem} uninstall -v #{spec.version} -x #{spec.name}"
66
+ end
@@ -0,0 +1,94 @@
1
+ module Rack
2
+
3
+ # AcceptMediaTypes is intended for wrapping env['HTTP_ACCEPT'].
4
+ #
5
+ # It allows ordering of its values (accepted media types) according to their
6
+ # "quality" (preference level).
7
+ #
8
+ # This wrapper is typically used to determine the request's prefered media
9
+ # type (see example below).
10
+ #
11
+ # ===== Examples
12
+ #
13
+ # env['HTTP_ACCEPT'] #=> 'text/html,application/xml;q=0.8,text/plain;0.9'
14
+ #
15
+ # types = Rack::AcceptMediaTypes.new(env['HTTP_ACCEPT'])
16
+ # types #=> ['text/html', 'text/plain', 'application/xml']
17
+ # types.prefered #=> 'text/html'
18
+ #
19
+ # ===== Notes
20
+ #
21
+ # For simplicity, media type parameters are striped, as they are seldom used
22
+ # in practice. Users who need them are excepted to parse the Accept header
23
+ # manually.
24
+ #
25
+ # ===== References
26
+ #
27
+ # HTTP 1.1 Specs:
28
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
29
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9
30
+ #
31
+ class AcceptMediaTypes < Array
32
+
33
+ def initialize(header)
34
+ replace(order(header.split(',')))
35
+ end
36
+
37
+ # The client's prefered media type.
38
+ def prefered
39
+ first
40
+ end
41
+
42
+ private
43
+
44
+ # Order media types by quality values, remove invalid types, and return media ranges.
45
+ #
46
+ def order(types) #:nodoc:
47
+ types.map {|type| AcceptMediaType.new(type) }.reverse.sort.reverse.select {|type| type.valid? }.map {|type| type.range }
48
+ end
49
+
50
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
51
+ #
52
+ class AcceptMediaType #:nodoc:
53
+ include Comparable
54
+
55
+ # media-range = ( "*/*"
56
+ # | ( type "/" "*" )
57
+ # | ( type "/" subtype )
58
+ # ) *( ";" parameter )
59
+ attr_accessor :range
60
+
61
+ # qvalue = ( "0" [ "." 0*3DIGIT ] )
62
+ # | ( "1" [ "." 0*3("0") ] )
63
+ attr_accessor :quality
64
+
65
+ def initialize(type)
66
+ self.range, *params = type.split(';')
67
+ self.quality = extract_quality(params)
68
+ end
69
+
70
+ def <=>(type)
71
+ self.quality <=> type.quality
72
+ end
73
+
74
+ # "A weight is normalized to a real number in the range 0 through 1,
75
+ # where 0 is the minimum and 1 the maximum value. If a parameter has a
76
+ # quality value of 0, then content with this parameter is `not
77
+ # acceptable' for the client."
78
+ #
79
+ def valid?
80
+ self.quality.between?(0.1, 1)
81
+ end
82
+
83
+ private
84
+ # Extract value from 'q=FLOAT' parameter if present, otherwise assume 1
85
+ #
86
+ # "The default value is q=1."
87
+ #
88
+ def extract_quality(params)
89
+ q = params.detect {|p| p.match(/q=\d\.?\d{0,3}/) }
90
+ q ? q.split('=').last.to_f : 1.0
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-accept-media-types
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.5"
5
+ platform: ruby
6
+ authors:
7
+ - Martin Aumont
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-05 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Rack convenience middleware for simplified handling of Accept header.
17
+ email: mynyml@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - test
27
+ - test/test_accept_media_types.rb
28
+ - test/test_helper.rb
29
+ - rack-accept-media-types.gemspec
30
+ - lib
31
+ - lib/rack
32
+ - lib/rack/accept_media_types.rb
33
+ - LICENSE
34
+ - README
35
+ has_rdoc: true
36
+ homepage: ""
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.3
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Rack convenience middleware for simplified handling of Accept header.
63
+ test_files: []
64
+
@@ -0,0 +1,76 @@
1
+ require 'test/test_helper'
2
+
3
+ Accept = Rack::AcceptMediaTypes
4
+
5
+ class AcceptMediaTypesTest < Test::Unit::TestCase
6
+
7
+ test "media type list" do
8
+ header = 'text/html,text/plain'
9
+ assert_equal %w( text/html text/plain ).to_set, Accept.new(header).to_set
10
+ end
11
+
12
+ test "ordered by quality value (highest first)" do
13
+ header = 'text/html;q=0.5,text/plain;q=0.9'
14
+ assert_equal %w( text/plain text/html ), Accept.new(header)
15
+ end
16
+
17
+ test "default quality value is 1" do
18
+ header = 'text/plain;q=0.1,text/html'
19
+ assert_equal %w( text/html text/plain ), Accept.new(header)
20
+ end
21
+
22
+ test "equal quality types keep original order" do
23
+ header = 'text/html,text/plain;q=0.9,application/xml'
24
+ assert_equal %w( text/html application/xml text/plain ), Accept.new(header)
25
+ end
26
+
27
+ test "prefered type" do
28
+ header = 'text/html;q=0.2,text/plain;q=0.5'
29
+ assert_equal 'text/plain', Accept.new(header).prefered
30
+ end
31
+
32
+ test "types with out of range quality values are ignored" do
33
+ header = 'text/html,text/plain;q=1.1'
34
+ assert_equal %w( text/html ), Accept.new(header)
35
+
36
+ header = 'text/html,text/plain;q=0'
37
+ assert_equal %w( text/html ), Accept.new(header)
38
+ end
39
+
40
+ test "custom media types are NOT ignored" do
41
+ # verifying that the media types exist in Rack::Mime::MIME_TYPES is
42
+ # explicitly outside the scope of this library.
43
+ header = 'application/x-custom'
44
+ assert_equal %w( application/x-custom ), Accept.new(header)
45
+ end
46
+
47
+ test "media-range parameters are discarted" do
48
+ header = 'text/html;version=5;q=0.5,text/plain'
49
+ assert_equal %w( text/plain text/html ), Accept.new(header)
50
+ end
51
+
52
+ test "accept-extension parameters are discarted" do
53
+ header = 'text/html;q=0.5;token=value,text/plain'
54
+ assert_equal %w( text/plain text/html ), Accept.new(header)
55
+ end
56
+ end
57
+
58
+ __END__
59
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
60
+ 14.1 Accept
61
+
62
+ Accept = "Accept" ":"
63
+ #( media-range [ accept-params ] )
64
+
65
+ media-range = ( "*/*"
66
+ | ( type "/" "*" )
67
+ | ( type "/" subtype )
68
+ ) *( ";" parameter )
69
+ accept-params = ";" "q" "=" qvalue *( accept-extension )
70
+ accept-extension = ";" token [ "=" ( token | quoted-string ) ]
71
+
72
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9
73
+ 3.9 Quality Values
74
+
75
+ qvalue = ( "0" [ "." 0*3DIGIT ] )
76
+ | ( "1" [ "." 0*3("0") ] )
@@ -0,0 +1,23 @@
1
+ require 'pathname'
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'rack'
5
+ begin
6
+ require 'ruby-debug'
7
+ require 'phocus/test_unit'
8
+ require 'pending'
9
+ rescue LoadError, RuntimeError
10
+ end
11
+
12
+ root = Pathname(__FILE__).dirname.parent.expand_path
13
+ $:.unshift(root.join('lib'))
14
+
15
+ require 'rack/accept_media_types'
16
+
17
+ class Test::Unit::TestCase
18
+ def self.test(name, &block)
19
+ name = :"test_#{name.gsub(/\s/,'_')}"
20
+ define_method(name, &block)
21
+ end
22
+ end
23
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mynyml-rack-accept-media-types
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.5"
5
+ platform: ruby
6
+ authors:
7
+ - Martin Aumont
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-04 21:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Rack convenience middleware for simplified handling of Accept header.
17
+ email: mynyml@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - test
27
+ - test/test_accept_media_types.rb
28
+ - test/test_helper.rb
29
+ - rack-accept-media-types.gemspec
30
+ - lib
31
+ - lib/rack
32
+ - lib/rack/accept_media_types.rb
33
+ - LICENSE
34
+ - README
35
+ has_rdoc: true
36
+ homepage: ""
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Rack convenience middleware for simplified handling of Accept header.
61
+ test_files: []
62
+