agent_orange 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ 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
data/README.rdoc ADDED
@@ -0,0 +1,153 @@
1
+ = agent_orange
2
+
3
+ User Agent detection for Ruby. ActionController integration coming soon.
4
+
5
+ == About
6
+
7
+ There are quite a few User Agent parsing libraries already, but none of them make adequate
8
+ detection of mobile browsers and clients. With such a significant number of requests
9
+ coming from mobile users a library with the ability to detect what type of device a user
10
+ is coming from is necessary.
11
+
12
+ This library for Ruby will allow you to detect whether a user is on a computer, a mobile
13
+ device, or is a bot (such as Google). It was composed by using techniques gathered from
14
+ several other libraries/gems in an effort to be consistent and cover as many types
15
+ of agents as possible.
16
+
17
+ == Status
18
+
19
+ === Improvement stage.
20
+
21
+ This library is not yet testable but does function. Please contact me if you'd like
22
+ to participate.
23
+
24
+ == Installation
25
+
26
+ gem install agent_orange
27
+
28
+ If you're going to use it with Rails then add the gem to your Gemfile.
29
+
30
+ == Example Usage
31
+
32
+ === Create new user agent parser
33
+
34
+ >> ua = AgentOrange::UserAgent.new(user_agent_string)
35
+
36
+ === Looking at the device
37
+
38
+ >> device = ua.device
39
+ => "iPhone 3G"
40
+ >> device.type
41
+ => "mobile"
42
+ >> device.manufacturer
43
+ => "Apple"
44
+ >> device.name
45
+ => "iPhone"
46
+ >> device.version
47
+ => nil
48
+
49
+ === Check to see if the device is mobile, a desktop/laptop/server computer, or a bot
50
+
51
+ >> device.is_mobile?
52
+ => true
53
+ >> device.is_computer?
54
+ => false
55
+ >> device.is_bot?
56
+ => false
57
+
58
+ === Use the proxies to check if the user is on a mobile device
59
+
60
+ >> ua.is_mobile?
61
+ => true
62
+
63
+ === Looking at the web engine
64
+
65
+ >> engine = ua.device.engine
66
+ => "WebKit 5.3"
67
+ >> engine.type
68
+ => "webkit"
69
+ >> engine.name
70
+ => "WebKit"
71
+ >> engine.version
72
+ => "5.3.1.2321"
73
+
74
+ === Looking at the browser
75
+
76
+ >> browser = ua.device.engine.browser
77
+ => "Internet Explorer 10"
78
+ >> browser.type
79
+ => "ie"
80
+ >> browser.name
81
+ => "Internet Explorer"
82
+ >> browser.version
83
+ => "10.0.112"
84
+
85
+ === Quickly get to the browser version
86
+
87
+ >> AgentOrange::UserAgent.new(user_agent_string).device.engine.browser.version
88
+ => "10.0.112"
89
+
90
+ == General Class Definition
91
+
92
+ This is not up to date or a reflection of the actual class structure. This will be updated eventually.
93
+
94
+ AgentOrange::UserAgent
95
+ device
96
+ parse
97
+ is_computer? # proxy
98
+ is_mobile? # proxy
99
+ is_bot? # proxy
100
+ to_s
101
+
102
+ AgentOrange::Device
103
+ parse
104
+ type
105
+ name
106
+ version
107
+ is_computer?(name=nil) #
108
+ is_mobile?(name=nil) # accepts a :symbol or "String"
109
+ is_bot?(name=nil) #
110
+ to_s
111
+
112
+ AgentOrange::Engine
113
+ parse
114
+ type
115
+ name
116
+ version
117
+ to_s
118
+
119
+ AgentOrange::Version
120
+ parse
121
+ major
122
+ minor
123
+ patch_level
124
+ build_number
125
+ to_s
126
+
127
+ == Maintainer
128
+
129
+ * Kevin Elliott - http://kevinelliott.net
130
+ * WeLike - http://www.welikeinc.com - http://github.com/welike
131
+
132
+ == License
133
+
134
+ (The MIT License)
135
+
136
+ Permission is hereby granted, free of charge, to any person obtaining
137
+ a copy of this software and associated documentation files (the
138
+ 'Software'), to deal in the Software without restriction, including
139
+ without limitation the rights to use, copy, modify, merge, publish,
140
+ distribute, sublicense, and/or sell copies of the Software, and to
141
+ permit persons to whom the Software is furnished to do so, subject to
142
+ the following conditions:
143
+
144
+ The above copyright notice and this permission notice shall be
145
+ included in all copies or substantial portions of the Software.
146
+
147
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
148
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
149
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
150
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
151
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
152
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
153
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "agent_orange/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "agent_orange"
7
+ s.version = AgentOrange::VERSION
8
+ s.authors = ["Kevin Elliott"]
9
+ s.email = ["kevin@welikeinc.com"]
10
+ s.homepage = "http://github.com/kevinelliott/agent_orange"
11
+ s.summary = %q{Parse and process User Agents like a secret one}
12
+ s.description = %q{Parse and process User Agents like a secret one}
13
+
14
+ s.rubyforge_project = "agent_orange"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "agent_orange/user_agent"
4
+
5
+ def process_user_agent(ua_str)
6
+ puts
7
+ puts '='*120
8
+ puts "User-Agent:"
9
+ puts " #{ua_str}"
10
+ puts
11
+ ua = AgentOrange::UserAgent.new(:user_agent => ua_str)
12
+ puts '='*120
13
+ end
14
+
15
+ ua_str = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; sv-se) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12"
16
+ process_user_agent(ua_str)
17
+
18
+ ua_str = "Mozilla/5.0 (iPhone; U; Linux i686; pt-br) AppleWebKit/532+ (KHTML, like Gecko) Version/3.0 Mobile/1A538b Safari/419.3 Midori/0.2.0"
19
+ process_user_agent(ua_str)
@@ -0,0 +1,64 @@
1
+ module AgentOrange
2
+ class Browser
3
+ attr_accessor :type, :name, :version
4
+ attr_accessor :security
5
+
6
+ BROWSERS = {
7
+ :ie => 'MSIE|Internet Explorer|IE',
8
+ :firefox => 'Firefox',
9
+ :safari => 'Safari'
10
+ }
11
+
12
+ def initialize(user_agent)
13
+ self.parse(user_agent)
14
+ end
15
+
16
+ def parse(user_agent)
17
+ AgentOrange.debug "BROWSER PARSING", 2
18
+ groups = user_agent.scan(/([^\/[:space:]]*)(\/([^[:space:]]*))?([[:space:]]*\[[a-zA-Z][a-zA-Z]\])?[[:space:]]*(\((([^()]|(\([^()]*\)))*)\))?[[:space:]]*/i)
19
+ groups.each_with_index do |pieces,i|
20
+ name = pieces[0]
21
+ version = pieces[2]
22
+ comment = pieces[5]
23
+
24
+ if name =~ /(#{BROWSERS.collect{|cat,regex| regex}.join(')|(')})/i
25
+ # Found the browser
26
+ AgentOrange.debug " Got a browser in group #{i+1}!", 2
27
+ AgentOrange.debug " Raw Name : #{name}", 2
28
+ AgentOrange.debug " Raw Version: #{version}", 2
29
+ AgentOrange.debug " Raw Comment: #{comment}", 2
30
+ AgentOrange.debug "", 2
31
+
32
+ # Determine browser type
33
+ if name =~ /(#{BROWSERS[:ie]})/i
34
+ self.type = "ie"
35
+ elsif name =~ /(#{BROWSERS[:firefox]})/i
36
+ self.type = "firefox"
37
+ elsif name =~ /(#{BROWSERS[:safari]})/i
38
+ self.type = "safari"
39
+ else
40
+ self.type = "other"
41
+ end
42
+
43
+ # Determine browser name
44
+ self.name = name
45
+
46
+ # Determine device version
47
+ self.version = version
48
+
49
+ end
50
+
51
+ end
52
+
53
+ AgentOrange.debug "BROWSER ANALYSIS", 2
54
+ AgentOrange.debug " Type: #{self.type}", 2
55
+ AgentOrange.debug " Name: #{self.name}", 2
56
+ AgentOrange.debug " Version: #{self.version}", 2
57
+ AgentOrange.debug "", 2
58
+ end
59
+
60
+ def to_s
61
+ [self.name, self.version].compact.join(' ')
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,109 @@
1
+ require 'agent_orange/engine'
2
+
3
+ module AgentOrange
4
+ class Device
5
+ attr_accessor :type, :name, :version
6
+ attr_accessor :platform
7
+ attr_accessor :operating_system
8
+ attr_accessor :engine
9
+
10
+ DEVICES = {
11
+ :computer => 'windows|macintosh|x11|linux',
12
+ :mobile => 'ipod|ipad|iphone|palm|android|opera mini|hiptop|windows ce|smartphone|mobile|treo|psp',
13
+ :bot => 'bot'
14
+ }
15
+
16
+ def initialize(user_agent)
17
+ self.parse(user_agent)
18
+ end
19
+
20
+ def parse(user_agent)
21
+ AgentOrange.debug "DEVICE PARSING", 2
22
+ groups = user_agent.scan(/([^\/[:space:]]*)(\/([^[:space:]]*))?([[:space:]]*\[[a-zA-Z][a-zA-Z]\])?[[:space:]]*(\((([^()]|(\([^()]*\)))*)\))?[[:space:]]*/i)
23
+ groups.each_with_index do |pieces,i|
24
+ name = pieces[0]
25
+ version = pieces[2]
26
+ comment = pieces[5]
27
+
28
+ if comment =~ /(#{DEVICES.collect{|cat,regex| regex}.join(')|(')})/i
29
+ # Found the device
30
+ AgentOrange.debug " Got a device in group #{i+1}!", 2
31
+ AgentOrange.debug " Raw Name : #{name}", 2
32
+ AgentOrange.debug " Raw Version: #{version}", 2
33
+ AgentOrange.debug " Raw Comment: #{comment}", 2
34
+ AgentOrange.debug "", 2
35
+
36
+ # Determine device type
37
+ if comment =~ /(#{DEVICES[:computer]})/i
38
+ self.type = "computer"
39
+ end
40
+ if comment =~ /(#{DEVICES[:mobile]})/i
41
+ self.type = "mobile"
42
+ end
43
+ if comment =~ /(#{DEVICES[:bot]})/i
44
+ self.type = "bot"
45
+ end
46
+
47
+ # Determine device name
48
+ self.name = comment.split(';')[0]
49
+
50
+ # Determine device version
51
+ self.version = nil
52
+
53
+ end
54
+
55
+ end
56
+
57
+ AgentOrange.debug "DEVICE ANALYSIS", 2
58
+ AgentOrange.debug " Type: #{self.type}", 2
59
+ AgentOrange.debug " Name: #{self.name}", 2
60
+ AgentOrange.debug " Version: #{self.version}", 2
61
+ AgentOrange.debug "", 2
62
+
63
+ self.engine = AgentOrange::Engine.new(user_agent)
64
+ end
65
+
66
+ def is_computer?(name=nil)
67
+ if name
68
+ case name
69
+ when String
70
+ return self.name.downcase.include?(name.downcase)
71
+ when Symbol
72
+ return self.name.downcase.include?(name.to_s.downcase)
73
+ end
74
+ else
75
+ (self.type == "computer")
76
+ end
77
+ end
78
+
79
+ def is_mobile?(name=nil)
80
+ if name
81
+ case name
82
+ when String
83
+ return self.name.downcase.include?(name.downcase)
84
+ when Symbol
85
+ return self.name.downcase.include?(name.to_s.downcase)
86
+ end
87
+ else
88
+ self.type == "mobile"
89
+ end
90
+ end
91
+
92
+ def is_bot?(name=nil)
93
+ if name
94
+ case name
95
+ when String
96
+ return self.name.downcase.include?(name.downcase)
97
+ when Symbol
98
+ return self.name.downcase.include?(name.to_s.downcase)
99
+ end
100
+ else
101
+ self.type == "bot"
102
+ end
103
+ end
104
+
105
+ def to_s
106
+ [self.name, self.version].compact.join(' ')
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,68 @@
1
+ require 'agent_orange/browser'
2
+
3
+ module AgentOrange
4
+ class Engine
5
+ attr_accessor :type, :name, :version
6
+ attr_accessor :browser
7
+
8
+ ENGINES = {
9
+ :gecko => 'Gecko',
10
+ :presto => 'Presto',
11
+ :webkit => 'AppleWebKit'
12
+ }
13
+
14
+ def initialize(user_agent)
15
+ self.parse(user_agent)
16
+ end
17
+
18
+ def parse(user_agent)
19
+ AgentOrange.debug "ENGINE PARSING", 2
20
+ groups = user_agent.scan(/([^\/[:space:]]*)(\/([^[:space:]]*))?([[:space:]]*\[[a-zA-Z][a-zA-Z]\])?[[:space:]]*(\((([^()]|(\([^()]*\)))*)\))?[[:space:]]*/i)
21
+ groups.each_with_index do |pieces,i|
22
+ name = pieces[0]
23
+ version = pieces[2]
24
+ comment = pieces[5]
25
+
26
+ if name =~ /(#{ENGINES.collect{|cat,regex| regex}.join(')|(')})/i
27
+ # Found the engine
28
+ AgentOrange.debug " Got an engine in group #{i+1}!", 2
29
+ AgentOrange.debug " Raw Name : #{name}", 2
30
+ AgentOrange.debug " Raw Version: #{version}", 2
31
+ AgentOrange.debug " Raw Comment: #{comment}", 2
32
+ AgentOrange.debug "", 2
33
+
34
+ # Determine engine type
35
+ if name =~ /(#{ENGINES[:gecko]})/i
36
+ self.type = "gecko"
37
+ end
38
+ if name =~ /(#{ENGINES[:presto]})/i
39
+ self.type = "presto"
40
+ end
41
+ if name =~ /(#{ENGINES[:webkit]})/i
42
+ self.type = "webkit"
43
+ end
44
+
45
+ # Determine engine name
46
+ self.name = name
47
+
48
+ # Determine device version
49
+ self.version = version
50
+
51
+ end
52
+
53
+ end
54
+
55
+ AgentOrange.debug "ENGINE ANALYSIS", 2
56
+ AgentOrange.debug " Type: #{self.type}", 2
57
+ AgentOrange.debug " Name: #{self.name}", 2
58
+ AgentOrange.debug " Version: #{self.version}", 2
59
+ AgentOrange.debug "", 2
60
+
61
+ self.browser = AgentOrange::Browser.new(user_agent)
62
+ end
63
+
64
+ def to_s
65
+ [self.name, self.version].compact.join(' ')
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,9 @@
1
+ module AgentOrange
2
+ class OperatingSystem
3
+ attr_accessor :name, :version
4
+
5
+ def to_s
6
+ "OS X 10.6"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module AgentOrange
2
+ class Platform
3
+ attr_accessor :name, :version
4
+
5
+ def to_s
6
+ "#{name} #{version}"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,71 @@
1
+ require 'agent_orange/device'
2
+
3
+ module AgentOrange
4
+ DEBUG = true
5
+ DEBUG_LEVEL = 1
6
+
7
+ class UserAgent
8
+ attr_accessor :user_agent_string
9
+ attr_accessor :user_language
10
+ attr_accessor :device
11
+
12
+ def initialize(options = {}, &block)
13
+ @user_agent_string = options[:user_agent].to_s
14
+ self.parse(@user_agent_string)
15
+
16
+ yield self if block_given?
17
+ end
18
+
19
+ def parse(user_agent)
20
+ self.device = AgentOrange::Device.new(user_agent)
21
+
22
+ AgentOrange.debug "Device = #{self.device}"
23
+ AgentOrange.debug " Is computer? #{self.is_computer?}"
24
+ AgentOrange.debug " Is mobile? #{self.is_mobile?}"
25
+ if self.is_mobile?
26
+ AgentOrange.debug " Is an iPhone? #{self.is_mobile? :iphone}"
27
+ AgentOrange.debug " Is an iPad? #{self.is_mobile? :ipad}"
28
+ AgentOrange.debug " Is an iPod? #{self.is_mobile? :ipod}"
29
+ AgentOrange.debug " Is an Android? #{self.is_mobile? :android}"
30
+ end
31
+ AgentOrange.debug " Is bot? #{self.is_bot?}"
32
+ AgentOrange.debug
33
+
34
+ AgentOrange.debug "Engine = #{self.device.engine}"
35
+ AgentOrange.debug "Browser = #{self.device.engine.browser}"
36
+
37
+ AgentOrange.debug "", 2
38
+ AgentOrange.debug "user_agent.to_s = #{self}", 2
39
+ end
40
+
41
+ def is_computer?(type=nil)
42
+ self.device.is_computer?(type)
43
+ end
44
+
45
+ def is_mobile?(type=nil)
46
+ self.device.is_mobile?(type)
47
+ end
48
+
49
+ def is_bot?(type=nil)
50
+ self.device.is_bot?(type)
51
+ end
52
+
53
+ def to_s
54
+ [self.device, self.device.engine, self.device.engine.browser].compact.join(", ")
55
+ end
56
+
57
+ def to_human_string
58
+ if self.device && self.device.engine && self.device.engine.browser
59
+ "User has a #{self.device} running #{self.device.engine.browser} (which is based on #{self.device.engine})."
60
+ else
61
+ "User has some kind of device that I've never seen."
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ def self.debug(str="", level=1)
68
+ puts str if DEBUG && (DEBUG_LEVEL >= level)
69
+ end
70
+
71
+ end
@@ -0,0 +1,11 @@
1
+ module AgentOrange
2
+ VERSION = "0.0.1" # This is for the gem and does not conflict with the rest of the functionality
3
+
4
+ class Version
5
+ attr_accessor :major, :minor, :patch_level, :build_number
6
+
7
+ def to_s
8
+ "#{self.major}.#{self.minor}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ require 'agent_orange/version'
2
+ require 'agent_orange/user_agent'
3
+ require 'agent_orange/device'
4
+ require 'agent_orange/engine'
5
+ require 'agent_orange/browser'
6
+
7
+ module AgentOrange
8
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent_orange
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Kevin Elliott
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-11 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Parse and process User Agents like a secret one
22
+ email:
23
+ - kevin@welikeinc.com
24
+ executables:
25
+ - agent_orange_example
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.rdoc
34
+ - Rakefile
35
+ - agent_orange.gemspec
36
+ - bin/agent_orange_example
37
+ - lib/agent_orange.rb
38
+ - lib/agent_orange/browser.rb
39
+ - lib/agent_orange/device.rb
40
+ - lib/agent_orange/engine.rb
41
+ - lib/agent_orange/operating_system.rb
42
+ - lib/agent_orange/platform.rb
43
+ - lib/agent_orange/user_agent.rb
44
+ - lib/agent_orange/version.rb
45
+ homepage: http://github.com/kevinelliott/agent_orange
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options: []
50
+
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project: agent_orange
74
+ rubygems_version: 1.8.5
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Parse and process User Agents like a secret one
78
+ test_files: []
79
+