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
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
4
  class MemoryCache
3
-
4
5
  DEFAULT_MAX_KEYS = 5000
6
+ STORES_NIL_VALUE = :__is_nil__
5
7
 
6
8
  attr_reader :data, :max_keys, :lock
7
9
  private :lock
@@ -15,41 +17,46 @@ class DeviceDetector
15
17
  def set(key, value)
16
18
  lock.synchronize do
17
19
  purge_cache
18
- data[String(key)] = value
20
+ # convert nil values into symbol so we know a value is present
21
+ cache_value = value.nil? ? STORES_NIL_VALUE : value
22
+ data[String(key)] = cache_value
23
+ value
19
24
  end
20
25
  end
21
26
 
22
27
  def get(key)
23
- data[String(key)]
24
- end
25
-
26
- def key?(string_key)
27
- data.key?(string_key)
28
+ value, _hit = get_hit(key)
29
+ value
28
30
  end
29
31
 
30
32
  def get_or_set(key, value = nil)
31
33
  string_key = String(key)
32
34
 
33
- if key?(string_key)
34
- get(string_key)
35
- else
36
- value = yield if block_given?
37
- set(string_key, value)
38
- end
35
+ result, hit = get_hit(string_key)
36
+ return result if hit
37
+
38
+ value = yield if block_given?
39
+ set(string_key, value)
39
40
  end
40
41
 
41
42
  private
42
43
 
44
+ def get_hit(key)
45
+ value = data[String(key)]
46
+ is_hit = !value.nil? || value == STORES_NIL_VALUE
47
+ value = nil if value == STORES_NIL_VALUE
48
+ [value, is_hit]
49
+ end
50
+
43
51
  def purge_cache
44
52
  key_size = data.size
45
53
 
46
- if key_size >= max_keys
47
- # always remove about 1/3 of keys to reduce garbage collecting
48
- amount_of_keys = key_size / 3
54
+ return if key_size < max_keys
49
55
 
50
- data.keys.first(amount_of_keys).each { |key| data.delete(key) }
51
- end
52
- end
56
+ # always remove about 1/3 of keys to reduce garbage collecting
57
+ amount_of_keys = key_size / 3
53
58
 
59
+ data.keys.first(amount_of_keys).each { |key| data.delete(key) }
60
+ end
54
61
  end
55
62
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
4
  class MetadataExtractor < Struct.new(:user_agent, :regex_meta)
3
-
4
5
  def call
5
6
  regex_meta.any? ? extract_metadata : nil
6
7
  end
@@ -8,22 +9,20 @@ class DeviceDetector
8
9
  private
9
10
 
10
11
  def metadata_string
11
- message = "#{self.name} (a child of MetadataExtractor) must implement the '#{__method__}' method."
12
- fail NotImplementedError, message
12
+ message = "#{name} (a child of MetadataExtractor) must implement the '#{__method__}' method."
13
+ raise NotImplementedError, message
13
14
  end
14
15
 
15
16
  def extract_metadata
16
17
  user_agent.match(regex) do |match_data|
17
- metadata_string.gsub(/\$(\d)/) {
18
- match_data[$1.to_i].to_s
19
- }.strip
18
+ metadata_string.gsub(/\$(\d)/) do
19
+ match_data[Regexp.last_match(1).to_i].to_s
20
+ end.strip
20
21
  end
21
22
  end
22
23
 
23
24
  def regex
24
25
  @regex ||= regex_meta[:regex]
25
26
  end
26
-
27
27
  end
28
28
  end
29
-
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
4
  class ModelExtractor < MetadataExtractor
3
-
4
5
  def call
5
- s = super.to_s.gsub('_',' ').strip
6
+ s = super.to_s.gsub('_', ' ').strip
6
7
  s = s.gsub(/ TD$/i, '')
7
8
 
8
9
  return nil if s == 'Build'
@@ -19,6 +20,5 @@ class DeviceDetector
19
20
  def regex
20
21
  @regex ||= regex_meta[:regex_model] || regex_meta[:regex]
21
22
  end
22
-
23
23
  end
24
24
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
4
  class NameExtractor < MetadataExtractor
3
-
4
5
  def call
5
6
  if /\$[0-9]/ =~ metadata_string
6
7
  extract_metadata
@@ -14,6 +15,5 @@ class DeviceDetector
14
15
  def metadata_string
15
16
  regex_meta[:name]
16
17
  end
17
-
18
18
  end
19
19
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  class DeviceDetector
4
6
  class OS < Parser
5
-
6
7
  def name
7
8
  os_info[:name]
8
9
  end
@@ -38,125 +39,134 @@ class DeviceDetector
38
39
  end
39
40
  end
40
41
 
41
- DESKTOP_OSS = Set.new(['AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS'])
42
+ DESKTOP_OSS = Set.new(
43
+ [
44
+ 'AmigaOS', 'IBM', 'GNU/Linux', 'Mac', 'Unix', 'Windows', 'BeOS', 'Chrome OS'
45
+ ]
46
+ )
42
47
 
43
48
  # OS short codes mapped to long names
44
49
  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
- 'MAE' => 'Maemo',
77
- 'MDR' => 'Mandriva',
78
- 'SMG' => 'MeeGo',
79
- 'MCD' => 'MocorDroid',
80
- 'MIN' => 'Mint',
81
- 'MLD' => 'MildWild',
82
- 'MOR' => 'MorphOS',
83
- 'NBS' => 'NetBSD',
84
- 'MTK' => 'MTK / Nucleus',
85
- 'WII' => 'Nintendo',
86
- 'NDS' => 'Nintendo Mobile',
87
- 'OS2' => 'OS/2',
88
- 'T64' => 'OSF1',
89
- 'OBS' => 'OpenBSD',
90
- 'PSP' => 'PlayStation Portable',
91
- 'PS3' => 'PlayStation',
92
- 'RHT' => 'Red Hat',
93
- 'ROS' => 'RISC OS',
94
- 'REM' => 'Remix OS',
95
- 'RZD' => 'RazoDroiD',
96
- 'SAB' => 'Sabayon',
97
- 'SSE' => 'SUSE',
98
- 'SAF' => 'Sailfish OS',
99
- 'SLW' => 'Slackware',
100
- 'SOS' => 'Solaris',
101
- 'SYL' => 'Syllable',
102
- 'SYM' => 'Symbian',
103
- 'SYS' => 'Symbian OS',
104
- 'S40' => 'Symbian OS Series 40',
105
- 'S60' => 'Symbian OS Series 60',
106
- 'SY3' => 'Symbian^3',
107
- 'TDX' => 'ThreadX',
108
- 'TIZ' => 'Tizen',
109
- 'UBT' => 'Ubuntu',
110
- 'WTV' => 'WebTV',
111
- 'WIN' => 'Windows',
112
- 'WCE' => 'Windows CE',
113
- 'WMO' => 'Windows Mobile',
114
- 'WPH' => 'Windows Phone',
115
- 'WRT' => 'Windows RT',
116
- 'XBX' => 'Xbox',
117
- 'XBT' => 'Xubuntu',
118
- 'YNS' => 'YunOs',
119
- 'IOS' => 'iOS',
120
- 'POS' => 'palmOS',
121
- 'WOS' => 'webOS'
122
- }
123
-
124
- DOWNCASED_OPERATING_SYSTEMS = OPERATING_SYSTEMS.each_with_object({}){|(short,long),h| h[long.downcase] = short}
50
+ 'AIX' => 'AIX',
51
+ 'AND' => 'Android',
52
+ 'AMG' => 'AmigaOS',
53
+ 'ATV' => 'Apple TV',
54
+ 'ARL' => 'Arch Linux',
55
+ 'BTR' => 'BackTrack',
56
+ 'SBA' => 'Bada',
57
+ 'BEO' => 'BeOS',
58
+ 'BLB' => 'BlackBerry OS',
59
+ 'QNX' => 'BlackBerry Tablet OS',
60
+ 'BMP' => 'Brew',
61
+ 'CES' => 'CentOS',
62
+ 'COS' => 'Chrome OS',
63
+ 'CYN' => 'CyanogenMod',
64
+ 'DEB' => 'Debian',
65
+ 'DFB' => 'DragonFly',
66
+ 'FED' => 'Fedora',
67
+ 'FOS' => 'Firefox OS',
68
+ 'FIR' => 'Fire OS',
69
+ 'BSD' => 'FreeBSD',
70
+ 'GNT' => 'Gentoo',
71
+ 'GTV' => 'Google TV',
72
+ 'HPX' => 'HP-UX',
73
+ 'HAI' => 'Haiku OS',
74
+ 'IRI' => 'IRIX',
75
+ 'INF' => 'Inferno',
76
+ 'KOS' => 'KaiOS',
77
+ 'KNO' => 'Knoppix',
78
+ 'KBT' => 'Kubuntu',
79
+ 'LIN' => 'GNU/Linux',
80
+ 'LBT' => 'Lubuntu',
81
+ 'VLN' => 'VectorLinux',
82
+ 'MAC' => 'Mac',
83
+ 'MAE' => 'Maemo',
84
+ 'MDR' => 'Mandriva',
85
+ 'SMG' => 'MeeGo',
86
+ 'MCD' => 'MocorDroid',
87
+ 'MIN' => 'Mint',
88
+ 'MLD' => 'MildWild',
89
+ 'MOR' => 'MorphOS',
90
+ 'NBS' => 'NetBSD',
91
+ 'MTK' => 'MTK / Nucleus',
92
+ 'WII' => 'Nintendo',
93
+ 'NDS' => 'Nintendo Mobile',
94
+ 'OS2' => 'OS/2',
95
+ 'T64' => 'OSF1',
96
+ 'OBS' => 'OpenBSD',
97
+ 'ORD' => 'Ordissimo',
98
+ 'PSP' => 'PlayStation Portable',
99
+ 'PS3' => 'PlayStation',
100
+ 'RHT' => 'Red Hat',
101
+ 'ROS' => 'RISC OS',
102
+ 'REM' => 'Remix OS',
103
+ 'RZD' => 'RazoDroiD',
104
+ 'SAB' => 'Sabayon',
105
+ 'SSE' => 'SUSE',
106
+ 'SAF' => 'Sailfish OS',
107
+ 'SLW' => 'Slackware',
108
+ 'SOS' => 'Solaris',
109
+ 'SYL' => 'Syllable',
110
+ 'SYM' => 'Symbian',
111
+ 'SYS' => 'Symbian OS',
112
+ 'S40' => 'Symbian OS Series 40',
113
+ 'S60' => 'Symbian OS Series 60',
114
+ 'SY3' => 'Symbian^3',
115
+ 'TDX' => 'ThreadX',
116
+ 'TIZ' => 'Tizen',
117
+ 'TOS' => 'TmaxOS',
118
+ 'UBT' => 'Ubuntu',
119
+ 'WTV' => 'WebTV',
120
+ 'WIN' => 'Windows',
121
+ 'WCE' => 'Windows CE',
122
+ 'WIO' => 'Windows IoT',
123
+ 'WMO' => 'Windows Mobile',
124
+ 'WPH' => 'Windows Phone',
125
+ 'WRT' => 'Windows RT',
126
+ 'XBX' => 'Xbox',
127
+ 'XBT' => 'Xubuntu',
128
+ 'YNS' => 'YunOs',
129
+ 'IOS' => 'iOS',
130
+ 'POS' => 'palmOS',
131
+ 'WOS' => 'webOS'
132
+ }.freeze
133
+
134
+ DOWNCASED_OPERATING_SYSTEMS = OPERATING_SYSTEMS.each_with_object({}) do |(short, long), h|
135
+ h[long.downcase] = short
136
+ end
125
137
 
126
138
  OS_FAMILIES = {
127
- 'Android' => ['AND', 'CYN', 'REM', 'RZD', 'MLD', 'MCD', 'YNS'],
128
- 'AmigaOS' => ['AMG', 'MOR'],
129
- 'Apple TV' => ['ATV'],
130
- 'BlackBerry' => ['BLB', 'QNX'],
131
- 'Brew' => ['BMP'],
132
- 'BeOS' => ['BEO', 'HAI'],
133
- 'Chrome OS' => ['COS'],
134
- 'Firefox OS' => ['FOS'],
135
- 'Gaming Console' => ['WII', 'PS3'],
136
- 'Google TV' => ['GTV'],
137
- 'IBM' => ['OS2'],
138
- 'iOS' => ['IOS'],
139
- 'RISC OS' => ['ROS'],
140
- 'GNU/Linux' => ['LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'VLN', 'MDR', 'GNT', 'SAB', 'SLW', 'SSE', 'CES', 'BTR', 'SAF'],
141
- 'Mac' => ['MAC'],
142
- 'Mobile Gaming Console' => ['PSP', 'NDS', 'XBX'],
143
- 'Real-time OS' => ['MTK', 'TDX'],
144
- 'Other Mobile' => ['WOS', 'POS', 'SBA', 'TIZ', 'SMG', 'MAE'],
145
- 'Symbian' => ['SYM', 'SYS', 'SY3', 'S60', 'S40'],
146
- 'Unix' => ['SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64', 'INF'],
147
- 'WebTV' => ['WTV'],
148
- 'Windows' => ['WIN'],
149
- 'Windows Mobile' => ['WPH', 'WMO', 'WCE', 'WRT']
150
- }
151
-
152
- FAMILY_TO_OS = OS_FAMILIES.each_with_object({}) do |(family,oss),h|
153
- oss.each{|os| h[os] = family}
139
+ 'Android' => %w[AND CYN FIR REM RZD MLD MCD YNS],
140
+ 'AmigaOS' => %w[AMG MOR],
141
+ 'Apple TV' => ['ATV'],
142
+ 'BlackBerry' => %w[BLB QNX],
143
+ 'Brew' => ['BMP'],
144
+ 'BeOS' => %w[BEO HAI],
145
+ 'Chrome OS' => ['COS'],
146
+ 'Firefox OS' => %w[FOS KOS],
147
+ 'Gaming Console' => %w[WII PS3],
148
+ 'Google TV' => ['GTV'],
149
+ 'IBM' => ['OS2'],
150
+ 'iOS' => ['IOS'],
151
+ '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],
153
+ 'Mac' => ['MAC'],
154
+ 'Mobile Gaming Console' => %w[PSP NDS XBX],
155
+ 'Real-time OS' => %w[MTK TDX],
156
+ 'Other Mobile' => %w[WOS POS SBA TIZ SMG MAE],
157
+ 'Symbian' => %w[SYM SYS SY3 S60 S40],
158
+ 'Unix' => %w[SOS AIX HPX BSD NBS OBS DFB SYL IRI T64 INF],
159
+ 'WebTV' => ['WTV'],
160
+ 'Windows' => ['WIN'],
161
+ 'Windows Mobile' => %w[WPH WMO WCE WRT WIO]
162
+ }.freeze
163
+
164
+ FAMILY_TO_OS = OS_FAMILIES.each_with_object({}) do |(family, oss), h|
165
+ oss.each { |os| h[os] = family }
154
166
  end
155
167
 
156
168
  def filenames
157
169
  ['oss.yml']
158
170
  end
159
-
160
171
  end
161
-
162
172
  end
@@ -1,7 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
- class Parser < Struct.new(:user_agent)
4
+ class Parser
5
+ ROOT = File.expand_path('../..', __dir__)
6
+
7
+ REGEX_CACHE = ::DeviceDetector::MemoryCache.new({})
8
+ private_constant :REGEX_CACHE
9
+
10
+ def initialize(user_agent)
11
+ @user_agent = user_agent
12
+ end
3
13
 
4
- ROOT = File.expand_path('../../..', __FILE__)
14
+ attr_reader :user_agent
5
15
 
6
16
  def name
7
17
  from_cache(['name', self.class.name, user_agent]) do
@@ -32,17 +42,17 @@ class DeviceDetector
32
42
  end
33
43
 
34
44
  def filenames
35
- fail NotImplementedError
45
+ raise NotImplementedError
36
46
  end
37
47
 
38
48
  def filepaths
39
49
  filenames.map do |filename|
40
- [ filename.to_sym, File.join(ROOT, 'regexes', filename) ]
50
+ [filename.to_sym, File.join(ROOT, 'regexes', filename)]
41
51
  end
42
52
  end
43
53
 
44
54
  def regexes_for(file_paths)
45
- from_cache(['regexes', self.class]) do
55
+ REGEX_CACHE.get_or_set(file_paths) do
46
56
  load_regexes(file_paths).flat_map { |path, regex| parse_regexes(path, regex) }
47
57
  end
48
58
  end
@@ -54,16 +64,20 @@ class DeviceDetector
54
64
  def symbolize_keys!(object)
55
65
  case object
56
66
  when Array
57
- object.map!{ |v| symbolize_keys!(v) }
67
+ object.map! { |v| symbolize_keys!(v) }
58
68
  when Hash
59
- object.keys.each{ |k| object[k.to_sym] = symbolize_keys!(object.delete(k)) if k.is_a?(String) }
69
+ keys = object.keys
70
+ keys.each do |k|
71
+ object[k.to_sym] = symbolize_keys!(object.delete(k)) if k.is_a?(String)
72
+ end
60
73
  end
61
74
  object
62
75
  end
63
76
 
64
77
  def parse_regexes(path, raw_regexes)
65
78
  raw_regexes.map do |meta|
66
- fail "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
79
+ raise "invalid device spec: #{meta.inspect}" unless meta[:regex].is_a? String
80
+
67
81
  meta[:regex] = build_regex(meta[:regex])
68
82
  meta[:path] = path
69
83
  meta
@@ -77,6 +91,5 @@ class DeviceDetector
77
91
  def from_cache(key)
78
92
  DeviceDetector.cache.get_or_set(key) { yield }
79
93
  end
80
-
81
94
  end
82
95
  end