device_detector 0.9.1 → 1.0.4

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.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +49 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +5 -9
  5. data/CHANGELOG.md +16 -3
  6. data/README.md +7 -9
  7. data/Rakefile +19 -13
  8. data/device_detector.gemspec +1 -0
  9. data/lib/device_detector.rb +32 -28
  10. data/lib/device_detector/bot.rb +2 -2
  11. data/lib/device_detector/client.rb +3 -2
  12. data/lib/device_detector/device.rb +44 -21
  13. data/lib/device_detector/memory_cache.rb +26 -19
  14. data/lib/device_detector/metadata_extractor.rb +7 -8
  15. data/lib/device_detector/model_extractor.rb +3 -3
  16. data/lib/device_detector/name_extractor.rb +2 -2
  17. data/lib/device_detector/os.rb +121 -111
  18. data/lib/device_detector/parser.rb +22 -9
  19. data/lib/device_detector/version.rb +3 -1
  20. data/lib/device_detector/version_extractor.rb +2 -3
  21. data/regexes/bots.yml +840 -20
  22. data/regexes/client/browser_engine.yml +11 -2
  23. data/regexes/client/browsers.yml +909 -108
  24. data/regexes/client/feed_readers.yml +38 -2
  25. data/regexes/client/libraries.yml +76 -2
  26. data/regexes/client/mediaplayers.yml +25 -5
  27. data/regexes/client/mobile_apps.yml +167 -2
  28. data/regexes/client/pim.yml +10 -1
  29. data/regexes/device/cameras.yml +1 -1
  30. data/regexes/device/car_browsers.yml +7 -3
  31. data/regexes/device/consoles.yml +3 -3
  32. data/regexes/device/mobiles.yml +10123 -465
  33. data/regexes/device/portable_media_player.yml +4 -6
  34. data/regexes/device/televisions.yml +18 -4
  35. data/regexes/oss.yml +115 -21
  36. data/regexes/vendorfragments.yml +6 -2
  37. data/spec/device_detector/concrete_user_agent_spec.rb +16 -17
  38. data/spec/device_detector/detector_fixtures_spec.rb +51 -11
  39. data/spec/device_detector/device_spec.rb +28 -48
  40. data/spec/device_detector/memory_cache_spec.rb +60 -28
  41. data/spec/device_detector/model_extractor_spec.rb +3 -3
  42. data/spec/device_detector/version_extractor_spec.rb +5 -6
  43. data/spec/device_detector_spec.rb +60 -69
  44. data/spec/fixtures/client/browser.yml +1785 -262
  45. data/spec/fixtures/client/feed_reader.yml +47 -35
  46. data/spec/fixtures/client/library.yml +112 -3
  47. data/spec/fixtures/client/mediaplayer.yml +32 -37
  48. data/spec/fixtures/client/mobile_app.yml +193 -6
  49. data/spec/fixtures/client/pim.yml +37 -18
  50. data/spec/fixtures/detector/bots.yml +1426 -118
  51. data/spec/fixtures/detector/camera.yml +36 -10
  52. data/spec/fixtures/detector/car_browser.yml +64 -3
  53. data/spec/fixtures/detector/console.yml +80 -26
  54. data/spec/fixtures/detector/desktop.yml +2222 -1589
  55. data/spec/fixtures/detector/feature_phone.yml +151 -42
  56. data/spec/fixtures/detector/feed_reader.yml +186 -121
  57. data/spec/fixtures/detector/mediaplayer.yml +113 -39
  58. data/spec/fixtures/detector/mobile_apps.yml +366 -21
  59. data/spec/fixtures/detector/phablet.yml +2597 -570
  60. data/spec/fixtures/detector/portable_media_player.yml +41 -16
  61. data/spec/fixtures/detector/smart_display.yml +8 -5
  62. data/spec/fixtures/detector/smart_speaker.yml +55 -0
  63. data/spec/fixtures/detector/smartphone-1.yml +5468 -5010
  64. data/spec/fixtures/detector/smartphone-10.yml +9977 -0
  65. data/spec/fixtures/detector/smartphone-11.yml +9891 -0
  66. data/spec/fixtures/detector/smartphone-12.yml +9906 -0
  67. data/spec/fixtures/detector/smartphone-13.yml +9920 -0
  68. data/spec/fixtures/detector/smartphone-14.yml +2662 -0
  69. data/spec/fixtures/detector/smartphone-2.yml +5213 -4635
  70. data/spec/fixtures/detector/smartphone-3.yml +5082 -4533
  71. data/spec/fixtures/detector/smartphone-4.yml +6806 -2625
  72. data/spec/fixtures/detector/smartphone-5.yml +9914 -0
  73. data/spec/fixtures/detector/smartphone-6.yml +9962 -0
  74. data/spec/fixtures/detector/smartphone-7.yml +9899 -0
  75. data/spec/fixtures/detector/smartphone-8.yml +9931 -0
  76. data/spec/fixtures/detector/smartphone-9.yml +9899 -0
  77. data/spec/fixtures/detector/smartphone.yml +5225 -4652
  78. data/spec/fixtures/detector/tablet-1.yml +4691 -4191
  79. data/spec/fixtures/detector/tablet-2.yml +9800 -71
  80. data/spec/fixtures/detector/tablet-3.yml +9959 -0
  81. data/spec/fixtures/detector/tablet-4.yml +4528 -0
  82. data/spec/fixtures/detector/tablet.yml +4664 -4177
  83. data/spec/fixtures/detector/tv.yml +3399 -1048
  84. data/spec/fixtures/detector/unknown.yml +1017 -977
  85. data/spec/fixtures/detector/wearable.yml +61 -0
  86. data/spec/fixtures/device/camera.yml +4 -3
  87. data/spec/fixtures/device/car_browser.yml +9 -2
  88. data/spec/fixtures/device/console.yml +15 -14
  89. data/spec/fixtures/parser/oss.yml +284 -2
  90. data/spec/fixtures/parser/vendorfragments.yml +8 -2
  91. metadata +50 -7
@@ -15,6 +15,11 @@ describe DeviceDetector do
15
15
  fail "Failed to parse #{fixture_file}, reason: #{e}"
16
16
  end
17
17
 
18
+ def str_or_nil(string)
19
+ return nil if string == ''
20
+ string
21
+ end
22
+
18
23
  fixtures.each do |f|
19
24
 
20
25
  user_agent = f["user_agent"]
@@ -24,29 +29,64 @@ describe DeviceDetector do
24
29
  describe user_agent do
25
30
  it "should be detected" do
26
31
  if detector.bot?
27
- assert_equal f["bot"]["name"], detector.bot_name, "failed bot name detection"
32
+ assert_equal str_or_nil(f["bot"]["name"]), detector.bot_name, "failed bot name detection"
28
33
  else
29
34
  if f["client"]
30
- assert_equal f["client"]["name"], detector.name, "failed client name detection"
35
+ assert_equal str_or_nil(f["client"]["name"]), detector.name, "failed client name detection"
31
36
  end
32
- if f["os_family"] != "Unknown"
33
- assert_equal f["os_family"], os.family, "failed os family detection"
34
- assert_equal f["os"]["name"], os.name, "failed os name detection"
35
- assert_equal f["os"]["short_name"], os.short_name, "failed os short name detection"
36
- assert_equal f["os"]["version"], os.full_version, "failed os version detection"
37
+
38
+ os_family = str_or_nil(f["os_family"])
39
+ if os_family != "Unknown"
40
+ if os_family.nil?
41
+ assert_nil os.family, "failed os family detection"
42
+ else
43
+ assert_equal os_family, os.family, "failed os family detection"
44
+ end
45
+
46
+ name = str_or_nil(f["os"]["name"])
47
+ if name.nil?
48
+ assert_nil os.name, "failed os name detection"
49
+ else
50
+ assert_equal name, os.name, "failed os name detection"
51
+ end
52
+
53
+ short_name = str_or_nil(f["os"]["short_name"])
54
+ if short_name.nil?
55
+ assert_nil os.short_name, "failed os short name detection"
56
+ else
57
+ assert_equal short_name, os.short_name, "failed os short name detection"
58
+ end
59
+
60
+ os_version = str_or_nil(f["os"]["version"])
61
+ if os_version.nil?
62
+ assert_nil os.full_version, "failed os version detection"
63
+ else
64
+ assert_equal os_version, os.full_version, "failed os version detection"
65
+ end
37
66
  end
38
67
  if f["device"]
39
- expected_type = f["device"]["type"]
68
+ expected_type = str_or_nil(f["device"]["type"])
40
69
  actual_type = detector.device_type
70
+
41
71
  if expected_type != actual_type
42
72
  # puts "\n", f.inspect, expected_type, actual_type, detector.device_name, regex_meta.inspect
43
73
  # debugger
44
74
  # detector.device_type
45
75
  end
46
- assert_equal expected_type, actual_type, "failed device type detection"
47
- model = f["device"]["model"]
76
+ if expected_type.nil?
77
+ assert_nil actual_type, "failed device type detection"
78
+ else
79
+ assert_equal expected_type, actual_type, "failed device type detection"
80
+ end
81
+
82
+ model = str_or_nil(f["device"]["model"])
48
83
  model = model.to_s unless model.nil?
49
- assert_equal model, detector.device_name, "failed device name detection"
84
+
85
+ if model.nil?
86
+ assert_nil detector.device_name, "failed device name detection"
87
+ else
88
+ assert_equal model, detector.device_name, "failed device name detection"
89
+ end
50
90
  end
51
91
  end
52
92
  end
@@ -1,18 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  describe DeviceDetector::Device do
4
-
5
6
  subject { DeviceDetector::Device.new(user_agent) }
6
7
 
7
- alias :device :subject
8
+ alias_method :device, :subject
8
9
 
9
10
  describe '#name' do
10
-
11
11
  describe 'when models are nested' do
12
12
  let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]' }
13
13
 
14
14
  it 'finds an Apple iPhone 6' do
15
- device.name.must_equal 'iPhone 6'
15
+ value(device.name).must_equal 'iPhone 6'
16
16
  end
17
17
  end
18
18
 
@@ -20,7 +20,7 @@ describe DeviceDetector::Device do
20
20
  let(:user_agent) { 'AIRNESS-AIR99/REV 2.2.1/Teleca Q03B1' }
21
21
 
22
22
  it 'finds an Airness AIR99' do
23
- device.name.must_equal 'AIR99'
23
+ value(device.name).must_equal 'AIR99'
24
24
  end
25
25
  end
26
26
 
@@ -28,19 +28,17 @@ describe DeviceDetector::Device do
28
28
  let(:user_agent) { 'UNKNOWN MODEL NAME' }
29
29
 
30
30
  it 'returns nil' do
31
- device.name.must_be_nil
31
+ value(device.name).must_be_nil
32
32
  end
33
33
  end
34
-
35
34
  end
36
35
 
37
36
  describe '#type' do
38
-
39
37
  describe 'when models are nested' do
40
38
  let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]' }
41
39
 
42
40
  it 'finds device of Apple iPhone 6' do
43
- device.type.must_equal 'smartphone'
41
+ value(device.type).must_equal 'smartphone'
44
42
  end
45
43
  end
46
44
 
@@ -48,7 +46,7 @@ describe DeviceDetector::Device do
48
46
  let(:user_agent) { 'AIRNESS-AIR99/REV 2.2.1/Teleca Q03B1' }
49
47
 
50
48
  it 'finds the device of Airness AIR99' do
51
- device.type.must_equal 'feature phone'
49
+ value(device.type).must_equal 'feature phone'
52
50
  end
53
51
  end
54
52
 
@@ -56,96 +54,78 @@ describe DeviceDetector::Device do
56
54
  let(:user_agent) { 'UNKNOWN MODEL TYPE' }
57
55
 
58
56
  it 'returns nil' do
59
- device.type.must_be_nil
57
+ value(device.type).must_be_nil
60
58
  end
61
-
62
59
  end
63
60
 
64
61
  describe 'device not specified in nested block' do
65
-
66
62
  let(:user_agent) { 'Mozilla/5.0 (Linux; Android 4.4.2; es-us; SAMSUNG SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko)' }
67
63
 
68
64
  it 'falls back to top-level device' do
69
- device.type.must_equal 'smartphone'
65
+ value(device.type).must_equal 'smartphone'
70
66
  end
71
-
72
67
  end
73
-
74
68
  end
75
69
 
76
70
  describe 'concrete device types' do
77
-
78
71
  describe 'mobiles' do
79
-
80
72
  let(:user_agent) { 'Mozilla/5.0 (Linux; Android 4.4.2; es-us; SAMSUNG SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko)' }
81
73
 
82
74
  it 'identifies the device' do
83
- device.name.must_equal 'GALAXY S5'
84
- device.type.must_equal 'smartphone'
85
- device.brand.must_equal 'Samsung'
75
+ value(device.name).must_equal 'GALAXY S5'
76
+ value(device.type).must_equal 'smartphone'
77
+ value(device.brand).must_equal 'Samsung'
86
78
  end
87
-
88
79
  end
89
80
 
90
81
  describe 'cameras' do
91
-
92
82
  let(:user_agent) { 'Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; EK-GC100 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30' }
93
83
 
94
84
  it 'identifies the device' do
95
- device.name.must_equal 'GALAXY Camera'
96
- device.type.must_equal 'camera'
97
- device.brand.must_equal 'Samsung'
85
+ value(device.name).must_equal 'GALAXY Camera'
86
+ value(device.type).must_equal 'camera'
87
+ value(device.brand).must_equal 'Samsung'
98
88
  end
99
-
100
89
  end
101
90
 
102
91
  describe 'car browsers' do
103
-
104
92
  let(:user_agent) { 'Mozilla/5.0 (X11; Linux) AppleWebKit/534.34 (KHTML, like Gecko) QtCarBrowser Safari/534.34' }
105
93
 
106
94
  it 'identifies the device' do
107
- device.name.must_equal 'Model S'
108
- device.type.must_equal 'car browser'
109
- device.brand.must_be_nil
95
+ value(device.name).must_equal 'Model S'
96
+ value(device.type).must_equal 'car browser'
97
+ value(device.brand).must_equal 'Tesla'
110
98
  end
111
-
112
99
  end
113
100
 
114
101
  describe '(gaming) consoles' do
115
-
116
102
  let(:user_agent) { 'Opera/9.30 (Nintendo Wii; U; ; 2047-7;en)' }
117
103
 
118
104
  it 'identifies the device' do
119
- device.name.must_equal 'Wii'
120
- device.type.must_equal 'console'
121
- device.brand.must_be_nil
105
+ value(device.name).must_equal 'Wii'
106
+ value(device.type).must_equal 'console'
107
+ value(device.brand).must_be_nil
122
108
  end
123
-
124
109
  end
125
110
 
126
111
  describe 'portable media players' do
127
-
128
112
  let(:user_agent) { 'Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_6 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B651 Safari/9537.53' }
129
113
 
130
114
  it 'identifies the device' do
131
- device.name.must_equal 'iPod Touch'
132
- device.type.must_equal 'portable media player'
133
- device.brand.must_equal 'Apple'
115
+ value(device.name).must_equal 'iPod Touch'
116
+ value(device.type).must_equal 'portable media player'
117
+ value(device.brand).must_equal 'Apple'
134
118
  end
135
-
136
119
  end
137
120
 
138
121
  describe 'televisions' do
139
-
140
122
  let(:user_agent) { 'Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ HbbTV/1.1.1 ( ;LGE ;NetCast 4.0 ;03.10.81 ;1.0M ;)' }
141
123
 
142
124
  it 'identifies the device' do
143
- device.name.must_equal 'NetCast 4.0'
144
- device.type.must_equal 'tv'
145
- device.brand.must_equal 'LG'
125
+ value(device.name).must_equal 'NetCast 4.0'
126
+ value(device.type).must_equal 'tv'
127
+ value(device.brand).must_equal 'LG'
146
128
  end
147
-
148
129
  end
149
130
  end
150
-
151
131
  end
@@ -1,73 +1,103 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  describe DeviceDetector::MemoryCache do
4
-
5
6
  let(:subject) { DeviceDetector::MemoryCache.new(config) }
6
7
 
7
8
  let(:config) { {} }
8
9
 
9
10
  describe '#set' do
10
-
11
11
  describe 'string key' do
12
-
13
12
  let(:key) { 'string' }
14
13
 
15
14
  it 'sets the value under the key' do
16
15
  subject.set(key, 'value')
17
16
 
18
- subject.data[key].must_equal 'value'
17
+ value(subject.data[key]).must_equal 'value'
19
18
  end
20
19
 
20
+ it 'returns the value' do
21
+ value(subject.set(key, 'value')).must_equal 'value'
22
+ value(subject.set(key, false)).must_equal false
23
+ assert_nil subject.set(key, nil)
24
+ end
21
25
  end
22
26
 
23
27
  describe 'array key' do
24
-
25
- let(:key) { ['string1', 'string2'] }
28
+ let(:key) { %w[string1 string2] }
26
29
 
27
30
  it 'sets the value under the key' do
28
31
  subject.set(key, 'value')
29
32
 
30
- subject.data[String(key)].must_equal 'value'
33
+ value(subject.data[String(key)]).must_equal 'value'
31
34
  end
35
+ end
36
+
37
+ describe 'nil value' do
38
+ let(:key) { 'string' }
39
+ let(:internal_value) { DeviceDetector::MemoryCache::STORES_NIL_VALUE }
40
+
41
+ it 'sets the value under the key' do
42
+ subject.set(key, nil)
32
43
 
44
+ value(subject.data[String(key)]).must_equal internal_value
45
+ assert_nil subject.get(key)
46
+ end
47
+
48
+ it 'sets the value under the key' do
49
+ subject.get_or_set(key, nil)
50
+
51
+ value(subject.data[String(key)]).must_equal internal_value
52
+ assert_nil subject.get(key)
53
+ end
33
54
  end
34
55
 
56
+ describe 'false value' do
57
+ let(:key) { 'string' }
58
+
59
+ it 'sets the value under the key' do
60
+ subject.set(key, false)
61
+
62
+ value(subject.data[String(key)]).must_equal false
63
+ value(subject.get(key)).must_equal false
64
+ end
65
+
66
+ it 'sets the value under the key' do
67
+ subject.get_or_set(key, false)
68
+
69
+ value(subject.data[String(key)]).must_equal false
70
+ value(subject.get(key)).must_equal false
71
+ end
72
+ end
35
73
  end
36
74
 
37
75
  describe '#get' do
38
-
39
76
  describe 'string key' do
40
-
41
77
  let(:key) { 'string' }
42
78
 
43
79
  it 'gets the value for the key' do
44
80
  subject.data[key] = 'value'
45
81
 
46
- subject.get(key).must_equal 'value'
82
+ value(subject.get(key)).must_equal 'value'
47
83
  end
48
-
49
84
  end
50
85
 
51
86
  describe 'array key' do
52
-
53
- let(:key) { ['string1', 'string2'] }
87
+ let(:key) { %w[string1 string2] }
54
88
 
55
89
  it 'gets the value for the key' do
56
90
  subject.data[String(key)] = 'value'
57
91
 
58
- subject.get(key).must_equal 'value'
92
+ value(subject.get(key)).must_equal 'value'
59
93
  end
60
-
61
94
  end
62
-
63
95
  end
64
96
 
65
97
  describe '#get_or_set' do
66
-
67
98
  let(:key) { 'string' }
68
99
 
69
100
  describe 'value already present' do
70
-
71
101
  it 'gets the value for the key from cache' do
72
102
  subject.data[key] = 'value'
73
103
 
@@ -76,30 +106,34 @@ describe DeviceDetector::MemoryCache do
76
106
  block_called = true
77
107
  end
78
108
 
79
- value.must_equal 'value'
80
- block_called.must_equal false
109
+ value(value).must_equal 'value'
110
+ value(block_called).must_equal false
81
111
  end
82
112
 
113
+ it 'returns the value' do
114
+ subject.data[key] = 'value2'
115
+ value(subject.get_or_set(key, 'value')).must_equal 'value2'
116
+ end
83
117
  end
84
118
 
85
119
  describe 'value not yet present' do
86
-
87
120
  it 'evaluates the block and sets the result' do
88
121
  block_called = false
89
122
  subject.get_or_set(key) do
90
123
  block_called = true
91
124
  end
92
125
 
93
- block_called.must_equal true
94
- subject.data[key].must_equal true
126
+ value(block_called).must_equal true
127
+ value(subject.data[key]).must_equal true
95
128
  end
96
129
 
130
+ it 'returns the value' do
131
+ value(subject.get_or_set(key, 'value')).must_equal 'value'
132
+ end
97
133
  end
98
-
99
134
  end
100
135
 
101
136
  describe 'cache purging' do
102
-
103
137
  let(:config) { { max_cache_keys: 3 } }
104
138
 
105
139
  it 'purges the cache when key size arrives at max' do
@@ -108,9 +142,7 @@ describe DeviceDetector::MemoryCache do
108
142
  subject.set('3', 'baz')
109
143
  subject.set('4', 'boz')
110
144
 
111
- subject.data.keys.size.must_equal 3
145
+ value(subject.data.keys.size).must_equal 3
112
146
  end
113
-
114
147
  end
115
-
116
148
  end
@@ -23,7 +23,7 @@ describe DeviceDetector::ModelExtractor do
23
23
  let(:device_name) { 'iPhone' }
24
24
 
25
25
  it 'returns the textual portion without trailing whitespace' do
26
- extractor.call.must_equal device_name
26
+ value(extractor.call).must_equal device_name
27
27
  end
28
28
 
29
29
  end
@@ -33,7 +33,7 @@ describe DeviceDetector::ModelExtractor do
33
33
  let(:device_name) { 'iPhone 5S' }
34
34
 
35
35
  it 'returns the full device name' do
36
- extractor.call.must_equal device_name
36
+ value(extractor.call).must_equal device_name
37
37
  end
38
38
 
39
39
  end
@@ -53,7 +53,7 @@ describe DeviceDetector::ModelExtractor do
53
53
  end
54
54
 
55
55
  it 'returns the model name' do
56
- extractor.call.must_equal device_name
56
+ value(extractor.call).must_equal device_name
57
57
  end
58
58
 
59
59
  end