device_detector 1.0.4 → 1.0.7

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +8 -7
  4. data/lib/device_detector/browser.rb +364 -0
  5. data/lib/device_detector/client.rb +8 -0
  6. data/lib/device_detector/device.rb +1204 -2
  7. data/lib/device_detector/os.rb +39 -12
  8. data/lib/device_detector/parser.rb +1 -1
  9. data/lib/device_detector/version.rb +1 -1
  10. data/lib/device_detector/version_extractor.rb +28 -0
  11. data/lib/device_detector.rb +57 -13
  12. data/regexes/bots.yml +651 -61
  13. data/regexes/client/browser_engine.yml +7 -1
  14. data/regexes/client/browsers.yml +774 -78
  15. data/regexes/client/feed_readers.yml +4 -10
  16. data/regexes/client/libraries.yml +71 -2
  17. data/regexes/client/mediaplayers.yml +25 -1
  18. data/regexes/client/mobile_apps.yml +923 -73
  19. data/regexes/client/pim.yml +65 -2
  20. data/regexes/device/cameras.yml +5 -5
  21. data/regexes/device/car_browsers.yml +16 -0
  22. data/regexes/device/consoles.yml +13 -1
  23. data/regexes/device/mobiles.yml +17029 -3889
  24. data/regexes/device/notebooks.yml +114 -0
  25. data/regexes/device/portable_media_player.yml +36 -9
  26. data/regexes/device/shell_tv.yml +117 -0
  27. data/regexes/device/televisions.yml +439 -34
  28. data/regexes/oss.yml +620 -284
  29. metadata +9 -131
  30. data/.gitignore +0 -14
  31. data/.rubocop.yml +0 -49
  32. data/.ruby-version +0 -1
  33. data/.travis.yml +0 -14
  34. data/Gemfile +0 -8
  35. data/Rakefile +0 -85
  36. data/device_detector.gemspec +0 -27
  37. data/spec/device_detector/bot_fixtures_spec.rb +0 -30
  38. data/spec/device_detector/client_fixtures_spec.rb +0 -31
  39. data/spec/device_detector/concrete_user_agent_spec.rb +0 -135
  40. data/spec/device_detector/detector_fixtures_spec.rb +0 -100
  41. data/spec/device_detector/device_fixtures_spec.rb +0 -36
  42. data/spec/device_detector/device_spec.rb +0 -131
  43. data/spec/device_detector/memory_cache_spec.rb +0 -148
  44. data/spec/device_detector/model_extractor_spec.rb +0 -63
  45. data/spec/device_detector/os_fixtures_spec.rb +0 -26
  46. data/spec/device_detector/version_extractor_spec.rb +0 -79
  47. data/spec/device_detector_spec.rb +0 -189
  48. data/spec/fixtures/client/browser.yml +0 -2836
  49. data/spec/fixtures/client/feed_reader.yml +0 -199
  50. data/spec/fixtures/client/library.yml +0 -193
  51. data/spec/fixtures/client/mediaplayer.yml +0 -163
  52. data/spec/fixtures/client/mobile_app.yml +0 -217
  53. data/spec/fixtures/client/pim.yml +0 -115
  54. data/spec/fixtures/detector/bots.yml +0 -3726
  55. data/spec/fixtures/detector/camera.yml +0 -141
  56. data/spec/fixtures/detector/car_browser.yml +0 -81
  57. data/spec/fixtures/detector/console.yml +0 -321
  58. data/spec/fixtures/detector/desktop.yml +0 -5461
  59. data/spec/fixtures/detector/feature_phone.yml +0 -891
  60. data/spec/fixtures/detector/feed_reader.yml +0 -551
  61. data/spec/fixtures/detector/mediaplayer.yml +0 -253
  62. data/spec/fixtures/detector/mobile_apps.yml +0 -494
  63. data/spec/fixtures/detector/phablet.yml +0 -4167
  64. data/spec/fixtures/detector/portable_media_player.yml +0 -178
  65. data/spec/fixtures/detector/smart_display.yml +0 -61
  66. data/spec/fixtures/detector/smart_speaker.yml +0 -55
  67. data/spec/fixtures/detector/smartphone-1.yml +0 -9927
  68. data/spec/fixtures/detector/smartphone-10.yml +0 -9977
  69. data/spec/fixtures/detector/smartphone-11.yml +0 -9891
  70. data/spec/fixtures/detector/smartphone-12.yml +0 -9906
  71. data/spec/fixtures/detector/smartphone-13.yml +0 -9920
  72. data/spec/fixtures/detector/smartphone-14.yml +0 -2662
  73. data/spec/fixtures/detector/smartphone-2.yml +0 -9992
  74. data/spec/fixtures/detector/smartphone-3.yml +0 -9945
  75. data/spec/fixtures/detector/smartphone-4.yml +0 -9923
  76. data/spec/fixtures/detector/smartphone-5.yml +0 -9914
  77. data/spec/fixtures/detector/smartphone-6.yml +0 -9962
  78. data/spec/fixtures/detector/smartphone-7.yml +0 -9899
  79. data/spec/fixtures/detector/smartphone-8.yml +0 -9931
  80. data/spec/fixtures/detector/smartphone-9.yml +0 -9899
  81. data/spec/fixtures/detector/smartphone.yml +0 -9984
  82. data/spec/fixtures/detector/tablet-1.yml +0 -9995
  83. data/spec/fixtures/detector/tablet-2.yml +0 -9977
  84. data/spec/fixtures/detector/tablet-3.yml +0 -9959
  85. data/spec/fixtures/detector/tablet-4.yml +0 -4528
  86. data/spec/fixtures/detector/tablet.yml +0 -9971
  87. data/spec/fixtures/detector/tv.yml +0 -4933
  88. data/spec/fixtures/detector/unknown.yml +0 -3236
  89. data/spec/fixtures/detector/wearable.yml +0 -61
  90. data/spec/fixtures/device/camera.yml +0 -19
  91. data/spec/fixtures/device/car_browser.yml +0 -13
  92. data/spec/fixtures/device/console.yml +0 -79
  93. data/spec/fixtures/parser/oss.yml +0 -1082
  94. data/spec/fixtures/parser/vendorfragments.yml +0 -168
  95. data/spec/spec_helper.rb +0 -9
@@ -30,7 +30,7 @@ class DeviceDetector
30
30
  def os_info
31
31
  from_cache(['os_info', self.class.name, user_agent]) do
32
32
  os_name = NameExtractor.new(user_agent, regex_meta).call
33
- if os_name && short = DOWNCASED_OPERATING_SYSTEMS[os_name.downcase]
33
+ if os_name && (short = DOWNCASED_OPERATING_SYSTEMS[os_name.downcase])
34
34
  os_name = OPERATING_SYSTEMS[short]
35
35
  else
36
36
  short = 'UNK'
@@ -50,7 +50,7 @@ class DeviceDetector
50
50
  'AIX' => 'AIX',
51
51
  'AND' => 'Android',
52
52
  'AMG' => 'AmigaOS',
53
- 'ATV' => 'Apple TV',
53
+ 'ATV' => 'tvOS',
54
54
  'ARL' => 'Arch Linux',
55
55
  'BTR' => 'BackTrack',
56
56
  'SBA' => 'Bada',
@@ -58,29 +58,42 @@ class DeviceDetector
58
58
  'BLB' => 'BlackBerry OS',
59
59
  'QNX' => 'BlackBerry Tablet OS',
60
60
  'BMP' => 'Brew',
61
+ 'CAI' => 'Caixa Mágica',
61
62
  'CES' => 'CentOS',
62
63
  'COS' => 'Chrome OS',
63
64
  'CYN' => 'CyanogenMod',
64
65
  'DEB' => 'Debian',
66
+ 'DEE' => 'Deepin',
65
67
  'DFB' => 'DragonFly',
68
+ 'DVK' => 'DVKBuntu',
66
69
  'FED' => 'Fedora',
70
+ 'FEN' => 'Fenix',
67
71
  'FOS' => 'Firefox OS',
68
72
  'FIR' => 'Fire OS',
73
+ 'FRE' => 'Freebox',
69
74
  'BSD' => 'FreeBSD',
75
+ 'FYD' => 'FydeOS',
70
76
  'GNT' => 'Gentoo',
77
+ 'GRI' => 'GridOS',
71
78
  'GTV' => 'Google TV',
72
79
  'HPX' => 'HP-UX',
73
80
  'HAI' => 'Haiku OS',
81
+ 'IPA' => 'iPadOS',
82
+ 'HAR' => 'HarmonyOS',
83
+ 'HAS' => 'HasCodingOS',
74
84
  'IRI' => 'IRIX',
75
85
  'INF' => 'Inferno',
86
+ 'JME' => 'Java ME',
76
87
  'KOS' => 'KaiOS',
77
88
  'KNO' => 'Knoppix',
78
89
  'KBT' => 'Kubuntu',
79
90
  'LIN' => 'GNU/Linux',
80
91
  'LBT' => 'Lubuntu',
92
+ 'LOS' => 'Lumin OS',
81
93
  'VLN' => 'VectorLinux',
82
94
  'MAC' => 'Mac',
83
95
  'MAE' => 'Maemo',
96
+ 'MAG' => 'Mageia',
84
97
  'MDR' => 'Mandriva',
85
98
  'SMG' => 'MeeGo',
86
99
  'MCD' => 'MocorDroid',
@@ -89,21 +102,28 @@ class DeviceDetector
89
102
  'MOR' => 'MorphOS',
90
103
  'NBS' => 'NetBSD',
91
104
  'MTK' => 'MTK / Nucleus',
105
+ 'MRE' => 'MRE',
92
106
  'WII' => 'Nintendo',
93
107
  'NDS' => 'Nintendo Mobile',
94
108
  'OS2' => 'OS/2',
95
109
  'T64' => 'OSF1',
96
110
  'OBS' => 'OpenBSD',
111
+ 'OWR' => 'OpenWrt',
97
112
  'ORD' => 'Ordissimo',
113
+ 'PCL' => 'PCLinuxOS',
98
114
  'PSP' => 'PlayStation Portable',
99
115
  'PS3' => 'PlayStation',
100
116
  'RHT' => 'Red Hat',
101
117
  'ROS' => 'RISC OS',
118
+ 'ROK' => 'Roku OS',
119
+ 'RSO' => 'Rosa',
102
120
  'REM' => 'Remix OS',
121
+ 'REX' => 'REX',
103
122
  'RZD' => 'RazoDroiD',
104
123
  'SAB' => 'Sabayon',
105
124
  'SSE' => 'SUSE',
106
125
  'SAF' => 'Sailfish OS',
126
+ 'SEE' => 'SeewoOS',
107
127
  'SLW' => 'Slackware',
108
128
  'SOS' => 'Solaris',
109
129
  'SYL' => 'Syllable',
@@ -116,7 +136,9 @@ class DeviceDetector
116
136
  'TIZ' => 'Tizen',
117
137
  'TOS' => 'TmaxOS',
118
138
  'UBT' => 'Ubuntu',
139
+ 'WAS' => 'watchOS',
119
140
  'WTV' => 'WebTV',
141
+ 'WHS' => 'Whale OS',
120
142
  'WIN' => 'Windows',
121
143
  'WCE' => 'Windows CE',
122
144
  'WIO' => 'Windows IoT',
@@ -125,7 +147,7 @@ class DeviceDetector
125
147
  'WRT' => 'Windows RT',
126
148
  'XBX' => 'Xbox',
127
149
  'XBT' => 'Xubuntu',
128
- 'YNS' => 'YunOs',
150
+ 'YNS' => 'YunOS',
129
151
  'IOS' => 'iOS',
130
152
  'POS' => 'palmOS',
131
153
  'WOS' => 'webOS'
@@ -133,37 +155,42 @@ class DeviceDetector
133
155
 
134
156
  DOWNCASED_OPERATING_SYSTEMS = OPERATING_SYSTEMS.each_with_object({}) do |(short, long), h|
135
157
  h[long.downcase] = short
136
- end
158
+ end.freeze
137
159
 
138
160
  OS_FAMILIES = {
139
- 'Android' => %w[AND CYN FIR REM RZD MLD MCD YNS],
161
+ 'Android' => %w[AND CYN FIR REM RZD MLD MCD YNS GRI HAR],
140
162
  'AmigaOS' => %w[AMG MOR],
141
- 'Apple TV' => ['ATV'],
142
163
  'BlackBerry' => %w[BLB QNX],
143
164
  'Brew' => ['BMP'],
144
165
  'BeOS' => %w[BEO HAI],
145
- 'Chrome OS' => ['COS'],
166
+ 'Chrome OS' => %w[COS FYD SEE],
146
167
  'Firefox OS' => %w[FOS KOS],
147
168
  'Gaming Console' => %w[WII PS3],
148
169
  'Google TV' => ['GTV'],
149
170
  'IBM' => ['OS2'],
150
- 'iOS' => ['IOS'],
171
+ 'iOS' => %w[IOS ATV WAS IPA],
151
172
  'RISC OS' => ['ROS'],
152
- 'GNU/Linux' => %w[LIN ARL DEB KNO MIN UBT KBT XBT LBT FED RHT VLN MDR GNT SAB SLW SSE CES BTR SAF ORD TOS],
173
+ 'GNU/Linux' => %w[
174
+ LIN ARL DEB KNO MIN UBT KBT XBT LBT FED
175
+ RHT VLN MDR GNT SAB SLW SSE CES BTR SAF
176
+ ORD TOS RSO DEE FRE MAG FEN CAI PCL HAS
177
+ LOS DVK ROK OWR
178
+ ],
153
179
  'Mac' => ['MAC'],
154
180
  'Mobile Gaming Console' => %w[PSP NDS XBX],
155
- 'Real-time OS' => %w[MTK TDX],
181
+ 'Real-time OS' => %w[MTK TDX MRE JME REX],
156
182
  'Other Mobile' => %w[WOS POS SBA TIZ SMG MAE],
157
183
  'Symbian' => %w[SYM SYS SY3 S60 S40],
158
184
  'Unix' => %w[SOS AIX HPX BSD NBS OBS DFB SYL IRI T64 INF],
159
185
  'WebTV' => ['WTV'],
160
186
  'Windows' => ['WIN'],
161
- 'Windows Mobile' => %w[WPH WMO WCE WRT WIO]
187
+ 'Windows Mobile' => %w[WPH WMO WCE WRT WIO],
188
+ 'Other Smart TV' => ['WHS']
162
189
  }.freeze
163
190
 
164
191
  FAMILY_TO_OS = OS_FAMILIES.each_with_object({}) do |(family, oss), h|
165
192
  oss.each { |os| h[os] = family }
166
- end
193
+ end.freeze
167
194
 
168
195
  def filenames
169
196
  ['oss.yml']
@@ -85,7 +85,7 @@ class DeviceDetector
85
85
  end
86
86
 
87
87
  def build_regex(src)
88
- Regexp.new('(?:^|[^A-Z0-9\-_]|[^A-Z0-9\-]_|sprd-)(?:' + src + ')', Regexp::IGNORECASE)
88
+ Regexp.new('(?:^|[^A-Z0-9\-_]|[^A-Z0-9\-]_|sprd-|MZ-)(?:' + src + ')', Regexp::IGNORECASE)
89
89
  end
90
90
 
91
91
  def from_cache(key)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DeviceDetector
4
- VERSION = '1.0.4'
4
+ VERSION = '1.0.7'
5
5
  end
@@ -2,8 +2,36 @@
2
2
 
3
3
  class DeviceDetector
4
4
  class VersionExtractor < MetadataExtractor
5
+ MAJOR_VERSION_2 = Gem::Version.new('2.0')
6
+ MAJOR_VERSION_3 = Gem::Version.new('3.0')
7
+ MAJOR_VERSION_4 = Gem::Version.new('4.0')
8
+ MAJOR_VERSION_8 = Gem::Version.new('8.0')
9
+
10
+ def call
11
+ simple_version = super&.chomp('.')
12
+
13
+ return simple_version unless simple_version&.empty?
14
+
15
+ os_version_by_regexes
16
+ end
17
+
5
18
  private
6
19
 
20
+ def os_version_by_regexes
21
+ version_matches = regex_meta[:versions]
22
+ return '' unless version_matches
23
+
24
+ version_matches.detect do |matcher|
25
+ user_agent.match(matcher[:regex]) do |match_data|
26
+ return matcher[:version].gsub(/\$(\d)/) do
27
+ match_data[Regexp.last_match(1).to_i].to_s
28
+ end.strip
29
+ end
30
+ end
31
+
32
+ ''
33
+ end
34
+
7
35
  def metadata_string
8
36
  String(regex_meta[:version])
9
37
  end
@@ -13,6 +13,7 @@ require 'device_detector/bot'
13
13
  require 'device_detector/client'
14
14
  require 'device_detector/device'
15
15
  require 'device_detector/os'
16
+ require 'device_detector/browser'
16
17
 
17
18
  class DeviceDetector
18
19
  attr_reader :user_agent
@@ -46,7 +47,10 @@ class DeviceDetector
46
47
  end
47
48
 
48
49
  def device_brand
49
- device.brand
50
+ # Assume all devices running iOS / Mac OS are from Apple
51
+ brand = device.brand
52
+ brand = 'Apple' if brand.nil? && ['Apple TV', 'iOS', 'Mac'].include?(os_name)
53
+ brand
50
54
  end
51
55
 
52
56
  def device_type
@@ -59,15 +63,19 @@ class DeviceDetector
59
63
  # that won't have a detected browser, but can still be detected. So we check the useragent for
60
64
  # Chrome instead.
61
65
  if t.nil? && os.family == 'Android' && user_agent =~ build_regex('Chrome\/[\.0-9]*')
62
- if user_agent =~ build_regex('Chrome\/[\.0-9]* Mobile')
66
+ if user_agent =~ build_regex('(?:Mobile|eliboM) Safari\/')
63
67
  t = 'smartphone'
64
- elsif user_agent =~ build_regex('Chrome\/[\.0-9]* (?!Mobile)')
68
+ elsif user_agent =~ build_regex('(?!Mobile )Safari\/')
65
69
  t = 'tablet'
66
70
  end
67
71
  end
68
72
 
73
+ # Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices
74
+ # as tablets
69
75
  t = 'tablet' if t.nil? && android_tablet_fragment? || opera_tablet?
70
76
 
77
+ # Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices
78
+ # as smartphones
71
79
  t = 'smartphone' if t.nil? && android_mobile_fragment?
72
80
 
73
81
  # Android up to 3.0 was designed for smartphones only. But as 3.0,
@@ -78,10 +86,12 @@ class DeviceDetector
78
86
  # So were are expecting that all devices running Android < 2 are
79
87
  # smartphones Devices running Android 3.X are tablets. Device type
80
88
  # of Android 2.X and 4.X+ are unknown
81
- if t.nil? && os.short_name == 'AND' && os.full_version && !os.full_version.empty?
82
- if os.full_version < '2'
89
+ if t.nil? && os_name == 'Android' && os.full_version && !os.full_version.empty?
90
+ full_version = Gem::Version.new(os.full_version)
91
+ if full_version < VersionExtractor::MAJOR_VERSION_2
83
92
  t = 'smartphone'
84
- elsif os.full_version >= '3' && os.full_version < '4'
93
+ elsif full_version >= VersionExtractor::MAJOR_VERSION_3 && \
94
+ full_version < VersionExtractor::MAJOR_VERSION_4
85
95
  t = 'tablet'
86
96
  end
87
97
  end
@@ -89,6 +99,9 @@ class DeviceDetector
89
99
  # All detected feature phones running android are more likely a smartphone
90
100
  t = 'smartphone' if t == 'feature phone' && os.family == 'Android'
91
101
 
102
+ # All unknown devices under running Java ME are more likely a features phones
103
+ t = 'feature phone' if t.nil? && os_name == 'Java ME'
104
+
92
105
  # According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
93
106
  # Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the
94
107
  # end of the UA string, the computer has touch capability, and is running Windows 8 (or later).
@@ -97,19 +110,29 @@ class DeviceDetector
97
110
  # As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we
98
111
  # assume that all Windows 8 touch devices are tablets.
99
112
  if t.nil? && touch_enabled? &&
100
- (os.short_name == 'WRT' || (os.short_name == 'WIN' && os.full_version && os.full_version >= '8'))
113
+ (os_name == 'Windows RT' ||
114
+ (os_name == 'Windows' && os.full_version &&
115
+ Gem::Version.new(os.full_version) >= VersionExtractor::MAJOR_VERSION_8))
101
116
  t = 'tablet'
102
117
  end
103
118
 
119
+ # All devices running Opera TV Store are assumed to be a tv
104
120
  t = 'tv' if opera_tv_store?
105
121
 
122
+ # All devices running Tizen TV or SmartTV are assumed to be a tv
123
+ t = 'tv' if t.nil? && tizen_samsung_tv?
124
+
125
+ # Devices running Kylo or Espital TV Browsers are assumed to be a TV
106
126
  t = 'tv' if t.nil? && ['Kylo', 'Espial TV Browser'].include?(client.name)
107
127
 
108
- # set device type to desktop for all devices running a desktop os that were
109
- # not detected as an other device type
110
- t = 'desktop' if t.nil? && os.desktop? && !puffin_browser?
128
+ has_desktop = t != 'desktop' && desktop_string? && desktop_fragment?
129
+ t = 'desktop' if has_desktop
111
130
 
112
- t
131
+ # set device type to desktop for all devices running a desktop os that were not detected as
132
+ # another device type
133
+ return t if t || !desktop?
134
+
135
+ 'desktop'
113
136
  end
114
137
 
115
138
  def known?
@@ -175,6 +198,10 @@ class DeviceDetector
175
198
  user_agent =~ build_regex('Android(?: \d.\d(?:.\d)?)?; Mobile;')
176
199
  end
177
200
 
201
+ def desktop_fragment?
202
+ user_agent =~ build_regex('Desktop (x(?:32|64)|WOW64);')
203
+ end
204
+
178
205
  def touch_enabled?
179
206
  user_agent =~ build_regex('Touch')
180
207
  end
@@ -187,9 +214,26 @@ class DeviceDetector
187
214
  user_agent =~ build_regex('Opera Tablet')
188
215
  end
189
216
 
217
+ def tizen_samsung_tv?
218
+ user_agent =~ build_regex('SmartTV|Tizen.+ TV .+$')
219
+ end
220
+
221
+ def uses_mobile_browser?
222
+ client.browser? && client.mobile_only_browser?
223
+ end
224
+
190
225
  # This is a workaround until we support detecting mobile only browsers
191
- def puffin_browser?
192
- client.name == 'Puffin'
226
+ def desktop_string?
227
+ user_agent =~ /Desktop/
228
+ end
229
+
230
+ def desktop?
231
+ return false if os_name.nil? || os_name == '' || os_name == 'UNK'
232
+
233
+ # Check for browsers available for mobile devices only
234
+ return false if uses_mobile_browser?
235
+
236
+ os.desktop?
193
237
  end
194
238
 
195
239
  def build_regex(src)