al 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2fa42cadb858988b7a7833de0e82584a1a396023
4
+ data.tar.gz: 9e455e216ad7176e2529ffff67a74d7f83efc3c2
5
+ SHA512:
6
+ metadata.gz: 6f6bf956fe3dcd1b41cd41521eb04898631c5ab8252c86a1cf8c1abf33dbd7f9b2f317f5f9b8a9b7c806298a09bfca171f26185009c9f877f6f08cedf3f6a5ae
7
+ data.tar.gz: 6fbd3ad62290013dbd3216aed8112f5df4ac38fa65d4e0f5e1ed7dd2e5c0698837412183395f6d872c5a68d9ba4c5bd3ee1498def9b19bcbc7ef99e7f0119fcd
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2015 Jip van Reijsen
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,70 @@
1
+ # Al
2
+
3
+ Al stands for `Accept-Language`
4
+
5
+ ## What does Al do?
6
+
7
+ Al picks the best match from an `Accept-Language` header given a set of
8
+ available languages.
9
+
10
+ * Al respects quality values
11
+ * Al respects language tag prefixes
12
+ * Al ignores character case
13
+ * Al ignores spaces (but not other whitespace)
14
+ * Al makes an effort to put up with invalid `Accept-Language` headers
15
+ * Al is fast!
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ require "al"
21
+
22
+ # Define the available languages and their values
23
+ al = Al.new("en", :greeting => "Hello")
24
+ al["en-GB"] = { :greeting => "How do you do?" }
25
+ al["en-US"] = { :greeting => "Howdy partner!" }
26
+
27
+ # Let Al make a sensible match
28
+ lang, match = al.pick("de-DE;q=0.5, de-AT;q=0.4, en-AU")
29
+ puts lang # => "en"
30
+ puts match # => { :greeting => "Hello" }
31
+
32
+ lang, match = al.pick("en-AU;q=0.4, en-US;q=0.5")
33
+ puts lang # => "en-us"
34
+ puts match # => { :greeting => "Howdy partner!" }
35
+
36
+ lang, match = al.pick("da")
37
+ puts lang # => "en"
38
+ puts match # => { :greeting => "Hello" }
39
+ ```
40
+
41
+ The default language is optional. Without a default language Al returns `[nil,
42
+ nil]` if it can't find a match.
43
+
44
+ ## Al and [RFC 2616]
45
+
46
+ RFC 2616 says a more specific language tag than the requested language tag can
47
+ be used if an exactly matching language tag is not available. The reverse is
48
+ illegal. This means that `en` is not a match when `en-GB` is requested, but
49
+ `en-GB` is a match when `en` is requested.
50
+
51
+ Al thinks this is silly.
52
+
53
+ The `#pick` method will ignore this rule and treat `en` as a valid match when
54
+ `en-GB` is requested. It otherwise follows RFC 2616. A `#strict_pick` method is
55
+ provided which follows RFC 2616 exactly.
56
+
57
+ [RFC 2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
58
+
59
+ ## Installation
60
+
61
+ `gem install al`
62
+
63
+ ## Tests
64
+
65
+ To be able to run the tests install Al with development dependencies (`gem
66
+ install al --development`). Then run `rake test`.
67
+
68
+ ## License
69
+
70
+ See the LICENSE file.
data/lib/al.rb ADDED
@@ -0,0 +1,119 @@
1
+ class Al
2
+ def initialize(tag = nil, val = nil)
3
+ @languages = { DEFAULT => [tag, val] }
4
+
5
+ if tag && val
6
+ self[tag] = val
7
+ end
8
+ end
9
+
10
+ def []=(tag, val)
11
+ tag = preprocess(tag)
12
+
13
+ tag.split(DASH).each_with_object("") do |bit, key|
14
+ key.concat("-#{bit}")
15
+ @languages[key[1..-1]] ||= [tag, val]
16
+ end
17
+
18
+ @languages[tag] = [tag, val] # ensure xx overwrites a previous xx-yy
19
+ end
20
+
21
+ def [](tag)
22
+ @languages[preprocess(tag)][1]
23
+ end
24
+
25
+ def pick(header)
26
+ unless header
27
+ return @languages[DEFAULT]
28
+ end
29
+
30
+ high = 0.0
31
+ best = nil
32
+
33
+ preprocess(header).split(COMMA).each do |tag|
34
+ len = tag.size
35
+ sem = tag.index(SEMICOLON) || len
36
+ pos = sem
37
+
38
+ while pos
39
+ attempt = tag[0, pos]
40
+ result = @languages[attempt]
41
+
42
+ if result
43
+ quality = quality(tag, sem, len)
44
+
45
+ if quality > high
46
+ high = quality
47
+ best = result
48
+
49
+ if high >= 1.0
50
+ return best
51
+ end
52
+
53
+ break
54
+ end
55
+ end
56
+
57
+ pos = tag.rindex(DASH, -(len - pos) - 1)
58
+ end
59
+ end
60
+
61
+ return best || @languages[DEFAULT]
62
+ end
63
+
64
+ def strict_pick(header)
65
+ unless header
66
+ return @languages[DEFAULT]
67
+ end
68
+
69
+ high = 0.0
70
+ best = nil
71
+
72
+ preprocess(header).split(COMMA).each do |tag|
73
+ sem = tag.index(SEMICOLON) || tag.size
74
+ result = @languages[tag[0, sem]]
75
+
76
+ if result
77
+ len = tag.size
78
+ sem = tag.index(SEMICOLON) || len
79
+ quality = quality(tag, sem, len)
80
+
81
+ if quality > high
82
+ high = quality
83
+ best = result
84
+
85
+ if high >= 1.0
86
+ return best
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ return best || @languages[DEFAULT]
93
+ end
94
+
95
+ private
96
+
97
+ SPACE = " ".freeze
98
+ EMPTY = "".freeze
99
+
100
+ def preprocess(s)
101
+ result = s.tr(SPACE, EMPTY)
102
+ result.downcase!
103
+
104
+ return result
105
+ end
106
+
107
+ def quality(tag, sem, len)
108
+ q = tag[sem + 3, 10]
109
+
110
+ return q ? Float(q) : 1.0
111
+ rescue ArgumentError
112
+ 0.0
113
+ end
114
+
115
+ DEFAULT = "*".freeze
116
+ COMMA = ",".freeze
117
+ SEMICOLON = ";".freeze
118
+ DASH = "-".freeze
119
+ end
@@ -0,0 +1,62 @@
1
+ require "al"
2
+ require "cutest"
3
+
4
+ test "no input" do
5
+ al = Al.new("en-gb", "british english")
6
+
7
+ assert_equal al.pick(nil), ["en-gb", "british english"]
8
+ end
9
+
10
+ test "case sensitivity" do |al|
11
+ al = Al.new
12
+ al["dA"] = 1
13
+ al["en-GB"] = 2
14
+ al["En"] = 3
15
+
16
+ assert_equal al.pick("da, en-gb;q=0.8, en;q=0.7"), ["da", 1]
17
+ assert_equal al.pick("Da;Q=0.1, EN-gb;q=0.3, eN;q=0.2"), ["en-gb", 2]
18
+ end
19
+
20
+ test "spaces" do
21
+ al = Al.new
22
+ al["da"] = 1
23
+ al["en-gb"] = 2
24
+ al["en"] = 3
25
+
26
+ assert_equal al.pick("da,en-gb;q=0.8,en;q=0.7"), ["da", 1]
27
+ assert_equal al.pick("da;q=0.1,en-gb;q=0.3,en;q=0.2"), ["en-gb", 2]
28
+
29
+ al = Al.new
30
+ al["da"] = 1
31
+ al["en-gb"] = 2
32
+ al["en"] = 3
33
+
34
+ assert_equal al.pick("da, en-gb;q =0.8,en ; q= 0.7"), ["da", 1]
35
+ assert_equal al.pick("da;q=0.1,en - gb ;q= 0. 3, e n;q=0.2"), ["en-gb", 2]
36
+ end
37
+
38
+ test "no language tags" do
39
+ al = Al.new
40
+ al["da"] = 2
41
+
42
+ assert_equal al.pick(";q=0.8, da;q=0.7"), ["da", 2]
43
+ end
44
+
45
+ test "numeric sub tags" do
46
+ al = Al.new
47
+ al["es-419"] = "latin american spanish"
48
+
49
+ assert_equal al.pick("es-419;q=0.7"), ["es-419", "latin american spanish"]
50
+ end
51
+
52
+ test "invalid quality values" do
53
+ al = Al.new
54
+ al["en-gb"] = "british english"
55
+ al["en-us"] = "american english"
56
+
57
+ assert_equal al.pick("en-gb;q, en-us;q=0.8"), ["en-gb", "british english"]
58
+ assert_equal al.pick("en-gb;q=, en-us;q=0.8"), ["en-us", "american english"]
59
+ assert_equal al.pick("en-gb;q=., en-us;q=0.8"), ["en-us", "american english"]
60
+ assert_equal al.pick("en-gb;q=.9, en-us;q=0.8"), ["en-gb", "british english"]
61
+ assert_equal al.pick("en-gb;q=aaa, en-us;q=0.8"), ["en-us", "american english"]
62
+ end
data/test/pick_test.rb ADDED
@@ -0,0 +1,50 @@
1
+ require "al"
2
+ require "cutest"
3
+
4
+ test "exact match" do
5
+ al = Al.new
6
+ al["en-gb"] = "british english"
7
+
8
+ assert_equal al.pick("en-gb"), ["en-gb", "british english"]
9
+ end
10
+
11
+ test "less specific match" do
12
+ al = Al.new
13
+ al["en"] = "english"
14
+
15
+ assert_equal al.pick("en-gb"), ["en", "english"]
16
+ end
17
+
18
+ test "more specific match" do
19
+ al = Al.new
20
+ al["en-gb"] = "british english"
21
+
22
+ assert_equal al.pick("en"), ["en-gb", "british english"]
23
+ end
24
+
25
+ test "quality values" do |al|
26
+ al = Al.new
27
+ al["da"] = "danish"
28
+ al["en-gb"] = "british english"
29
+ al["en"] = "english"
30
+
31
+ assert_equal al.pick("da, en-gb;q=0.8, en;q=0.7"), ["da", "danish"]
32
+ assert_equal al.pick("da;q=0.1, en-gb;q=0.3, en;q=0.2"), ["en-gb", "british english"]
33
+ end
34
+
35
+ test "default value" do
36
+ al = Al.new
37
+
38
+ assert_equal al.pick("*"), [nil, nil]
39
+ assert_equal al.pick("da"), [nil, nil]
40
+
41
+ al = Al.new("en", "english")
42
+
43
+ assert_equal al.pick("*"), ["en", "english"]
44
+ assert_equal al.pick("da"), ["en", "english"]
45
+
46
+ al = Al.new("en", "english")
47
+ al["en-GB"] = "british english"
48
+
49
+ assert_equal al.pick("en-us"), ["en", "english"]
50
+ end
@@ -0,0 +1,62 @@
1
+ require "al"
2
+ require "cutest"
3
+
4
+ test "no input" do
5
+ al = Al.new("en-gb", "british english")
6
+
7
+ assert_equal al.strict_pick(nil), ["en-gb", "british english"]
8
+ end
9
+
10
+ test "case sensitivity" do |al|
11
+ al = Al.new
12
+ al["dA"] = 1
13
+ al["en-GB"] = 2
14
+ al["En"] = 3
15
+
16
+ assert_equal al.strict_pick("da, en-gb;q=0.8, en;q=0.7"), ["da", 1]
17
+ assert_equal al.strict_pick("Da;Q=0.1, EN-gb;q=0.3, eN;q=0.2"), ["en-gb", 2]
18
+ end
19
+
20
+ test "spaces" do
21
+ al = Al.new
22
+ al["da"] = 1
23
+ al["en-gb"] = 2
24
+ al["en"] = 3
25
+
26
+ assert_equal al.strict_pick("da,en-gb;q=0.8,en;q=0.7"), ["da", 1]
27
+ assert_equal al.strict_pick("da;q=0.1,en-gb;q=0.3,en;q=0.2"), ["en-gb", 2]
28
+
29
+ al = Al.new
30
+ al["da"] = 1
31
+ al["en-gb"] = 2
32
+ al["en"] = 3
33
+
34
+ assert_equal al.strict_pick("da, en-gb;q =0.8,en ; q= 0.7"), ["da", 1]
35
+ assert_equal al.strict_pick("da;q=0.1,en - gb ;q= 0. 3, e n;q=0.2"), ["en-gb", 2]
36
+ end
37
+
38
+ test "no language tags" do
39
+ al = Al.new
40
+ al["da"] = 2
41
+
42
+ assert_equal al.strict_pick(";q=0.8, da;q=0.7"), ["da", 2]
43
+ end
44
+
45
+ test "numeric sub tags" do
46
+ al = Al.new
47
+ al["es-419"] = "latin american spanish"
48
+
49
+ assert_equal al.strict_pick("es-419;q=0.7"), ["es-419", "latin american spanish"]
50
+ end
51
+
52
+ test "invalid quality values" do
53
+ al = Al.new
54
+ al["en-gb"] = "british english"
55
+ al["en-us"] = "american english"
56
+
57
+ assert_equal al.strict_pick("en-gb;q, en-us;q=0.8"), ["en-gb", "british english"]
58
+ assert_equal al.strict_pick("en-gb;q=, en-us;q=0.8"), ["en-us", "american english"]
59
+ assert_equal al.strict_pick("en-gb;q=., en-us;q=0.8"), ["en-us", "american english"]
60
+ assert_equal al.strict_pick("en-gb;q=.9, en-us;q=0.8"), ["en-gb", "british english"]
61
+ assert_equal al.strict_pick("en-gb;q=aaa, en-us;q=0.8"), ["en-us", "american english"]
62
+ end
@@ -0,0 +1,50 @@
1
+ require "al"
2
+ require "cutest"
3
+
4
+ test "exact match" do
5
+ al = Al.new
6
+ al["en-gb"] = "british english"
7
+
8
+ assert_equal al.strict_pick("en-gb"), ["en-gb", "british english"]
9
+ end
10
+
11
+ test "less specific match" do
12
+ al = Al.new
13
+ al["en"] = "english"
14
+
15
+ assert_equal al.strict_pick("en-gb"), [nil, nil]
16
+ end
17
+
18
+ test "more specific match" do
19
+ al = Al.new
20
+ al["en-gb"] = "british english"
21
+
22
+ assert_equal al.strict_pick("en"), ["en-gb", "british english"]
23
+ end
24
+
25
+ test "quality values" do |al|
26
+ al = Al.new
27
+ al["da"] = "danish"
28
+ al["en-gb"] = "british english"
29
+ al["en"] = "english"
30
+
31
+ assert_equal al.strict_pick("da, en-gb;q=0.8, en;q=0.7"), ["da", "danish"]
32
+ assert_equal al.strict_pick("da;q=0.1, en-gb;q=0.3, en;q=0.2"), ["en-gb", "british english"]
33
+ end
34
+
35
+ test "default value" do
36
+ al = Al.new
37
+
38
+ assert_equal al.strict_pick("*"), [nil, nil]
39
+ assert_equal al.strict_pick("da"), [nil, nil]
40
+
41
+ al = Al.new("en", "english")
42
+
43
+ assert_equal al.strict_pick("*"), ["en", "english"]
44
+ assert_equal al.strict_pick("da"), ["en", "english"]
45
+
46
+ al = Al.new("en", "english")
47
+ al["en-GB"] = "british english"
48
+
49
+ assert_equal al.pick("en-us"), ["en", "english"]
50
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: al
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jip van Reijsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cutest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.2
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.2
33
+ description: Al picks the best match from an `Accept-Language` header given a set
34
+ of available languages.
35
+ email: jipvanreijsen@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - LICENSE
41
+ - README.md
42
+ - lib/al.rb
43
+ - test/parse_test.rb
44
+ - test/pick_test.rb
45
+ - test/strict_parse_test.rb
46
+ - test/strict_pick_test.rb
47
+ homepage: https://github.com/britishtea/al
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.4.5
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Al stands for Accept-Language
71
+ test_files:
72
+ - test/parse_test.rb
73
+ - test/pick_test.rb
74
+ - test/strict_parse_test.rb
75
+ - test/strict_pick_test.rb
76
+ has_rdoc: