agent_orange 0.0.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.
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
+