handset_detection 0.1.1 → 0.1.2
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.
- checksums.yaml +13 -5
- data/lib/handset_detection/base.rb +51 -43
- data/lib/handset_detection/cache/memcached.rb +3 -2
- data/lib/handset_detection/device.rb +19 -6
- data/lib/handset_detection/hd4.rb +2 -1
- data/lib/handset_detection/store.rb +1 -4
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2YxM2E1MjA2ODNmOTVkODM2ODVkMTY5M2NhNWEzZjUzMTc2ZmM3Mg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NjNkYTM1ZDAzOGM0NzkwZTcwNTVhMzFmZjBlNDlhYjY4MGU2YmY0ZQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MjFlZWIwNGRkM2NiOTkwZmYxNGYxM2YxZTlmNDU4ZjZhMWNlZTY0YmMwM2Rj
|
10
|
+
MzgzOWM0MDYxNTkyNjE4OWIxYzg3MzUxMTFhYjM2NTcxOWRhNjc2ZGQyZmI1
|
11
|
+
MGQ2ZTEzNTRiZjFhMjFmYWQ2Y2Q5Yzk4NDE5ZmQ4YmJlODE4YjQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YWI1ZTkwYmQyNWIwNmE5NmZlMjhlOWU4MjQwNjg5MmJkYjhiMzA4MTc5ODg2
|
14
|
+
OGY3NTBmMzhjYTAwNGMzZDI5NDcxYTljZDBhMjkzZTI5OGQzNzY4NzUwYzE0
|
15
|
+
MjZjODA3MWQ3MDZjODJmY2VhOTQ2NTU0YTA4ZTJjYzQ4N2RmNzI=
|
@@ -27,7 +27,6 @@
|
|
27
27
|
require 'active_support/core_ext/object/blank'
|
28
28
|
require 'json'
|
29
29
|
require 'digest/md5'
|
30
|
-
require 'socket'
|
31
30
|
require 'tcp_timeout'
|
32
31
|
require 'uri'
|
33
32
|
|
@@ -36,9 +35,9 @@ DETECTIONV4_GENERIC = 1
|
|
36
35
|
|
37
36
|
class Base
|
38
37
|
def initialize
|
39
|
-
@config = {}
|
38
|
+
@config = {}
|
40
39
|
@api_base = '/apiv4/'
|
41
|
-
@detected_rule_key = {}
|
40
|
+
@detected_rule_key = {}
|
42
41
|
@device_ua_filter = /[ _\\#\-,.\/:"']/
|
43
42
|
@extra_ua_filter = /[ ]/
|
44
43
|
@apikit = 'Ruby 4.0.0'
|
@@ -46,42 +45,46 @@ class Base
|
|
46
45
|
@logger_port = 80
|
47
46
|
@reply = {}
|
48
47
|
@tree = {}
|
49
|
-
@detection_config = {
|
48
|
+
@detection_config = {
|
50
49
|
'device-ua-order' => ['x-operamini-phone-ua', 'x-mobile-ua', 'device-stock-ua', 'user-agent', 'agent'],
|
51
50
|
'platform-ua-order' => ['x-operamini-phone-ua', 'x-mobile-ua', 'device-stock-ua', 'user-agent', 'agent'],
|
52
51
|
'browser-ua-order' => ['user-agent', 'agent', 'device-stock-ua'],
|
53
52
|
'app-ua-order' => ['user-agent', 'agent', 'device-stock-ua'],
|
54
53
|
'language-ua-order' => ['user-agent', 'agent', 'device-stock-ua'],
|
55
|
-
'device-bi-order' => {
|
56
|
-
'android' => [
|
57
|
-
['ro.product.brand','ro.product.model'],
|
58
|
-
['ro.product.manufacturer','ro.product.model'],
|
59
|
-
['ro-product-brand','ro-product-model'],
|
60
|
-
['ro-product-manufacturer','ro-product-model'],
|
54
|
+
'device-bi-order' => {
|
55
|
+
'android' => [
|
56
|
+
['ro.product.brand', 'ro.product.model'],
|
57
|
+
['ro.product.manufacturer', 'ro.product.model'],
|
58
|
+
['ro-product-brand', 'ro-product-model'],
|
59
|
+
['ro-product-manufacturer', 'ro-product-model'],
|
61
60
|
],
|
62
|
-
'ios' => [
|
63
|
-
['utsname.brand','utsname.machine']
|
61
|
+
'ios' => [
|
62
|
+
['utsname.brand', 'utsname.machine']
|
64
63
|
],
|
65
|
-
'windows phone' => [
|
66
|
-
['devicemanufacturer','devicename']
|
64
|
+
'windows phone' => [
|
65
|
+
['devicemanufacturer', 'devicename']
|
67
66
|
]
|
68
67
|
},
|
69
68
|
'platform-bi-order' => {
|
70
|
-
'android' => [
|
71
|
-
['
|
72
|
-
['
|
69
|
+
'android' => [
|
70
|
+
['hd-platform', 'ro.build.version.release'],
|
71
|
+
['hd-platform', 'ro-build-version-release'],
|
72
|
+
['hd-platform', 'ro.build.id'],
|
73
|
+
['hd-platform', 'ro-build-id'],
|
73
74
|
],
|
74
|
-
'ios' => [
|
75
|
-
['uidevice.
|
75
|
+
'ios' => [
|
76
|
+
['uidevice.systemname', 'uidevice.systemversion'],
|
77
|
+
['hd-platform', 'uidevice.systemversion']
|
76
78
|
],
|
77
|
-
'windows phone' => [
|
78
|
-
['osname','osversion']
|
79
|
+
'windows phone' => [
|
80
|
+
['osname', 'osversion'],
|
81
|
+
['hd-platform', 'osversion']
|
79
82
|
]
|
80
83
|
},
|
81
84
|
'browser-bi-order' => [],
|
82
|
-
'app-bi-order' => []
|
85
|
+
'app-bi-order' => []
|
83
86
|
}
|
84
|
-
@detection_languages = {
|
87
|
+
@detection_languages = {
|
85
88
|
'af' => 'Afrikaans',
|
86
89
|
'sq' => 'Albanian',
|
87
90
|
'ar-dz' => 'Arabic (Algeria)',
|
@@ -242,15 +245,15 @@ class Base
|
|
242
245
|
|
243
246
|
# Get reply status
|
244
247
|
#
|
245
|
-
# +param+ void
|
246
|
-
# +return+ int error status, 0 is Ok, anything else is probably not Ok
|
248
|
+
# +param+ void
|
249
|
+
# +return+ int error status, 0 is Ok, anything else is probably not Ok
|
247
250
|
#
|
248
251
|
def get_status
|
249
252
|
@reply['status']
|
250
253
|
end
|
251
254
|
|
252
255
|
# Get reply message
|
253
|
-
#
|
256
|
+
#
|
254
257
|
# +param+ void
|
255
258
|
# +return+ string A message
|
256
259
|
#
|
@@ -274,7 +277,7 @@ class Base
|
|
274
277
|
#
|
275
278
|
def set_reply(reply)
|
276
279
|
@reply = reply
|
277
|
-
end
|
280
|
+
end
|
278
281
|
|
279
282
|
# Error handling helper. Sets a message and an error code.
|
280
283
|
#
|
@@ -287,8 +290,8 @@ class Base
|
|
287
290
|
@reply['status'] = status
|
288
291
|
@reply['message'] = msg
|
289
292
|
status == 0
|
290
|
-
end
|
291
|
-
|
293
|
+
end
|
294
|
+
|
292
295
|
# String cleanse for extras matching.
|
293
296
|
#
|
294
297
|
# +param+ string str
|
@@ -310,7 +313,7 @@ class Base
|
|
310
313
|
str = str.gsub(/[^\x20-\x7F]/, '')
|
311
314
|
str.strip
|
312
315
|
end
|
313
|
-
|
316
|
+
|
314
317
|
# Log function - User defined functions can be supplied in the 'logger' config variable.
|
315
318
|
#
|
316
319
|
def log(msg)
|
@@ -319,7 +322,7 @@ class Base
|
|
319
322
|
@config['logger'].call(msg)
|
320
323
|
end
|
321
324
|
end
|
322
|
-
|
325
|
+
|
323
326
|
# Makes requests to the various web services of Handset Detection.
|
324
327
|
#
|
325
328
|
# Note : suburl - the url fragment of the web service eg site/detect/${site_id}
|
@@ -331,19 +334,19 @@ class Base
|
|
331
334
|
# +return+ bool true on success, false otherwise
|
332
335
|
#
|
333
336
|
def remote(suburl, data, filetype='json', auth_required=true)
|
334
|
-
@reply = {}
|
335
|
-
@raw_reply = {}
|
337
|
+
@reply = {}
|
338
|
+
@raw_reply = {}
|
336
339
|
set_error 0, ''
|
337
340
|
|
338
341
|
if data.blank?
|
339
|
-
data = []
|
342
|
+
data = []
|
340
343
|
end
|
341
344
|
|
342
345
|
url = @api_base + suburl
|
343
346
|
attempts = @config['retries'] + 1
|
344
347
|
trys = 0
|
345
348
|
|
346
|
-
requestdata = JSON.generate(data)
|
349
|
+
requestdata = JSON.generate(data)
|
347
350
|
|
348
351
|
success = false
|
349
352
|
while (trys+=1) < attempts and success == false
|
@@ -419,10 +422,10 @@ class Base
|
|
419
422
|
'nonce="' + snonce + '", ' +
|
420
423
|
'uri="' + uri.path + '", ' +
|
421
424
|
'qop=' + qop + ', ' +
|
422
|
-
'nc=' + nc + ', ' +
|
425
|
+
'nc=' + nc + ', ' +
|
423
426
|
'cnonce="' + cnonce + '", ' +
|
424
427
|
'response="' + response + '", ' +
|
425
|
-
'opaque="' + realm + '"' +
|
428
|
+
'opaque="' + realm + '"' +
|
426
429
|
"\r\n"
|
427
430
|
end
|
428
431
|
out += "Content-length: " + jsondata.length.to_s + "\r\n\r\n"
|
@@ -430,17 +433,22 @@ class Base
|
|
430
433
|
|
431
434
|
socket = nil
|
432
435
|
begin
|
433
|
-
socket = TCPTimeout::TCPSocket.new(host, port,
|
436
|
+
socket = TCPTimeout::TCPSocket.new(host, port,
|
434
437
|
connect_timeout: @config['timeout'], read_timeout: @config['timeout'], write_timeout: @config['timeout'])
|
435
438
|
socket.write(out)
|
436
|
-
reply =
|
439
|
+
reply = []
|
440
|
+
r = ''
|
441
|
+
until r.nil? do
|
442
|
+
reply << r
|
443
|
+
r = socket.read 8192
|
444
|
+
end
|
437
445
|
rescue SocketError => e
|
438
|
-
return set_error 299, e.to_s
|
446
|
+
return set_error 299, e.to_s
|
439
447
|
ensure
|
440
448
|
socket.close unless socket.nil?
|
441
449
|
end
|
442
|
-
hunks = reply.split("\r\n\r\n")
|
443
|
-
return set_error(299, "Error : Reply is too short.") if hunks.length < 2
|
450
|
+
hunks = reply.join.split("\r\n\r\n")
|
451
|
+
return set_error(299, "Error : Reply is too short.") if hunks.length < 2
|
444
452
|
# header = hunks[hunks.length - 2]
|
445
453
|
# headers = header.split("\n")
|
446
454
|
body = hunks[hunks.length - 1]
|
@@ -544,7 +552,7 @@ class Base
|
|
544
552
|
# +return+ void
|
545
553
|
#
|
546
554
|
def send_remote_syslog(headers)
|
547
|
-
headers['version'] = RUBY_VERSION
|
555
|
+
headers['version'] = RUBY_VERSION
|
548
556
|
headers['apikit'] = @apikit
|
549
557
|
sock = UDPSocket.new(Socket::AF_INET)
|
550
558
|
message = JSON.generate headers
|
@@ -41,8 +41,9 @@ class Memcached
|
|
41
41
|
else
|
42
42
|
options = { 'value_max_bytes' => 4000000 }
|
43
43
|
end
|
44
|
-
|
45
|
-
|
44
|
+
o = {}
|
45
|
+
options.each { |k, v| o[k.to_sym] = v }
|
46
|
+
@cache = Dalli::Client.new(servers, o)
|
46
47
|
end
|
47
48
|
|
48
49
|
# Get key
|
@@ -264,9 +264,14 @@ class Device < Base
|
|
264
264
|
def specs_overlay(specs_field, device, specs)
|
265
265
|
if specs.include? 'hd_specs'
|
266
266
|
if specs_field == 'platform'
|
267
|
-
unless specs['hd_specs']['general_platform'].blank?
|
267
|
+
unless specs['hd_specs']['general_platform'].blank? or specs['hd_specs']['general_platform_version'].blank?
|
268
268
|
device['Device']['hd_specs']['general_platform'] = specs['hd_specs']['general_platform']
|
269
269
|
device['Device']['hd_specs']['general_platform_version'] = specs['hd_specs']['general_platform_version']
|
270
|
+
else
|
271
|
+
unless specs['hd_specs']['general_platform'].blank? or specs['hd_specs']['general_platform'] == device['Device']['hd_specs']['general_platform']
|
272
|
+
device['Device']['hd_specs']['general_platform'] = specs['hd_specs']['general_platform']
|
273
|
+
device['Device']['hd_specs']['general_platform_version'] = ''
|
274
|
+
end
|
270
275
|
end
|
271
276
|
elsif specs_field == 'browser'
|
272
277
|
unless specs['hd_specs']['general_browser'].blank?
|
@@ -451,7 +456,7 @@ class Device < Base
|
|
451
456
|
# Platform Detection
|
452
457
|
@platform = v4_match_bi_helper build_info, 'platform'
|
453
458
|
unless @platform.blank?
|
454
|
-
@device = specs_overlay 'platform', @device, @platform
|
459
|
+
@device = specs_overlay 'platform', @device, @platform['Extra']
|
455
460
|
end
|
456
461
|
|
457
462
|
@reply['hd_specs'] = @device['Device']['hd_specs']
|
@@ -471,11 +476,13 @@ class Device < Base
|
|
471
476
|
|
472
477
|
hints = []
|
473
478
|
conf_bi_keys.each do |platform, set|
|
474
|
-
value = ''
|
475
479
|
set.each do |tuple|
|
476
480
|
checking = true
|
481
|
+
value = ''
|
477
482
|
tuple.each do |item|
|
478
|
-
|
483
|
+
if item == 'hd-platform'
|
484
|
+
value += "|#{platform}"
|
485
|
+
elsif not build_info.include?(item)
|
479
486
|
checking = false
|
480
487
|
break
|
481
488
|
else
|
@@ -536,6 +543,7 @@ class Device < Base
|
|
536
543
|
|
537
544
|
# Sanitize headers & cleanup language
|
538
545
|
headers.each do |key, value|
|
546
|
+
key = key.downcase
|
539
547
|
if key == 'accept-language' or key == 'content-language'
|
540
548
|
key = 'language'
|
541
549
|
tmp = value.downcase.gsub(/ /, '').split(/[,;]/)
|
@@ -544,9 +552,14 @@ class Device < Base
|
|
544
552
|
else
|
545
553
|
next
|
546
554
|
end
|
555
|
+
elsif key != 'profile' and key != 'x-wap-profile'
|
556
|
+
# Handle strings that have had + substituted for a space
|
557
|
+
if value.count(' ') == 0 and value.count('+') > 5 and value.length > 20
|
558
|
+
value.gsub!('+', ' ')
|
559
|
+
end
|
547
560
|
end
|
548
|
-
@device_headers[key
|
549
|
-
@extra_headers[key
|
561
|
+
@device_headers[key] = clean_str value
|
562
|
+
@extra_headers[key] = @extra.extra_clean_str value
|
550
563
|
end
|
551
564
|
|
552
565
|
@device = match_device @device_headers
|
@@ -327,7 +327,8 @@ class HD4 < Base
|
|
327
327
|
#
|
328
328
|
def device_detect(data={})
|
329
329
|
id = data['id'].blank? ? @config['site_id'] : data['id']
|
330
|
-
|
330
|
+
data.delete 'id'
|
331
|
+
request_body = data.blank? ? @detect.request: data
|
331
332
|
fast_key = ''
|
332
333
|
|
333
334
|
# If caching enabled then check cache
|
@@ -34,10 +34,7 @@ class Store
|
|
34
34
|
attr_reader :directory, :cache
|
35
35
|
@@instance = nil
|
36
36
|
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# +param+ string $path Location of storage ROOT dir.
|
40
|
-
# +param+ boolean $createDirectory - Create storage directory if it does not exist
|
37
|
+
# Constructor
|
41
38
|
#
|
42
39
|
def initialize
|
43
40
|
@dirname = "hd40store"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: handset_detection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Handset Detection
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Use the HandsetDetection.com API from Ruby.
|
14
14
|
email: hello@handsetdetection.com
|
@@ -37,17 +37,17 @@ require_paths:
|
|
37
37
|
- lib
|
38
38
|
required_ruby_version: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ! '>='
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ! '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
requirements: []
|
49
49
|
rubyforge_project:
|
50
|
-
rubygems_version: 2.
|
50
|
+
rubygems_version: 2.4.8
|
51
51
|
signing_key:
|
52
52
|
specification_version: 4
|
53
53
|
summary: API kit for HandsetDetection.com
|