wurfl_device 0.0.10 → 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/lib/wurfl_device/cli.rb +12 -1
- data/lib/wurfl_device/constants.rb +2 -8
- data/lib/wurfl_device/device.rb +2 -1
- data/lib/wurfl_device/user_agent.rb +139 -0
- data/lib/wurfl_device/user_agent_matcher.rb +568 -94
- data/lib/wurfl_device/version.rb +1 -1
- data/lib/wurfl_device.rb +55 -34
- data/spec/cache/device_spec.rb +19 -3
- metadata +25 -24
data/lib/wurfl_device/cli.rb
CHANGED
@@ -29,7 +29,7 @@ module WurflDevice
|
|
29
29
|
method_option :yaml, :type => :boolean, :banner => "show the dump in yaml format", :aliases => "-y"
|
30
30
|
def dump(device_id)
|
31
31
|
device = WurflDevice.get_device_from_id(device_id)
|
32
|
-
device = WurflDevice.get_device_from_ua(device_id
|
32
|
+
device = WurflDevice.get_device_from_ua(device_id) if device.nil?
|
33
33
|
|
34
34
|
if options.json?
|
35
35
|
WurflDevice.ui.info device.capabilities.to_json
|
@@ -103,6 +103,17 @@ module WurflDevice
|
|
103
103
|
WurflDevice.ui.info " " + commify(devices.length) + " device id's"
|
104
104
|
WurflDevice.ui.info " " + commify(user_agents.length) + " exact user agents" + user_agents_message
|
105
105
|
WurflDevice.ui.info " " + commify(WurflDevice.get_user_agents_in_cache.length) + " user agents found in cache"
|
106
|
+
indexes = Array.new
|
107
|
+
WurflDevice.get_indexes.each do |index|
|
108
|
+
index.gsub!(WurflDevice::Constants::WURFL_DEVICES_INDEX, '')
|
109
|
+
indexes << "#{index}(" + commify(WurflDevice.get_user_agents_in_index(index).length) + ")"
|
110
|
+
end
|
111
|
+
indexes.sort!
|
112
|
+
WurflDevice.ui.info "wurfl user agent indexes:"
|
113
|
+
while !indexes.empty?
|
114
|
+
sub = indexes.slice!(0, 7)
|
115
|
+
WurflDevice.ui.info " " + sub.join(', ')
|
116
|
+
end
|
106
117
|
WurflDevice.ui.info ""
|
107
118
|
end
|
108
119
|
map %w(stats stat info) => :status
|
@@ -7,6 +7,8 @@ module WurflDevice
|
|
7
7
|
|
8
8
|
DB_INDEX = "7".freeze
|
9
9
|
GENERIC = 'generic'
|
10
|
+
GENERIC_XHTML = 'generic_xhtml'
|
11
|
+
GENERIC_WEB_BROWSER = 'generic_web_browser'
|
10
12
|
WURFL = "wurfl:"
|
11
13
|
WURFL_INFO = "wurfl:info"
|
12
14
|
WURFL_DEVICES = "wurfl:devices:"
|
@@ -16,14 +18,6 @@ module WurflDevice
|
|
16
18
|
WURFL_USER_AGENTS = "wurfl:user_agents"
|
17
19
|
WURFL_USER_AGENTS_CACHED = "wurfl:user_agents_cached"
|
18
20
|
|
19
|
-
USER_AGENT_MATCHERS =
|
20
|
-
[
|
21
|
-
"Alcatel", "Android", "AOL", "Apple", "BenQ", "BlackBerry", "Bot", "CatchAll", "Chrome", "DoCoMo",
|
22
|
-
"Firefox", "Grundig", "HTC", "Kddi", "Konqueror", "Kyocera", "LG", "Mitsubishi", "Motorola", "MSIE",
|
23
|
-
"Nec", "Nintendo", "Nokia", "Opera", "OperaMini", "Panasonic", "Pantech", "Philips", "Portalmmm", "Qtek",
|
24
|
-
"Safari", "Sagem", "Samsung", "Sanyo", "Sharp", "Siemens", "SonyEricsson", "SPV", "Toshiba", "Vodafone", "WindowsCE"
|
25
|
-
]
|
26
|
-
|
27
21
|
MOBILE_BROWSERS = [
|
28
22
|
'cldc', 'symbian', 'midp', 'j2me', 'mobile', 'wireless', 'palm', 'phone', 'pocket pc', 'pocketpc', 'netfront',
|
29
23
|
'bolt', 'iris', 'brew', 'openwave', 'windows ce', 'wap2.', 'android', 'opera mini', 'opera mobi', 'maemo', 'fennec',
|
data/lib/wurfl_device/device.rb
CHANGED
@@ -12,6 +12,7 @@ module WurflDevice
|
|
12
12
|
else
|
13
13
|
@capabilities = build_device(device_id)
|
14
14
|
end
|
15
|
+
@capabilities['actual_device_root'] ||= ((@capabilities['user_agent'] !~ /^DO_NOT_MATCH/i) ? true : false)
|
15
16
|
end
|
16
17
|
|
17
18
|
def is_valid?
|
@@ -32,7 +33,7 @@ module WurflDevice
|
|
32
33
|
|
33
34
|
capabilities['fall_back_tree'] ||= Array.new
|
34
35
|
|
35
|
-
if !device['fall_back'].nil? && !device['fall_back'].empty? && device['
|
36
|
+
if !device['fall_back'].nil? && !device['fall_back'].empty? && device['fall_back'] != 'root'
|
36
37
|
fall_back = build_device(device['fall_back'])
|
37
38
|
unless fall_back.nil?
|
38
39
|
capabilities['fall_back_tree'].unshift(fall_back['id'])
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module WurflDevice
|
2
|
+
class UserAgent < String
|
3
|
+
def is_desktop_browser?
|
4
|
+
ua = self.downcase
|
5
|
+
WurflDevice::Constants::DESKTOP_BROWSERS.each do |sig|
|
6
|
+
return true if ua.index(sig)
|
7
|
+
end
|
8
|
+
return false
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_mobile_browser?
|
12
|
+
ua = self.downcase
|
13
|
+
return false if self.is_desktop_browser?
|
14
|
+
return true if ua =~ /[^\d]\d{3}x\d{3}/
|
15
|
+
WurflDevice::Constants::DESKTOP_BROWSERS.each do |sig|
|
16
|
+
return true if ua.index(sig)
|
17
|
+
end
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_robot?
|
22
|
+
ua = self.downcase
|
23
|
+
WurflDevice::Constants::ROBOTS.each do |sig|
|
24
|
+
return true if ua.index(sig)
|
25
|
+
end
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
def first_slash
|
30
|
+
pos = self.index('/')
|
31
|
+
return self.length if pos.nil?
|
32
|
+
return pos
|
33
|
+
end
|
34
|
+
|
35
|
+
def second_slash
|
36
|
+
first = self.index('/')
|
37
|
+
return self.length if first.nil?
|
38
|
+
second = self.index('/', first + 1)
|
39
|
+
return self.length if second.nil?
|
40
|
+
return second
|
41
|
+
end
|
42
|
+
|
43
|
+
def first_space
|
44
|
+
pos = self.index(' ')
|
45
|
+
return self.length if pos.nil?
|
46
|
+
return pos
|
47
|
+
end
|
48
|
+
|
49
|
+
def first_open_paren
|
50
|
+
pos = self.index('(')
|
51
|
+
return self.length if pos.nil?
|
52
|
+
return pos
|
53
|
+
end
|
54
|
+
|
55
|
+
def contains(find, ignore_case=false)
|
56
|
+
user_agent = self.downcase if ignore_case
|
57
|
+
user_agent = self unless ignore_case
|
58
|
+
if find.kind_of?(Array)
|
59
|
+
find.map do |needle|
|
60
|
+
needle = needle.downcase if ignore_case
|
61
|
+
return true unless user_agent.index(needle).nil?
|
62
|
+
end
|
63
|
+
return false
|
64
|
+
else
|
65
|
+
find = find.downcase if ignore_case
|
66
|
+
return true unless user_agent.index(find).nil?
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def starts_with(find, ignore_case=false)
|
72
|
+
user_agent = self.downcase if ignore_case
|
73
|
+
user_agent = self unless ignore_case
|
74
|
+
if find.kind_of?(Array)
|
75
|
+
find.map do |needle|
|
76
|
+
needle = needle.downcase if ignore_case
|
77
|
+
return true if user_agent.index(needle) == 0
|
78
|
+
end
|
79
|
+
return false
|
80
|
+
else
|
81
|
+
find = find.downcase if ignore_case
|
82
|
+
return true if user_agent.index(find) == 0
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def index_of_or_length(target, starting_index=nil)
|
88
|
+
length = self.length
|
89
|
+
return length if starting_index.nil?
|
90
|
+
if target.kind_of?(Array)
|
91
|
+
target.map do |needle|
|
92
|
+
next if needle.nil?
|
93
|
+
pos = self.index(needle, starting_index)
|
94
|
+
return pos unless pos.nil?
|
95
|
+
end
|
96
|
+
return length
|
97
|
+
else
|
98
|
+
pos = self.index(target, starting_index)
|
99
|
+
return length if pos.nil?
|
100
|
+
return pos unless pos.nil?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def ordinal_index_of(needle, ordinal)
|
105
|
+
return -1 if self.nil? || self.empty? || ordinal.is_a?(Integer)
|
106
|
+
found = 0
|
107
|
+
index = -1
|
108
|
+
begin
|
109
|
+
index = self.index(needle, index + 1)
|
110
|
+
index = index.is_a?(Integer) ? index : -1
|
111
|
+
return index if index < 0
|
112
|
+
found = found + 1
|
113
|
+
end while found < ordinal
|
114
|
+
return index
|
115
|
+
end
|
116
|
+
|
117
|
+
def cleaned
|
118
|
+
user_agent = self.dup
|
119
|
+
user_agent.sub!('UP.Link', '')
|
120
|
+
user_agent.replace($1) if user_agent =~ /^(.+)NOKIA-MSISDN\:\ (.+)$/i
|
121
|
+
user_agent.sub!("'M', 'Y' 'P', 'H', 'O', 'N', 'E'", "MyPhone")
|
122
|
+
user_agent.sub!('(null)', '')
|
123
|
+
|
124
|
+
# remove serial numbers
|
125
|
+
user_agent.sub!(/\/SN\d{15}/, '/SNXXXXXXXXXXXXXXX')
|
126
|
+
user_agent.sub!(/\[(ST|TF|NT)\d+\]/, '')
|
127
|
+
|
128
|
+
# remove locale identifiers
|
129
|
+
user_agent.sub!(/([ ;])[a-zA-Z]{2}-[a-zA-Z]{2}([ ;\)])/, '\1xx-xx\2')
|
130
|
+
|
131
|
+
pos = self.index('BlackBerry')
|
132
|
+
user_agent.replace(self.slice(pos, user_agent.length-pos)) unless pos.nil?
|
133
|
+
|
134
|
+
user_agent.sub!(/(Android \d\.\d)([^; \/\)]+)/, '\1')
|
135
|
+
user_agent.strip!
|
136
|
+
user_agent
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -2,53 +2,39 @@ require 'text'
|
|
2
2
|
|
3
3
|
module WurflDevice
|
4
4
|
class UserAgentMatcher
|
5
|
-
attr_accessor :user_agent, :
|
5
|
+
attr_accessor :user_agent, :device
|
6
6
|
|
7
7
|
def match(user_agent)
|
8
|
-
@user_agent = user_agent
|
9
|
-
@user_agent_cleaned = UserAgentMatcher.clean_user_agent(user_agent)
|
10
|
-
@device = nil
|
8
|
+
@user_agent = UserAgent.new(user_agent)
|
11
9
|
|
12
10
|
# exact match
|
13
11
|
@device = WurflDevice.get_device_from_ua_cache(@user_agent)
|
14
|
-
@device = WurflDevice.get_device_from_ua_cache(@
|
12
|
+
@device = WurflDevice.get_device_from_ua_cache(@user_agent.cleaned) if @device.nil?
|
15
13
|
|
16
14
|
# already in cache so return immediately
|
17
15
|
return self if !@device.nil? && @device.is_valid?
|
18
16
|
|
19
17
|
# ris match
|
18
|
+
user_agent = @user_agent.cleaned
|
20
19
|
if @device.nil?
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
index_matcher = get_index(user_agent)
|
21
|
+
matcher = "matcher_#{index_matcher.downcase}"
|
22
|
+
if self.respond_to?(matcher)
|
23
|
+
self.send(matcher, user_agent)
|
24
|
+
else
|
25
|
+
if user_agent =~ /^Mozilla/i
|
26
|
+
tolerance = 5
|
27
|
+
@device = ld_match(user_agent, tolerance)
|
28
|
+
else
|
29
|
+
tolerance = user_agent.first_slash
|
30
|
+
@device = ris_match(user_agent, tolerance)
|
31
31
|
end
|
32
|
-
break unless @device.nil?
|
33
|
-
user_agent = user_agent.slice(0, curlen-1)
|
34
|
-
curlen = user_agent.length
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
38
35
|
# last attempts
|
39
36
|
if @device.nil?
|
40
|
-
user_agent
|
41
|
-
device_id = WurflDevice::Constants::GENERIC
|
42
|
-
device_id = 'opwv_v7_generic' if user_agent.index('UP.Browser/7')
|
43
|
-
device_id = 'opwv_v6_generic' if user_agent.index('UP.Browser/6')
|
44
|
-
device_id = 'upgui_generic' if user_agent.index('UP.Browser/5')
|
45
|
-
device_id = 'uptext_generic' if user_agent.index('UP.Browser/4')
|
46
|
-
device_id = 'uptext_generic' if user_agent.index('UP.Browser/3')
|
47
|
-
device_id = 'nokia_generic_series60' if user_agent.index('Series60')
|
48
|
-
device_id = 'generic_web_browser' if user_agent.index('Mozilla/4.0')
|
49
|
-
device_id = 'generic_web_browser' if user_agent.index('Mozilla/5.0')
|
50
|
-
device_id = 'generic_web_browser' if user_agent.index('Mozilla/6.0')
|
51
|
-
@device = WurflDevice.get_device_from_id(device_id)
|
37
|
+
last_attempts(user_agent)
|
52
38
|
end
|
53
39
|
|
54
40
|
WurflDevice.save_device_in_ua_cache(@user_agent, @device)
|
@@ -56,101 +42,589 @@ module WurflDevice
|
|
56
42
|
return self
|
57
43
|
end
|
58
44
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
45
|
+
def ris_match(user_agent, tolerance)
|
46
|
+
device = nil
|
47
|
+
user_agent_list = WurflDevice.get_user_agents_in_index(get_index(user_agent))
|
48
|
+
curlen = user_agent.length
|
49
|
+
while curlen >= tolerance
|
50
|
+
user_agent_list.each do |ua|
|
51
|
+
next if ua.length < curlen
|
52
|
+
if ua.index(user_agent) == 0
|
53
|
+
device = WurflDevice.get_device_from_ua_cache(ua, true)
|
66
54
|
break
|
67
55
|
end
|
68
56
|
end
|
69
|
-
|
57
|
+
break unless device.nil?
|
58
|
+
user_agent = user_agent.slice(0, curlen-1)
|
59
|
+
curlen = user_agent.length
|
60
|
+
end
|
61
|
+
return device
|
62
|
+
end
|
63
|
+
|
64
|
+
def ld_match(user_agent, tolerance=nil)
|
65
|
+
tolerance = 7 if tolerance.nil?
|
66
|
+
device = nil
|
67
|
+
user_agent_list = WurflDevice.get_user_agents_in_index(get_index(user_agent))
|
68
|
+
|
69
|
+
length = user_agent.length
|
70
|
+
best = tolerance
|
71
|
+
current = 0
|
72
|
+
match = nil
|
73
|
+
user_agent_list.select do |ua|
|
74
|
+
next if !ua.length.between?(length - tolerance, length + tolerance)
|
75
|
+
current = Text::Levenshtein.distance(ua, user_agent)
|
76
|
+
if current <= best
|
77
|
+
best = current
|
78
|
+
match = ua
|
79
|
+
end
|
80
|
+
puts "#{current}: #{ua}"
|
70
81
|
end
|
71
82
|
|
72
|
-
|
73
|
-
|
83
|
+
device = WurflDevice.get_device_from_ua_cache(match, true)
|
84
|
+
return device
|
85
|
+
end
|
74
86
|
|
75
|
-
|
76
|
-
|
87
|
+
def get_index(user_agent)
|
88
|
+
ua = UserAgent.new(user_agent)
|
89
|
+
# Process MOBILE user agents
|
90
|
+
unless ua.is_desktop_browser?
|
91
|
+
return 'Nokia' if ua.contains('Nokia')
|
92
|
+
return 'Samsung' if ua.contains(['Samsung/SGH', 'SAMSUNG-SGH']) || ua.starts_with(['SEC-', 'Samsung', 'SAMSUNG', 'SPH', 'SGH', 'SCH']) || ua.starts_with('samsung', true)
|
93
|
+
return 'BlackBerry' if ua.contains('blackberry', true)
|
94
|
+
return 'SonyEricsson' if ua.contains('Sony')
|
95
|
+
return 'Motorola' if ua.starts_with(['Mot-', 'MOT-', 'MOTO', 'moto']) || ua.contains('Motorola')
|
77
96
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
97
|
+
return 'Alcatel' if ua.starts_with('alcatel', true)
|
98
|
+
return 'Apple' if ua.contains(['iPhone', 'iPod', 'iPad', '(iphone'])
|
99
|
+
return 'BenQ' if ua.starts_with('benq', true)
|
100
|
+
return 'DoCoMo' if ua.starts_with('DoCoMo')
|
101
|
+
return 'Grundig' if ua.starts_with('grundig', true)
|
102
|
+
return 'HTC' if ua.contains(['HTC', 'XV6875'])
|
103
|
+
return 'Kddi' if ua.contains('KDDI-')
|
104
|
+
return 'Kyocera' if ua.starts_with(['kyocera', 'QC-', 'KWC-'])
|
105
|
+
return 'LG' if ua.starts_with('lg', true)
|
106
|
+
return 'Mitsubishi' if ua.starts_with('Mitsu')
|
107
|
+
return 'Nec' if ua.starts_with(['NEC-', 'KGT'])
|
108
|
+
return 'Nintendo' if ua.contains('Nintendo') || (ua.starts_with('Mozilla/') && ua.starts_with('Nitro') && ua.starts_with('Opera'))
|
109
|
+
return 'Panasonic' if ua.contains('Panasonic')
|
110
|
+
return 'Pantech' if ua.starts_with(['Pantech', 'PT-', 'PANTECH', 'PG-'])
|
111
|
+
return 'Philips' if ua.starts_with('philips', true)
|
112
|
+
return 'Portalmmm' if ua.starts_with('portalmmm')
|
113
|
+
return 'Qtek' if ua.starts_with('Qtek')
|
114
|
+
return 'Sagem' if ua.starts_with('sagem', true)
|
115
|
+
return 'Sharp' if ua.starts_with('sharp', true)
|
116
|
+
return 'Siemens' if ua.starts_with('SIE-')
|
117
|
+
return 'SPV' if ua.starts_with('SPV')
|
118
|
+
return 'Toshiba' if ua.starts_with('Toshiba')
|
119
|
+
return 'Vodafone' if ua.starts_with('Vodafone')
|
84
120
|
|
85
|
-
#
|
86
|
-
|
121
|
+
# mobile browsers
|
122
|
+
return 'Android' if ua.contains('Android')
|
123
|
+
return 'OperaMini' if ua.contains(['Opera Mini', 'Opera Mobi'])
|
124
|
+
return 'WindowsCE' if ua.contains('Mozilla/') && ua.contains('Windows CE')
|
125
|
+
end
|
87
126
|
|
88
|
-
|
89
|
-
|
127
|
+
# Process Robots (Web Crawlers and the like)
|
128
|
+
return 'Bot' if ua.is_robot?
|
90
129
|
|
91
|
-
|
130
|
+
# Process NON-MOBILE user agents
|
131
|
+
unless ua.is_mobile_browser?
|
132
|
+
return 'MSIE' if ua.starts_with('Mozilla') && ua.contains('MSIE') && !ua.contains(['Opera', 'armv', 'MOTO', 'BREW'])
|
133
|
+
return 'Firefox' if ua.contains('Firefox') && !ua.contains(['Sony', 'Novarra', 'Opera'])
|
134
|
+
return 'Chrome' if ua.contains('Chrome')
|
135
|
+
return 'Konqueror' if ua.contains('Konqueror')
|
136
|
+
return 'Opera' if ua.contains('Opera')
|
137
|
+
return 'Safari' if ua.starts_with('Mozilla') && ua.contains('Safari')
|
138
|
+
return 'AOL' if ua.contains(['AOL', 'America Online']) || ua.contains('aol 9', true)
|
92
139
|
end
|
93
140
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
141
|
+
return 'CatchAll'
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
# user agent matchers
|
146
|
+
def last_attempts(user_agent)
|
147
|
+
device_id = case
|
148
|
+
# OpenWave
|
149
|
+
when user_agent.contains('UP.Browser/7.2')
|
150
|
+
'opwv_v72_generic'
|
151
|
+
when user_agent.contains('UP.Browser/7')
|
152
|
+
'opwv_v7_generic'
|
153
|
+
when user_agent.contains('UP.Browser/6.2')
|
154
|
+
'opwv_v62_generic'
|
155
|
+
when user_agent.contains('UP.Browser/6')
|
156
|
+
'opwv_v6_generic'
|
157
|
+
when user_agent.contains('UP.Browser/5')
|
158
|
+
'upgui_generic'
|
159
|
+
when user_agent.contains('UP.Browser/4')
|
160
|
+
'uptext_generic'
|
161
|
+
when user_agent.contains('UP.Browser/3')
|
162
|
+
'uptext_generic'
|
163
|
+
# Series 60
|
164
|
+
when user_agent.contains('Series60')
|
165
|
+
'nokia_generic_series60'
|
166
|
+
when user_agent.contains('Series80')
|
167
|
+
'nokia_generic_series80'
|
168
|
+
# Access/Net Front
|
169
|
+
when user_agent.contains(['NetFront/3.0', 'ACS-NF/3.0'])
|
170
|
+
'generic_netfront_ver3'
|
171
|
+
when user_agent.contains(['NetFront/3.1', 'ACS-NF/3.1'])
|
172
|
+
'generic_netfront_ver3_1'
|
173
|
+
when user_agent.contains(['NetFront/3.2', 'ACS-NF/3.2'])
|
174
|
+
'generic_netfront_ver3_2'
|
175
|
+
when user_agent.contains(['NetFront/3.3', 'ACS-NF/3.3'])
|
176
|
+
'generic_netfront_ver3_3'
|
177
|
+
when user_agent.contains('NetFront/3.4')
|
178
|
+
'generic_netfront_ver3_4'
|
179
|
+
when user_agent.contains('NetFront/3.5')
|
180
|
+
'generic_netfront_ver3_5'
|
181
|
+
# Contains Mozilla/, but not at the beginning of the UA
|
182
|
+
when user_agent.starts_with('Mozilla/') || @user_agent.contains('Mozilla/')
|
183
|
+
WurflDevice::Constants::GENERIC_XHTML
|
184
|
+
# Obigo
|
185
|
+
when user_agent.contains(['ObigoInternetBrowser/Q03C', 'AU-MIC/2', 'AU-MIC-', 'AU-OBIGO', 'Obigo/Q03', 'Obigo/Q04', 'ObigoInternetBrowser/2', 'Teleca Q03B1'])
|
186
|
+
WurflDevice::Constants::GENERIC_XHTML
|
187
|
+
# DoCoMo
|
188
|
+
when user_agent.starts_with('DoCoMo') || @user_agent.starts_with('KDDI')
|
189
|
+
'docomo_generic_jap_ver1'
|
190
|
+
# Generic Mozilla
|
191
|
+
when user_agent.contains(['Mozilla/4.0', 'Mozilla/5.0', 'Mozilla/6.0'])
|
192
|
+
WurflDevice::Constants::GENERIC_WEB_BROWSER
|
193
|
+
else
|
194
|
+
WurflDevice::Constants::GENERIC
|
98
195
|
end
|
196
|
+
@device = WurflDevice.get_device_from_id(device_id)
|
197
|
+
end
|
198
|
+
|
199
|
+
# mobile user agents
|
200
|
+
def match_nokia(user_agent)
|
201
|
+
tolerance = user_agent.index_of_or_length(['/', ' '], user_agent.index('Nokia'))
|
202
|
+
@device = ris_match(user_agent, tolerance)
|
203
|
+
end
|
99
204
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
205
|
+
def matcher_samsung(user_agent)
|
206
|
+
if user_agent.starts_with('SAMSUNG') || user_agent.starts_with('SEC-') || user_agent.starts_with('SCH-')
|
207
|
+
tolerance = user_agent.first_slash
|
208
|
+
elsif user_agent.starts_with('Samsung') || user_agent.starts_with('SPH') || user_agent.starts_with('SGH')
|
209
|
+
tolerance = user_agent.first_space
|
210
|
+
else
|
211
|
+
tolerance = user_agent.second_slash
|
212
|
+
end
|
213
|
+
@device = ris_match(user_agent, tolerance)
|
214
|
+
if @device.nil?
|
215
|
+
if user_agent.starts_with('SAMSUNG')
|
216
|
+
tolerance = 8
|
217
|
+
@device = ld_match(user_agent, tolerance)
|
218
|
+
else
|
219
|
+
tolerance = user_agent.index_of_or_length('/', user_agent.index('Samsung'))
|
220
|
+
@device = ris_match(user_agent, tolerance)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def matcher_blackberry(user_agent)
|
226
|
+
if user_agent.starts_with('BlackBerry')
|
227
|
+
tolerance = user_agent.ordinal_index_of(';', 3)
|
228
|
+
else
|
229
|
+
tolerance = user_agent.first_slash
|
230
|
+
end
|
231
|
+
@device = ris_match(user_agent, tolerance)
|
232
|
+
if @device.nil?
|
233
|
+
if user_agent =~ /\#Black[Bb]erry[^\/\s]+\/(\d.\d)\#/
|
234
|
+
versions = {
|
235
|
+
'2.' => 'blackberry_generic_ver2',
|
236
|
+
'3.2' => 'blackberry_generic_ver3_sub2',
|
237
|
+
'3.3' => 'blackberry_generic_ver3_sub30',
|
238
|
+
'3.5' => 'blackberry_generic_ver3_sub50',
|
239
|
+
'3.6' => 'blackberry_generic_ver3_sub60',
|
240
|
+
'3.7' => 'blackberry_generic_ver3_sub70',
|
241
|
+
'4.1' => 'blackberry_generic_ver4_sub10',
|
242
|
+
'4.2' => 'blackberry_generic_ver4_sub20',
|
243
|
+
'4.3' => 'blackberry_generic_ver4_sub30',
|
244
|
+
'4.5' => 'blackberry_generic_ver4_sub50',
|
245
|
+
'4.6' => 'blackberry_generic_ver4_sub60',
|
246
|
+
'4.7' => 'blackberry_generic_ver4_sub70',
|
247
|
+
'4.' => 'blackberry_generic_ver4',
|
248
|
+
'5.' => 'blackberry_generic_ver5',
|
249
|
+
'6.' => 'blackberry_generic_ver6',
|
250
|
+
}.each_pair do |version, device_id|
|
251
|
+
if version.index($1)
|
252
|
+
@device = Device.new(device_id)
|
253
|
+
break
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def matcher_sonyericsson(user_agent)
|
261
|
+
if user_agent.starts_with('SonyEricsson')
|
262
|
+
tolerance = user_agent.first_slash - 1
|
263
|
+
@device = ris_match(user_agent, tolerance)
|
264
|
+
else
|
265
|
+
tolerance = user_agent.second_slash
|
266
|
+
@device = ris_match(user_agent, tolerance)
|
267
|
+
end
|
268
|
+
if @device.nil?
|
269
|
+
tolerance = 14
|
270
|
+
@device = ris_match(user_agent, tolerance)
|
107
271
|
end
|
272
|
+
end
|
108
273
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
274
|
+
def matcher_motorola(user_agent)
|
275
|
+
tolerance = 5
|
276
|
+
if user_agent.starts_with('Mot-') || user_agent.starts_with('MOT-') || user_agent.starts_with('Motorola')
|
277
|
+
@device = ris_match(user_agent, tolerance)
|
278
|
+
else
|
279
|
+
@device = ld_match(user_agent, tolerance)
|
280
|
+
end
|
281
|
+
if @device.nil?
|
282
|
+
@device = Device.new('mot_mib22_generic') if @user_agent.contains('MIB/2.2') || @user_agent.contains('MIB/BER2.2')
|
113
283
|
end
|
284
|
+
end
|
114
285
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
286
|
+
def matcher_alcatel(user_agent)
|
287
|
+
tolerance = user_agent.first_slash
|
288
|
+
@device = ris_match(user_agent, tolerance)
|
289
|
+
end
|
290
|
+
|
291
|
+
def matcher_apple(user_agent)
|
292
|
+
if user_agent.starts_with('Apple')
|
293
|
+
tolerance = user_agent.ordinal_index_of(' ')
|
294
|
+
tolerance = user_agent.length if tolerance == -1
|
295
|
+
else
|
296
|
+
tolerance = user_agent.ordinal_index_of(';')
|
297
|
+
end
|
298
|
+
@device = ris_match(user_agent, tolerance)
|
299
|
+
if @device.nil?
|
300
|
+
device_id = case
|
301
|
+
when user_agent.contains('iPod')
|
302
|
+
'apple_ipod_touch_ver1'
|
303
|
+
when user_agent.contains('iPad')
|
304
|
+
'apple_ipad_ver1'
|
305
|
+
when user_agent.contains('iPhone')
|
306
|
+
'apple_iphone_ver1'
|
307
|
+
else
|
308
|
+
WurfDevice::Contants::GENERIC
|
309
|
+
end
|
310
|
+
@device = Device.new(device_id)
|
119
311
|
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def matcher_benq(user_agent)
|
315
|
+
tolerance = user_agent.first_slash
|
316
|
+
@device = ris_match(user_agent, tolerance)
|
317
|
+
end
|
120
318
|
|
121
|
-
|
122
|
-
|
123
|
-
|
319
|
+
def matcher_docomo(user_agent)
|
320
|
+
if user_agent.num_slashes >= 2
|
321
|
+
tolerance = user_agent.second_slash
|
322
|
+
else
|
323
|
+
tolerance = user_agent.first_open_paren
|
324
|
+
end
|
325
|
+
@device = ris_match(user_agent, tolerance)
|
326
|
+
if @device.nil?
|
327
|
+
version_index = 7
|
328
|
+
device_id = case
|
329
|
+
when user_agent[version_index] == '2'
|
330
|
+
'docomo_generic_jap_ver2'
|
331
|
+
else
|
332
|
+
'docomo_generic_jap_ver1'
|
124
333
|
end
|
125
|
-
|
334
|
+
@device = Device.new(device_id)
|
126
335
|
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def matcher_grundig(user_agent)
|
339
|
+
tolerance = user_agent.first_slash
|
340
|
+
@device = ris_match(user_agent, tolerance)
|
341
|
+
end
|
127
342
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
343
|
+
def matcher_htc(user_agent)
|
344
|
+
tolerance = user_agent.first_slash
|
345
|
+
@device = ris_match(user_agent, tolerance)
|
346
|
+
if @device.nil?
|
347
|
+
tolerance = 6
|
348
|
+
@device = ris_match(user_agent, tolerance)
|
132
349
|
end
|
350
|
+
end
|
133
351
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
352
|
+
def matcher_kddi(user_agent)
|
353
|
+
if user_agent.starts_with('KDDI/')
|
354
|
+
tolerance = user_agent.second_slash
|
355
|
+
@device = ris_match(user_agent, tolerance)
|
356
|
+
else
|
357
|
+
tolerance = user_agent.first_slash
|
358
|
+
@device = ris_match(user_agent, tolerance)
|
359
|
+
end
|
360
|
+
if @device.nil?
|
361
|
+
@device = Device.new('opwv_v62_generic')
|
138
362
|
end
|
363
|
+
end
|
139
364
|
|
140
|
-
|
141
|
-
|
365
|
+
def matcher_kyocera(user_agent)
|
366
|
+
tolerance = user_agent.first_slash
|
367
|
+
@device = ris_match(user_agent, tolerance)
|
368
|
+
end
|
369
|
+
|
370
|
+
def matcher_lg(user_agent)
|
371
|
+
tolerance = user_agent.index_of_or_length('/', user_agent.index('LG'))
|
372
|
+
@device = ris_match(user_agent, tolerance)
|
373
|
+
if @device.nil?
|
374
|
+
tolerance = 7
|
375
|
+
@device = ris_match(user_agent, tolerance)
|
142
376
|
end
|
377
|
+
end
|
143
378
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
379
|
+
def matcher_mitsubishi(user_agent)
|
380
|
+
tolerance = user_agent.first_slash
|
381
|
+
@device = ris_match(user_agent, tolerance)
|
382
|
+
end
|
383
|
+
|
384
|
+
def matcher_nec(user_agent)
|
385
|
+
tolerance = user_agent.first_slash
|
386
|
+
@device = ris_match(user_agent, tolerance)
|
387
|
+
if @device.nil?
|
388
|
+
tolerance = 2
|
389
|
+
@device = ris_match(user_agent, tolerance)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def matcher_nintendo(user_agent)
|
394
|
+
@device = ld_match(user_agent, tolerance)
|
395
|
+
if @device.nil?
|
396
|
+
device_id = case
|
397
|
+
when user_agent.contains('Nintendo Wii')
|
398
|
+
'nintendo_wii_browser'
|
399
|
+
when user_agent.contains('Nintendo DSi')
|
400
|
+
'nintendo_dsi_ver1'
|
401
|
+
when user_agent.starts_with('Mozilla/') && user_agent.contains('Nitro') && user_agent.contains('Opera')
|
402
|
+
'nintendo_ds_ver1'
|
403
|
+
else
|
404
|
+
'nintendo_wii_browser'
|
151
405
|
end
|
152
|
-
|
406
|
+
@device = Device.new(device_id)
|
153
407
|
end
|
154
408
|
end
|
409
|
+
|
410
|
+
def matcher_panasonic(user_agent)
|
411
|
+
tolerance = user_agent.first_slash
|
412
|
+
@device = ris_match(user_agent, tolerance)
|
413
|
+
end
|
414
|
+
|
415
|
+
def matcher_pantech(user_agent)
|
416
|
+
if user_agent.starts_with('Pantech')
|
417
|
+
tolerance = 5
|
418
|
+
@device = ld_match(user_agent, tolerance)
|
419
|
+
else
|
420
|
+
tolerance = user_agent.first_slash
|
421
|
+
@device = ris_match(user_agent, tolerance)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def matcher_philips(user_agent)
|
426
|
+
tolerance = user_agent.first_slash
|
427
|
+
@device = ris_match(user_agent, tolerance)
|
428
|
+
end
|
429
|
+
|
430
|
+
def matcher_portalmmm(user_agent)
|
431
|
+
@device = Device.new(WurflDevice::Constants::GENERIC)
|
432
|
+
end
|
433
|
+
|
434
|
+
def matcher_qtek(user_agent)
|
435
|
+
tolerance = user_agent.first_slash
|
436
|
+
@device = ris_match(user_agent, tolerance)
|
437
|
+
end
|
438
|
+
|
439
|
+
def matcher_sagem(user_agent)
|
440
|
+
tolerance = user_agent.first_slash
|
441
|
+
@device = ris_match(user_agent, tolerance)
|
442
|
+
end
|
443
|
+
|
444
|
+
def matcher_sharp(user_agent)
|
445
|
+
tolerance = user_agent.first_slash
|
446
|
+
@device = ris_match(user_agent, tolerance)
|
447
|
+
end
|
448
|
+
|
449
|
+
def matcher_siemens(user_agent)
|
450
|
+
tolerance = user_agent.first_slash
|
451
|
+
@device = ris_match(user_agent, tolerance)
|
452
|
+
end
|
453
|
+
|
454
|
+
def matcher_spv(user_agent)
|
455
|
+
pos = user_agent.index(';')
|
456
|
+
tolerance = pos || user_agent.index('SPV')
|
457
|
+
@device = ris_match(user_agent, tolerance)
|
458
|
+
end
|
459
|
+
|
460
|
+
def matcher_toshiba(user_agent)
|
461
|
+
tolerance = user_agent.first_slash
|
462
|
+
@device = ris_match(user_agent, tolerance)
|
463
|
+
end
|
464
|
+
|
465
|
+
def matcher_vodafone(user_agent)
|
466
|
+
tolerance = user_agent.first_slash
|
467
|
+
@device = ris_match(user_agent, tolerance)
|
468
|
+
if @device.nil?
|
469
|
+
@device = ld_match(user_agent)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# mobile browsers
|
474
|
+
def matcher_android(user_agent)
|
475
|
+
device_id = 'generic_android'
|
476
|
+
if user_agent.contains('Froyo')
|
477
|
+
device_id = Device.new('generic_android_ver2_2')
|
478
|
+
elsif user_agent =~ /#Android[\s\/](\d).(\d)#/
|
479
|
+
version = "generic_android_ver#{$1}_#{$2}"
|
480
|
+
version = 'generic_android_ver2' if version == 'generic_android_ver2_0'
|
481
|
+
device_id = version if [
|
482
|
+
'generic_android',
|
483
|
+
'generic_android_ver1_5',
|
484
|
+
'generic_android_ver1_6',
|
485
|
+
'generic_android_ver2',
|
486
|
+
'generic_android_ver2_1',
|
487
|
+
'generic_android_ver2_2',
|
488
|
+
].include?(version)
|
489
|
+
end
|
490
|
+
@device = Device.new(device_id)
|
491
|
+
end
|
492
|
+
|
493
|
+
def matcher_operamini(user_agent)
|
494
|
+
tolerance = user_agent.first_slash
|
495
|
+
@device = ris_match(user_agent, tolerance)
|
496
|
+
if @device.nil?
|
497
|
+
device_id = 'browser_opera_mini_release1';
|
498
|
+
if user_agent =~ /#Opera Mini\/([1-5])#/
|
499
|
+
device_id = "browser_opera_mini_release#{$1}"
|
500
|
+
elsif user_agent.contains('Opera Mobi')
|
501
|
+
device_id = 'browser_opera_mini_release4'
|
502
|
+
end
|
503
|
+
@device = Device.new(device_id)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def matcher_windowsce(user_agent)
|
508
|
+
tolerance = 3
|
509
|
+
@device = ld_match(user_agent, tolerance)
|
510
|
+
if @device.nil?
|
511
|
+
@device = Device.new('generic_ms_mobile_browser_ver1')
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# robots
|
516
|
+
def matcher_bot(user_agent)
|
517
|
+
tolerance = user_agent.first_slash
|
518
|
+
@device = ris_match(user_agent, tolerance)
|
519
|
+
if @device.nil?
|
520
|
+
@device = Device.new(WurflDevice::Constants::GENERIC_WEB_BROWSER)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
# desktop browsers
|
525
|
+
def matcher_msie(user_agent)
|
526
|
+
if user_agent =~ /^Mozilla\/4\.0 \(compatible; MSIE (\d)\.(\d);/
|
527
|
+
version = $1.to_i
|
528
|
+
version_sub = $2.to_i
|
529
|
+
device_id = case
|
530
|
+
when version == 7
|
531
|
+
'msie_7'
|
532
|
+
when version == 8
|
533
|
+
'msie_8'
|
534
|
+
when version == 6
|
535
|
+
'msie_6'
|
536
|
+
when version == 4
|
537
|
+
'msie_4'
|
538
|
+
when version == 5
|
539
|
+
version_sub == 5 ? 'msie_5_5' : 'msie_5'
|
540
|
+
else
|
541
|
+
'msie'
|
542
|
+
end
|
543
|
+
@device = Device.new(device_id)
|
544
|
+
end
|
545
|
+
user_agent.sub!(/( \.NET CLR [\d\.]+;?| Media Center PC [\d\.]+;?| OfficeLive[a-zA-Z0-9\.\d]+;?| InfoPath[\.\d]+;?)/, '')
|
546
|
+
tolerance = user_agent.first_slash
|
547
|
+
@device = ris_match(user_agent, tolerance)
|
548
|
+
if @device.nil?
|
549
|
+
if user_agent.contains(['SLCC1', 'Media Center PC', '.NET CLR', 'OfficeLiveConnector'])
|
550
|
+
@device = Device.new(WurfDevice::Constants::GENERIC_WEB_BROWSER)
|
551
|
+
else
|
552
|
+
@device = Device.new(WurfDevice::Constants::GENERIC)
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
def matcher_firefox(user_agent)
|
558
|
+
if user_agent =~ /Firefox\/(\d)\.(\d)/
|
559
|
+
version = $1.to_i
|
560
|
+
version_sub = $2.to_i
|
561
|
+
device_id = case
|
562
|
+
when version == 3
|
563
|
+
version_sub == 5 ? 'firefox_3_5' : 'firefox_3'
|
564
|
+
when version == 2
|
565
|
+
'firefox_2'
|
566
|
+
when version == 1
|
567
|
+
version_sub == 5 ? 'firefox_1_5' : 'firefox_1'
|
568
|
+
else
|
569
|
+
nil
|
570
|
+
end
|
571
|
+
@device = Device.new(device_id) unless device_id.nil?
|
572
|
+
else
|
573
|
+
tolerance = 5
|
574
|
+
@device = ld_match(user_agent, tolerance)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def matcher_chrome(user_agent)
|
579
|
+
tolerance = user_agent.index_of_or_length('/', user_agent.index('Chrome'))
|
580
|
+
@device = ris_match(user_agent, tolerance)
|
581
|
+
if @device.nil?
|
582
|
+
@device = Device.new('google_chrome')
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def matcher_konqueror(user_agent)
|
587
|
+
tolerance = user_agent.first_slash
|
588
|
+
@device = ris_match(user_agent, tolerance)
|
589
|
+
end
|
590
|
+
|
591
|
+
def matcher_opera(user_agent)
|
592
|
+
device_id = case
|
593
|
+
when user_agent.contains('Opera/10')
|
594
|
+
'opera_10'
|
595
|
+
when user_agent.contains('Opera/9')
|
596
|
+
'opera_9'
|
597
|
+
when user_agent.contains('Opera/8')
|
598
|
+
'opera_8'
|
599
|
+
when user_agent.contains('Opera/7')
|
600
|
+
'opera_7'
|
601
|
+
else
|
602
|
+
nil
|
603
|
+
end
|
604
|
+
@device = Device.new(device_id) unless device_id.nil?
|
605
|
+
if @device.nil?
|
606
|
+
tolerance = 5
|
607
|
+
@device = ld_match(user_agent, tolerance)
|
608
|
+
end
|
609
|
+
@device = Device.new('opera') if @device.nil?
|
610
|
+
end
|
611
|
+
|
612
|
+
def matcher_safari(user_agent)
|
613
|
+
tolerance = user_agent.first_slash
|
614
|
+
@device = ris_match(user_agent, tolerance)
|
615
|
+
if @device.nil?
|
616
|
+
device_id = case
|
617
|
+
when user_agent.contains('Macintosh') || user_agent.contains('Windows')
|
618
|
+
WurflDevice::Constants::GENERIC_WEB_BROWSER
|
619
|
+
else
|
620
|
+
WurflDevice::Constants::GENERIC
|
621
|
+
end
|
622
|
+
@device = Device.new(device_id)
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
def matcher_aol(user_agent)
|
627
|
+
@device = Device.new(WurflDevice::Constants::GENERIC_WEB_BROWSER)
|
628
|
+
end
|
155
629
|
end
|
156
630
|
end
|
data/lib/wurfl_device/version.rb
CHANGED
data/lib/wurfl_device.rb
CHANGED
@@ -10,6 +10,7 @@ module WurflDevice
|
|
10
10
|
autoload :Handset, 'wurfl_device/handset'
|
11
11
|
autoload :UI, 'wurfl_device/ui'
|
12
12
|
autoload :CLI, 'wurfl_device/cli'
|
13
|
+
autoload :UserAgent, 'wurfl_device/user_agent'
|
13
14
|
autoload :UserAgentMatcher, 'wurfl_device/user_agent_matcher'
|
14
15
|
autoload :XmlLoader, 'wurfl_device/xml_loader'
|
15
16
|
|
@@ -62,9 +63,11 @@ module WurflDevice
|
|
62
63
|
return matcher.device
|
63
64
|
end
|
64
65
|
|
65
|
-
def get_device_from_ua_cache(user_agent)
|
66
|
-
|
67
|
-
|
66
|
+
def get_device_from_ua_cache(user_agent, bypass_main_cache=false)
|
67
|
+
unless bypass_main_cache
|
68
|
+
cached_device = db.hget(Constants::WURFL_USER_AGENTS_CACHED, user_agent)
|
69
|
+
return Marshal::load(cached_device) unless cached_device.nil?
|
70
|
+
end
|
68
71
|
cached_device = db.hget(Constants::WURFL_USER_AGENTS, user_agent)
|
69
72
|
return Marshal::load(cached_device) unless cached_device.nil?
|
70
73
|
return nil
|
@@ -89,8 +92,8 @@ module WurflDevice
|
|
89
92
|
|
90
93
|
def clear_devices
|
91
94
|
db.keys("#{Constants::WURFL_DEVICES}*").each { |k| db.del k }
|
92
|
-
db.del(
|
93
|
-
db.del(
|
95
|
+
db.del(Constants::WURFL_INITIALIZED)
|
96
|
+
db.del(Constants::WURFL_INFO)
|
94
97
|
end
|
95
98
|
|
96
99
|
def get_user_agents
|
@@ -102,7 +105,7 @@ module WurflDevice
|
|
102
105
|
end
|
103
106
|
|
104
107
|
def get_user_agents_in_index(matcher)
|
105
|
-
db.
|
108
|
+
db.hkeys("#{Constants::WURFL_DEVICES_INDEX}#{matcher}")
|
106
109
|
end
|
107
110
|
|
108
111
|
def get_actual_device_raw(device_id)
|
@@ -119,9 +122,8 @@ module WurflDevice
|
|
119
122
|
|
120
123
|
def rebuild_user_agent_cache
|
121
124
|
# update the cache's
|
122
|
-
get_indexes.each { |k| db.del k }
|
123
125
|
db.del(Constants::WURFL_USER_AGENTS)
|
124
|
-
db.del(
|
126
|
+
get_indexes.each { |k| db.del(k) }
|
125
127
|
|
126
128
|
get_devices.each do |device_id|
|
127
129
|
device_id.gsub!(Constants::WURFL_DEVICES, '')
|
@@ -131,7 +133,8 @@ module WurflDevice
|
|
131
133
|
next if user_agent.nil? || user_agent.empty?
|
132
134
|
db.hset(Constants::WURFL_USER_AGENTS, user_agent, Marshal::dump(Device.new(device_id)))
|
133
135
|
|
134
|
-
|
136
|
+
next if user_agent =~ /^DO_NOT_MATCH/i
|
137
|
+
matcher = UserAgentMatcher.new.get_index(user_agent)
|
135
138
|
db.hset("#{Constants::WURFL_DEVICES_INDEX}#{matcher}", user_agent, device_id)
|
136
139
|
end
|
137
140
|
|
@@ -155,35 +158,42 @@ module WurflDevice
|
|
155
158
|
db.set(Constants::WURFL_INITIALIZED, false)
|
156
159
|
|
157
160
|
# download & parse the wurfl xml
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
161
|
+
xml_list = Array.new
|
162
|
+
xml_list << download_wurfl_xml_file
|
163
|
+
xml_list << download_wurfl_web_patch_xml_file
|
164
|
+
|
165
|
+
xml_list.each do |xml_file|
|
166
|
+
(devices, version, last_updated) = XmlLoader.load_xml_file(xml_file) do |capabilities|
|
167
|
+
device_id = capabilities.delete('id')
|
168
|
+
next if device_id.nil? || device_id.empty?
|
169
|
+
db.del("#{Constants::WURFL_DEVICES}#{device_id}")
|
170
|
+
user_agent = capabilities.delete('user_agent')
|
171
|
+
fall_back = capabilities.delete('fall_back')
|
172
|
+
|
173
|
+
device_id.strip! unless device_id.nil?
|
174
|
+
user_agent.strip! unless user_agent.nil?
|
175
|
+
fall_back.strip! unless fall_back.nil?
|
176
|
+
|
177
|
+
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "id", device_id)
|
178
|
+
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "user_agent", user_agent)
|
179
|
+
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "fall_back", fall_back)
|
180
|
+
|
181
|
+
capabilities.each_pair do |key, value|
|
182
|
+
if value.is_a?(Hash)
|
183
|
+
value.each_pair do |k, v|
|
184
|
+
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "#{key.to_s}:#{k.to_s}", v)
|
185
|
+
end
|
186
|
+
else
|
187
|
+
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "#{key.to_s}", value)
|
177
188
|
end
|
178
|
-
else
|
179
|
-
db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "#{key.to_s}", value)
|
180
189
|
end
|
181
190
|
end
|
182
|
-
end
|
183
191
|
|
184
|
-
|
185
|
-
|
186
|
-
|
192
|
+
next if version.nil?
|
193
|
+
db.set(Constants::WURFL_INITIALIZED, true)
|
194
|
+
db.hset(Constants::WURFL_INFO, "version", version)
|
195
|
+
db.hset(Constants::WURFL_INFO, "last_updated", Time.now)
|
196
|
+
end
|
187
197
|
end
|
188
198
|
end
|
189
199
|
|
@@ -215,6 +225,17 @@ module WurflDevice
|
|
215
225
|
wurfl_xml_file_extracted
|
216
226
|
end
|
217
227
|
|
228
|
+
def download_wurfl_web_patch_xml_file
|
229
|
+
wurfl_web_patch_xml_source = 'http://sourceforge.net/projects/wurfl/files/WURFL/2.2/web_browsers_patch.xml'
|
230
|
+
`wget --timeout=60 -qN -- #{wurfl_web_patch_xml_source} > /dev/null`
|
231
|
+
raise "Failed to download wurfl-latest.xml.gz" unless $? == 0
|
232
|
+
|
233
|
+
wurfl_web_patch_xml = File.join(WurflDevice.tmp_dir, 'web_browsers_patch.xml')
|
234
|
+
raise "web_browsers_patch.xml does not exists!" unless File.exists?(wurfl_web_patch_xml)
|
235
|
+
|
236
|
+
wurfl_web_patch_xml
|
237
|
+
end
|
238
|
+
|
218
239
|
def lock_the_cache_for_initializing
|
219
240
|
start_at = Time.now
|
220
241
|
success = false
|
data/spec/cache/device_spec.rb
CHANGED
@@ -13,9 +13,25 @@ describe WurflDevice do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "check for user agent matcher" do
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
{
|
17
|
+
'nokia_x3_02_ver1' => 'NokiaX3-02/5.0 (05.60) Profile/MIDP-2.1 Configuration/CLDC-1.1',
|
18
|
+
'nokia_n90_ver1_sub2052414' => 'NokiaN90-1/3.0541.5.2 Series60/2.8 Profile/MIDP-2.0 Configuration/CLDC-1.1',
|
19
|
+
|
20
|
+
#'opwv_v6_generic' => 'SAMSUNG-B5712C/1.0 RTK-E/1.0 DF/1.0 Release/08.17.2007 Browser/Openwave6.2.3.3.c.1.101 Profile/MIDP-2.0 Configuration/CLDC-1.1/*MzU3ODEwMDIwNzc5ODgx UP.Browser/6.',
|
21
|
+
'samsung_a707_ver1_subshpvppr5' => 'SAMSUNG-SGH-A707/1.0 SHP/VPP/R5 NetFront/3.3 SMM-MMS/1.2.0 profile/MIDP-2.0 configuration/CLDC-1.1',
|
22
|
+
'samsung_u700_ver1_subua' => 'SAMSUNG-SGH-U700/1.0 SHP/VPP/R5 NetFront/3.4 SMM-MMS/1.2.0 profile/MIDP-2.0 configuration/CLDC-1.1',
|
23
|
+
#'generic' => 'SAMSUNG-SCH-M710/(null)ID4 (compatible; MSIE 6.0; Windows CE; PPC) Opera 9.5',
|
24
|
+
|
25
|
+
'sonyericsson_k700i_ver1subr2ay' => 'SonyEricssonK700i/R2AC SEMC-Browser/4.0.2 Profile/MIDP-2.0 Configuration/CLDC-1.1',
|
26
|
+
'sonyericsson_k550i_ver1_subr1jd' => 'SonyEricssonK550i/R1JD Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1',
|
27
|
+
|
28
|
+
'blackberry8520_ver1_subos5' => 'BlackBerry8520/5.0.0.592 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/603',
|
29
|
+
|
30
|
+
'te' => "'M', 'Y' 'P', 'H', 'O', 'N', 'E' Browser/WAP2.0 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
31
|
+
}.each_pair do |device_id, user_agent|
|
32
|
+
device = WurflDevice.get_device_from_ua(user_agent)
|
33
|
+
device.id.should == device_id
|
34
|
+
end
|
19
35
|
end
|
20
36
|
end
|
21
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wurfl_device
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.1'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
16
|
-
requirement: &
|
16
|
+
requirement: &255091900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *255091900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: nokogiri
|
27
|
-
requirement: &
|
27
|
+
requirement: &255091200 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *255091200
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: redis
|
38
|
-
requirement: &
|
38
|
+
requirement: &255090680 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *255090680
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: text
|
49
|
-
requirement: &
|
49
|
+
requirement: &255090220 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *255090220
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bundler
|
60
|
-
requirement: &
|
60
|
+
requirement: &255089020 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.0.10
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *255089020
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
|
-
requirement: &
|
71
|
+
requirement: &255088420 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.9.2
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *255088420
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rspec-core
|
82
|
-
requirement: &
|
82
|
+
requirement: &255087880 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '2.0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *255087880
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: rspec-expectations
|
93
|
-
requirement: &
|
93
|
+
requirement: &255087420 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '2.0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *255087420
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: rr
|
104
|
-
requirement: &
|
104
|
+
requirement: &255086940 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ~>
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '1.0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *255086940
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: faker
|
115
|
-
requirement: &
|
115
|
+
requirement: &255086420 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ~>
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0.9'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *255086420
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: simplecov
|
126
|
-
requirement: &
|
126
|
+
requirement: &255085900 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ~>
|
@@ -131,7 +131,7 @@ dependencies:
|
|
131
131
|
version: 0.5.3
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *255085900
|
135
135
|
description: Ruby client library for mobile handset detection
|
136
136
|
email:
|
137
137
|
- ahutalla@gmail.com
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- lib/wurfl_device/constants.rb
|
155
155
|
- lib/wurfl_device/device.rb
|
156
156
|
- lib/wurfl_device/ui.rb
|
157
|
+
- lib/wurfl_device/user_agent.rb
|
157
158
|
- lib/wurfl_device/user_agent_matcher.rb
|
158
159
|
- lib/wurfl_device/version.rb
|
159
160
|
- lib/wurfl_device/xml_loader.rb
|