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.
@@ -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, use_cache) if device.nil?
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',
@@ -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['id'] != WurflDevice::Constants::GENERIC
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, :user_agent_cleaned, :device
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(@user_agent_cleaned) if @device.nil?
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
- user_agent = @user_agent
22
- user_agent_list = WurflDevice.get_user_agents_in_index(UserAgentMatcher.get_index(user_agent)).sort { |a, b| a[0] <=> b[0] }
23
- tolerance = UserAgentMatcher.first_slash(user_agent)-1
24
- curlen = user_agent.length
25
- while curlen >= tolerance
26
- user_agent_list.map do |ua, device_id|
27
- if ua.index(user_agent) == 0
28
- @device = Device.new(device_id)
29
- break
30
- end
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 = @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
- class << self
60
- def get_index(user_agent)
61
- # create device index
62
- matcher = 'Generic'
63
- WurflDevice::Constants::USER_AGENT_MATCHERS.each do |m|
64
- if Regexp.new(m, Regexp::IGNORECASE) =~ user_agent
65
- matcher = m
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
- return matcher
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
- def clean_user_agent(user_agent)
73
- user_agent = remove_up_link_from_ua(user_agent)
83
+ device = WurflDevice.get_device_from_ua_cache(match, true)
84
+ return device
85
+ end
74
86
 
75
- # remove nokia-msisdn header
76
- user_agent = remove_nokia_msisdn(user_agent)
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
- # clean up myphone id's
79
- user_agent = user_agent.sub("'M', 'Y' 'P', 'H', 'O', 'N', 'E'", "MyPhone")
80
-
81
- # remove serial numbers
82
- user_agent = user_agent.sub(/\/SN\d{15}/, '/SNXXXXXXXXXXXXXXX')
83
- user_agent = user_agent.sub(/\[(ST|TF|NT)\d+\]/, '')
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
- # remove locale identifiers
86
- user_agent = user_agent.sub(/([ ;])[a-zA-Z]{2}-[a-zA-Z]{2}([ ;\)])/, '\1xx-xx\2')
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
- user_agent = normalize_blackberry(user_agent)
89
- user_agent = normalize_android(user_agent)
127
+ # Process Robots (Web Crawlers and the like)
128
+ return 'Bot' if ua.is_robot?
90
129
 
91
- return user_agent.strip
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
- def first_slash(user_agent)
95
- pos = user_agent.index('/')
96
- return user_agent.length if pos.nil?
97
- return pos
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
- def second_slash(user_agent)
101
- first = user_agent.index('/')
102
- return user_agent.length if first.nil?
103
- first = first + 1
104
- second = user_agent.index('/', first)
105
- return user_agent.length if second.nil?
106
- return second
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
- def first_space(user_agent)
110
- pos = user_agent.index(' ')
111
- return user_agent.length if pos.nil?
112
- return pos
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
- def first_open_paren(user_agent)
116
- pos = user_agent.index('(')
117
- return user_agent.length if pos.nil?
118
- return pos
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
- def remove_nokia_msisdn(user_agent)
122
- if user_agent =~ /^(.+)NOKIA-MSISDN\:\ (.+)$/i
123
- user_agent = $1
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
- return user_agent
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
- def remove_up_link_from_ua(user_agent)
129
- pos = user_agent.index('UP.Link')
130
- return user_agent unless pos
131
- return user_agent.slice(0..(pos-1))
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
- def normalize_blackberry(user_agent)
135
- pos = user_agent.index('BlackBerry')
136
- return user_agent if pos.nil?
137
- return user_agent.slice(pos, user_agent.length - pos)
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
- def normalize_android(user_agent)
141
- return user_agent.sub(/(Android \d\.\d)([^; \/\)]+)/, '\1')
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
- def parse(user_agent)
145
- # grab all Agent/version strings as 'agents'
146
- agents = Array.new
147
- user_agent.split(/\s+/).each do |string|
148
- if string =~ /\//
149
- agents << string
150
- end
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
- return agents
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
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  module WurflDevice
4
- VERSION = "0.0.10".freeze
4
+ VERSION = "0.1".freeze
5
5
  end
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
- cached_device = db.hget(Constants::WURFL_USER_AGENTS_CACHED, user_agent)
67
- return Marshal::load(cached_device) unless cached_device.nil?
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(Contants::WURFL_INITIALIZED)
93
- db.del(Contants::WURFL_INFO)
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.hgetall("#{Constants::WURFL_DEVICES_INDEX}#{matcher}")
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(Constants::WURFL_DEVICES_INDEX)
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
- matcher = UserAgentMatcher.get_index(user_agent)
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
- (devices, version, last_updated) = XmlLoader.load_xml_file(download_wurfl_xml_file) do |capabilities|
159
- device_id = capabilities.delete('id')
160
- next if device_id.nil? || device_id.empty?
161
- db.del("#{Constants::WURFL_DEVICES}#{device_id}")
162
- user_agent = capabilities.delete('user_agent')
163
- fall_back = capabilities.delete('fall_back')
164
-
165
- device_id.strip! unless device_id.nil?
166
- user_agent.strip! unless user_agent.nil?
167
- fall_back.strip! unless fall_back.nil?
168
-
169
- db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "id", device_id)
170
- db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "user_agent", user_agent)
171
- db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "fall_back", fall_back)
172
-
173
- capabilities.each_pair do |key, value|
174
- if value.is_a?(Hash)
175
- value.each_pair do |k, v|
176
- db.hset("#{Constants::WURFL_DEVICES}#{device_id}", "#{key.to_s}:#{k.to_s}", v)
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
- db.set(Constants::WURFL_INITIALIZED, true)
185
- db.hset(Constants::WURFL_INFO, "version", version)
186
- db.hset(Constants::WURFL_INFO, "last_updated", Time.now)
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
@@ -13,9 +13,25 @@ describe WurflDevice do
13
13
  end
14
14
 
15
15
  it "check for user agent matcher" do
16
- user_agent = 'Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/6.01; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413'
17
- device = WurflDevice.get_device_from_ua(user_agent)
18
- device.id.should == 'nokia_6120c_ver1_sub601'
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.0.10
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-01 00:00:00.000000000 Z
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: &66803460 !ruby/object:Gem::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: *66803460
24
+ version_requirements: *255091900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: nokogiri
27
- requirement: &66802960 !ruby/object:Gem::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: *66802960
35
+ version_requirements: *255091200
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: redis
38
- requirement: &66802500 !ruby/object:Gem::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: *66802500
46
+ version_requirements: *255090680
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: text
49
- requirement: &66802060 !ruby/object:Gem::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: *66802060
57
+ version_requirements: *255090220
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bundler
60
- requirement: &66801540 !ruby/object:Gem::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: *66801540
68
+ version_requirements: *255089020
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
- requirement: &66801020 !ruby/object:Gem::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: *66801020
79
+ version_requirements: *255088420
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec-core
82
- requirement: &66800540 !ruby/object:Gem::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: *66800540
90
+ version_requirements: *255087880
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rspec-expectations
93
- requirement: &66800060 !ruby/object:Gem::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: *66800060
101
+ version_requirements: *255087420
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rr
104
- requirement: &66799560 !ruby/object:Gem::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: *66799560
112
+ version_requirements: *255086940
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: faker
115
- requirement: &66799000 !ruby/object:Gem::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: *66799000
123
+ version_requirements: *255086420
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: simplecov
126
- requirement: &66798340 !ruby/object:Gem::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: *66798340
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