user_agent_parser 1.0.0 → 2.1.1
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 +7 -0
- data/Readme.md +54 -28
- 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 -14
- data/lib/user_agent_parser/version.rb +30 -20
- data/vendor/ua-parser/regexes.yaml +423 -160
- metadata +9 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5e20682e690e1e3552bc54b1ef9849ab8febc3ec
|
4
|
+
data.tar.gz: 8f7578787fc31f82e35909d34c86b1b00594b0b6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 488d4f2b60187907f65ad244786c6babfef042fa16d5a70292a29aac70fda3cbbf7dead070d2c30123b9a81c46c3385e4347994692342d3b127fb5baba01d6dd
|
7
|
+
data.tar.gz: a8213bbf5f3b4fb36665d36b7c401c6ec40acfcacc0b52340bcb36a402b038ea5c658837d449c2cd04ad04142334f080bd2479ae4ae5c7b4fd67f54e49c46b0b
|
data/Readme.md
CHANGED
@@ -4,9 +4,7 @@ UserAgentParser is a simple, comprehensive Ruby gem for parsing user agent strin
|
|
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,46 +17,62 @@ $ 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
|
+
In a larger application, you could store a parser in a global to avoid repeat pattern loading:
|
39
47
|
|
40
|
-
|
48
|
+
```ruby
|
49
|
+
module MyApplication
|
41
50
|
|
42
|
-
|
51
|
+
# Instantiate the parser on load as it's quite expensive
|
52
|
+
USER_AGENT_PARSER = UserAgentParser::Parser.new
|
43
53
|
|
44
|
-
|
45
|
-
|
54
|
+
def self.user_agent_parser
|
55
|
+
USER_AGENT_PARSER
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
46
59
|
```
|
47
60
|
|
48
|
-
##
|
61
|
+
## The pattern database
|
49
62
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
Finished tests in 144.220280s, 89.0027 tests/s, 234.9739 assertions/s.
|
63
|
+
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!).
|
64
|
+
|
65
|
+
You can also specify the path to your own, updated and/or customised `regexes.yaml` file as a second argument to `UserAgentParser.parse`:
|
55
66
|
|
56
|
-
|
67
|
+
```ruby
|
68
|
+
UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
|
57
69
|
```
|
58
70
|
|
59
|
-
|
71
|
+
or when instantiating a `UserAgentParser::Parser`:
|
60
72
|
|
61
|
-
|
73
|
+
```ruby
|
74
|
+
UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string)
|
75
|
+
```
|
62
76
|
|
63
77
|
## Contributing
|
64
78
|
|
@@ -69,6 +83,18 @@ There's no support for providing overrides from Javascript user agent detection
|
|
69
83
|
|
70
84
|
All accepted pull requests will earn you commit and release rights.
|
71
85
|
|
86
|
+
## Releasing a new version
|
87
|
+
|
88
|
+
1. Update the version in `user_agent_parser.gemspec`
|
89
|
+
2. `git commit user_agent_parser.gemspec` with the following message format:
|
90
|
+
|
91
|
+
Version x.x.x
|
92
|
+
|
93
|
+
Changelog:
|
94
|
+
* Some new feature
|
95
|
+
* Some new bug fix
|
96
|
+
3. `rake release`
|
97
|
+
|
72
98
|
## License
|
73
99
|
|
74
|
-
MIT
|
100
|
+
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,31 +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
|
-
|
16
|
+
string = name
|
17
|
+
string += " #{version}" if version
|
18
|
+
string
|
17
19
|
end
|
18
20
|
|
19
21
|
def inspect
|
20
|
-
|
22
|
+
string = to_s
|
23
|
+
string += " (#{os})" if os
|
24
|
+
string += " (#{device})" if device
|
25
|
+
"#<#{self.class} #{string}>"
|
21
26
|
end
|
22
|
-
|
23
|
-
def
|
24
|
-
|
27
|
+
|
28
|
+
def eql?(other)
|
29
|
+
self.class.eql?(other.class) &&
|
30
|
+
name == other.name &&
|
25
31
|
version == other.version &&
|
26
32
|
os == other.os
|
27
33
|
end
|
28
34
|
|
35
|
+
alias_method :==, :eql?
|
29
36
|
end
|
30
|
-
|
31
37
|
end
|