user_agent_parser 0.1.2 → 2.1.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.
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 [](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
|