browser 1.1.0 → 2.0.0.rc1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.hound.yml +26 -6
  4. data/.rubocop.yml +26 -6
  5. data/CHANGELOG.md +10 -0
  6. data/README.md +177 -41
  7. data/Rakefile +1 -1
  8. data/bin/rake +16 -0
  9. data/bot_exceptions.yml +1 -0
  10. data/bots.yml +188 -192
  11. data/browser.gemspec +14 -28
  12. data/languages.yml +54 -54
  13. data/lib/browser.rb +82 -202
  14. data/lib/browser/accept_language.rb +47 -0
  15. data/lib/browser/action_controller.rb +1 -1
  16. data/lib/browser/base.rb +159 -0
  17. data/lib/browser/blackberry.rb +21 -0
  18. data/lib/browser/bot.rb +63 -0
  19. data/lib/browser/chrome.rb +23 -0
  20. data/lib/browser/detect_version.rb +19 -0
  21. data/lib/browser/device.rb +175 -0
  22. data/lib/browser/device/base.rb +11 -0
  23. data/lib/browser/device/blackberry_playbook.rb +17 -0
  24. data/lib/browser/device/ipad.rb +17 -0
  25. data/lib/browser/device/iphone.rb +17 -0
  26. data/lib/browser/device/ipod_touch.rb +17 -0
  27. data/lib/browser/device/kindle.rb +17 -0
  28. data/lib/browser/device/kindle_fire.rb +17 -0
  29. data/lib/browser/device/playstation3.rb +17 -0
  30. data/lib/browser/device/playstation4.rb +17 -0
  31. data/lib/browser/device/psp.rb +17 -0
  32. data/lib/browser/device/psvita.rb +17 -0
  33. data/lib/browser/device/surface.rb +23 -0
  34. data/lib/browser/device/tv.rb +17 -0
  35. data/lib/browser/device/unknown.rb +17 -0
  36. data/lib/browser/device/wii.rb +17 -0
  37. data/lib/browser/device/wiiu.rb +17 -0
  38. data/lib/browser/device/xbox_360.rb +17 -0
  39. data/lib/browser/device/xbox_one.rb +17 -0
  40. data/lib/browser/edge.rb +19 -0
  41. data/lib/browser/firefox.rb +19 -0
  42. data/lib/browser/generic.rb +32 -0
  43. data/lib/browser/internet_explorer.rb +61 -0
  44. data/lib/browser/meta/base.rb +1 -1
  45. data/lib/browser/meta/device.rb +9 -0
  46. data/lib/browser/meta/generic_browser.rb +1 -1
  47. data/lib/browser/meta/id.rb +1 -1
  48. data/lib/browser/meta/ie.rb +1 -1
  49. data/lib/browser/meta/ios.rb +2 -2
  50. data/lib/browser/meta/mobile.rb +2 -2
  51. data/lib/browser/meta/modern.rb +1 -1
  52. data/lib/browser/meta/platform.rb +2 -2
  53. data/lib/browser/meta/proxy.rb +1 -1
  54. data/lib/browser/meta/safari.rb +1 -1
  55. data/lib/browser/meta/tablet.rb +9 -0
  56. data/lib/browser/meta/webkit.rb +1 -1
  57. data/lib/browser/middleware.rb +1 -1
  58. data/lib/browser/middleware/context.rb +2 -2
  59. data/lib/browser/middleware/context/additions.rb +1 -1
  60. data/lib/browser/middleware/context/url_methods.rb +3 -3
  61. data/lib/browser/nokia.rb +19 -0
  62. data/lib/browser/opera.rb +19 -0
  63. data/lib/browser/phantom_js.rb +19 -0
  64. data/lib/browser/platform.rb +152 -0
  65. data/lib/browser/platform/adobe_air.rb +21 -0
  66. data/lib/browser/platform/android.rb +21 -0
  67. data/lib/browser/platform/base.rb +15 -0
  68. data/lib/browser/platform/blackberry.rb +21 -0
  69. data/lib/browser/platform/chrome_os.rb +21 -0
  70. data/lib/browser/platform/firefox_os.rb +21 -0
  71. data/lib/browser/platform/ios.rb +28 -0
  72. data/lib/browser/platform/linux.rb +21 -0
  73. data/lib/browser/platform/mac.rb +21 -0
  74. data/lib/browser/platform/other.rb +21 -0
  75. data/lib/browser/platform/windows.rb +21 -0
  76. data/lib/browser/platform/windows_mobile.rb +21 -0
  77. data/lib/browser/platform/windows_phone.rb +21 -0
  78. data/lib/browser/rails.rb +3 -2
  79. data/lib/browser/safari.rb +22 -0
  80. data/lib/browser/testing.rb +23 -0
  81. data/lib/browser/uc_browser.rb +19 -0
  82. data/lib/browser/version.rb +2 -7
  83. data/search_engines.yml +5 -5
  84. data/test/browser_test.rb +71 -172
  85. data/test/middleware_test.rb +7 -7
  86. data/test/sample_app.rb +3 -3
  87. data/test/test_helper.rb +11 -10
  88. data/test/ua.yml +15 -26
  89. data/test/ua_bots.yml +27 -0
  90. data/test/ua_search_engines.yml +6 -0
  91. data/test/unit/accept_language_test.rb +92 -0
  92. data/test/unit/adobe_air_test.rb +5 -10
  93. data/test/unit/android_test.rb +44 -49
  94. data/test/unit/blackberry_test.rb +55 -77
  95. data/test/unit/bots_test.rb +52 -84
  96. data/test/unit/chrome_test.rb +48 -42
  97. data/test/unit/console_test.rb +4 -62
  98. data/test/unit/device_test.rb +193 -0
  99. data/test/unit/firefox_test.rb +32 -27
  100. data/test/unit/ie_test.rb +223 -238
  101. data/test/unit/ios_app_test.rb +3 -3
  102. data/test/unit/ios_test.rb +87 -94
  103. data/test/unit/kindle_test.rb +10 -24
  104. data/test/unit/nokia_test.rb +3 -7
  105. data/test/unit/opera_test.rb +20 -30
  106. data/test/unit/platform_test.rb +161 -0
  107. data/test/unit/proxy_test.rb +3 -3
  108. data/test/unit/safari_test.rb +66 -0
  109. data/test/unit/uc_browser_test.rb +3 -7
  110. data/test/unit/windows_phone_test.rb +19 -40
  111. data/test/unit/windows_test.rb +32 -34
  112. metadata +73 -28
  113. data/lib/browser/methods/blackberry.rb +0 -51
  114. data/lib/browser/methods/bots.rb +0 -35
  115. data/lib/browser/methods/consoles.rb +0 -43
  116. data/lib/browser/methods/devices.rb +0 -41
  117. data/lib/browser/methods/ie.rb +0 -99
  118. data/lib/browser/methods/language.rb +0 -16
  119. data/lib/browser/methods/mobile.rb +0 -35
  120. data/lib/browser/methods/platform.rb +0 -152
  121. data/lib/browser/methods/proxy.rb +0 -8
  122. data/lib/browser/methods/tv.rb +0 -8
@@ -0,0 +1,47 @@
1
+ module Browser
2
+ class AcceptLanguage
3
+ def self.languages
4
+ @languages ||= YAML.load_file(Browser.root.join("languages.yml"))
5
+ end
6
+
7
+ def self.parse(accept_language)
8
+ return [] unless accept_language
9
+
10
+ accept_language
11
+ .split(",")
12
+ .map {|string| string.squeeze(" ").strip }
13
+ .map {|part| new(part) }
14
+ .sort_by(&:quality)
15
+ .reverse
16
+ end
17
+
18
+ attr_reader :part
19
+
20
+ def initialize(part)
21
+ @part = part
22
+ end
23
+
24
+ def full
25
+ @full ||= [code, region].compact.join("-")
26
+ end
27
+
28
+ def name
29
+ self.class.languages[full] || self.class.languages[code]
30
+ end
31
+
32
+ def code
33
+ @code ||= part[/\A([^-;]+)/, 1]
34
+ end
35
+
36
+ def region
37
+ @region ||= begin
38
+ region = part[/\A(?:.*?)-([^;-]+)/, 1]
39
+ region.upcase if region
40
+ end
41
+ end
42
+
43
+ def quality
44
+ @quality ||= Float(part[/;q=([\d.]+)/, 1] || 1.0)
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,6 @@
1
1
  require "action_controller/railtie"
2
2
 
3
- class Browser
3
+ module Browser
4
4
  module ActionController
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -0,0 +1,159 @@
1
+ module Browser
2
+ class Base
3
+ include DetectVersion
4
+
5
+ attr_reader :ua
6
+
7
+ # Return an array with all preferred languages that this browser accepts.
8
+ attr_reader :accept_language
9
+
10
+ def initialize(ua, accept_language: nil)
11
+ @ua = ua
12
+ @accept_language = AcceptLanguage.parse(accept_language)
13
+ end
14
+
15
+ # Return a meta info about this browser.
16
+ def meta
17
+ Meta.constants.each_with_object(Set.new) do |meta_name, meta|
18
+ meta_class = Meta.const_get(meta_name)
19
+ meta.merge(meta_class.new(self).to_a)
20
+ end.to_a
21
+ end
22
+
23
+ alias_method :to_a, :meta
24
+
25
+ # Return meta representation as string.
26
+ def to_s
27
+ meta.to_a.join(" ")
28
+ end
29
+
30
+ def version
31
+ full_version.split(".").first
32
+ end
33
+
34
+ # Return the platform.
35
+ def platform
36
+ @platform ||= Platform.new(ua)
37
+ end
38
+
39
+ # Return the bot info.
40
+ def bot
41
+ @bot ||= Bot.new(ua)
42
+ end
43
+
44
+ # Detect if current user agent is from a bot.
45
+ def bot?
46
+ bot.bot?
47
+ end
48
+
49
+ # Return the device info.
50
+ def device
51
+ @device ||= Device.new(ua)
52
+ end
53
+
54
+ # Return true if browser is modern (Webkit, Firefox 17+, IE9+, Opera 12+).
55
+ def modern?
56
+ Browser.modern_rules.any? {|rule| rule === self } # rubocop:disable Metrics/LineLength, Style/CaseEquality
57
+ end
58
+
59
+ # Detect if browser is Microsoft Internet Explorer.
60
+ def ie?(expected_version = nil)
61
+ InternetExplorer.new(ua).match? &&
62
+ detect_version?(version, expected_version)
63
+ end
64
+
65
+ # Detect if browser is Microsoft Edge.
66
+ def edge?
67
+ Edge.new(ua).match?
68
+ end
69
+
70
+ def compatibility_view?
71
+ false
72
+ end
73
+
74
+ def msie_full_version
75
+ "0.0"
76
+ end
77
+
78
+ def msie_version
79
+ "0"
80
+ end
81
+
82
+ # Detect if browser is WebKit-based.
83
+ def webkit?
84
+ ua =~ /AppleWebKit/i && !edge?
85
+ end
86
+
87
+ # Detect if browser is QuickTime
88
+ def quicktime?
89
+ ua =~ /QuickTime/i
90
+ end
91
+
92
+ # Detect if browser is Apple CoreMedia.
93
+ def core_media?
94
+ ua =~ /CoreMedia/
95
+ end
96
+
97
+ # Detect if browser is PhantomJS
98
+ def phantom_js?
99
+ PhantomJS.new(ua).match?
100
+ end
101
+
102
+ # Detect if browser is Safari.
103
+ def safari?(expected_version = nil)
104
+ Safari.new(ua).match? && detect_version?(version, expected_version)
105
+ end
106
+
107
+ def safari_webapp_mode?
108
+ (device.ipad? || device.iphone?) && ua =~ /AppleWebKit/
109
+ end
110
+
111
+ # Detect if browser is Firefox.
112
+ def firefox?
113
+ Firefox.new(ua).match?
114
+ end
115
+
116
+ # Detect if browser is Chrome.
117
+ def chrome?
118
+ Chrome.new(ua).match?
119
+ end
120
+
121
+ # Detect if browser is Opera.
122
+ def opera?
123
+ Opera.new(ua).match?
124
+ end
125
+
126
+ # Detect if browser is Yandex.
127
+ def yandex?
128
+ ua =~ /YaBrowser/
129
+ end
130
+
131
+ # Detect if browser is UCBrowser.
132
+ def uc_browser?
133
+ UCBrowser.new(ua).match?
134
+ end
135
+
136
+ # Detect if browser is Nokia S40 Ovi Browser.
137
+ def nokia?
138
+ Nokia.new(ua).match?
139
+ end
140
+
141
+ # Detect if browser is Opera Mini.
142
+ def opera_mini?
143
+ ua =~ /Opera Mini/
144
+ end
145
+
146
+ def webkit_full_version
147
+ ua[%r[AppleWebKit/([\d.]+)], 1] || "0.0"
148
+ end
149
+
150
+ def known?
151
+ id != :generic
152
+ end
153
+
154
+ # Detect if browser is a proxy browser.
155
+ def proxy?
156
+ nokia? || uc_browser? || opera_mini?
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,21 @@
1
+ module Browser
2
+ class BlackBerry < Base
3
+ def id
4
+ :blackberry
5
+ end
6
+
7
+ def name
8
+ "BlackBerry"
9
+ end
10
+
11
+ def full_version
12
+ ua[%r[BlackBerry[\da-z]+/([\d.]+)], 1] ||
13
+ ua[%r[Version/([\d.]+)], 1] ||
14
+ "0.0"
15
+ end
16
+
17
+ def match?
18
+ ua =~ /BlackBerry|BB10/
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,63 @@
1
+ module Browser
2
+ class Bot
3
+ def self.detect_empty_ua!
4
+ @detect_empty_ua = true
5
+ end
6
+
7
+ def self.detect_empty_ua?
8
+ !!@detect_empty_ua
9
+ end
10
+
11
+ def self.bots
12
+ @bots ||= YAML.load_file(Browser.root.join("bots.yml"))
13
+ end
14
+
15
+ def self.bot_exceptions
16
+ @bot_exceptions ||= YAML
17
+ .load_file(Browser.root.join("bot_exceptions.yml"))
18
+ end
19
+
20
+ def self.search_engines
21
+ @search_engines ||= YAML
22
+ .load_file(Browser.root.join("search_engines.yml"))
23
+ end
24
+
25
+ attr_reader :ua
26
+
27
+ def initialize(ua)
28
+ @ua = ua
29
+ end
30
+
31
+ def bot?
32
+ bot_with_empty_ua? || (!bot_exception? && detect_bot?)
33
+ end
34
+
35
+ def search_engine?
36
+ self.class.search_engines.any? {|key, _| downcased_ua.include?(key) }
37
+ end
38
+
39
+ def name
40
+ return unless bot?
41
+ return "Generic Bot" if bot_with_empty_ua?
42
+ self.class.bots.find {|key, _| downcased_ua.include?(key) }.last
43
+ end
44
+
45
+ private
46
+
47
+ def bot_with_empty_ua?
48
+ self.class.detect_empty_ua? && ua.strip == ""
49
+ end
50
+
51
+ def bot_exception?
52
+ self.class.bot_exceptions.any? {|key| downcased_ua.include?(key) }
53
+ end
54
+
55
+ def detect_bot?
56
+ self.class.bots.any? {|key, _| downcased_ua.include?(key) }
57
+ end
58
+
59
+ def downcased_ua
60
+ @downcased_ua ||= ua.downcase
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,23 @@
1
+ module Browser
2
+ class Chrome < Base
3
+ def id
4
+ :chrome
5
+ end
6
+
7
+ def name
8
+ "Chrome"
9
+ end
10
+
11
+ def full_version
12
+ # Each regex on its own line to enforce precedence.
13
+ ua[%r[Chrome/([\d.]+)], 1] ||
14
+ ua[%r[CriOS/([\d.]+)], 1] ||
15
+ ua[%r[Safari/([\d.]+)], 1] ||
16
+ ua[%r[AppleWebKit/([\d.]+)], 1]
17
+ end
18
+
19
+ def match?
20
+ ua =~ /Chrome|CriOS/ && !opera? && !edge?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Browser
2
+ module DetectVersion
3
+ private
4
+
5
+ def detect_version?(actual_version, expected_version)
6
+ return true unless expected_version
7
+
8
+ expected_version = parse_version(expected_version)
9
+ actual_version = parse_version(actual_version)
10
+
11
+ Gem::Requirement.create(expected_version)
12
+ .satisfied_by?(Gem::Version.create(actual_version))
13
+ end
14
+
15
+ def parse_version(version)
16
+ version.kind_of?(Numeric) ? "#{version}" : version
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,175 @@
1
+ module Browser
2
+ class Device
3
+ attr_reader :ua
4
+
5
+ def initialize(ua)
6
+ @ua = ua
7
+ end
8
+
9
+ def subject
10
+ @subject ||= [
11
+ XboxOne.new(ua),
12
+ Xbox360.new(ua),
13
+ Surface.new(ua),
14
+ TV.new(ua),
15
+ BlackBerryPlaybook.new(ua),
16
+ WiiU.new(ua),
17
+ Wii.new(ua),
18
+ KindleFire.new(ua),
19
+ Kindle.new(ua),
20
+ PlayStation4.new(ua),
21
+ PlayStation3.new(ua),
22
+ PSVita.new(ua),
23
+ PSP.new(ua),
24
+ Iphone.new(ua),
25
+ Ipad.new(ua),
26
+ IpodTouch.new(ua),
27
+ Unknown.new(ua)
28
+ ].find(&:match?)
29
+ end
30
+
31
+ def id
32
+ subject.id
33
+ end
34
+
35
+ def name
36
+ subject.name
37
+ end
38
+
39
+ # Detect if browser is tablet (currently iPad, Android, Surface or
40
+ # Playbook).
41
+ def tablet?
42
+ !!(
43
+ ipad? ||
44
+ (platform.android? && !detect_mobile?) || surface? || playbook?
45
+ )
46
+ end
47
+
48
+ # Detect if browser is mobile.
49
+ def mobile?
50
+ detect_mobile? && !tablet?
51
+ end
52
+
53
+ def ipad?
54
+ id == :ipad
55
+ end
56
+
57
+ def unknown?
58
+ id == :unknown
59
+ end
60
+
61
+ def ipod_touch?
62
+ id == :ipod_touch
63
+ end
64
+ alias_method :ipod?, :ipod_touch?
65
+
66
+ def iphone?
67
+ id == :iphone
68
+ end
69
+
70
+ def ps3?
71
+ id == :ps3
72
+ end
73
+ alias_method :playstation3?, :ps3?
74
+
75
+ def ps4?
76
+ id == :ps4
77
+ end
78
+ alias_method :playstation4?, :ps4?
79
+ alias_method :playstation4?, :ps4?
80
+
81
+ def psp?
82
+ id == :psp
83
+ end
84
+
85
+ def playstation_vita?
86
+ id == :psvita
87
+ end
88
+ alias_method :vita?, :playstation_vita?
89
+ alias_method :psp_vita?, :playstation_vita?
90
+
91
+ def kindle?
92
+ id == :kindle || kindle_fire?
93
+ end
94
+
95
+ def kindle_fire?
96
+ id == :kindle_fire
97
+ end
98
+
99
+ def nintendo_wii?
100
+ id == :wii
101
+ end
102
+ alias_method :wii?, :nintendo_wii?
103
+
104
+ def nintendo_wiiu?
105
+ id == :wiiu
106
+ end
107
+ alias_method :wiiu?, :nintendo_wiiu?
108
+
109
+ def blackberry_playbook?
110
+ id == :playbook
111
+ end
112
+ alias_method :playbook?, :blackberry_playbook?
113
+
114
+ def surface?
115
+ id == :surface
116
+ end
117
+
118
+ def tv?
119
+ id == :tv
120
+ end
121
+
122
+ # Detect if browser is Silk.
123
+ def silk?
124
+ ua =~ /Silk/
125
+ end
126
+
127
+ # Detect if browser is running under Xbox.
128
+ def xbox?
129
+ ua =~ /Xbox/
130
+ end
131
+
132
+ # Detect if browser is running under Xbox 360.
133
+ def xbox_360?
134
+ id == :xbox_360
135
+ end
136
+
137
+ # Detect if browser is running under Xbox One.
138
+ def xbox_one?
139
+ id == :xbox_one
140
+ end
141
+
142
+ # Detect if browser is running under PlayStation.
143
+ def playstation?
144
+ ps3? || ps4?
145
+ end
146
+
147
+ # Detect if browser is Nintendo.
148
+ def nintendo?
149
+ wii? || wiiu?
150
+ end
151
+
152
+ # Detect if browser is console (currently Xbox, PlayStation, or Nintendo).
153
+ def console?
154
+ xbox? || playstation? || nintendo?
155
+ end
156
+
157
+ private
158
+
159
+ # Regex taken from http://detectmobilebrowsers.com
160
+ # rubocop:disable Metrics/LineLength
161
+ def detect_mobile?
162
+ !!(
163
+ psp? ||
164
+ /zunewp7/i.match(ua) ||
165
+ /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.match(ua) ||
166
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.match(ua[0..3])
167
+ )
168
+ end
169
+ # rubocop:enable Metrics/LineLength
170
+
171
+ def platform
172
+ @platform ||= Platform.new(ua)
173
+ end
174
+ end
175
+ end