user_agent_parser 0.1.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of user_agent_parser might be problematic. Click here for more details.
- checksums.yaml +15 -0
- data/Readme.md +34 -31
- data/lib/user_agent_parser.rb +4 -15
- data/lib/user_agent_parser/device.rb +23 -0
- data/lib/user_agent_parser/operating_system.rb +20 -18
- data/lib/user_agent_parser/parser.rb +106 -49
- data/lib/user_agent_parser/user_agent.rb +20 -15
- data/lib/user_agent_parser/version.rb +30 -15
- data/vendor/ua-parser/regexes.yaml +388 -139
- metadata +7 -11
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDY2ZWUzN2RkZmIzYWU2NDcwZmM2Zjk0OTUxMGE3ZDVmNTZjN2ZkNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NWE4ODE2OTY3NjIwOGY2MWYxY2E3YmEzMjgxYWIxYzU3YTJiZGYxZQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MGJhMmVmNjMxMDk0ZWUzNDM1MDlmM2NmY2JlNDk4MTVjYzEyNjIxZGFjMDli
|
10
|
+
MWYyMzg1ZTIwZTdkNzIxNmRjZTI3NTMyNWU1YzFhNDE1ZGMxZjM5MTZjMTYw
|
11
|
+
ZGM3NWFmODMyY2NkZWRiMjkwNTcyODUwY2ZhZWM4Njg4YTk4ZDc=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZWU1ZjE4YTQ1MmFiNjZlZGE0NGQzOTQ2ODQ2OGEwZmE0MTBhYWZkOGIzZjE5
|
14
|
+
ZWQwYjJjZDI1MGJmMWMyMDVlYTE4YzkxNmRhZjJlMDYyNGQ2YWEzY2IyYTE0
|
15
|
+
ZmQ4MjM4ZjlhOTA2MDQ1MGZiZGVjMmEwNjAwMjE4OTIxN2Q1Zjk=
|
data/Readme.md
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# UserAgentParser [![Build Status](https://secure.travis-ci.org/toolmantim/user_agent_parser.png?branch=master)](http://travis-ci.org/toolmantim/user_agent_parser)
|
2
2
|
|
3
|
-
UserAgentParser is a simple, comprehensive Ruby gem for parsing user agent strings. It uses [BrowserScope](http://www.browserscope.org/)'s parsing patterns
|
3
|
+
UserAgentParser is a simple, comprehensive Ruby gem for parsing user agent strings. It uses [BrowserScope](http://www.browserscope.org/)'s [parsing patterns](https://github.com/tobie/ua-parser).
|
4
4
|
|
5
5
|
## Requirements
|
6
6
|
|
7
|
-
* Ruby >= 1.
|
8
|
-
|
9
|
-
Note: Ruby 1.8.7 is not supported due to the requirement for the newer psych YAML parser. If you can get it working on 1.8.7 please send a pull request.
|
7
|
+
* Ruby >= 1.8.7
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
@@ -19,49 +17,48 @@ $ gem install user_agent_parser
|
|
19
17
|
```ruby
|
20
18
|
require 'user_agent_parser'
|
21
19
|
=> true
|
22
|
-
|
20
|
+
user_agent = UserAgentParser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
|
23
21
|
=> #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
|
24
|
-
|
25
|
-
=> "IE 9.0
|
26
|
-
|
22
|
+
user_agent.to_s
|
23
|
+
=> "IE 9.0"
|
24
|
+
user_agent.name
|
27
25
|
=> "IE"
|
28
|
-
|
26
|
+
user_agent.version.to_s
|
29
27
|
=> "9.0"
|
30
|
-
|
31
|
-
=> 9
|
32
|
-
|
33
|
-
=> 0
|
34
|
-
|
28
|
+
user_agent.version.major
|
29
|
+
=> "9"
|
30
|
+
user_agent.version.minor
|
31
|
+
=> "0"
|
32
|
+
operating_system = user_agent.os
|
33
|
+
=> #<UserAgentParser::OperatingSystem Windows Vista>
|
34
|
+
operating_system.to_s
|
35
35
|
=> "Windows Vista"
|
36
|
+
|
37
|
+
# The parser database will be loaded and parsed on every call to
|
38
|
+
# UserAgentParser.parse. To avoid this, instantiate your own Parser instance.
|
39
|
+
parser = UserAgentParser::Parser.new
|
40
|
+
parser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
|
41
|
+
=> #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
|
42
|
+
parser.parse 'Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.5.24 Version/10.53'
|
43
|
+
=> #<UserAgentParser::UserAgent Opera 10.53 (Windows XP)>
|
36
44
|
```
|
37
45
|
|
38
46
|
## The pattern database
|
39
47
|
|
40
48
|
The [ua-parser database](https://github.com/tobie/ua-parser/blob/master/regexes.yaml) is included via a [git submodule](http://help.github.com/submodules/). To update the database the submodule needs to be updated and the gem re-released (pull requests for this are very welcome!).
|
41
49
|
|
42
|
-
You can also specify the path to your own, updated and/or customised `regexes.yaml` file
|
50
|
+
You can also specify the path to your own, updated and/or customised `regexes.yaml` file as a second argument to `UserAgentParser.parse`:
|
43
51
|
|
44
52
|
```ruby
|
45
|
-
UserAgentParser.patterns_path
|
53
|
+
UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
|
46
54
|
```
|
47
55
|
|
48
|
-
|
56
|
+
or when instantiating a `UserAgentParser::Parser`:
|
49
57
|
|
50
|
-
```
|
51
|
-
|
52
|
-
...
|
53
|
-
|
54
|
-
Finished tests in 144.220280s, 89.0027 tests/s, 234.9739 assertions/s.
|
55
|
-
|
56
|
-
12836 tests, 33888 assertions, 0 failures, 0 errors, 0 skips
|
58
|
+
```ruby
|
59
|
+
UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string)
|
57
60
|
```
|
58
61
|
|
59
|
-
## Limitations
|
60
|
-
|
61
|
-
Chrome Frame detection is not yet included, but once [ua-parser issue #14](https://github.com/tobie/ua-parser/issues/14) is resolved this gem will be updated along with it.
|
62
|
-
|
63
|
-
There's no support for providing overrides from Javascript user agent detection like there is with original BrowserScope library. The Javascript overrides were only necessary for detecting IE 9 Platform Preview and older versions of [Chrome Frame](https://developers.google.com/chrome/chrome-frame/).
|
64
|
-
|
65
62
|
## Contributing
|
66
63
|
|
67
64
|
1. Fork
|
@@ -71,6 +68,12 @@ There's no support for providing overrides from Javascript user agent detection
|
|
71
68
|
|
72
69
|
All accepted pull requests will earn you commit and release rights.
|
73
70
|
|
71
|
+
## Releasing a new version
|
72
|
+
|
73
|
+
1. Update the version in user_agent_parser.gemspec
|
74
|
+
2. `git commit user_agent_parser.gemspec -m 'Version bump'`
|
75
|
+
3. `rake release`
|
76
|
+
|
74
77
|
## License
|
75
78
|
|
76
|
-
MIT
|
79
|
+
MIT
|
data/lib/user_agent_parser.rb
CHANGED
@@ -2,24 +2,13 @@ require 'user_agent_parser/parser'
|
|
2
2
|
require 'user_agent_parser/user_agent'
|
3
3
|
require 'user_agent_parser/version'
|
4
4
|
require 'user_agent_parser/operating_system'
|
5
|
+
require 'user_agent_parser/device'
|
5
6
|
|
6
7
|
module UserAgentParser
|
7
|
-
|
8
|
-
# Path to the ua-parser regexes pattern database
|
9
|
-
def self.patterns_path
|
10
|
-
@patterns_path
|
11
|
-
end
|
12
|
-
|
13
|
-
# Sets the path to the ua-parser regexes pattern database
|
14
|
-
def self.patterns_path=(path)
|
15
|
-
@patterns_path = path
|
16
|
-
end
|
17
|
-
|
18
|
-
self.patterns_path = File.join(File.dirname(__FILE__), "../vendor/ua-parser/regexes.yaml")
|
8
|
+
DefaultPatternsPath = File.join(File.dirname(__FILE__), "../vendor/ua-parser/regexes.yaml")
|
19
9
|
|
20
10
|
# Parse the given +user_agent_string+, returning a +UserAgent+
|
21
|
-
def self.parse
|
22
|
-
Parser.new.parse
|
11
|
+
def self.parse(user_agent_string, options={})
|
12
|
+
Parser.new(options).parse(user_agent_string)
|
23
13
|
end
|
24
|
-
|
25
14
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module UserAgentParser
|
2
|
+
class Device
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name = nil)
|
6
|
+
@name = name || 'Other'
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
name
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"#<#{self.class} #{to_s}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def eql?(other)
|
18
|
+
self.class.eql?(other.class) && name == other.name
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :==, :eql?
|
22
|
+
end
|
23
|
+
end
|
@@ -1,28 +1,30 @@
|
|
1
1
|
module UserAgentParser
|
2
|
-
|
3
2
|
class OperatingSystem
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
self.version = version
|
3
|
+
attr_reader :name, :version
|
4
|
+
|
5
|
+
def initialize(name = 'Other', version = nil)
|
6
|
+
@name = name
|
7
|
+
@version = version
|
10
8
|
end
|
11
|
-
|
9
|
+
|
12
10
|
def to_s
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
string = name
|
12
|
+
unless version.nil?
|
13
|
+
string += " #{version}"
|
14
|
+
end
|
15
|
+
string
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def inspect
|
19
19
|
"#<#{self.class} #{to_s}>"
|
20
20
|
end
|
21
|
-
|
22
|
-
def
|
23
|
-
|
21
|
+
|
22
|
+
def eql?(other)
|
23
|
+
self.class.eql?(other.class) &&
|
24
|
+
name == other.name &&
|
25
|
+
version == other.version
|
24
26
|
end
|
25
|
-
|
26
|
-
end
|
27
27
|
|
28
|
-
|
28
|
+
alias_method :==, :eql?
|
29
|
+
end
|
30
|
+
end
|
@@ -1,41 +1,49 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
3
|
module UserAgentParser
|
4
|
-
|
4
|
+
|
5
5
|
class Parser
|
6
|
+
attr_reader :patterns_path
|
6
7
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
ua
|
8
|
+
def initialize(options={})
|
9
|
+
@patterns_path = options[:patterns_path] || UserAgentParser::DefaultPatternsPath
|
10
|
+
@ua_patterns, @os_patterns, @device_patterns = load_patterns(patterns_path)
|
11
11
|
end
|
12
|
-
|
13
|
-
private
|
14
12
|
|
15
|
-
def
|
16
|
-
|
13
|
+
def parse(user_agent)
|
14
|
+
os = parse_os(user_agent)
|
15
|
+
device = parse_device(user_agent)
|
16
|
+
parse_ua(user_agent, os, device)
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_patterns(path)
|
22
|
+
yml = YAML.load_file(path)
|
23
|
+
|
24
|
+
# Parse all the regexs
|
25
|
+
yml.each_pair do |type, patterns|
|
26
|
+
patterns.each do |pattern|
|
27
|
+
pattern["regex"] = Regexp.new(pattern["regex"])
|
24
28
|
end
|
25
29
|
end
|
30
|
+
|
31
|
+
[ yml["user_agent_parsers"], yml["os_parsers"], yml["device_parsers"] ]
|
26
32
|
end
|
27
|
-
|
28
|
-
def parse_ua
|
29
|
-
pattern, match = first_pattern_match(
|
33
|
+
|
34
|
+
def parse_ua(user_agent, os = nil, device = nil)
|
35
|
+
pattern, match = first_pattern_match(@ua_patterns, user_agent)
|
36
|
+
|
30
37
|
if match
|
31
|
-
user_agent_from_pattern_match(pattern, match)
|
38
|
+
user_agent_from_pattern_match(pattern, match, os, device)
|
32
39
|
else
|
33
|
-
UserAgent.new
|
40
|
+
UserAgent.new(nil, nil, os, device)
|
34
41
|
end
|
35
42
|
end
|
36
|
-
|
37
|
-
def parse_os
|
38
|
-
pattern, match = first_pattern_match(
|
43
|
+
|
44
|
+
def parse_os(user_agent)
|
45
|
+
pattern, match = first_pattern_match(@os_patterns, user_agent)
|
46
|
+
|
39
47
|
if match
|
40
48
|
os_from_pattern_match(pattern, match)
|
41
49
|
else
|
@@ -43,45 +51,94 @@ module UserAgentParser
|
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
54
|
+
def parse_device(user_agent)
|
55
|
+
pattern, match = first_pattern_match(@device_patterns, user_agent)
|
56
|
+
|
57
|
+
if match
|
58
|
+
device_from_pattern_match(pattern, match)
|
59
|
+
else
|
60
|
+
Device.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def first_pattern_match(patterns, value)
|
65
|
+
patterns.each do |pattern|
|
66
|
+
if match = pattern["regex"].match(value)
|
67
|
+
return [pattern, match]
|
50
68
|
end
|
51
69
|
end
|
52
70
|
nil
|
53
71
|
end
|
54
72
|
|
55
|
-
def user_agent_from_pattern_match
|
56
|
-
|
73
|
+
def user_agent_from_pattern_match(pattern, match, os = nil, device = nil)
|
74
|
+
name, v1, v2, v3, v4 = match[1], match[2], match[3], match[4], match[5]
|
75
|
+
|
57
76
|
if pattern["family_replacement"]
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
77
|
+
name = pattern["family_replacement"].sub('$1', name || '')
|
78
|
+
end
|
79
|
+
|
80
|
+
if pattern["v1_replacement"]
|
81
|
+
v1 = pattern["v1_replacement"].sub('$1', v1 || '')
|
82
|
+
end
|
83
|
+
|
84
|
+
if pattern["v2_replacement"]
|
85
|
+
v2 = pattern["v2_replacement"].sub('$1', v2 || '')
|
86
|
+
end
|
87
|
+
|
88
|
+
if pattern["v3_replacement"]
|
89
|
+
v3 = pattern["v3_replacement"].sub('$1', v3 || '')
|
90
|
+
end
|
91
|
+
|
92
|
+
if pattern["v4_replacement"]
|
93
|
+
v4 = pattern["v4_replacement"].sub('$1', v4 || '')
|
94
|
+
end
|
95
|
+
|
96
|
+
version = version_from_segments(v1, v2, v3, v4)
|
97
|
+
|
98
|
+
UserAgent.new(name, version, os, device)
|
66
99
|
end
|
67
|
-
|
68
|
-
def os_from_pattern_match
|
100
|
+
|
101
|
+
def os_from_pattern_match(pattern, match)
|
69
102
|
os, v1, v2, v3, v4 = match[1], match[2], match[3], match[4], match[5]
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
103
|
+
|
104
|
+
if pattern["os_replacement"]
|
105
|
+
os = pattern["os_replacement"].sub('$1', os || '')
|
106
|
+
end
|
107
|
+
|
108
|
+
if pattern["os_v1_replacement"]
|
109
|
+
v1 = pattern["os_v1_replacement"].sub('$1', v1 || '')
|
110
|
+
end
|
111
|
+
|
112
|
+
if pattern["os_v2_replacement"]
|
113
|
+
v2 = pattern["os_v2_replacement"].sub('$1', v2 || '')
|
114
|
+
end
|
115
|
+
|
116
|
+
if pattern["os_v3_replacement"]
|
117
|
+
v3 = pattern["os_v3_replacement"].sub('$1', v3 || '')
|
118
|
+
end
|
119
|
+
|
120
|
+
if pattern["os_v4_replacement"]
|
121
|
+
v4 = pattern["os_v4_replacement"].sub('$1', v4 || '')
|
122
|
+
end
|
123
|
+
|
124
|
+
version = version_from_segments(v1, v2, v3, v4)
|
125
|
+
|
126
|
+
OperatingSystem.new(os, version)
|
78
127
|
end
|
79
|
-
|
128
|
+
|
129
|
+
def device_from_pattern_match(pattern, match)
|
130
|
+
device = match[1]
|
131
|
+
|
132
|
+
if pattern["device_replacement"]
|
133
|
+
device = pattern["device_replacement"].sub('$1', device || '')
|
134
|
+
end
|
135
|
+
|
136
|
+
Device.new(device)
|
137
|
+
end
|
138
|
+
|
80
139
|
def version_from_segments(*segments)
|
81
140
|
version_string = segments.compact.join(".")
|
82
141
|
version_string.empty? ? nil : Version.new(version_string)
|
83
142
|
end
|
84
|
-
|
85
143
|
end
|
86
|
-
|
87
144
|
end
|
@@ -1,32 +1,37 @@
|
|
1
1
|
module UserAgentParser
|
2
|
-
|
3
2
|
class UserAgent
|
3
|
+
attr_reader :name, :version, :os, :device
|
4
4
|
|
5
|
-
|
5
|
+
# For backwards compatibility with older versions of this gem.
|
6
|
+
alias_method :family, :name
|
6
7
|
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(name = nil, version = nil, os = nil, device = nil)
|
9
|
+
@name = name || 'Other'
|
10
|
+
@version = version
|
11
|
+
@os = os
|
12
|
+
@device = device
|
11
13
|
end
|
12
14
|
|
13
15
|
def to_s
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
s
|
16
|
+
string = name
|
17
|
+
string += " #{version}" if version
|
18
|
+
string
|
18
19
|
end
|
19
20
|
|
20
21
|
def inspect
|
21
|
-
|
22
|
+
string = to_s
|
23
|
+
string += " (#{os})" if os
|
24
|
+
string += " (#{device})" if device
|
25
|
+
"#<#{self.class} #{string}>"
|
22
26
|
end
|
23
|
-
|
24
|
-
def
|
25
|
-
|
27
|
+
|
28
|
+
def eql?(other)
|
29
|
+
self.class.eql?(other.class) &&
|
30
|
+
name == other.name &&
|
26
31
|
version == other.version &&
|
27
32
|
os == other.os
|
28
33
|
end
|
29
34
|
|
35
|
+
alias_method :==, :eql?
|
30
36
|
end
|
31
|
-
|
32
37
|
end
|