scorched-accept 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +7 -0
- data/README.md +18 -0
- data/TODO.md +4 -0
- data/lib/.DS_Store +0 -0
- data/lib/scorched/accept.rb +7 -0
- data/lib/scorched/accept/accept_header.rb +130 -0
- data/lib/scorched/accept/rack.rb +15 -0
- data/scorched-accept.gemspec +19 -0
- data/spec/accept_header_parser_spec.rb +24 -0
- data/spec/accept_header_spec.rb +82 -0
- data/spec/rack_spec.rb +20 -0
- data/spec/spec_helper.rb +4 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 46ea82e8392e2bba39152ce99220a4f0b36e13a1
|
4
|
+
data.tar.gz: 4a38940f1c317f5a4f23f5ca2f10e86f6d66e51e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a33772055e16ad5b267eea0107e6cd3be945543787e65365fedcce77ab7d5b14672813ebc66501011c0a1344526ce6a95ff409d2d2019d420a54be72f564196
|
7
|
+
data.tar.gz: d6ee7e415e5182831764acccd8cc2f1e68c0d345acbc60ce5843933d94b5c2af4c02a9ab861b8d622cf266047b96bd597572cfa576c9a8db1a115ed499bc120a
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
scorched-accept (0.1)
|
5
|
+
parslet (~> 1.7)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
blankslate (3.1.3)
|
11
|
+
maxitest (1.4.1)
|
12
|
+
minitest (>= 5.0.0, < 5.6.0)
|
13
|
+
minitest (5.5.1)
|
14
|
+
parslet (1.7.0)
|
15
|
+
blankslate (>= 2.0, <= 4.0)
|
16
|
+
rack (1.6.0)
|
17
|
+
rack-test (0.6.3)
|
18
|
+
rack (>= 1.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
maxitest (~> 1.4)
|
25
|
+
rack-test (~> 0.6)
|
26
|
+
scorched-accept!
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2015 Tom Wardrop
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
An accept header parser and API. Contains nothing Scorched specific, it's only named such because every other obvious
|
2
|
+
name has been used by the other libraries already in existence, none of which actually parse the accept header
|
3
|
+
correctly, or implement proper prioritisation of media ranges.
|
4
|
+
|
5
|
+
Example
|
6
|
+
-------
|
7
|
+
```ruby
|
8
|
+
accept_header = AcceptHeader.new('text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8')
|
9
|
+
accept_header.best_of('application/json', 'video/mp4')
|
10
|
+
```
|
11
|
+
|
12
|
+
Refer to specs and API documentation for more information.
|
13
|
+
|
14
|
+
Tests
|
15
|
+
-----
|
16
|
+
```bash
|
17
|
+
mtest spec
|
18
|
+
```
|
data/TODO.md
ADDED
data/lib/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
require 'parslet'
|
3
|
+
|
4
|
+
module Scorched
|
5
|
+
module Accept
|
6
|
+
DEFAULT_QVALUE = 1
|
7
|
+
|
8
|
+
class AcceptHeader
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def initialize(raw_str = nil)
|
12
|
+
@raw_str = (raw_str && not(raw_str =~ /^\s*$/)) ? raw_str : '*/*'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns true if the given media type is acceptable.
|
16
|
+
def acceptable?(media_type)
|
17
|
+
!matches(media_type).empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Ranks the supplied media type based on its appropriatness. Scores range from 0 (the most appropriate), to
|
21
|
+
# `n-1`, where `n` is the number of media types defined in the accept header of the request. If _invert_ is true,
|
22
|
+
# `n-1` is returned for the most appropriate match, and 0 for the least appropriate. `nil` is returned if
|
23
|
+
# the media type is unacceptable.
|
24
|
+
def rank(media_type, invert = false)
|
25
|
+
match = matches(media_type).first
|
26
|
+
if(invert)
|
27
|
+
match && length - 1 - match.last
|
28
|
+
else
|
29
|
+
match && match.last
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Of the media types given, returns the most appropriate. If none are appropriate, returns nil. If all media types
|
34
|
+
# are of equal appropriatness, or multiple media types are equal best, the media type that was listed highest in
|
35
|
+
# argument list is returned.
|
36
|
+
def best_of(*media_types)
|
37
|
+
media_types.min_by { |m| rank(m) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a hash with each media range as the key, and the rest of the attributes as the value.
|
41
|
+
# Possible attributes are: :parameters, :q, and :extensions.
|
42
|
+
def to_h
|
43
|
+
@to_h ||= ordered_values.map { |v|
|
44
|
+
v = v.dup
|
45
|
+
media_type = v.delete(:media_type)
|
46
|
+
[media_type, v]
|
47
|
+
}.to_h
|
48
|
+
end
|
49
|
+
|
50
|
+
def values
|
51
|
+
@values ||= AcceptHeaderTransform.new.apply(AcceptHeaderParser.new.parse(@raw_str))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Orders media ranges based on q-value, specificity, and the order they are defined
|
55
|
+
def ordered_values
|
56
|
+
@ordered_values ||= values.sort_by { |m| [m[:q] || DEFAULT_QVALUE, specificity(m)] }.reverse
|
57
|
+
end
|
58
|
+
|
59
|
+
# Enumerabilitisationing
|
60
|
+
def each(*args, &block)
|
61
|
+
ordered_values.each(*args, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the number of media range definitions in the accept header of the request.
|
65
|
+
def length
|
66
|
+
values.length
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def matches(media_type)
|
72
|
+
matched = {}
|
73
|
+
ordered_values.each_with_index do |v,i|
|
74
|
+
matched[v] = i if Regexp.new(v[:media_type].split('/').map { |v| v == '*' ? '.+' : v }.join('/')) =~ media_type
|
75
|
+
end
|
76
|
+
matched
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a number representing how specific the media range definition is. Higher numbers represent a more
|
80
|
+
# specific media range definition.
|
81
|
+
def specificity(m)
|
82
|
+
if m[:media_type] == '*/*' then 0
|
83
|
+
elsif m[:media_type] =~ %r{/\*$} then 1
|
84
|
+
else 2 + (m[:parameters] ? m[:parameters].count : 0)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class AcceptHeaderParser < Parslet::Parser
|
90
|
+
root(:accept)
|
91
|
+
rule(:accept) { (accept_element >> (ows >> str(",") >> accept_element).repeat).as(:accept) >> ows }
|
92
|
+
rule(:accept_element) { ows >> (media_range >> (ows >> accept_params).maybe).as(:accept_element) }
|
93
|
+
rule(:media_range) { (str("*/*") | (token >> str('/') >> str('*').as(:subtype)) | (token >> str("/") >> token.as(:subtype))).as(:media_type) >> (ows >> str(";") >> ows >> parameter).repeat.as(:parameters) }
|
94
|
+
rule(:accept_params) { str(";") >> ows >> str("q=") >> qvalue.as(:q) >> extension.repeat.as(:extensions) }
|
95
|
+
rule(:extension) { ows >> str(";") >> ows >> token.as(:key) >> (str("=") >> (token | quoted_string).as(:value)).maybe }
|
96
|
+
rule(:parameter) { str("q").absent? >> token.as(:key) >> str("=") >> (token | quoted_string).as(:value) }
|
97
|
+
rule(:token) { tchar.repeat(1).as(:token) }
|
98
|
+
rule(:qvalue) { (str("0") >> (str(".") >> digit.repeat(0,3)).maybe | str("1") >> (str(".") >> str("0").repeat(0,3)).maybe).as(:qvalue) }
|
99
|
+
rule(:quoted_string) { str('"') >> (qdtext | quoted_pair).repeat.as(:quoted_string) >> str('"') }
|
100
|
+
rule(:qdtext) { (match["\t !#-\\[\\]-~"] | obs_text).as(:qdtext) }
|
101
|
+
rule(:obs_text) { match["\x80-\xFF"] }
|
102
|
+
rule(:quoted_pair) { (str("\\") >> (match["\t "] | vchar | obs_text)).as(:quoted_pair) }
|
103
|
+
rule(:tchar) { match["!#$%&'*+\\-.^_`|~"] | digit | alpha }
|
104
|
+
rule(:vchar) { match["\x20-\x7E"] }
|
105
|
+
rule(:ows) { match["\t "].repeat }
|
106
|
+
rule(:alpha) { match["a-zA-Z"] }
|
107
|
+
rule(:digit) { match["0-9"] }
|
108
|
+
end
|
109
|
+
|
110
|
+
class AcceptHeaderTransform < Parslet::Transform
|
111
|
+
rule(accept: subtree(:x)) do
|
112
|
+
Array === x ? x : [x]
|
113
|
+
end
|
114
|
+
rule(accept_element: subtree(:x)) do
|
115
|
+
x[:media_type] = (x[:media_type].respond_to? :values) ? x[:media_type].values.join("/") : x[:media_type].to_s
|
116
|
+
x[:parameters] = x[:parameters].reduce(&:merge) if x[:parameters].respond_to? :reduce
|
117
|
+
x[:extensions] = x[:extensions].reduce(&:merge) if x[:extensions].respond_to? :reduce
|
118
|
+
x.delete_if { |k,v| v.nil? }
|
119
|
+
end
|
120
|
+
rule(media_type: subtree(:x)) { x.values.join('/') }
|
121
|
+
rule(key: simple(:k), value: simple(:v)) { {k.to_sym => v} }
|
122
|
+
rule(token: simple(:t)) { t.to_s }
|
123
|
+
rule(quoted_string: sequence(:x)) { x.join }
|
124
|
+
rule(qvalue: simple(:x)) { x.to_f }
|
125
|
+
rule(qdtext: simple(:x)) { x.to_s }
|
126
|
+
rule(quoted_pair: simple(:x)) { x.to_s[1] }
|
127
|
+
rule(ows: simple(:x)) { " " }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Scorched
|
2
|
+
module Accept
|
3
|
+
class Rack
|
4
|
+
def initialize(app, env_prefix = 'scorched')
|
5
|
+
@app = app
|
6
|
+
@env_prefix = env_prefix
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
env["#{@env_prefix}.accept"] = {accept: AcceptHeader.new(env["HTTP_ACCEPT"])}
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new 'scorched-accept', '0.1' do |s|
|
4
|
+
s.summary = 'HTTP accept header parser'
|
5
|
+
s.description = 'A parser for parsing the accept headers of a HTTP request'
|
6
|
+
s.authors = ['Tom Wardrop']
|
7
|
+
s.email = 'tom@tomwardrop.com'
|
8
|
+
s.homepage = 'http://github.com/wardrop/scorched-accept'
|
9
|
+
s.license = 'MIT'
|
10
|
+
s.files = Dir.glob(`git ls-files`.split("\n") - %w[.gitignore])
|
11
|
+
s.test_files = Dir.glob('spec/**/*_spec.rb')
|
12
|
+
s.rdoc_options = %w[--line-numbers --inline-source --title Scorched::Accept --encoding=UTF-8]
|
13
|
+
|
14
|
+
s.required_ruby_version = '>= 2.0.0'
|
15
|
+
|
16
|
+
s.add_dependency 'parslet', '~> 1.7'
|
17
|
+
s.add_development_dependency 'rack-test', '~> 0.6'
|
18
|
+
s.add_development_dependency 'maxitest', '~>1.4'
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#require_relative './spec_helper.rb'
|
2
|
+
require_relative "../lib/scorched/accept/accept_header"
|
3
|
+
require "maxitest/autorun"
|
4
|
+
|
5
|
+
module Scorched
|
6
|
+
module Accept
|
7
|
+
describe AcceptHeaderParser do
|
8
|
+
it "parses correctly" do
|
9
|
+
tree = AcceptHeaderParser.new.parse(' text/plain; name=bob ; age=19; q=0.10; dog="r\o\o\\f\" bark"; cat=meow, */*, video/*')
|
10
|
+
AcceptHeaderTransform.new.apply(tree).must_equal([
|
11
|
+
{media_type: "text/plain", parameters: {name: "bob", age: "19"}, q: 0.1, extensions: {dog: %q{roof" bark}, cat: "meow"}},
|
12
|
+
{media_type: "*/*"},
|
13
|
+
{media_type: "video/*"}
|
14
|
+
])
|
15
|
+
|
16
|
+
lambda { AcceptHeaderParser.new.parse('text/plain; name = bob') }.must_raise Parslet::ParseFailed
|
17
|
+
end
|
18
|
+
|
19
|
+
it "deals with a single media range correctly" do
|
20
|
+
AcceptHeaderTransform.new.apply(AcceptHeaderParser.new.parse('*/*')).must_equal [{:media_type=>"*/*"}]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
|
3
|
+
module Scorched
|
4
|
+
module Accept
|
5
|
+
describe "AcceptHeader" do
|
6
|
+
let_all :long_header do
|
7
|
+
'*/*, text/html;level=3,video/webm;codecs=vp8;videoonly=1, text/*, video/*;q=0.4, application/json, text/plain'
|
8
|
+
end
|
9
|
+
|
10
|
+
let_all :short_header do
|
11
|
+
'text/*;encoding=utf-8;q=1;speed=high, video/mpeg'
|
12
|
+
end
|
13
|
+
|
14
|
+
# How the `long_header` should be once parsed and ordered.
|
15
|
+
let_all :long_header_ordered do
|
16
|
+
%w{
|
17
|
+
video/webm
|
18
|
+
text/html
|
19
|
+
application/json
|
20
|
+
text/plain
|
21
|
+
text/*
|
22
|
+
*/*
|
23
|
+
video/*
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
let :accept_header do
|
28
|
+
AcceptHeader.new(long_header)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "orders media ranges based on qvalue, specificity, and the order they are defined" do
|
32
|
+
accept_header.ordered_values.map { |v| v[:media_type] }.must_equal long_header_ordered
|
33
|
+
end
|
34
|
+
|
35
|
+
it "determines whether a media type is acceptable or not" do
|
36
|
+
accept_header.acceptable?('text/html').must_equal true
|
37
|
+
accept_header.acceptable?('large/dog').must_equal true
|
38
|
+
AcceptHeader.new(short_header).acceptable?('video/mp4').must_equal false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "ranks a media type against list of acceptable media types" do
|
42
|
+
accept_header.rank('text/x-markdown').must_equal long_header_ordered.index('text/*')
|
43
|
+
accept_header.rank('video/mpeg').must_equal long_header_ordered.index('*/*')
|
44
|
+
AcceptHeader.new(short_header).rank('video/mp4').must_equal nil
|
45
|
+
# Test inverting
|
46
|
+
accept_header.rank('text/x-markdown', true).must_equal long_header_ordered.reverse.index('text/*')
|
47
|
+
end
|
48
|
+
|
49
|
+
it "selects the most appropriate media type out of an array" do
|
50
|
+
accept_header.best_of('text/plain', 'text/html').must_equal 'text/html'
|
51
|
+
accept_header.best_of('video/mpeg', 'text/x-markdown', 'cat/meow').must_equal 'text/x-markdown'
|
52
|
+
AcceptHeader.new(short_header).rank('video/mp4').must_equal nil
|
53
|
+
|
54
|
+
# Uses the order of arguments if the provided arguments are equally acceptable.
|
55
|
+
accept_header.best_of('dog/roof', 'cat/meow').must_equal 'dog/roof'
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can return a hash" do
|
59
|
+
AcceptHeader.new(short_header).to_h.must_equal(
|
60
|
+
'video/mpeg' => {},
|
61
|
+
'text/*' => {parameters: {encoding: 'utf-8'}, q: 1, extensions: {speed: 'high'}}
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "is enumerable" do
|
66
|
+
AcceptHeader.include?(Enumerable).must_equal true
|
67
|
+
accept_header.map { |v| v[:media_type] }[-2].must_equal long_header_ordered[-2]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "tells you how many media ranges were parsed" do
|
71
|
+
accept_header.length.must_equal long_header_ordered.length
|
72
|
+
end
|
73
|
+
|
74
|
+
it "gracefully handles not being given an accept header string" do
|
75
|
+
AcceptHeader.new.length.must_equal 1
|
76
|
+
AcceptHeader.new.acceptable?('text/html').must_equal true
|
77
|
+
AcceptHeader.new(nil).length.must_equal 1
|
78
|
+
AcceptHeader.new(' ').length.must_equal 1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/rack_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
require_relative "../lib/scorched/accept"
|
3
|
+
|
4
|
+
module Scorched
|
5
|
+
module Accept
|
6
|
+
describe "Rack" do
|
7
|
+
|
8
|
+
it "adds an AcceptHeader instance to Rack env" do
|
9
|
+
internal_env = nil
|
10
|
+
rt = ::Rack::Test::Session.new(::Rack::Builder.new do
|
11
|
+
internal_env = nil
|
12
|
+
use Scorched::Accept::Rack
|
13
|
+
run proc { |env| internal_env = env; [200, {}, 'ok'] }
|
14
|
+
end)
|
15
|
+
rt.get('/')
|
16
|
+
internal_env['scorched.accept'][:accept].must_be_instance_of Scorched::Accept::AcceptHeader
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: scorched-accept
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Wardrop
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parslet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-test
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: maxitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
55
|
+
description: A parser for parsing the accept headers of a HTTP request
|
56
|
+
email: tom@tomwardrop.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- Gemfile
|
62
|
+
- Gemfile.lock
|
63
|
+
- LICENSE
|
64
|
+
- README.md
|
65
|
+
- TODO.md
|
66
|
+
- lib/.DS_Store
|
67
|
+
- lib/scorched/accept.rb
|
68
|
+
- lib/scorched/accept/accept_header.rb
|
69
|
+
- lib/scorched/accept/rack.rb
|
70
|
+
- scorched-accept.gemspec
|
71
|
+
- spec/accept_header_parser_spec.rb
|
72
|
+
- spec/accept_header_spec.rb
|
73
|
+
- spec/rack_spec.rb
|
74
|
+
- spec/spec_helper.rb
|
75
|
+
homepage: http://github.com/wardrop/scorched-accept
|
76
|
+
licenses:
|
77
|
+
- MIT
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- "--line-numbers"
|
82
|
+
- "--inline-source"
|
83
|
+
- "--title"
|
84
|
+
- Scorched::Accept
|
85
|
+
- "--encoding=UTF-8"
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 2.0.0
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.4.5
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: HTTP accept header parser
|
104
|
+
test_files:
|
105
|
+
- spec/accept_header_parser_spec.rb
|
106
|
+
- spec/accept_header_spec.rb
|
107
|
+
- spec/rack_spec.rb
|