device_detector 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/Rakefile +12 -6
- data/device_detector.gemspec +1 -1
- data/lib/device_detector.rb +65 -4
- data/lib/device_detector/client.rb +6 -6
- data/lib/device_detector/device.rb +62 -31
- data/lib/device_detector/memory_cache.rb +1 -1
- data/lib/device_detector/metadata_extractor.rb +4 -13
- data/lib/device_detector/model_extractor.rb +10 -1
- data/lib/device_detector/name_extractor.rb +1 -1
- data/lib/device_detector/os.rb +141 -5
- data/lib/device_detector/parser.rb +23 -12
- data/lib/device_detector/version.rb +1 -1
- data/lib/device_detector/version_extractor.rb +1 -1
- data/regexes/bots.yml +36 -1
- data/regexes/{browser_engines.yml → client/browser_engine.yml} +4 -1
- data/regexes/{browsers.yml → client/browsers.yml} +31 -27
- data/regexes/{feed_readers.yml → client/feed_readers.yml} +0 -1
- data/regexes/{libraries.yml → client/libraries.yml} +1 -1
- data/regexes/{mediaplayers.yml → client/mediaplayers.yml} +1 -1
- data/regexes/{mobile_apps.yml → client/mobile_apps.yml} +0 -1
- data/regexes/{pim.yml → client/pim.yml} +1 -1
- data/regexes/{devices → device}/cameras.yml +1 -1
- data/regexes/{devices → device}/car_browsers.yml +0 -1
- data/regexes/{devices → device}/consoles.yml +0 -1
- data/regexes/{devices → device}/mobiles.yml +193 -49
- data/regexes/{devices/portable_media_players.yml → device/portable_media_player.yml} +0 -1
- data/regexes/{devices → device}/televisions.yml +0 -3
- data/regexes/oss.yml +105 -102
- data/regexes/vendorfragments.yml +70 -0
- data/spec/device_detector/bot_fixtures_spec.rb +30 -0
- data/spec/device_detector/client_fixtures_spec.rb +31 -0
- data/spec/device_detector/concrete_user_agent_spec.rb +41 -1
- data/spec/device_detector/detector_fixtures_spec.rb +56 -0
- data/spec/device_detector/device_fixtures_spec.rb +36 -0
- data/spec/device_detector/device_spec.rb +3 -3
- data/spec/device_detector/memory_cache_spec.rb +1 -1
- data/spec/device_detector/model_extractor_spec.rb +7 -7
- data/spec/device_detector/os_fixtures_spec.rb +26 -0
- data/spec/device_detector/version_extractor_spec.rb +10 -10
- data/spec/device_detector_spec.rb +1 -1
- data/spec/fixtures/client/browser.yml +986 -0
- data/spec/fixtures/client/feed_reader.yml +180 -0
- data/spec/fixtures/client/library.yml +78 -0
- data/spec/fixtures/client/mediaplayer.yml +144 -0
- data/spec/fixtures/client/mobile_app.yml +24 -0
- data/spec/fixtures/client/pim.yml +90 -0
- data/spec/fixtures/detector/bots.yml +716 -0
- data/spec/fixtures/detector/camera.yml +91 -0
- data/spec/fixtures/detector/car_browser.yml +19 -0
- data/spec/fixtures/detector/console.yml +253 -0
- data/spec/fixtures/detector/desktop.yml +4568 -0
- data/spec/fixtures/detector/feature_phone.yml +719 -0
- data/spec/fixtures/detector/feed_reader.yml +462 -0
- data/spec/fixtures/detector/mediaplayer.yml +141 -0
- data/spec/fixtures/detector/mobile_apps.yml +32 -0
- data/spec/fixtures/detector/phablet.yml +1095 -0
- data/spec/fixtures/detector/portable_media_player.yml +145 -0
- data/spec/fixtures/detector/smart_display.yml +19 -0
- data/spec/fixtures/detector/smartphone.yml +28673 -0
- data/spec/fixtures/detector/tablet.yml +13201 -0
- data/spec/fixtures/detector/tv.yml +1380 -0
- data/spec/fixtures/detector/unknown.yml +3536 -0
- data/spec/fixtures/device/camera.yml +18 -0
- data/spec/fixtures/device/car_browser.yml +6 -0
- data/spec/fixtures/device/console.yml +72 -0
- data/spec/fixtures/parser/bots.yml +2055 -0
- data/spec/fixtures/parser/oss.yml +607 -0
- data/spec/fixtures/parser/vendorfragments.yml +156 -0
- data/spec/spec_helper.rb +6 -2
- metadata +84 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98cdaab8265efd95799dcb71e4588cef6c300528
|
4
|
+
data.tar.gz: f43a7ef253ca782d672d8856d1b1214c6b300cfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 579c755cd334bb9dbb4f23402de2f7982d7b62f1d6a852524834ddc28f09a099e849d8a7c3111e89dfbed39ece1eb2467a07cb333dc8b7f1b458cc209eda1bc1
|
7
|
+
data.tar.gz: 84c58c57130167b1eab5162aaf4aa000e62ec1f38fd3e550f802602f483e85c1a4ff969fe31d7bc30fff2cfc14e3c8243a90dfde32495257cb4bb0070bc509b5
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/testtask'
|
3
3
|
|
4
|
+
$:.unshift 'lib'
|
5
|
+
require 'device_detector'
|
6
|
+
|
4
7
|
Rake::TestTask.new do |t|
|
5
8
|
t.pattern = 'spec/**/*_spec.rb'
|
6
9
|
t.libs.push 'spec'
|
@@ -9,14 +12,12 @@ end
|
|
9
12
|
task default: :test
|
10
13
|
|
11
14
|
task :detectable_names do
|
12
|
-
|
13
|
-
bot_names = DeviceDetector::Bot.new.send(:regexes).map { |r| r['name'] }.uniq
|
15
|
+
bot_names = DeviceDetector::Bot.new.send(:regexes).map { |r| r['name'] }.uniq.sort_by { |n| n.downcase }
|
14
16
|
bot_names.delete('$1')
|
15
|
-
client_names = DeviceDetector::Client.new.send(:regexes).map { |r| r['name'] }.uniq
|
17
|
+
client_names = DeviceDetector::Client.new.send(:regexes).map { |r| r['name'] }.uniq.sort_by { |n| n.downcase }
|
16
18
|
client_names.delete('$1')
|
17
|
-
|
18
|
-
|
19
|
-
device_names = device_regexes.flat_map { |dn| dn.keys }.sort.uniq
|
19
|
+
device_regexes = DeviceDetector::Device.new.send(:load_regexes)
|
20
|
+
device_names = device_regexes.flat_map { |dn| dn.keys }.uniq.sort_by { |n| n.downcase }
|
20
21
|
|
21
22
|
today = Date.today.strftime
|
22
23
|
|
@@ -38,3 +39,8 @@ task :detectable_names do
|
|
38
39
|
puts
|
39
40
|
end
|
40
41
|
|
42
|
+
desc 'update regex database from piwik project'
|
43
|
+
task :update_regexes do
|
44
|
+
top = File.expand_path('..', __FILE__)
|
45
|
+
system "curl -s -L https://api.github.com/repos/piwik/device-detector/tarball/master | tar xzvf - --strip-components 1 --include */regexes/*.yml -C #{top}"
|
46
|
+
end
|
data/device_detector.gemspec
CHANGED
data/lib/device_detector.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
-
|
6
3
|
require 'device_detector/version'
|
7
4
|
require 'device_detector/metadata_extractor'
|
8
5
|
require 'device_detector/version_extractor'
|
@@ -44,7 +41,55 @@ class DeviceDetector
|
|
44
41
|
end
|
45
42
|
|
46
43
|
def device_type
|
47
|
-
device.type
|
44
|
+
t = device.type
|
45
|
+
|
46
|
+
if t.nil? && android_tablet_fragment?
|
47
|
+
t = 'tablet'
|
48
|
+
end
|
49
|
+
|
50
|
+
if t.nil? && android_mobile_fragment?
|
51
|
+
t = 'smartphone'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Android up to 3.0 was designed for smartphones only. But as 3.0,
|
55
|
+
# which was tablet only, was published too late, there were a
|
56
|
+
# bunch of tablets running with 2.x With 4.0 the two trees were
|
57
|
+
# merged and it is for smartphones and tablets
|
58
|
+
#
|
59
|
+
# So were are expecting that all devices running Android < 2 are
|
60
|
+
# smartphones Devices running Android 3.X are tablets. Device type
|
61
|
+
# of Android 2.X and 4.X+ are unknown
|
62
|
+
if t.nil? && os.short_name == 'AND' && os.full_version && !os.full_version.empty?
|
63
|
+
if os.full_version < '2'
|
64
|
+
t = 'smartphone'
|
65
|
+
elsif os.full_version >= '3' && os.full_version < '4'
|
66
|
+
t = 'tablet'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# All detected feature phones running android are more likely a smartphone
|
71
|
+
if t == 'feature phone' && os.family == 'Android'
|
72
|
+
t = 'smartphone'
|
73
|
+
end
|
74
|
+
|
75
|
+
# According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
|
76
|
+
# Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the end of the
|
77
|
+
# UA string, the computer has touch capability, and is running Windows 8 (or later).
|
78
|
+
# This UA string will be transmitted on a touch-enabled system running Windows 8 (RT)
|
79
|
+
#
|
80
|
+
# As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we assume that
|
81
|
+
# all Windows 8 touch devices are tablets.
|
82
|
+
if t.nil? && touch_enabled? &&
|
83
|
+
(os.short_name == 'WRT' || (os.short_name == 'WIN' && os.full_version && os.full_version >= '8'))
|
84
|
+
t = 'tablet'
|
85
|
+
end
|
86
|
+
|
87
|
+
# set device type to desktop for all devices running a desktop os that were not detected as an other device type
|
88
|
+
if t.nil? && os.desktop?
|
89
|
+
t = 'desktop'
|
90
|
+
end
|
91
|
+
|
92
|
+
t
|
48
93
|
end
|
49
94
|
|
50
95
|
def known?
|
@@ -104,4 +149,20 @@ class DeviceDetector
|
|
104
149
|
@os ||= OS.new(user_agent)
|
105
150
|
end
|
106
151
|
|
152
|
+
def android_tablet_fragment?
|
153
|
+
user_agent =~ build_regex('Android; Tablet;')
|
154
|
+
end
|
155
|
+
|
156
|
+
def android_mobile_fragment?
|
157
|
+
user_agent =~ build_regex('Android; Mobile;')
|
158
|
+
end
|
159
|
+
|
160
|
+
def touch_enabled?
|
161
|
+
user_agent =~ build_regex('Touch')
|
162
|
+
end
|
163
|
+
|
164
|
+
def build_regex(src)
|
165
|
+
Regexp.new('(?:^|[^A-Z0-9\_\-])(?:' + src + ')', Regexp::IGNORECASE)
|
166
|
+
end
|
167
|
+
|
107
168
|
end
|
@@ -9,12 +9,12 @@ class DeviceDetector
|
|
9
9
|
|
10
10
|
def filenames
|
11
11
|
[
|
12
|
-
'
|
13
|
-
'
|
14
|
-
'
|
15
|
-
'
|
16
|
-
'
|
17
|
-
'
|
12
|
+
'client/feed_readers.yml',
|
13
|
+
'client/mobile_apps.yml',
|
14
|
+
'client/mediaplayers.yml',
|
15
|
+
'client/pim.yml',
|
16
|
+
'client/browsers.yml',
|
17
|
+
'client/libraries.yml',
|
18
18
|
]
|
19
19
|
end
|
20
20
|
end
|
@@ -1,6 +1,21 @@
|
|
1
1
|
class DeviceDetector
|
2
2
|
class Device < Parser
|
3
3
|
|
4
|
+
# order is relevant for testing with fixtures
|
5
|
+
DEVICE_NAMES = [
|
6
|
+
'desktop',
|
7
|
+
'smartphone',
|
8
|
+
'tablet',
|
9
|
+
'feature phone',
|
10
|
+
'console',
|
11
|
+
'tv',
|
12
|
+
'car browser',
|
13
|
+
'smart display',
|
14
|
+
'camera',
|
15
|
+
'portable media player',
|
16
|
+
'phablet'
|
17
|
+
]
|
18
|
+
|
4
19
|
def known?
|
5
20
|
regex_meta.any?
|
6
21
|
end
|
@@ -10,50 +25,66 @@ class DeviceDetector
|
|
10
25
|
end
|
11
26
|
|
12
27
|
def type
|
13
|
-
regex_meta[
|
28
|
+
hbbtv? ? 'tv' : regex_meta[:device]
|
14
29
|
end
|
15
30
|
|
16
31
|
private
|
17
32
|
|
18
|
-
# The order of files
|
19
|
-
#
|
20
|
-
# which would otherwise be detected as iPhones
|
21
|
-
# televisions.yml works best at the end
|
33
|
+
# The order of files needs to be the same as the order of device
|
34
|
+
# parser classes used in the piwik project.
|
22
35
|
def filenames
|
23
36
|
[
|
24
|
-
'
|
25
|
-
'
|
26
|
-
'
|
27
|
-
'
|
28
|
-
'
|
29
|
-
'
|
37
|
+
'device/televisions.yml',
|
38
|
+
'device/consoles.yml',
|
39
|
+
'device/car_browsers.yml',
|
40
|
+
'device/cameras.yml',
|
41
|
+
'device/portable_media_player.yml',
|
42
|
+
'device/mobiles.yml',
|
30
43
|
]
|
31
44
|
end
|
32
45
|
|
33
|
-
def
|
34
|
-
|
46
|
+
def matching_regex
|
47
|
+
from_cache([self.class.name, user_agent]) do
|
48
|
+
regex_list = hbbtv? ? regexes_for_hbbtv : regexes_other
|
49
|
+
regex = regex_list.find { |r| user_agent =~ r[:regex] }
|
50
|
+
if regex && regex[:models]
|
51
|
+
model_regex = regex[:models].find { |m| user_agent =~ m[:regex]}
|
52
|
+
if model_regex
|
53
|
+
regex = regex.merge(:regex_model => model_regex[:regex], :model => model_regex[:model])
|
54
|
+
regex[:device] = model_regex[:device] if model_regex.key?(:device)
|
55
|
+
regex.delete(:models)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
regex
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def hbbtv?
|
63
|
+
@regex_hbbtv ||= build_regex('HbbTV/([1-9]{1}(\.[0-9]{1}){1,2})')
|
64
|
+
user_agent =~ @regex_hbbtv
|
65
|
+
end
|
35
66
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
else
|
40
|
-
regex =
|
41
|
-
case base
|
42
|
-
when Hash
|
43
|
-
base
|
44
|
-
when String
|
45
|
-
nest
|
46
|
-
else
|
47
|
-
fail "#{filenames.join(', ')} regexes are either malformed or format has changes."
|
48
|
-
end
|
67
|
+
def regexes_for_hbbtv
|
68
|
+
regexes.select { |r| r[:path] == :'device/televisions.yml' }
|
69
|
+
end
|
49
70
|
|
50
|
-
|
51
|
-
|
71
|
+
def regexes_other
|
72
|
+
regexes.select { |r| r[:path] != :'device/televisions.yml' }
|
73
|
+
end
|
52
74
|
|
53
|
-
|
75
|
+
def parse_regexes(path, raw_regexes)
|
76
|
+
raw_regexes.map do |brand, meta|
|
77
|
+
fail "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
|
78
|
+
meta[:regex] = build_regex(meta[:regex])
|
79
|
+
if meta.key?(:models)
|
80
|
+
meta[:models].each do |model|
|
81
|
+
fail "invalid model spec: #{model.inspect}" unless model[:regex].is_a? String
|
82
|
+
model[:regex] = build_regex(model[:regex])
|
83
|
+
end
|
54
84
|
end
|
55
|
-
|
56
|
-
|
85
|
+
meta[:path] = path
|
86
|
+
meta
|
87
|
+
end
|
57
88
|
end
|
58
89
|
|
59
90
|
end
|
@@ -14,23 +14,14 @@ class DeviceDetector
|
|
14
14
|
|
15
15
|
def extract_metadata
|
16
16
|
user_agent.match(regex) do |match_data|
|
17
|
-
|
17
|
+
metadata_string.gsub(/\$(\d)/) {
|
18
|
+
match_data[$1.to_i].to_s
|
19
|
+
}.strip
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def replace_metadata_string_with(match_data)
|
22
|
-
string = metadata_string
|
23
|
-
|
24
|
-
1.upto(9) do |index|
|
25
|
-
matched_data = String(match_data[index])
|
26
|
-
string = string.gsub(/\$#{index}/, matched_data)
|
27
|
-
end
|
28
|
-
|
29
|
-
string.strip
|
30
|
-
end
|
31
|
-
|
32
23
|
def regex
|
33
|
-
@regex ||= regex_meta[
|
24
|
+
@regex ||= regex_meta[:regex]
|
34
25
|
end
|
35
26
|
|
36
27
|
end
|
@@ -1,10 +1,19 @@
|
|
1
1
|
class DeviceDetector
|
2
2
|
class ModelExtractor < MetadataExtractor
|
3
3
|
|
4
|
+
def call
|
5
|
+
s = super.to_s.gsub('_',' ').strip
|
6
|
+
s.empty? ? nil : s
|
7
|
+
end
|
8
|
+
|
4
9
|
private
|
5
10
|
|
6
11
|
def metadata_string
|
7
|
-
String(regex_meta[
|
12
|
+
String(regex_meta[:model])
|
13
|
+
end
|
14
|
+
|
15
|
+
def regex
|
16
|
+
@regex ||= regex_meta[:regex_model] || regex_meta[:regex]
|
8
17
|
end
|
9
18
|
|
10
19
|
end
|
data/lib/device_detector/os.rb
CHANGED
@@ -1,16 +1,152 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
class DeviceDetector
|
2
4
|
class OS < Parser
|
3
5
|
|
6
|
+
def name
|
7
|
+
os_info[:name]
|
8
|
+
end
|
9
|
+
|
10
|
+
def short_name
|
11
|
+
os_info[:short]
|
12
|
+
end
|
13
|
+
|
14
|
+
def family
|
15
|
+
os_info[:family]
|
16
|
+
end
|
17
|
+
|
18
|
+
def desktop?
|
19
|
+
DESKTOP_OSS.include?(family)
|
20
|
+
end
|
21
|
+
|
4
22
|
def full_version
|
5
|
-
raw_version = super
|
23
|
+
raw_version = super.to_s.split('_').join('.')
|
24
|
+
raw_version == '' ? nil : raw_version
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
6
28
|
|
7
|
-
|
8
|
-
|
9
|
-
|
29
|
+
def os_info
|
30
|
+
from_cache(['os_info', self.class.name, user_agent]) do
|
31
|
+
os_name = NameExtractor.new(user_agent, regex_meta).call
|
32
|
+
if os_name && short = DOWNCASED_OPERATING_SYSTEMS[os_name.downcase]
|
33
|
+
os_name = OPERATING_SYSTEMS[short]
|
34
|
+
else
|
35
|
+
short = 'UNK'
|
36
|
+
end
|
37
|
+
{ name: os_name, short: short, family: FAMILY_TO_OS[short] }
|
10
38
|
end
|
11
39
|
end
|
12
40
|
|
13
|
-
|
41
|
+
DESKTOP_OSS = Set.new(['AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS'])
|
42
|
+
|
43
|
+
# OS short codes mapped to long names
|
44
|
+
OPERATING_SYSTEMS = {
|
45
|
+
'AIX' => 'AIX',
|
46
|
+
'AND' => 'Android',
|
47
|
+
'AMG' => 'AmigaOS',
|
48
|
+
'ATV' => 'Apple TV',
|
49
|
+
'ARL' => 'Arch Linux',
|
50
|
+
'BTR' => 'BackTrack',
|
51
|
+
'SBA' => 'Bada',
|
52
|
+
'BEO' => 'BeOS',
|
53
|
+
'BLB' => 'BlackBerry OS',
|
54
|
+
'QNX' => 'BlackBerry Tablet OS',
|
55
|
+
'BMP' => 'Brew',
|
56
|
+
'CES' => 'CentOS',
|
57
|
+
'COS' => 'Chrome OS',
|
58
|
+
'CYN' => 'CyanogenMod',
|
59
|
+
'DEB' => 'Debian',
|
60
|
+
'DFB' => 'DragonFly',
|
61
|
+
'FED' => 'Fedora',
|
62
|
+
'FOS' => 'Firefox OS',
|
63
|
+
'BSD' => 'FreeBSD',
|
64
|
+
'GNT' => 'Gentoo',
|
65
|
+
'GTV' => 'Google TV',
|
66
|
+
'HPX' => 'HP-UX',
|
67
|
+
'HAI' => 'Haiku OS',
|
68
|
+
'IRI' => 'IRIX',
|
69
|
+
'INF' => 'Inferno',
|
70
|
+
'KNO' => 'Knoppix',
|
71
|
+
'KBT' => 'Kubuntu',
|
72
|
+
'LIN' => 'GNU/Linux',
|
73
|
+
'LBT' => 'Lubuntu',
|
74
|
+
'VLN' => 'VectorLinux',
|
75
|
+
'MAC' => 'Mac',
|
76
|
+
'MDR' => 'Mandriva',
|
77
|
+
'SMG' => 'MeeGo',
|
78
|
+
'MCD' => 'MocorDroid',
|
79
|
+
'MIN' => 'Mint',
|
80
|
+
'MLD' => 'MildWild',
|
81
|
+
'MOR' => 'MorphOS',
|
82
|
+
'NBS' => 'NetBSD',
|
83
|
+
'WII' => 'Nintendo',
|
84
|
+
'NDS' => 'Nintendo Mobile',
|
85
|
+
'OS2' => 'OS/2',
|
86
|
+
'T64' => 'OSF1',
|
87
|
+
'OBS' => 'OpenBSD',
|
88
|
+
'PSP' => 'PlayStation Portable',
|
89
|
+
'PS3' => 'PlayStation',
|
90
|
+
'RHT' => 'Red Hat',
|
91
|
+
'ROS' => 'RISC OS',
|
92
|
+
'RZD' => 'RazoDroiD',
|
93
|
+
'SAB' => 'Sabayon',
|
94
|
+
'SSE' => 'SUSE',
|
95
|
+
'SAF' => 'Sailfish OS',
|
96
|
+
'SLW' => 'Slackware',
|
97
|
+
'SOS' => 'Solaris',
|
98
|
+
'SYL' => 'Syllable',
|
99
|
+
'SYM' => 'Symbian',
|
100
|
+
'SYS' => 'Symbian OS',
|
101
|
+
'S40' => 'Symbian OS Series 40',
|
102
|
+
'S60' => 'Symbian OS Series 60',
|
103
|
+
'SY3' => 'Symbian^3',
|
104
|
+
'TIZ' => 'Tizen',
|
105
|
+
'UBT' => 'Ubuntu',
|
106
|
+
'WTV' => 'WebTV',
|
107
|
+
'WIN' => 'Windows',
|
108
|
+
'WCE' => 'Windows CE',
|
109
|
+
'WMO' => 'Windows Mobile',
|
110
|
+
'WPH' => 'Windows Phone',
|
111
|
+
'WRT' => 'Windows RT',
|
112
|
+
'XBX' => 'Xbox',
|
113
|
+
'XBT' => 'Xubuntu',
|
114
|
+
'YNS' => 'YunOs',
|
115
|
+
'IOS' => 'iOS',
|
116
|
+
'POS' => 'palmOS',
|
117
|
+
'WOS' => 'webOS'
|
118
|
+
}
|
119
|
+
|
120
|
+
DOWNCASED_OPERATING_SYSTEMS = OPERATING_SYSTEMS.each_with_object({}){|(short,long),h| h[long.downcase] = short}
|
121
|
+
|
122
|
+
OS_FAMILIES = {
|
123
|
+
'Android' => ['AND', 'CYN', 'RZD', 'MLD', 'MCD'],
|
124
|
+
'AmigaOS' => ['AMG', 'MOR'],
|
125
|
+
'Apple TV' => ['ATV'],
|
126
|
+
'BlackBerry' => ['BLB', 'QNX'],
|
127
|
+
'Brew' => ['BMP'],
|
128
|
+
'BeOS' => ['BEO', 'HAI'],
|
129
|
+
'Chrome OS' => ['COS'],
|
130
|
+
'Firefox OS' => ['FOS'],
|
131
|
+
'Gaming Console' => ['WII', 'PS3'],
|
132
|
+
'Google TV' => ['GTV'],
|
133
|
+
'IBM' => ['OS2'],
|
134
|
+
'iOS' => ['IOS'],
|
135
|
+
'RISC OS' => ['ROS'],
|
136
|
+
'GNU/Linux' => ['LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'VLN', 'MDR', 'GNT', 'SAB', 'SLW', 'SSE', 'CES', 'BTR', 'YNS', 'SAF'],
|
137
|
+
'Mac' => ['MAC'],
|
138
|
+
'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'],
|
139
|
+
'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG'],
|
140
|
+
'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'],
|
141
|
+
'Unix' => ['SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64', 'INF'],
|
142
|
+
'WebTV' => ['WTV'],
|
143
|
+
'Windows' => ['WIN'],
|
144
|
+
'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT']
|
145
|
+
}
|
146
|
+
|
147
|
+
FAMILY_TO_OS = OS_FAMILIES.each_with_object({}) do |(family,oss),h|
|
148
|
+
oss.each{|os| h[os] = family}
|
149
|
+
end
|
14
150
|
|
15
151
|
def filenames
|
16
152
|
['oss.yml']
|