user_agent_parser 1.0.2 → 2.1.3

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.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a721f503d9e59cd7813d9c18db733ef6ecb24c5e
4
+ data.tar.gz: 7acb12c8b075c1f2cae73b8e3ce29023e5bae740
5
+ SHA512:
6
+ metadata.gz: d929dfb2af11c55a29a51cb81ae1a3210327275aed9c45c4055374d95d7d1957d6a446ec6cb3c78b9db90911ca7a041d66ca03ecae9e72a0fa423267005d1bc6
7
+ data.tar.gz: 482194e192ffaac970357316d7ee7976b1add1c18d922de35210d82018d4b3dd003723072abb6f37556c24011a72f20b2cb1820c5af06cad7ec71696485eee01
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,48 +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
22
+ user_agent.to_s
25
23
  => "IE 9.0"
26
- ua.family
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.major
31
- => 9
32
- ua.version.minor
33
- => 0
34
- os = ua.os
28
+ user_agent.version.major
29
+ => "9"
30
+ user_agent.version.minor
31
+ => "0"
32
+ operating_system = user_agent.os
35
33
  => #<UserAgentParser::OperatingSystem Windows Vista>
36
- os.to_s
34
+ operating_system.to_s
37
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)>
38
44
  ```
39
45
 
40
- ## The pattern database
46
+ In a larger application, you could store a parser in a global to avoid repeat pattern loading:
41
47
 
42
- 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
43
50
 
44
- 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
45
53
 
46
- ```ruby
47
- UserAgentParser.patterns_path = '/some/path/to/regexes.yaml'
54
+ def self.user_agent_parser
55
+ USER_AGENT_PARSER
56
+ end
57
+
58
+ end
48
59
  ```
49
60
 
50
- ## Comprehensive you say?
61
+ ## The pattern database
51
62
 
52
- ```bash
53
- $ rake test
54
- ...
55
-
56
- 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`:
57
66
 
58
- 12836 tests, 33888 assertions, 0 failures, 0 errors, 0 skips
67
+ ```ruby
68
+ UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
59
69
  ```
60
70
 
61
- ## Limitations
71
+ or when instantiating a `UserAgentParser::Parser`:
62
72
 
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/).
73
+ ```ruby
74
+ UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string)
75
+ ```
64
76
 
65
77
  ## Contributing
66
78
 
@@ -71,6 +83,18 @@ There's no support for providing overrides from Javascript user agent detection
71
83
 
72
84
  All accepted pull requests will earn you commit and release rights.
73
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
+
74
98
  ## License
75
99
 
76
- MIT
100
+ MIT
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', File.readlink(__FILE__))
4
+
5
+ require 'optparse'
6
+
7
+ require 'user_agent_parser'
8
+ require 'user_agent_parser/cli'
9
+
10
+ options = {}
11
+
12
+ optparse = OptionParser.new do|opts|
13
+ opts.on('--name', 'Print name only') do
14
+ options[:name] = true
15
+ end
16
+
17
+ opts.on('--version', 'Print version only') do
18
+ options[:version] = true
19
+ end
20
+
21
+ opts.on('--major', 'Print major version only') do
22
+ options[:major] = true
23
+ end
24
+
25
+ opts.on('--minor', 'Print minor version only') do
26
+ options[:minor] = true
27
+ end
28
+
29
+ opts.on('--os', 'Print operating system only') do
30
+ options[:os] = true
31
+ end
32
+
33
+ opts.on('--format format',
34
+ 'Print output in specified format. The available formatters are:',
35
+ ' - %n: name',
36
+ ' - %v: version',
37
+ ' - %M: major version',
38
+ ' - %m: minor version',
39
+ ' - %o: operating system'
40
+ ) do |format|
41
+ options[:format] = format
42
+ end
43
+
44
+ opts.on('-h', '--help', 'Display this screen') do
45
+ puts opts
46
+ exit
47
+ end
48
+ end
49
+
50
+ optparse.parse!
51
+
52
+ parser = UserAgentParser::Parser.new
53
+
54
+ ARGF.each do |line|
55
+ puts UserAgentParser::Cli.new(parser.parse(line), options).run!
56
+ end
@@ -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,54 @@
1
+ module UserAgentParser
2
+ class Cli
3
+ def initialize(user_agent, options = {})
4
+ @user_agent = user_agent
5
+ @options = options
6
+ end
7
+
8
+ def run!
9
+ if @options[:name]
10
+ @user_agent.name
11
+ elsif @options[:version]
12
+ with_version do |version|
13
+ version.to_s
14
+ end
15
+ elsif @options[:major]
16
+ major
17
+ elsif @options[:minor]
18
+ minor
19
+ elsif @options[:os]
20
+ @user_agent.os.to_s
21
+ elsif format = @options[:format]
22
+ format.gsub('%n', @user_agent.name).
23
+ gsub('%v', version.to_s).
24
+ gsub('%M', major.to_s).
25
+ gsub('%m', minor.to_s).
26
+ gsub('%o', @user_agent.os.to_s)
27
+ else
28
+ @user_agent.to_s
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def major
35
+ with_version do |version|
36
+ version.major
37
+ end
38
+ end
39
+
40
+ def minor
41
+ with_version do |version|
42
+ version.minor
43
+ end
44
+ end
45
+
46
+ def version
47
+ @version ||= @user_agent.version
48
+ end
49
+
50
+ def with_version(&block)
51
+ block.call(version) if version
52
+ end
53
+ end
54
+ 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