user_agent_parser 1.0.0 → 2.1.1

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.

@@ -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.9.2
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
- ua = UserAgentParser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
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
- ua.to_s
25
- => "IE 9.0 (Windows Vista)"
26
- ua.family
22
+ user_agent.to_s
23
+ => "IE 9.0"
24
+ user_agent.name
27
25
  => "IE"
28
- ua.version.to_s
26
+ user_agent.version.to_s
29
27
  => "9.0"
30
- ua.version[0]
31
- => 9
32
- ua.version[1]
33
- => 0
34
- ua.os.name
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
- ## The pattern database
46
+ In a larger application, you could store a parser in a global to avoid repeat pattern loading:
39
47
 
40
- 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!).
48
+ ```ruby
49
+ module MyApplication
41
50
 
42
- You can also specify the path to your own, updated and/or customised `regexes.yaml` file:
51
+ # Instantiate the parser on load as it's quite expensive
52
+ USER_AGENT_PARSER = UserAgentParser::Parser.new
43
53
 
44
- ```ruby
45
- UserAgentParser.patterns_path = '/some/path/to/regexes.yaml'
54
+ def self.user_agent_parser
55
+ USER_AGENT_PARSER
56
+ end
57
+
58
+ end
46
59
  ```
47
60
 
48
- ## Comprehensive you say?
61
+ ## The pattern database
49
62
 
50
- ```bash
51
- $ rake test
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
- 12836 tests, 33888 assertions, 0 failures, 0 errors, 0 skips
67
+ ```ruby
68
+ UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
57
69
  ```
58
70
 
59
- ## Limitations
71
+ or when instantiating a `UserAgentParser::Parser`:
60
72
 
61
- 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/).
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
@@ -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 user_agent_string
22
- Parser.new.parse user_agent_string
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
- attr_accessor :name, :version
6
-
7
- def initialize(name="Other", version=nil)
8
- self.name = name
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
- s = name
14
- s += " #{version}" if version
15
- s
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 ==(other)
23
- name == other.name && version == other.version
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
- end
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 parse user_agent
8
- ua = parse_ua(user_agent)
9
- ua.os = parse_os(user_agent)
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 all_patterns
16
- @all_patterns ||= YAML.load_file(UserAgentParser.patterns_path)
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
- def patterns type
20
- @patterns ||= {}
21
- @patterns[type] ||= begin
22
- all_patterns[type].each do |p|
23
- p["regex"] = Regexp.new(p["regex"])
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 user_agent
29
- pattern, match = first_pattern_match(patterns("user_agent_parsers"), user_agent)
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 user_agent
38
- pattern, match = first_pattern_match(patterns("os_parsers"), user_agent)
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 first_pattern_match patterns, value
47
- for p in patterns
48
- if m = p["regex"].match(value)
49
- return [p, m]
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 pattern, match
56
- family, v1, v2, v3 = match[1], match[2], match[3], match[4]
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
- family = pattern["family_replacement"].sub('$1', family || '')
59
- end
60
- v1 = pattern["v1_replacement"].sub('$1', v1 || '') if pattern["v1_replacement"]
61
- v2 = pattern["v2_replacement"].sub('$1', v2 || '') if pattern["v2_replacement"]
62
- v3 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v3_replacement"]
63
- ua = UserAgent.new(family)
64
- ua.version = version_from_segments(v1, v2, v3)
65
- ua
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 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
- os = pattern["os_replacement"].sub('$1', os || '') if pattern["os_replacement"]
71
- v1 = pattern["v1_replacement"].sub('$1', v1 || '') if pattern["v1_replacement"]
72
- v2 = pattern["v2_replacement"].sub('$1', v2 || '') if pattern["v2_replacement"]
73
- v3 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v3_replacement"]
74
- v4 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v4_replacement"]
75
- os = OperatingSystem.new(os)
76
- os.version = version_from_segments(v1, v2, v3, v4)
77
- os
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
- attr_accessor :family, :version, :os
5
+ # For backwards compatibility with older versions of this gem.
6
+ alias_method :family, :name
6
7
 
7
- def initialize(family="Other", version=nil, os=nil)
8
- self.family = family
9
- self.version = version
10
- self.os = os
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
- s = family
15
- s += " #{version}" if version
16
- s
16
+ string = name
17
+ string += " #{version}" if version
18
+ string
17
19
  end
18
20
 
19
21
  def inspect
20
- "#<#{self.class} #{to_s}>"
22
+ string = to_s
23
+ string += " (#{os})" if os
24
+ string += " (#{device})" if device
25
+ "#<#{self.class} #{string}>"
21
26
  end
22
-
23
- def ==(other)
24
- family == other.family &&
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