double_agent 0.0.1 → 0.0.3
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/CHANGELOG +17 -0
- data/README.rdoc +118 -0
- data/data/browsers.yml +22 -14
- data/data/oses.yml +31 -11
- data/lib/double_agent/core.rb +51 -19
- data/lib/double_agent/logs.rb +13 -0
- data/lib/double_agent/resources.rb +15 -1
- data/lib/double_agent/stats.rb +5 -9
- data/spec/core_spec.rb +81 -0
- data/spec/data/httpd.access.log +20 -0
- data/spec/data/httpd.access.log.1.gz +0 -0
- data/spec/parser_spec.rb +100 -0
- data/spec/resources_spec.rb +78 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/stats_spec.rb +22 -0
- metadata +20 -23
data/CHANGELOG
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
== Release 0.0.3 (May 6, 2011)
|
2
|
+
|
3
|
+
* Fixed bug from 0.0.2 in Ruby 1.8 where browser versions never got parsed
|
4
|
+
|
5
|
+
== Release 0.0.2 (May 6, 2011)
|
6
|
+
|
7
|
+
* Fixed detection for some versions of Windows XP and Opera
|
8
|
+
|
9
|
+
* Added detection for Windows 8, Fedora, Slackware, FreeBSD, BlackBerry, Konqueror, Epiphany
|
10
|
+
|
11
|
+
* Added parsing tests for each detectable OS and browser
|
12
|
+
|
13
|
+
* Misc. minor internal refactoring and bug fixes
|
14
|
+
|
15
|
+
== Release 0.0.1 (April 30, 2011) **
|
16
|
+
|
17
|
+
* Initial release
|
data/README.rdoc
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
= Double Agent
|
2
|
+
|
3
|
+
double_agent is a library for parsing browser and operating system info out of user
|
4
|
+
agent strings. It is designed for parsing large sets for review or analysis.
|
5
|
+
|
6
|
+
# Load core, resources and stats
|
7
|
+
require 'double_agent'
|
8
|
+
|
9
|
+
# Load pieces individually
|
10
|
+
require 'double_agent/core'
|
11
|
+
require 'double_agent/resources'
|
12
|
+
require 'double_agent/stats'
|
13
|
+
require 'double_agent/logs'
|
14
|
+
|
15
|
+
# Load everything at once
|
16
|
+
require 'double_agent/all'
|
17
|
+
|
18
|
+
= Core
|
19
|
+
|
20
|
+
The core parser.
|
21
|
+
|
22
|
+
ua_string = "pretent I'm a user agent string for Firefox 4 on Ubuntu"
|
23
|
+
|
24
|
+
DoubleAgent.browser(ua_string)
|
25
|
+
=> "Firefox 4"
|
26
|
+
|
27
|
+
DoubleAgent.browser_family(ua_string)
|
28
|
+
=> "Firefox"
|
29
|
+
|
30
|
+
DoubleAgent.browser_sym(ua_string)
|
31
|
+
=> :firefox
|
32
|
+
|
33
|
+
DoubleAgent.browser_family_sym(ua_string)
|
34
|
+
=> :firefox
|
35
|
+
|
36
|
+
DoubleAgent.os(ua_string)
|
37
|
+
=> "Ubuntu"
|
38
|
+
|
39
|
+
DoubleAgent.os_family(ua_string)
|
40
|
+
=> "GNU/Linux"
|
41
|
+
|
42
|
+
DoubleAgent.os_sym(ua_string)
|
43
|
+
=> :ubuntu
|
44
|
+
|
45
|
+
DoubleAgent.os_family_sym(ua_string)
|
46
|
+
=> :linux
|
47
|
+
|
48
|
+
= Resources
|
49
|
+
|
50
|
+
Say you're saving a record of each user's login so you can determine their favorite browser. Just make sure
|
51
|
+
the user agent is available through user_agent, and presto!
|
52
|
+
|
53
|
+
class Login
|
54
|
+
include DoubleAgent::Resource
|
55
|
+
|
56
|
+
def user_agent
|
57
|
+
#returns this object's user agent string
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
login = Login.find(76)
|
62
|
+
|
63
|
+
login.browser
|
64
|
+
=> "Firefox 4"
|
65
|
+
|
66
|
+
login.os_family
|
67
|
+
=> "OS X"
|
68
|
+
|
69
|
+
= Stats
|
70
|
+
|
71
|
+
Figure out what percent use which browser, browser family, os, etc.
|
72
|
+
|
73
|
+
== Example 1
|
74
|
+
|
75
|
+
logins = Login.all
|
76
|
+
stats = DoubleAgent.percentages_for(logins, :browser)
|
77
|
+
|
78
|
+
p stats
|
79
|
+
=> [["Firefox 4", 20.0], ["Internet Explorer 8", 18.0], ...]
|
80
|
+
|
81
|
+
stats.each do |browser, percent|
|
82
|
+
puts "#{browser} - #{percent}%"
|
83
|
+
end
|
84
|
+
|
85
|
+
== Example 2
|
86
|
+
|
87
|
+
logins = Login.all
|
88
|
+
stats = DoubleAgent.percentages_for(logins, :browser_family, :os_family)
|
89
|
+
|
90
|
+
p stats
|
91
|
+
=> [["Firefox", "Windows", 50.0], ["Internet Explorer", "Windows", 20.0], ["Safari", "OS X", 20.0], ["Firefox", "GNU/Linux", 10.0]]
|
92
|
+
|
93
|
+
stats.each do |browser_family, os_family, percent|
|
94
|
+
puts "#{browser_family} on #{os_family} - #{percent}%"
|
95
|
+
end
|
96
|
+
|
97
|
+
= Logs
|
98
|
+
|
99
|
+
DoubleAgent#log_entries parses through Apache and Nginx access logs, instantiating each log line into a LogEntry
|
100
|
+
object. It even reads gzipped logs (requires zlib)! The LogEntry class mixes in DoubleAgent::Resource, so this is
|
101
|
+
a great way to generate browser and OS breakdowns of who's visiting your site.
|
102
|
+
|
103
|
+
require 'double_agent'
|
104
|
+
require 'double_agent/logs'
|
105
|
+
|
106
|
+
entries = DoubleAgent.log_entries("/var/log/nginx/my-site.access.log*")
|
107
|
+
entries.each do |entry|
|
108
|
+
puts entry.browser
|
109
|
+
end
|
110
|
+
|
111
|
+
Firefox 3
|
112
|
+
Internet Explorer 8
|
113
|
+
Internet Explorer 9
|
114
|
+
Firefox 4
|
115
|
+
Internet Explorer
|
116
|
+
Safari
|
117
|
+
Chrome
|
118
|
+
...
|
data/data/browsers.yml
CHANGED
@@ -3,18 +3,10 @@
|
|
3
3
|
|
4
4
|
- :name: Internet Explorer %s
|
5
5
|
:sym: :msie
|
6
|
-
:family_sym: :msie
|
7
6
|
:regex: 'msie \d+'
|
8
7
|
:version: '(?<=msie )[0-9]+'
|
9
8
|
:safe_version: ["msie [0-9]+", "[0-9]+"]
|
10
9
|
|
11
|
-
- :name: Chromium %s
|
12
|
-
:sym: :chromium
|
13
|
-
:family_sym: :chromium
|
14
|
-
:regex: 'chromium/\d+'
|
15
|
-
:version: '(?<=chromium/)[0-9]+'
|
16
|
-
:safe_version: ["chromium/[0-9]+", "[0-9]+"]
|
17
|
-
|
18
10
|
- :name: Chrome %s
|
19
11
|
:sym: :chrome
|
20
12
|
:family_sym: :chromium
|
@@ -22,6 +14,12 @@
|
|
22
14
|
:version: '(?<=chrome/)[0-9]+'
|
23
15
|
:safe_version: ["chrome/[0-9]+", "[0-9]+"]
|
24
16
|
|
17
|
+
- :name: Chromium %s
|
18
|
+
:sym: :chromium
|
19
|
+
:regex: 'chromium/\d+'
|
20
|
+
:version: '(?<=chromium/)[0-9]+'
|
21
|
+
:safe_version: ["chromium/[0-9]+", "[0-9]+"]
|
22
|
+
|
25
23
|
- :name: Android %s
|
26
24
|
:sym: :android
|
27
25
|
:family_sym: :chromium
|
@@ -29,29 +27,39 @@
|
|
29
27
|
:version: '(?<=android )[0-9]+(\.[0-9]+)?'
|
30
28
|
:safe_version: ["android [0-9]+(\.[0-9]+)?", "[0-9]+(\.[0-9]+)?"]
|
31
29
|
|
30
|
+
- :name: BlackBerry
|
31
|
+
:sym: :blackberry
|
32
|
+
:regex: blackberry
|
33
|
+
|
34
|
+
- :name: Epiphany
|
35
|
+
:sym: :epiphany
|
36
|
+
:regex: epiphany
|
37
|
+
:version: '(?<=epiphany/)[0-9]+'
|
38
|
+
:safe_version: ["epiphany/[0-9]+", "[0-9]+"]
|
39
|
+
|
32
40
|
- :name: Safari %s
|
33
41
|
:sym: :safari
|
34
|
-
:family_sym: :safari
|
35
42
|
:regex: safari
|
36
43
|
:version: '(?<=version/)[0-9]+'
|
37
44
|
:safe_version: ["version/[0-9]+", "[0-9]+"]
|
38
45
|
|
39
46
|
- :name: Opera %s
|
40
47
|
:sym: :opera
|
41
|
-
:family_sym: :opera
|
42
48
|
:regex: opera
|
43
|
-
:version: '(?<=version/)[0-9]+'
|
44
|
-
:safe_version: ["version/[0-9]+", "[0-9]+"]
|
49
|
+
:version: '((?<=version/)[0-9]+)|((?<=opera )[0-9]+)'
|
50
|
+
:safe_version: ["(version/[0-9]+)|(opera [0-9]+)", "[0-9]+"]
|
51
|
+
|
52
|
+
- :name: Konqueror
|
53
|
+
:sym: :konqueror
|
54
|
+
:regex: konqueror
|
45
55
|
|
46
56
|
- :name: Firefox %s
|
47
57
|
:sym: :firefox
|
48
|
-
:family_sym: :firefox
|
49
58
|
:regex: firefox
|
50
59
|
:version: '(?<=firefox/)[0-9]+'
|
51
60
|
:safe_version: ["firefox/[0-9]+", "[0-9]+"]
|
52
61
|
|
53
62
|
- :name: Unknown
|
54
63
|
:sym: :unknown
|
55
|
-
:family_sym: :unkown
|
56
64
|
:regex: .+
|
57
65
|
:version: .+
|
data/data/oses.yml
CHANGED
@@ -8,11 +8,24 @@
|
|
8
8
|
:family_sym: :linux
|
9
9
|
:regex: ubuntu
|
10
10
|
|
11
|
+
- :name: Fedora
|
12
|
+
:sym: :fedora
|
13
|
+
:family_sym: :linux
|
14
|
+
:regex: fedora
|
15
|
+
|
16
|
+
- :name: Slackware
|
17
|
+
:sym: :slackware
|
18
|
+
:family_sym: :linux
|
19
|
+
:regex: slackware
|
20
|
+
|
11
21
|
- :name: GNU/Linux
|
12
22
|
:sym: :linux
|
13
|
-
:family_sym: :linux
|
14
23
|
:regex: linux
|
15
24
|
|
25
|
+
- :name: FreeBSD
|
26
|
+
:sym: :freebsd
|
27
|
+
:regex: freebsd
|
28
|
+
|
16
29
|
- :name: iOS
|
17
30
|
:sym: :ios
|
18
31
|
:family_sym: :osx
|
@@ -20,13 +33,18 @@
|
|
20
33
|
|
21
34
|
- :name: OS X
|
22
35
|
:sym: :osx
|
23
|
-
:family_sym: :osx
|
24
36
|
:regex: macintosh
|
25
37
|
|
26
|
-
- :name: Windows
|
27
|
-
:sym: :
|
38
|
+
- :name: Windows 8
|
39
|
+
:sym: :windows_8
|
40
|
+
:family_sym: :windows
|
41
|
+
:regex: windows nt 6\.2
|
42
|
+
:icon: windows
|
43
|
+
|
44
|
+
- :name: Windows 7
|
45
|
+
:sym: :windows_7
|
28
46
|
:family_sym: :windows
|
29
|
-
:regex: windows nt
|
47
|
+
:regex: windows nt 6\.1
|
30
48
|
:icon: windows
|
31
49
|
|
32
50
|
- :name: Windows Vista
|
@@ -35,18 +53,20 @@
|
|
35
53
|
:regex: windows nt 6\.0
|
36
54
|
:icon: windows
|
37
55
|
|
38
|
-
- :name: Windows
|
39
|
-
:sym: :
|
56
|
+
- :name: Windows XP
|
57
|
+
:sym: :windows_xp
|
40
58
|
:family_sym: :windows
|
41
|
-
:regex: windows nt
|
59
|
+
:regex: windows nt 5\.[12]
|
42
60
|
:icon: windows
|
43
61
|
|
44
62
|
- :name: Windows
|
45
63
|
:sym: :windows
|
46
|
-
:family_sym: :windows
|
47
64
|
:regex: windows
|
48
65
|
|
49
|
-
- :
|
66
|
+
- :name: BlackBerry
|
67
|
+
:sym: :blackberry
|
68
|
+
:regex: blackberry
|
69
|
+
|
70
|
+
- :name: Unknown
|
50
71
|
:sym: :unknown
|
51
|
-
:name: Unknown
|
52
72
|
:regex: .+
|
data/lib/double_agent/core.rb
CHANGED
@@ -1,50 +1,60 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
3
|
module DoubleAgent
|
4
|
-
#
|
5
|
-
# Map browser/os ids to names, families and icons
|
6
|
-
#
|
4
|
+
# An array of "browser knowledge hashes," the basis of browser parsing. You may edit this data and call load_browsers! to customize parsing.
|
7
5
|
BROWSER_DATA = YAML.load_file(File.expand_path('../../../data/browsers.yml', __FILE__))
|
6
|
+
|
7
|
+
# An array of "OS knowledge hashes," the basis of OS parsing. You may edit this data and call load_oses! to customize parsing.
|
8
8
|
OS_DATA = YAML.load_file(File.expand_path('../../../data/oses.yml', __FILE__))
|
9
9
|
|
10
|
+
# An array of BrowserParser objects created from the data in BROWSER_DATA.
|
10
11
|
BROWSERS = {}
|
12
|
+
|
13
|
+
# An array of OSParser objects created from the data in OS_DATA.
|
11
14
|
OSES = {}
|
12
15
|
|
16
|
+
# Each browser in BROWSER_DATA gets its own BrowserParser object. These
|
17
|
+
# parser objects are then used to parse specific data out of a user agent string.
|
18
|
+
|
13
19
|
class BrowserParser
|
14
20
|
BLANK = ''
|
15
21
|
MIN_VERSION = '1.9.2'
|
16
22
|
attr_reader :sym, :family_sym, :icon
|
17
23
|
|
24
|
+
# Instantiate a new BrowserParser using a "browser family" element from BROWSER_DATA
|
18
25
|
def initialize(attrs={})
|
19
|
-
@family_sym = attrs[:family_sym]
|
20
|
-
@name = attrs[:name]
|
21
26
|
@sym = attrs[:sym]
|
27
|
+
@family_sym = attrs[:family_sym] || @sym
|
28
|
+
@name = attrs[:name]
|
22
29
|
@icon = attrs[:icon] || @sym
|
23
30
|
if RUBY_VERSION < MIN_VERSION and attrs[:safe_version]
|
24
31
|
@safe_version = attrs[:safe_version].map { |r| Regexp.new r, Regexp::IGNORECASE }
|
25
|
-
|
32
|
+
elsif attrs[:version]
|
26
33
|
@version = Regexp.new(attrs[:version], Regexp::IGNORECASE)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
37
|
+
# Returns the browser's name. If you provide an user agent string as an argument,
|
38
|
+
# it will attempt to also return the major version number. E.g. "Firefox 4".
|
30
39
|
def browser(ua=nil)
|
31
|
-
if ua
|
40
|
+
if ua and (@version or @safe_version)
|
32
41
|
@name % version(ua)
|
33
42
|
else
|
34
43
|
(@name % BLANK).rstrip
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
47
|
+
# Returns the BrowserParser for this BrowserParser object's Family. E.g. the Chrome
|
48
|
+
# BrowserParser would return the Chromium BrowserParser. For browsers that are their
|
49
|
+
# own family (e.g. Firefox, IE) it will end up returning itself.
|
42
50
|
def family
|
43
51
|
BROWSERS[family_sym]
|
44
52
|
end
|
45
53
|
|
46
54
|
private
|
47
55
|
|
56
|
+
# Attempts to parse and return the browser's version from a user agent string. Returns
|
57
|
+
# nil if nothing is found.
|
48
58
|
def version(ua)
|
49
59
|
if @safe_version and RUBY_VERSION < MIN_VERSION
|
50
60
|
ua.slice(@safe_version[0]).slice(@safe_version[1])
|
@@ -54,81 +64,101 @@ module DoubleAgent
|
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
67
|
+
# Each OS in OS_DATA gets its own OSParser object. In theory, these parser
|
68
|
+
# objects can then be used to grab further info from user agent strings, though
|
69
|
+
# that is not currently happening.
|
70
|
+
|
57
71
|
class OSParser
|
58
72
|
attr_reader :os, :sym, :family_sym, :icon
|
59
73
|
|
74
|
+
# Instantiate a new OSParser using an "OS family" element from OS_DATA
|
60
75
|
def initialize(attrs={})
|
61
|
-
@family_sym = attrs[:family_sym]
|
62
|
-
@os = attrs[:name]
|
63
76
|
@sym = attrs[:sym]
|
77
|
+
@family_sym = attrs[:family_sym] || @sym
|
78
|
+
@os = attrs[:name]
|
64
79
|
@icon = attrs[:icon] || @sym
|
65
80
|
end
|
66
81
|
|
82
|
+
# Returns the OSParser for this OSParser object's Family. E.g. the Ubuntu
|
83
|
+
# OSParser would return the GNU/Linux OSerParser. For OSes that are their own
|
84
|
+
# family (e.g. OS X) it will end up returning itself.
|
67
85
|
def family
|
68
86
|
OSES[family_sym]
|
69
87
|
end
|
70
88
|
end
|
71
89
|
|
72
|
-
#
|
73
|
-
# Methods for getting browser/os names, families, and icons either by passing a user agent string.
|
74
|
-
#
|
75
|
-
|
90
|
+
# Returns the browser's name, possibly including the version number, e.g. "Chrome 12"
|
76
91
|
def self.browser(ua)
|
77
92
|
browser_parser(ua).browser(ua)
|
78
93
|
end
|
79
94
|
|
95
|
+
# Returns the browser's symbol name, e.g. :chrome
|
80
96
|
def self.browser_sym(user_agent)
|
97
|
+
# This method is overwitten by load_browsers!
|
81
98
|
end
|
82
99
|
|
100
|
+
# Returns the browser's icon name, e.g. :chrome
|
83
101
|
def self.browser_icon(ua)
|
84
102
|
browser_parser(ua).icon
|
85
103
|
end
|
86
104
|
|
105
|
+
# Returns the browser's family name, e.g. "Chromium"
|
87
106
|
def self.browser_family(ua)
|
88
107
|
browser_parser(ua).family.browser
|
89
108
|
end
|
90
109
|
|
110
|
+
# Returns the browser's family's symbol name, e.g. :chromium
|
91
111
|
def self.browser_family_sym(ua)
|
92
112
|
browser_parser(ua).family_sym
|
93
113
|
end
|
94
114
|
|
115
|
+
# Returns the browser's family's icon name, e.g. :chromium
|
95
116
|
def self.browser_family_icon(ua)
|
96
117
|
browser_parser(ua).family.icon
|
97
118
|
end
|
98
119
|
|
120
|
+
# Returns the OS's name, e.g. "Ubuntu"
|
99
121
|
def self.os(ua)
|
100
122
|
os_parser(ua).os
|
101
123
|
end
|
102
124
|
|
125
|
+
# Returns the OS's symbol name, e.g. :ubuntu
|
103
126
|
def self.os_sym(user_agent)
|
127
|
+
# This method is overwitten by load_oses!
|
104
128
|
end
|
105
129
|
|
130
|
+
# Returns the OS's icon name, e.g. :ubuntu
|
106
131
|
def self.os_icon(ua)
|
107
132
|
os_parser(ua).icon
|
108
133
|
end
|
109
134
|
|
135
|
+
# Returns the OS's family, e.g. "GNU/Linux"
|
110
136
|
def self.os_family(ua)
|
111
137
|
os_parser(ua).family.os
|
112
138
|
end
|
113
139
|
|
140
|
+
# Returns the OS's family's symbol name, e.g. :linux
|
114
141
|
def self.os_family_sym(ua)
|
115
142
|
os_parser(ua).family_sym
|
116
143
|
end
|
117
144
|
|
145
|
+
# Returns the OS's family's symbol icon, e.g. :linux
|
118
146
|
def self.os_family_icon(ua)
|
119
147
|
os_parser(ua).family.icon
|
120
148
|
end
|
121
149
|
|
122
|
-
#
|
150
|
+
# Returns the correct BrowerParser for the given user agent or symbol
|
123
151
|
def self.browser_parser(ua_or_sym)
|
124
152
|
BROWSERS[ua_or_sym.is_a?(Symbol) ? ua_or_sym : browser_sym(ua_or_sym)]
|
125
153
|
end
|
126
154
|
|
127
|
-
#
|
155
|
+
# Returns the correct OSParser for the given user agent or symbol
|
128
156
|
def self.os_parser(ua_or_sym)
|
129
157
|
OSES[ua_or_sym.is_a?(Symbol) ? ua_or_sym : os_sym(ua_or_sym)]
|
130
158
|
end
|
131
159
|
|
160
|
+
# Parses BROWSER_DATA into BROWSERS, a hash of BrowserParser objects indexed by their symbol names.
|
161
|
+
# Parses and evals BROWSER_DATA into a case statement inside of the browser_sym method.
|
132
162
|
def self.load_browsers!
|
133
163
|
BROWSERS.clear
|
134
164
|
str = "case user_agent\n"
|
@@ -140,6 +170,8 @@ module DoubleAgent
|
|
140
170
|
module_eval "def self.browser_sym(user_agent); #{str}; end"
|
141
171
|
end
|
142
172
|
|
173
|
+
# Parses OS_DATA into OSES, a hash of OSParser objects indexed by their symbol names.
|
174
|
+
# Parses and evals OS_DATA into a case statement inside of the os_sym method.
|
143
175
|
def self.load_oses!
|
144
176
|
OSES.clear
|
145
177
|
str = "case user_agent\n"
|
data/lib/double_agent/logs.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'zlib'
|
2
2
|
|
3
3
|
module DoubleAgent
|
4
|
+
# Accepts a glob path like /var/logs/apache/my-site.access.log*,
|
5
|
+
# parses all matching files into an array of LegEntry objects, and returns them.
|
6
|
+
#
|
7
|
+
# If a Regexp is passed as the second argument, lines which do not match
|
8
|
+
# it will be ignored.
|
4
9
|
def self.log_entries(glob_str, regex=nil)
|
5
10
|
entries, gz_regexp = [], /\.gz\Z/i
|
6
11
|
Dir.glob(glob_str).each do |f|
|
@@ -14,12 +19,20 @@ module DoubleAgent
|
|
14
19
|
entries
|
15
20
|
end
|
16
21
|
|
22
|
+
# This class represents a line in an Apache or Nginx access log.
|
23
|
+
# The user agent string is parsed out and available through the
|
24
|
+
# user_agent attribute, making it available to the mixed-in DoubleAgent::Resource.
|
25
|
+
|
17
26
|
class LogEntry
|
27
|
+
# Regular expression for pulling a user agent string out of a log entry
|
18
28
|
USER_AGENT_REGEXP = /[^"]+(?="$)/
|
19
29
|
include DoubleAgent::Resource
|
20
30
|
|
31
|
+
# Returns the user agent string
|
21
32
|
attr_reader :user_agent
|
22
33
|
|
34
|
+
# Initializes a new LogEntry object. An Apache or Nginx log line should be
|
35
|
+
# passed to it.
|
23
36
|
def initialize(line)
|
24
37
|
#@line = line
|
25
38
|
@user_agent = line.slice(USER_AGENT_REGEXP)
|
@@ -1,63 +1,77 @@
|
|
1
1
|
module DoubleAgent
|
2
2
|
#
|
3
|
-
# Any class with a "user_agent" method
|
3
|
+
# Any class with a "user_agent" method returning a User Agent string
|
4
4
|
# may include this module to easily parse out Browser and OS info.
|
5
5
|
#
|
6
6
|
module Resource
|
7
|
+
# Return's this object's browser name
|
7
8
|
def browser
|
8
9
|
_browser_parser.browser(user_agent)
|
9
10
|
end
|
10
11
|
|
12
|
+
# Return's this object's browser symbol name
|
11
13
|
def browser_sym
|
12
14
|
_browser_parser.sym
|
13
15
|
end
|
14
16
|
|
17
|
+
# Return's this object's browser icon name
|
15
18
|
def browser_icon
|
16
19
|
_browser_parser.icon
|
17
20
|
end
|
18
21
|
|
22
|
+
# Return's this object's browser family name
|
19
23
|
def browser_family
|
20
24
|
_browser_parser.family.browser
|
21
25
|
end
|
22
26
|
|
27
|
+
# Return's this object's browser family symbol name
|
23
28
|
def browser_family_sym
|
24
29
|
_browser_parser.family_sym
|
25
30
|
end
|
26
31
|
|
32
|
+
# Return's this object's browser family icon name
|
27
33
|
def browser_family_icon
|
28
34
|
_browser_parser.family.icon
|
29
35
|
end
|
30
36
|
|
37
|
+
# Return's this object's OS name
|
31
38
|
def os
|
32
39
|
_os_parser.os
|
33
40
|
end
|
34
41
|
|
42
|
+
# Return's this object's OS symbol name
|
35
43
|
def os_sym
|
36
44
|
_os_parser.sym
|
37
45
|
end
|
38
46
|
|
47
|
+
# Return's this object's OS icon name
|
39
48
|
def os_icon
|
40
49
|
_os_parser.icon
|
41
50
|
end
|
42
51
|
|
52
|
+
# Return's this object's OS family name
|
43
53
|
def os_family
|
44
54
|
_os_parser.family.os
|
45
55
|
end
|
46
56
|
|
57
|
+
# Return's this object's OS family symbol name
|
47
58
|
def os_family_sym
|
48
59
|
_os_parser.family_sym
|
49
60
|
end
|
50
61
|
|
62
|
+
# Return's this object's OS family icon name
|
51
63
|
def os_family_icon
|
52
64
|
_os_parser.family.icon
|
53
65
|
end
|
54
66
|
|
55
67
|
private
|
56
68
|
|
69
|
+
# Returns and caches a BrowserParser for this object
|
57
70
|
def _browser_parser
|
58
71
|
@browser_parser ||= DoubleAgent.browser_parser(user_agent)
|
59
72
|
end
|
60
73
|
|
74
|
+
# Returns and caches an OSParser for this object
|
61
75
|
def _os_parser
|
62
76
|
@os_parser ||= DoubleAgent.os_parser(user_agent)
|
63
77
|
end
|
data/lib/double_agent/stats.rb
CHANGED
@@ -1,33 +1,29 @@
|
|
1
|
-
# Time for some monkey patching!
|
2
|
-
|
3
1
|
module DoubleAgent
|
2
|
+
# True if running under less than Ruby 1.9
|
4
3
|
BAD_RUBY = RUBY_VERSION < '1.9.0'
|
5
4
|
|
6
5
|
if BAD_RUBY
|
7
6
|
require 'bigdecimal'
|
7
|
+
# If BAD_RUBY, this is used in lieu of the native round method
|
8
8
|
def self.better_round(f, n)
|
9
9
|
d = BigDecimal.new f.to_s
|
10
10
|
d.round(n).to_f
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
#
|
15
14
|
# For the given "things", returns the share of the group that each attr has.
|
16
15
|
#
|
17
16
|
# "things" is an array of objects who's classes "include DoubleAgent::Resource".
|
18
17
|
#
|
19
|
-
# "attrs" is one or more method symbols from DoubleAgent::Resource.
|
20
|
-
# are probably the most useful here, as you can use the symbol results to query DoubleAgent
|
21
|
-
# methods and get the browser/os name, family and icons.
|
18
|
+
# "attrs" is one or more method symbols from DoubleAgent::Resource.
|
22
19
|
#
|
23
20
|
# Example, Browser Family share:
|
24
21
|
# DoubleAgent.percentages_for(logins, :browser_family)
|
25
|
-
#
|
22
|
+
# [['Firefox', 50.4], ['Chrome', 19.6], ['Internet Explorer', 15], ['Safari', 10], ['Unknown', 5]]
|
26
23
|
#
|
27
24
|
# Example, Browser/OS share, asking for symbols back:
|
28
25
|
# DoubleAgent.percentages_for(server_log_entries, :browser_sym, :os_sym)
|
29
|
-
#
|
30
|
-
#
|
26
|
+
# [[:firefox, :windows_7, 50.4], [:chrome, :osx, 19.6], [:msie, :windows_xp, 15], [:safari, :osx, 10], [:other, :other, 5]]
|
31
27
|
def self.percentages_for(things, *attrs)
|
32
28
|
p = {}
|
33
29
|
things.each do |h|
|
data/spec/core_spec.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe DoubleAgent do
|
4
|
+
context 'Core' do
|
5
|
+
before do
|
6
|
+
@ua_string = 'Mozilla/5.0 (X11; Ubuntu Linux i686; rv:2.0) Gecko/20100101 Firefox/4.0'
|
7
|
+
end
|
8
|
+
|
9
|
+
#browser
|
10
|
+
it 'returns Firefox 4 for browser' do
|
11
|
+
DoubleAgent.browser(@ua_string).should == 'Firefox 4'
|
12
|
+
end
|
13
|
+
it 'returns Unknown for browser' do
|
14
|
+
DoubleAgent.browser('froofroo').should == 'Unknown'
|
15
|
+
end
|
16
|
+
|
17
|
+
#browser_sym
|
18
|
+
it 'returns :firefox for browser_sym' do
|
19
|
+
DoubleAgent.browser_sym(@ua_string).should == :firefox
|
20
|
+
end
|
21
|
+
it 'returns :unknown for browser_sym' do
|
22
|
+
DoubleAgent.browser_sym('froofroo').should == :unknown
|
23
|
+
end
|
24
|
+
|
25
|
+
#browser_family
|
26
|
+
it 'returns Firefox for browser family' do
|
27
|
+
DoubleAgent.browser_family(@ua_string).should == 'Firefox'
|
28
|
+
end
|
29
|
+
|
30
|
+
#browser_family_sym
|
31
|
+
it 'returns :firefox for browser_family_sym' do
|
32
|
+
DoubleAgent.browser_family_sym(@ua_string).should == :firefox
|
33
|
+
end
|
34
|
+
|
35
|
+
#browser_icon
|
36
|
+
it 'returns :firefox for browser_sym' do
|
37
|
+
DoubleAgent.browser_icon(@ua_string).should == :firefox
|
38
|
+
end
|
39
|
+
|
40
|
+
#browser_family_icon
|
41
|
+
it 'returns :firefox for browser_family_sym' do
|
42
|
+
DoubleAgent.browser_family_icon(@ua_string).should == :firefox
|
43
|
+
end
|
44
|
+
|
45
|
+
#os
|
46
|
+
it 'returns Ubuntua for OS' do
|
47
|
+
DoubleAgent.os(@ua_string).should == 'Ubuntu'
|
48
|
+
end
|
49
|
+
it 'returns Unknowna for OS' do
|
50
|
+
DoubleAgent.os('froofroo').should == 'Unknown'
|
51
|
+
end
|
52
|
+
|
53
|
+
#os_sym
|
54
|
+
it 'returns :ubuntu for os_sym' do
|
55
|
+
DoubleAgent.os_sym(@ua_string).should == :ubuntu
|
56
|
+
end
|
57
|
+
it 'returns :unknown for os_sym' do
|
58
|
+
DoubleAgent.os_sym('froofroo').should == :unknown
|
59
|
+
end
|
60
|
+
|
61
|
+
#os_family
|
62
|
+
it 'returns GNU/Linux OS family' do
|
63
|
+
DoubleAgent.os_family(@ua_string).should == 'GNU/Linux'
|
64
|
+
end
|
65
|
+
|
66
|
+
#os_family_sym
|
67
|
+
it 'returns :linux for os_family_sym' do
|
68
|
+
DoubleAgent.os_family_sym(@ua_string).should == :linux
|
69
|
+
end
|
70
|
+
|
71
|
+
#os_icon
|
72
|
+
it 'returns :ubuntu for os_sym' do
|
73
|
+
DoubleAgent.os_icon(@ua_string).should == :ubuntu
|
74
|
+
end
|
75
|
+
|
76
|
+
#os_family_icon
|
77
|
+
it 'returns :linux for os_family_sym' do
|
78
|
+
DoubleAgent.os_family_icon(@ua_string).should == :linux
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
68.52.99.211 - - [05/May/2011:08:21:04 -0400] "GET / HTTP/1.1" 200 1312 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17"
|
2
|
+
68.52.99.211 - - [05/May/2011:08:25:47 -0400] "POST /entries HTTP/1.1" 200 7889 "http://www.example.com/entries/new" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17"
|
3
|
+
216.84.130.30 - - [04/May/2011:09:45:42 -0400] "GET /dashboard HTTP/1.1" 302 104 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_6 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8E200 Safari/6533.18.5"
|
4
|
+
173.190.91.2 - - [04/May/2011:10:51:09 -0400] "GET /images/icons/opera_browser.png?1295928127 HTTP/1.1" 304 0 "http://cur8.cur8.net/sbin/sites/2/page_hit_stats" "Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
|
5
|
+
173.190.91.2 - - [04/May/2011:10:51:09 -0400] "GET /images/icons/ubuntu_os.png?1295928127 HTTP/1.1" 304 0 "http://cur8.cur8.net/sbin/sites/2/page_hit_stats" "Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
|
6
|
+
173.190.91.2 - - [04/May/2011:10:52:29 -0400] "GET /sbin/sites/2/login_stats HTTP/1.1" 200 10131 "http://cur8.cur8.net/sbin/sites/2/page_hit_stats" "Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
|
7
|
+
173.190.91.2 - - [04/May/2011:10:52:34 -0400] "GET /sbin/sites/2/page_hit_stats HTTP/1.1" 200 12460 "http://cur8.cur8.net/sbin/sites/2/login_stats" "Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
|
8
|
+
bad log line!
|
9
|
+
173.190.91.2 - - [04/May/2011:10:53:10 -0400] "GET /sbin/sites/2 HTTP/1.1" 304 0 "http://cur8.cur8.net/sbin/sites/2/page_hit_stats" "Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"
|
10
|
+
216.84.130.30 - - [04/May/2011:11:11:39 -0400] "GET /login HTTP/1.1" 200 1104 "-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
11
|
+
216.84.130.30 - - [04/May/2011:11:11:39 -0400] "GET /stylesheets/styles.css?1303352447 HTTP/1.1" 304 0 "http://www.example.com/login" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
12
|
+
216.84.130.30 - - [04/May/2011:11:11:39 -0400] "GET /javascripts/lib.js?1303099416 HTTP/1.1" 304 0 "http://www.example.com/login" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
13
|
+
216.84.130.30 - - [04/May/2011:11:11:39 -0400] "GET /stylesheets/print.css?1290464093 HTTP/1.1" 304 0 "http://www.example.com/login" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
14
|
+
bad log line!
|
15
|
+
216.84.130.30 - - [04/May/2011:11:11:39 -0400] "GET /javascripts/application.js?1303352447 HTTP/1.1" 304 0 "http://www.example.com/login" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
16
|
+
75.201.10.91 - - [04/May/2011:18:37:10 -0400] "GET /entries/new HTTP/1.1" 302 104 "-" "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; ADR6400L 4G Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
|
17
|
+
75.201.10.91 - - [04/May/2011:18:37:10 -0400] "GET /login HTTP/1.1" 200 1107 "-" "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; ADR6400L 4G Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
|
18
|
+
75.201.10.91 - - [04/May/2011:18:43:33 -0400] "GET /dashboard HTTP/1.1" 200 8098 "http://www.example.com/login" "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; ADR6400L 4G Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
|
19
|
+
75.201.10.91 - - [04/May/2011:18:43:55 -0400] "GET /search HTTP/1.1" 200 4070 "http://www.example.com/dashboard" "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; ADR6400L 4G Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
|
20
|
+
bad log line!
|
Binary file
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
DA = DoubleAgent
|
4
|
+
|
5
|
+
describe DoubleAgent do
|
6
|
+
# Internet Explorer
|
7
|
+
it 'should be Internet Explorer 10 on Windows 8' do
|
8
|
+
ua = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/5.0"
|
9
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Internet Explorer 10 on Windows 8'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should be Internet Explorer 9 on Windows 7' do
|
13
|
+
ua = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0"
|
14
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Internet Explorer 9 on Windows 7'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should be Internet Explorer 8 on Windows Vista' do
|
18
|
+
ua = "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Win64; x64; Trident/5.0"
|
19
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Internet Explorer 8 on Windows Vista'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be Internet Explorer 7 on Windows XP' do
|
23
|
+
ua = "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.2; Win64; x64; Trident/5.0"
|
24
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Internet Explorer 7 on Windows XP'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should be Internet Explorer 7 on Windows XP' do
|
28
|
+
ua = "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 5.1; Win64; x64; Trident/5.0"
|
29
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Internet Explorer 7 on Windows XP'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Chrome
|
33
|
+
it 'should be Chrome 12 on Windows XP' do
|
34
|
+
ua = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.25 (KHTML, like Gecko) Chrome/12.0.706.0 Safari/534.25"
|
35
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Chrome 12 on Windows XP'
|
36
|
+
end
|
37
|
+
|
38
|
+
# Chromium
|
39
|
+
it 'should be Chrome 12 on Ubuntu' do
|
40
|
+
ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24"
|
41
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Chrome 12 on Ubuntu'
|
42
|
+
end
|
43
|
+
|
44
|
+
# Android
|
45
|
+
it 'should be Android 2.3 on Android' do
|
46
|
+
ua = "Mozilla/5.0 (Linux; U; Android 2.3.3; zh-tw; HTC_Pyramid Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
|
47
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Android 2.3 on Android'
|
48
|
+
end
|
49
|
+
|
50
|
+
# Safari
|
51
|
+
it 'should be Safari 5 on OS X' do
|
52
|
+
ua = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; zh-cn) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27"
|
53
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Safari 5 on OS X'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Opera
|
57
|
+
it 'should be Opera 11 on GNU/Linux' do
|
58
|
+
ua = "Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00"
|
59
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Opera 11 on GNU/Linux'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should be Opera 11 on Windows 7' do
|
63
|
+
ua = "Mozilla/5.0 (Windows NT 6.1; U; nl; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 Opera 11.01"
|
64
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Opera 11 on Windows 7'
|
65
|
+
end
|
66
|
+
|
67
|
+
# Firefox
|
68
|
+
it 'should be Firefox 4 on GNU/Linux' do
|
69
|
+
ua = "Mozilla/5.0 (X11; U; Linux x86_64; pl-PL; rv:2.0) Gecko/20110307 Firefox/4.0"
|
70
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Firefox 4 on GNU/Linux'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Epiphany
|
74
|
+
it 'should be Epiphany on GNU/Linux' do
|
75
|
+
ua = "Mozilla/5.0 (X11; U; Linux x86_64; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Epiphany/2.30.6 Safari/534.7"
|
76
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Epiphany on GNU/Linux'
|
77
|
+
end
|
78
|
+
|
79
|
+
# Konqueror
|
80
|
+
it 'should be Konqueror on FreeBSD' do
|
81
|
+
ua = "Mozilla/5.0 (compatible; Konqueror/4.5; FreeBSD) KHTML/4.5.4 (like Gecko)"
|
82
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Konqueror on FreeBSD'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be Konqueror on Fedora' do
|
86
|
+
ua = "Mozilla/5.0 (compatible; Konqueror/4.4; Linux) KHTML/4.4.1 (like Gecko) Fedora/4.4.1-1.fc12"
|
87
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Konqueror on Fedora'
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be Konqueror on Slackware' do
|
91
|
+
ua = "Mozilla/5.0 (compatible; Konqueror/4.2; Linux) KHTML/4.2.4 (like Gecko) Slackware/13.0"
|
92
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'Konqueror on Slackware'
|
93
|
+
end
|
94
|
+
|
95
|
+
# BlackBerry
|
96
|
+
it 'should be BlackBerry on BlackBerry' do
|
97
|
+
ua = "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; zh-TW) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8+"
|
98
|
+
"#{DA.browser ua} on #{DA.os ua}".should == 'BlackBerry on BlackBerry'
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class Login
|
4
|
+
include DoubleAgent::Resource
|
5
|
+
attr_accessor :user_agent
|
6
|
+
|
7
|
+
def initialize(ua)
|
8
|
+
@user_agent = ua
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe DoubleAgent do
|
13
|
+
context 'Resource' do
|
14
|
+
before do
|
15
|
+
@login = Login.new 'Mozilla/5.0 (X11; Ubuntu Linux i686; rv:2.0) Gecko/20100101 Firefox/4.0'
|
16
|
+
end
|
17
|
+
|
18
|
+
#browser
|
19
|
+
it 'returns Firefox 4 for browser' do
|
20
|
+
@login.browser.should == 'Firefox 4'
|
21
|
+
end
|
22
|
+
|
23
|
+
#browser_sym
|
24
|
+
it 'returns :firefox for browser_sym' do
|
25
|
+
@login.browser_sym.should == :firefox
|
26
|
+
end
|
27
|
+
|
28
|
+
#browser_family
|
29
|
+
it 'returns Firefox for browser family' do
|
30
|
+
@login.browser_family.should == 'Firefox'
|
31
|
+
end
|
32
|
+
|
33
|
+
#browser_family_sym
|
34
|
+
it 'returns :firefox for browser_family_sym' do
|
35
|
+
@login.browser_family_sym.should == :firefox
|
36
|
+
end
|
37
|
+
|
38
|
+
#browser_icon
|
39
|
+
it 'returns :firefox for browser_sym' do
|
40
|
+
@login.browser_icon.should == :firefox
|
41
|
+
end
|
42
|
+
|
43
|
+
#browser_family_icon
|
44
|
+
it 'returns :firefox for browser_family_sym' do
|
45
|
+
@login.browser_family_icon.should == :firefox
|
46
|
+
end
|
47
|
+
|
48
|
+
#os
|
49
|
+
it 'returns Ubuntua for OS' do
|
50
|
+
@login.os.should == 'Ubuntu'
|
51
|
+
end
|
52
|
+
|
53
|
+
#os_sym
|
54
|
+
it 'returns :ubuntu for os_sym' do
|
55
|
+
@login.os_sym.should == :ubuntu
|
56
|
+
end
|
57
|
+
|
58
|
+
#os_family
|
59
|
+
it 'returns GNU/Linux OS family' do
|
60
|
+
@login.os_family.should == 'GNU/Linux'
|
61
|
+
end
|
62
|
+
|
63
|
+
#os_family_sym
|
64
|
+
it 'returns :linux for os_family_sym' do
|
65
|
+
@login.os_family_sym.should == :linux
|
66
|
+
end
|
67
|
+
|
68
|
+
#os_icon
|
69
|
+
it 'returns :ubuntu for os_sym' do
|
70
|
+
@login.os_icon.should == :ubuntu
|
71
|
+
end
|
72
|
+
|
73
|
+
#os_family_icon
|
74
|
+
it 'returns :linux for os_family_sym' do
|
75
|
+
@login.os_family_icon.should == :linux
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/double_agent/core'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/double_agent/resources'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/double_agent/stats'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/double_agent/logs'
|
6
|
+
|
7
|
+
Rspec.configure do |c|
|
8
|
+
c.mock_with :rspec
|
9
|
+
end
|
data/spec/stats_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
log_glob = File.dirname(__FILE__) + '/data/*.access.log*'
|
4
|
+
entries = DoubleAgent::log_entries(log_glob, /^\d/)
|
5
|
+
|
6
|
+
describe DoubleAgent do
|
7
|
+
context 'Logs' do
|
8
|
+
it 'should have loaded n log entries' do
|
9
|
+
entries.size.should == 47
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'Stats' do
|
14
|
+
stats = DoubleAgent.percentages_for entries, :browser_family, :os_family
|
15
|
+
answer = [["Internet Explorer", "Windows", 42.55],
|
16
|
+
["Chromium", "GNU/Linux", 40.43],
|
17
|
+
["Firefox", "GNU/Linux", 10.64],
|
18
|
+
["Firefox", "OS X", 4.26],
|
19
|
+
["Safari", "OS X", 2.13]]
|
20
|
+
stats.should == answer
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: double_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 0.0.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.3
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- Jordan Hollinger
|
@@ -15,8 +10,7 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2011-04-30 00:00:00
|
19
|
-
default_executable:
|
13
|
+
date: 2011-04-30 00:00:00 Z
|
20
14
|
dependencies: []
|
21
15
|
|
22
16
|
description: Browser User Agent string parser with resource, stats, and a log reader
|
@@ -25,19 +19,28 @@ executables: []
|
|
25
19
|
|
26
20
|
extensions: []
|
27
21
|
|
28
|
-
extra_rdoc_files:
|
29
|
-
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- CHANGELOG
|
30
25
|
files:
|
31
|
-
- lib/double_agent.rb
|
32
26
|
- lib/double_agent/all.rb
|
33
|
-
- lib/double_agent/stats.rb
|
34
|
-
- lib/double_agent/resources.rb
|
35
27
|
- lib/double_agent/core.rb
|
28
|
+
- lib/double_agent/resources.rb
|
36
29
|
- lib/double_agent/logs.rb
|
37
|
-
-
|
30
|
+
- lib/double_agent/stats.rb
|
31
|
+
- lib/double_agent.rb
|
38
32
|
- data/oses.yml
|
33
|
+
- data/browsers.yml
|
34
|
+
- spec/resources_spec.rb
|
35
|
+
- spec/core_spec.rb
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
- spec/parser_spec.rb
|
38
|
+
- spec/data/httpd.access.log
|
39
|
+
- spec/data/httpd.access.log.1.gz
|
40
|
+
- spec/stats_spec.rb
|
41
|
+
- README.rdoc
|
39
42
|
- LICENSE
|
40
|
-
|
43
|
+
- CHANGELOG
|
41
44
|
homepage: http://github.com/jhollinger/double_agent
|
42
45
|
licenses: []
|
43
46
|
|
@@ -51,23 +54,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
54
|
requirements:
|
52
55
|
- - ">="
|
53
56
|
- !ruby/object:Gem::Version
|
54
|
-
hash: 3
|
55
|
-
segments:
|
56
|
-
- 0
|
57
57
|
version: "0"
|
58
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
59
|
none: false
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
hash: 3
|
64
|
-
segments:
|
65
|
-
- 0
|
66
63
|
version: "0"
|
67
64
|
requirements: []
|
68
65
|
|
69
66
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.7.2
|
71
68
|
signing_key:
|
72
69
|
specification_version: 3
|
73
70
|
summary: Browser User Agent string parser
|