special_agent 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ .bundle
2
+ Gemfile.lock
3
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in agent_orange.gemspec
4
+ gemspec
@@ -0,0 +1,193 @@
1
+ Special Agent
2
+ =============
3
+
4
+ User Agent detection for Ruby. Based on [agent_orange](https://github.com/kevinelliott/agent_orange)
5
+ but we didn't like the name so we forked it.
6
+
7
+ About
8
+ -----
9
+
10
+ There are quite a few User Agent parsing libraries already, but none of them make adequate
11
+ detection of mobile browsers and clients. With such a significant number of requests
12
+ coming from mobile users a library with the ability to detect what type of device a user
13
+ is coming from is necessary.
14
+
15
+ This library for Ruby will allow you to detect whether a user is on a computer, a mobile
16
+ device, or is a bot (such as Google). It was composed by using techniques gathered from
17
+ several other libraries/gems in an effort to be consistent and cover as many types
18
+ of agents as possible.
19
+
20
+ Installation
21
+ ------------
22
+
23
+ ```bash
24
+ gem install special_agent
25
+ ```
26
+
27
+ If you're going to use it with Rails then add the gem to your Gemfile.
28
+
29
+ Example Usage
30
+ -------------
31
+
32
+ Create new user agent parser
33
+
34
+ ```ruby
35
+ ua = SpecialAgent::UserAgent.new(user_agent_string)
36
+ ```
37
+
38
+ Looking at the device
39
+
40
+ ```ruby
41
+ >> device = ua.device
42
+ => "Mobile"
43
+ >> device.type
44
+ => "mobile"
45
+ >> device.name
46
+ => "Mobile"
47
+ >> device.version
48
+ => nil
49
+ ```
50
+
51
+ Check to see if the device is mobile, a desktop/laptop/server computer, or a bot
52
+
53
+ ```ruby
54
+ >> device.is_mobile?
55
+ => true
56
+ >> device.is_computer?
57
+ => false
58
+ >> device.is_bot?
59
+ => false
60
+ ```
61
+
62
+ Use the proxies to check if the user is on a mobile device
63
+
64
+ ```ruby
65
+ >> ua.is_mobile?
66
+ => true
67
+ ```
68
+
69
+ Looking at the platform
70
+
71
+ ```ruby
72
+ >> platform = ua.device.platform
73
+ => "iPhone"
74
+ >> platform.type
75
+ => "iphone"
76
+ >> platform.name
77
+ => "iPhone"
78
+ >> platform.version
79
+ => "3GS"
80
+ ```
81
+
82
+ Looking at the operating system
83
+
84
+ ```ruby
85
+ >> os = ua.device.os
86
+ => "iPhone OS 4.3"
87
+ >> os.type
88
+ => "iphone_os"
89
+ >> os.name
90
+ => "iPhone OS"
91
+ >> os.version
92
+ => "4.3"
93
+ ```
94
+
95
+ Looking at the web engine
96
+
97
+ ```ruby
98
+ >> engine = ua.device.engine
99
+ => "WebKit 5.3"
100
+ >> engine.type
101
+ => "webkit"
102
+ >> engine.name
103
+ => "WebKit"
104
+ >> engine.version
105
+ => "5.3.1.2321"
106
+ ```
107
+
108
+ Looking at the browser
109
+
110
+ ```ruby
111
+ >> browser = ua.device.engine.browser
112
+ => "Internet Explorer 10"
113
+ >> browser.type
114
+ => "ie"
115
+ >> browser.name
116
+ => "Internet Explorer"
117
+ >> browser.version
118
+ => "10.0.112"
119
+ ```
120
+
121
+ Quickly get to the browser version
122
+
123
+ ```ruby
124
+ >> SpecialAgent::UserAgent.new(user_agent_string).device.engine.browser.version
125
+ => "10.0.112"
126
+ ```
127
+
128
+
129
+ General Class Definition
130
+ ------------------------
131
+
132
+ SpecialAgent::UserAgent
133
+ device
134
+ parse
135
+ is_computer? # proxy
136
+ is_mobile? # proxy
137
+ is_bot? # proxy
138
+ to_s
139
+
140
+ SpecialAgent::Device
141
+ parse
142
+ type
143
+ name
144
+ version
145
+ is_computer?(name=nil) #
146
+ is_mobile?(name=nil) # accepts a :symbol or "String"
147
+ is_bot?(name=nil) #
148
+ to_s
149
+
150
+ SpecialAgent::Engine
151
+ parse
152
+ type
153
+ name
154
+ version
155
+ to_s
156
+
157
+ SpecialAgent::Version
158
+ parse
159
+ major
160
+ minor
161
+ patch_level
162
+ build_number
163
+ to_s
164
+
165
+ Credit
166
+ ------
167
+
168
+ As noted above, special_agent is just a fork of Kevin Elliot's [agent_orange](https://github.com/kevinelliott/agent_orange) gem.
169
+ He deserves all the credit for writing them gem. We just didn't want to perpetuate the use of such a culturally sensitive name.
170
+
171
+ License
172
+ -------
173
+
174
+ (The MIT License)
175
+
176
+ Permission is hereby granted, free of charge, to any person obtaining
177
+ a copy of this software and associated documentation files (the
178
+ 'Software'), to deal in the Software without restriction, including
179
+ without limitation the rights to use, copy, modify, merge, publish,
180
+ distribute, sublicense, and/or sell copies of the Software, and to
181
+ permit persons to whom the Software is furnished to do so, subject to
182
+ the following conditions:
183
+
184
+ The above copyright notice and this permission notice shall be
185
+ included in all copies or substantial portions of the Software.
186
+
187
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
188
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
189
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
190
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
191
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
192
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
193
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ require 'bundler/gem_tasks'
2
+ Dir.glob('./lib/tasks/*.rake').each { |r| import r }
3
+
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run all specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ # t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
10
+ # Put spec opts in a file named .rspec in root
11
+ end
12
+
13
+ task :default => :spec
14
+ end
@@ -0,0 +1,8 @@
1
+ require 'special_agent/version'
2
+ require 'special_agent/user_agent'
3
+ require 'special_agent/device'
4
+ require 'special_agent/engine'
5
+ require 'special_agent/browser'
6
+
7
+ module SpecialAgent
8
+ end
@@ -0,0 +1,58 @@
1
+ module SpecialAgent
2
+ class Base
3
+
4
+ def initialize(user_agent)
5
+ self.parse(user_agent)
6
+ end
7
+
8
+ def parse_user_agent_string_into_groups(user_agent)
9
+ results = user_agent.scan(/([^\/[:space:]]*)(\/([^[:space:]]*))?([[:space:]]*\[[a-zA-Z][a-zA-Z]\])?[[:space:]]*(\((([^()]|(\([^()]*\)))*)\))?[[:space:]]*/i)
10
+ groups = []
11
+ results.each do |result|
12
+ if result[0] != "" # Add the group of content if name isn't blank
13
+ groups << { :name => result[0], :version => result[2], :comment => result[5] }
14
+ end
15
+ end
16
+ groups
17
+ end
18
+
19
+ def parse_comment(comment)
20
+ groups = []
21
+ comment.split('; ').each do |piece|
22
+ content = { :name => nil, :version => nil }
23
+
24
+ # Remove 'like Mac OS X' or similar since it distracts from real results
25
+ piece = piece.scan(/(.+) like .+$/i)[0][0] if piece =~ /(.+) like (.+)$/i
26
+
27
+ if piece =~ /(.+)[ \/]([\w.]+)$/i
28
+ chopped = piece.scan(/(.+)[ \/]([\w.]+)$/i)[0]
29
+ groups << { :name => chopped[0], :version => chopped[1] }
30
+ end
31
+ end
32
+ groups
33
+ end
34
+
35
+ def determine_type(types={}, content="")
36
+ # Determine type
37
+ type = nil
38
+ types.each do |key, value|
39
+ type = key if content =~ /(#{value})/i
40
+ end
41
+ type = "other" if type.nil?
42
+ type
43
+ end
44
+
45
+ def debug_raw_content(content)
46
+ SpecialAgent.debug " Raw Name : #{content[:name]}", 2
47
+ SpecialAgent.debug " Raw Version: #{content[:version]}", 2
48
+ SpecialAgent.debug " Raw Comment: #{content[:comment]}", 2
49
+ end
50
+
51
+ def debug_content(content)
52
+ SpecialAgent.debug " Type: #{self.type}", 2
53
+ SpecialAgent.debug " Name: #{self.name}", 2
54
+ SpecialAgent.debug " Version: #{self.version}", 2
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,61 @@
1
+ require 'special_agent/base'
2
+ require 'special_agent/version'
3
+
4
+ module SpecialAgent
5
+ class Browser < Base
6
+ attr_accessor :type, :name, :version
7
+ attr_accessor :security
8
+
9
+ BROWSERS = {
10
+ :ie => 'MSIE|Internet Explorer|IE',
11
+ :firefox => 'Firefox',
12
+ :opera => 'Opera',
13
+ :safari => 'Safari'
14
+ }
15
+
16
+ def parse(user_agent)
17
+ SpecialAgent.debug "BROWSER PARSING", 2
18
+
19
+ groups = parse_user_agent_string_into_groups(user_agent)
20
+ groups.each_with_index do |content,i|
21
+ if content[:name] =~ /(#{BROWSERS.collect{|cat,regex| regex}.join(')|(')})/i
22
+ # Matched group against name
23
+ self.populate(content)
24
+ elsif content[:comment] =~ /(#{BROWSERS.collect{|cat,regex| regex}.join(')|(')})/i
25
+ # Matched group against comment
26
+ chosen_content = { :name => nil, :version => nil }
27
+ additional_groups = parse_comment(content[:comment])
28
+ additional_groups.each do |additional_content|
29
+ if additional_content[:name] =~ /(#{BROWSERS.collect{|cat,regex| regex}.join(')|(')})/i
30
+ chosen_content = additional_content
31
+ end
32
+ end
33
+
34
+ self.populate(chosen_content)
35
+ end
36
+ end
37
+
38
+ self.analysis
39
+ end
40
+
41
+ def populate(content={})
42
+ self.debug_raw_content(content)
43
+ SpecialAgent.debug "", 2
44
+
45
+ self.type = self.determine_type(BROWSERS, content[:name])
46
+ self.name = content[:name]
47
+ self.version = SpecialAgent::Version.new(content[:version])
48
+ self
49
+ end
50
+
51
+ def analysis
52
+ SpecialAgent.debug "BROWSER ANALYSIS", 2
53
+ self.debug_content(:type => self.type, :name => self.name, :version => self.version)
54
+ SpecialAgent.debug "", 2
55
+ end
56
+
57
+ def to_s
58
+ [self.name, self.version].compact.join(' ')
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,97 @@
1
+ require 'special_agent/base'
2
+ require 'special_agent/platform'
3
+ require 'special_agent/operating_system'
4
+ require 'special_agent/engine'
5
+ require 'special_agent/version'
6
+
7
+ module SpecialAgent
8
+ class Device < Base
9
+ attr_accessor :type, :name, :version
10
+ attr_accessor :platform
11
+ attr_accessor :operating_system
12
+ attr_accessor :engine
13
+
14
+ DEVICES = {
15
+ :computer => 'windows|macintosh|x11|linux',
16
+ :mobile => 'ipod|ipad|iphone|palm|android|opera mini|hiptop|windows ce|smartphone|mobile|treo|psp',
17
+ :bot => 'bot|googlebot|crawler|spider|robot|crawling'
18
+ }
19
+
20
+ def parse(user_agent)
21
+ SpecialAgent.debug "DEVICE PARSING", 2
22
+
23
+ groups = parse_user_agent_string_into_groups(user_agent)
24
+ groups.each_with_index do |content,i|
25
+ if content[:comment] =~ /(#{DEVICES.collect{|cat,regex| regex}.join(')|(')})/i
26
+ # Matched group against name
27
+ self.populate(content)
28
+ end
29
+ end
30
+
31
+ self.analysis
32
+
33
+ self.platform = SpecialAgent::Platform.new(user_agent)
34
+ self.operating_system = SpecialAgent::OperatingSystem.new(user_agent)
35
+ self.engine = SpecialAgent::Engine.new(user_agent)
36
+ end
37
+
38
+ def populate(content={})
39
+ self.debug_raw_content(content)
40
+ SpecialAgent.debug "", 2
41
+
42
+ self.type = self.determine_type(DEVICES, content[:comment])
43
+ self.name = self.type.to_s.capitalize
44
+ self.version = nil
45
+ self
46
+ end
47
+
48
+ def analysis
49
+ SpecialAgent.debug "DEVICE ANALYSIS", 2
50
+ self.debug_content(:type => self.type, :name => self.name, :version => self.version)
51
+ SpecialAgent.debug "", 2
52
+ end
53
+
54
+ def is_computer?(name=nil)
55
+ if name
56
+ case name
57
+ when String
58
+ return self.platform.name.downcase.include?(name.downcase)
59
+ when Symbol
60
+ return self.platform.name.downcase.include?(name.to_s.downcase)
61
+ end
62
+ else
63
+ (self.type == :computer)
64
+ end
65
+ end
66
+
67
+ def is_mobile?(name=nil)
68
+ if !name.nil? && !self.platform.name.nil?
69
+ case name
70
+ when String
71
+ return self.platform.name.downcase.include?(name.downcase) || self.platform.version.downcase.include?(name.downcase)
72
+ when Symbol
73
+ return self.platform.name.downcase.include?(name.to_s.downcase) || self.platform.version.to_s.downcase.include?(name.to_s.downcase)
74
+ end
75
+ else
76
+ (self.type == :mobile)
77
+ end
78
+ end
79
+
80
+ def is_bot?(name=nil)
81
+ if name
82
+ case name
83
+ when String
84
+ return self.name.downcase.include?(name.downcase)
85
+ when Symbol
86
+ return self.name.downcase.include?(name.to_s.downcase)
87
+ end
88
+ else
89
+ (self.type == :bot)
90
+ end
91
+ end
92
+
93
+ def to_s
94
+ [self.name, self.version].compact.join(' ')
95
+ end
96
+ end
97
+ end