device_detector 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +21 -0
- data/README.md +65 -4
- data/Rakefile +6 -7
- data/device_detector.gemspec +2 -1
- data/lib/device_detector.rb +1 -0
- data/lib/device_detector/device.rb +2 -2
- data/lib/device_detector/metadata_extractor.rb +1 -1
- data/lib/device_detector/name_extractor.rb +19 -0
- data/lib/device_detector/os.rb +9 -0
- data/lib/device_detector/parser.rb +25 -25
- data/lib/device_detector/version.rb +1 -1
- data/spec/device_detector/concrete_user_agent_spec.rb +60 -0
- data/spec/device_detector/device_spec.rb +30 -28
- data/spec/device_detector/memory_cache_spec.rb +16 -16
- data/spec/device_detector/model_extractor_spec.rb +10 -8
- data/spec/device_detector/version_extractor_spec.rb +13 -11
- data/spec/device_detector_spec.rb +29 -49
- data/spec/spec_helper.rb +3 -11
- metadata +22 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89802f3d1a2276198bb17c6889848941b692e962
|
4
|
+
data.tar.gz: e4ee7d45dd2e0307a5e547f1f77e0e5f4f99e725
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c52585792a08a2bd4e9ee96439af347898113872f888119e60f0096239fec438d058c6782c89c4ed52e42cc1414d0776219b2b350730888b71ccf19e05cab65
|
7
|
+
data.tar.gz: 988d986ea34e7a9fa96efdf3414ea97eba1931d4b74e7db18b794dca732b2787907c8323f91f54d6102b7cb345367961881d812e6137690fd979ae208b3f3f52
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## [0.7.0]
|
4
|
+
- [Issue #8](https://github.com/podigee/device_detector/issues/8) Fixed Mac OS X full version format. Thanks to [aaronchi](https://github.com/aaronchi) for reporting.
|
5
|
+
|
6
|
+
## [0.6.0]
|
7
|
+
|
8
|
+
- [Issue #7](https://github.com/podigee/device_detector/issues/7) Fixed missing name extraction from regexp. Thanks to [janxious](https://github.com/janxious) for reporting.
|
9
|
+
- Optimized performance of name and version extraction, by using the built-in memory cache
|
10
|
+
- Move specs from RSpec to the more lightweight Minitest
|
11
|
+
|
12
|
+
## [0.5.1]
|
13
|
+
|
14
|
+
- Added the minimum required Ruby version (>= 1.9.3)
|
15
|
+
|
16
|
+
## [0.5.0]
|
17
|
+
|
18
|
+
- Added rake task for automatic generation of supported and detectable clients and devices
|
19
|
+
- Updated detection rules
|
20
|
+
- Fixed device type detection, when type is specified on top level of a nested regex
|
21
|
+
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
DeviceDetector is a precise and fast user agent parser and device detector written in Ruby, backed by the largest and most up-to-date user agent database.
|
6
6
|
|
7
|
-
DeviceDetector will parse any user agent and detect the browser, operating system, device used (desktop, tablet, mobile, tv, cars, console, etc.), brand and model.
|
7
|
+
DeviceDetector will parse any user agent and detect the browser, operating system, device used (desktop, tablet, mobile, tv, cars, console, etc.), brand and model. DeviceDetector detects thousands of user agent strings, even from rare and obscure browsers and devices.
|
8
8
|
|
9
9
|
The DeviceDetector is optimized for speed of detection, by providing optimized code and in-memory caching.
|
10
10
|
|
@@ -62,15 +62,76 @@ client.known? # => will return false if user_agent is unknown
|
|
62
62
|
### Memory cache
|
63
63
|
|
64
64
|
`DeviceDetector` will cache up 5,000 user agent strings to boost parsing performance.
|
65
|
-
You can tune the amount of keys that will get saved in the cache
|
65
|
+
You can tune the amount of keys that will get saved in the cache. You have to call this code **before** you initialize the Detector.
|
66
66
|
|
67
67
|
```ruby
|
68
|
-
# You have to call this code **before** you initialize the Detector
|
69
68
|
DeviceDetector.configure do |config|
|
70
|
-
config.max_cache_keys =
|
69
|
+
config.max_cache_keys = 5_000 # increment this if you have enough RAM, proceed with care
|
71
70
|
end
|
72
71
|
```
|
73
72
|
|
73
|
+
If you have a Rails application, you can create an initializer, for example `config/initializers/device_detector.rb`.
|
74
|
+
|
75
|
+
## Benchmarks
|
76
|
+
|
77
|
+
We have measured the parsing speed of almost 200,000 non-unique user agent strings and compared the speed of DeviceDetector with the two most popular user agent parsers in the Ruby community, Browser and UserAgent.
|
78
|
+
|
79
|
+
### Testing machine specs
|
80
|
+
|
81
|
+
- MacBook Pro 15", Late 2013
|
82
|
+
- 2.6 GHz Intel Core i7
|
83
|
+
- 16 GB 1600 MHz DDR3
|
84
|
+
|
85
|
+
### Gem versions
|
86
|
+
|
87
|
+
- DeviceDetector - 0.5.1
|
88
|
+
- Browser - 0.8.0
|
89
|
+
- UserAgent - 0.13.1
|
90
|
+
|
91
|
+
### Code
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
require 'device_detector'
|
95
|
+
require 'browser'
|
96
|
+
require 'user_agent'
|
97
|
+
require 'benchmark'
|
98
|
+
|
99
|
+
user_agent_strings = File.read('./tmp/user_agent_strings.txt').split("\n")
|
100
|
+
|
101
|
+
## Benchmarks
|
102
|
+
|
103
|
+
Benchmark.bm(15) do |x|
|
104
|
+
x.report('device_detector') {
|
105
|
+
user_agent_strings.each { |uas| DeviceDetector.new(uas).name }
|
106
|
+
}
|
107
|
+
x.report('browser') {
|
108
|
+
user_agent_strings.each { |uas| Browser.new(ua: uas).name }
|
109
|
+
}
|
110
|
+
x.report('useragent') {
|
111
|
+
user_agent_strings.each { |uas| UserAgent.parse(uas).browser }
|
112
|
+
}
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
### Results
|
117
|
+
|
118
|
+
```
|
119
|
+
user system total real
|
120
|
+
device_detector 1.180000 0.010000 1.190000 ( 1.198721)
|
121
|
+
browser 2.240000 0.010000 2.250000 ( 2.245493)
|
122
|
+
useragent 4.490000 0.020000 4.510000 ( 4.500673)
|
123
|
+
|
124
|
+
user system total real
|
125
|
+
device_detector 1.190000 0.020000 1.210000 ( 1.201447)
|
126
|
+
browser 2.250000 0.010000 2.260000 ( 2.261001)
|
127
|
+
useragent 4.440000 0.010000 4.450000 ( 4.451693)
|
128
|
+
|
129
|
+
user system total real
|
130
|
+
device_detector 1.210000 0.020000 1.230000 ( 1.228617)
|
131
|
+
browser 2.220000 0.010000 2.230000 ( 2.222565)
|
132
|
+
useragent 4.450000 0.000000 4.450000 ( 4.452741)
|
133
|
+
```
|
134
|
+
|
74
135
|
## Detectable clients, bots and devices
|
75
136
|
|
76
137
|
Updated on 2015-03-17
|
data/Rakefile
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
7
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
4
|
+
Rake::TestTask.new do |t|
|
5
|
+
t.pattern = 'spec/**/*_spec.rb'
|
6
|
+
t.libs.push 'spec'
|
8
7
|
end
|
9
8
|
|
10
|
-
task default: :
|
9
|
+
task default: :test
|
11
10
|
|
12
11
|
task :detectable_names do
|
13
12
|
require 'device_detector'
|
data/device_detector.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.required_ruby_version = '>= 1.9.3'
|
22
22
|
|
23
|
-
spec.add_development_dependency '
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
spec.add_development_dependency 'rake'
|
24
25
|
spec.add_development_dependency 'pry', '~> 0.10'
|
25
26
|
end
|
data/lib/device_detector.rb
CHANGED
@@ -7,6 +7,7 @@ require 'device_detector/version'
|
|
7
7
|
require 'device_detector/metadata_extractor'
|
8
8
|
require 'device_detector/version_extractor'
|
9
9
|
require 'device_detector/model_extractor'
|
10
|
+
require 'device_detector/name_extractor'
|
10
11
|
require 'device_detector/memory_cache'
|
11
12
|
require 'device_detector/parser'
|
12
13
|
require 'device_detector/bot'
|
@@ -30,8 +30,8 @@ class DeviceDetector
|
|
30
30
|
]
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
34
|
-
|
33
|
+
def parse_regexes(raw_regexes, device = nil)
|
34
|
+
raw_regexes.map { |base, nest|
|
35
35
|
|
36
36
|
if !nest.nil? && nest.key?('models')
|
37
37
|
default_device = nest['device']
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class DeviceDetector
|
2
|
+
class NameExtractor < MetadataExtractor
|
3
|
+
|
4
|
+
def call
|
5
|
+
if /\$[0-9]/ =~ metadata_string
|
6
|
+
extract_metadata
|
7
|
+
else
|
8
|
+
metadata_string
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def metadata_string
|
15
|
+
regex_meta['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/device_detector/os.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
class DeviceDetector
|
2
2
|
class OS < Parser
|
3
3
|
|
4
|
+
def full_version
|
5
|
+
raw_version = super
|
6
|
+
|
7
|
+
# This solution won't scale, but for now it will do
|
8
|
+
if raw_version && name == 'Mac'
|
9
|
+
raw_version.split('_').join('.')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
private
|
5
14
|
|
6
15
|
def filenames
|
@@ -4,11 +4,15 @@ class DeviceDetector
|
|
4
4
|
ROOT = File.expand_path('../../..', __FILE__)
|
5
5
|
|
6
6
|
def name
|
7
|
-
|
7
|
+
from_cache(['name', self.class.name, user_agent]) do
|
8
|
+
NameExtractor.new(user_agent, regex_meta).call
|
9
|
+
end
|
8
10
|
end
|
9
11
|
|
10
12
|
def full_version
|
11
|
-
|
13
|
+
from_cache(['full_version', self.class.name, user_agent]) do
|
14
|
+
VersionExtractor.new(user_agent, regex_meta).call
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
private
|
@@ -18,13 +22,13 @@ class DeviceDetector
|
|
18
22
|
end
|
19
23
|
|
20
24
|
def matching_regex
|
21
|
-
|
25
|
+
from_cache([self.class.name, user_agent]) do
|
22
26
|
regexes.find { |r| user_agent =~ r['regex'] }
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
def regexes
|
27
|
-
|
31
|
+
@regexes ||= regexes_for(filepaths)
|
28
32
|
end
|
29
33
|
|
30
34
|
def filenames
|
@@ -37,34 +41,30 @@ class DeviceDetector
|
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
|
44
|
+
def regexes_for(filepaths)
|
45
|
+
from_cache(['regexes', self.class]) do
|
46
|
+
raw_regexes = load_regexes
|
47
|
+
parsed_regexes = raw_regexes.map { |r| parse_regexes(r) }
|
41
48
|
|
42
|
-
|
43
|
-
# We cache the regexes on the class for better performance
|
44
|
-
# Thread-safety shouldn't be an issue, as we do only perform reads
|
45
|
-
def regexes_for(filepaths)
|
46
|
-
@regexes ||=
|
47
|
-
begin
|
48
|
-
regexes = load_regexes(filepaths)
|
49
|
-
parsed_regexes = regexes.map { |r| parse_regexes(r) }
|
50
|
-
|
51
|
-
parsed_regexes.flatten
|
52
|
-
end
|
49
|
+
parsed_regexes.flatten
|
53
50
|
end
|
51
|
+
end
|
54
52
|
|
55
|
-
|
56
|
-
|
53
|
+
def load_regexes
|
54
|
+
raw_files = filepaths.map { |path| File.read(path) }
|
57
55
|
|
58
|
-
|
59
|
-
|
56
|
+
raw_files.map { |f| YAML.load(f) }
|
57
|
+
end
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
59
|
+
def parse_regexes(raw_regexes)
|
60
|
+
raw_regexes.map do |meta|
|
61
|
+
meta['regex'] = Regexp.new(meta['regex']) if meta['regex'].is_a? String
|
62
|
+
meta
|
66
63
|
end
|
64
|
+
end
|
67
65
|
|
66
|
+
def from_cache(key)
|
67
|
+
DeviceDetector.cache.get_or_set(key) { yield }
|
68
68
|
end
|
69
69
|
|
70
70
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DeviceDetector do
|
4
|
+
|
5
|
+
subject { DeviceDetector.new(user_agent) }
|
6
|
+
|
7
|
+
alias :client :subject
|
8
|
+
|
9
|
+
describe 'mobile iPhone 5S' do
|
10
|
+
|
11
|
+
let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B440 [FBDV/iPhone6,1]' }
|
12
|
+
|
13
|
+
describe '#device_name' do
|
14
|
+
|
15
|
+
it 'returns device name' do
|
16
|
+
client.device_name.must_equal 'iPhone 5S'
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#device_type' do
|
22
|
+
|
23
|
+
it 'returns the device type' do
|
24
|
+
client.device_type.must_equal 'smartphone'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'Ubuntu 10' do
|
32
|
+
|
33
|
+
let(:user_agent) { 'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.133 Chrome/10.0.648.133 Safari/534.16' }
|
34
|
+
|
35
|
+
describe '#os_name' do
|
36
|
+
|
37
|
+
it 'returns the OS name' do
|
38
|
+
client.os_name.must_equal 'Ubuntu'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'Mac OS X' do
|
46
|
+
|
47
|
+
let(:user_agent) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36' }
|
48
|
+
|
49
|
+
describe '#full_version' do
|
50
|
+
|
51
|
+
it 'returns the correct OS version' do
|
52
|
+
client.os_full_version.must_equal '10.10.1'
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
@@ -2,31 +2,33 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe DeviceDetector::Device do
|
4
4
|
|
5
|
-
subject
|
5
|
+
subject { DeviceDetector::Device.new(user_agent) }
|
6
|
+
|
7
|
+
alias :device :subject
|
6
8
|
|
7
9
|
describe '#name' do
|
8
10
|
|
9
|
-
|
11
|
+
describe 'when models are nested' do
|
10
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]' }
|
11
13
|
|
12
14
|
it 'finds an Apple iPhone 6' do
|
13
|
-
|
15
|
+
device.name.must_equal 'iPhone 6'
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
describe 'when models are NOT nested' do
|
18
20
|
let(:user_agent) { 'AIRNESS-AIR99/REV 2.2.1/Teleca Q03B1' }
|
19
21
|
|
20
22
|
it 'finds an Airness AIR99' do
|
21
|
-
|
23
|
+
device.name.must_equal 'AIR99'
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
27
|
+
describe 'when it cannot find a device name' do
|
26
28
|
let(:user_agent) { 'UNKNOWN MODEL NAME' }
|
27
29
|
|
28
30
|
it 'returns nil' do
|
29
|
-
|
31
|
+
device.name.must_be_nil
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
@@ -34,52 +36,52 @@ describe DeviceDetector::Device do
|
|
34
36
|
|
35
37
|
describe '#type' do
|
36
38
|
|
37
|
-
|
39
|
+
describe 'when models are nested' do
|
38
40
|
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]' }
|
39
41
|
|
40
42
|
it 'finds device of Apple iPhone 6' do
|
41
|
-
|
43
|
+
device.type.must_equal 'smartphone'
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
|
-
|
47
|
+
describe 'when models are NOT nested' do
|
46
48
|
let(:user_agent) { 'AIRNESS-AIR99/REV 2.2.1/Teleca Q03B1' }
|
47
49
|
|
48
50
|
it 'finds the device of Airness AIR99' do
|
49
|
-
|
51
|
+
device.type.must_equal 'feature phone'
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
|
-
|
55
|
+
describe 'when it cannot find a device type' do
|
54
56
|
let(:user_agent) { 'UNKNOWN MODEL TYPE' }
|
55
57
|
|
56
58
|
it 'returns nil' do
|
57
|
-
|
59
|
+
device.type.must_be_nil
|
58
60
|
end
|
59
61
|
|
60
62
|
end
|
61
63
|
|
62
|
-
|
64
|
+
describe 'device not specified in nested block' do
|
63
65
|
|
64
66
|
let(:user_agent) { 'Mozilla/5.0 (Linux; Android 4.4.2; es-us; SAMSUNG SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko)' }
|
65
67
|
|
66
68
|
it 'falls back to top-level device' do
|
67
|
-
|
69
|
+
device.type.must_equal 'smartphone'
|
68
70
|
end
|
69
71
|
|
70
72
|
end
|
71
73
|
|
72
74
|
end
|
73
75
|
|
74
|
-
|
76
|
+
describe 'concrete device types' do
|
75
77
|
|
76
78
|
describe 'mobiles' do
|
77
79
|
|
78
80
|
let(:user_agent) { 'Mozilla/5.0 (Linux; Android 4.4.2; es-us; SAMSUNG SM-G900F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko)' }
|
79
81
|
|
80
82
|
it 'identifies the device' do
|
81
|
-
|
82
|
-
|
83
|
+
device.name.must_equal 'GALAXY S5'
|
84
|
+
device.type.must_equal 'smartphone'
|
83
85
|
end
|
84
86
|
|
85
87
|
end
|
@@ -89,8 +91,8 @@ describe DeviceDetector::Device do
|
|
89
91
|
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' }
|
90
92
|
|
91
93
|
it 'identifies the device' do
|
92
|
-
|
93
|
-
|
94
|
+
device.name.must_equal 'GALAXY Camera'
|
95
|
+
device.type.must_equal 'camera'
|
94
96
|
end
|
95
97
|
|
96
98
|
end
|
@@ -100,8 +102,8 @@ describe DeviceDetector::Device do
|
|
100
102
|
let(:user_agent) { 'Mozilla/5.0 (X11; Linux) AppleWebKit/534.34 (KHTML, like Gecko) QtCarBrowser Safari/534.34' }
|
101
103
|
|
102
104
|
it 'identifies the device' do
|
103
|
-
|
104
|
-
|
105
|
+
device.name.must_equal 'Model S'
|
106
|
+
device.type.must_equal 'car browser'
|
105
107
|
end
|
106
108
|
|
107
109
|
end
|
@@ -111,8 +113,8 @@ describe DeviceDetector::Device do
|
|
111
113
|
let(:user_agent) { 'Opera/9.30 (Nintendo Wii; U; ; 2047-7;en)' }
|
112
114
|
|
113
115
|
it 'identifies the device' do
|
114
|
-
|
115
|
-
|
116
|
+
device.name.must_equal 'Wii'
|
117
|
+
device.type.must_equal 'console'
|
116
118
|
end
|
117
119
|
|
118
120
|
end
|
@@ -122,8 +124,8 @@ describe DeviceDetector::Device do
|
|
122
124
|
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' }
|
123
125
|
|
124
126
|
it 'identifies the device' do
|
125
|
-
|
126
|
-
|
127
|
+
device.name.must_equal 'iPod Touch'
|
128
|
+
device.type.must_equal 'portable media player'
|
127
129
|
end
|
128
130
|
|
129
131
|
end
|
@@ -133,8 +135,8 @@ describe DeviceDetector::Device do
|
|
133
135
|
let(:user_agent) { 'Mozilla/5.0 (Linux; NetCast; U) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.33 Safari/537.31 SmartTV/5.0' }
|
134
136
|
|
135
137
|
it 'identifies the device' do
|
136
|
-
|
137
|
-
|
138
|
+
device.name.must_equal 'NetCast'
|
139
|
+
device.type.must_equal 'tv'
|
138
140
|
end
|
139
141
|
|
140
142
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
describe DeviceDetector::MemoryCache do
|
4
4
|
|
5
5
|
let(:subject) { DeviceDetector::MemoryCache.new(config) }
|
6
6
|
|
@@ -8,26 +8,26 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
8
8
|
|
9
9
|
describe '#set' do
|
10
10
|
|
11
|
-
|
11
|
+
describe 'string key' do
|
12
12
|
|
13
13
|
let(:key) { 'string' }
|
14
14
|
|
15
15
|
it 'sets the value under the key' do
|
16
16
|
subject.set(key, 'value')
|
17
17
|
|
18
|
-
|
18
|
+
subject.data[key].must_equal 'value'
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
describe 'array key' do
|
24
24
|
|
25
25
|
let(:key) { ['string1', 'string2'] }
|
26
26
|
|
27
27
|
it 'sets the value under the key' do
|
28
28
|
subject.set(key, 'value')
|
29
29
|
|
30
|
-
|
30
|
+
subject.data[String(key)].must_equal 'value'
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
@@ -36,26 +36,26 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
36
36
|
|
37
37
|
describe '#get' do
|
38
38
|
|
39
|
-
|
39
|
+
describe 'string key' do
|
40
40
|
|
41
41
|
let(:key) { 'string' }
|
42
42
|
|
43
43
|
it 'gets the value for the key' do
|
44
44
|
subject.data[key] = 'value'
|
45
45
|
|
46
|
-
|
46
|
+
subject.get(key).must_equal 'value'
|
47
47
|
end
|
48
48
|
|
49
49
|
end
|
50
50
|
|
51
|
-
|
51
|
+
describe 'array key' do
|
52
52
|
|
53
53
|
let(:key) { ['string1', 'string2'] }
|
54
54
|
|
55
55
|
it 'gets the value for the key' do
|
56
56
|
subject.data[String(key)] = 'value'
|
57
57
|
|
58
|
-
|
58
|
+
subject.get(key).must_equal 'value'
|
59
59
|
end
|
60
60
|
|
61
61
|
end
|
@@ -66,7 +66,7 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
66
66
|
|
67
67
|
let(:key) { 'string' }
|
68
68
|
|
69
|
-
|
69
|
+
describe 'value already present' do
|
70
70
|
|
71
71
|
it 'gets the value for the key from cache' do
|
72
72
|
subject.data[key] = 'value'
|
@@ -76,13 +76,13 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
76
76
|
block_called = true
|
77
77
|
end
|
78
78
|
|
79
|
-
|
80
|
-
|
79
|
+
value.must_equal 'value'
|
80
|
+
block_called.must_equal false
|
81
81
|
end
|
82
82
|
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
describe 'value not yet present' do
|
86
86
|
|
87
87
|
it 'evaluates the block and sets the result' do
|
88
88
|
block_called = false
|
@@ -90,8 +90,8 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
90
90
|
block_called = true
|
91
91
|
end
|
92
92
|
|
93
|
-
|
94
|
-
|
93
|
+
block_called.must_equal true
|
94
|
+
subject.data[key].must_equal true
|
95
95
|
end
|
96
96
|
|
97
97
|
end
|
@@ -108,7 +108,7 @@ RSpec.describe DeviceDetector::MemoryCache do
|
|
108
108
|
subject.set('3', 'baz')
|
109
109
|
subject.set('4', 'boz')
|
110
110
|
|
111
|
-
|
111
|
+
subject.data.keys.size.must_equal 3
|
112
112
|
end
|
113
113
|
|
114
114
|
end
|
@@ -2,11 +2,13 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe DeviceDetector::ModelExtractor do
|
4
4
|
|
5
|
-
subject
|
5
|
+
subject { DeviceDetector::ModelExtractor.new(user_agent, regex_meta) }
|
6
|
+
|
7
|
+
alias :extractor :subject
|
6
8
|
|
7
9
|
describe '#call' do
|
8
10
|
|
9
|
-
|
11
|
+
describe 'when matching against dynamic model' do
|
10
12
|
|
11
13
|
let(:regex_meta) do
|
12
14
|
{
|
@@ -16,29 +18,29 @@ describe DeviceDetector::ModelExtractor do
|
|
16
18
|
}
|
17
19
|
end
|
18
20
|
|
19
|
-
|
21
|
+
describe 'when no dynamic match is found' do
|
20
22
|
let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B466 Safari/600.1.4' }
|
21
23
|
let(:device_name) { 'iPhone' }
|
22
24
|
|
23
25
|
it 'returns the textual portion without trailing whitespace' do
|
24
|
-
|
26
|
+
extractor.call.must_equal device_name
|
25
27
|
end
|
26
28
|
|
27
29
|
end
|
28
30
|
|
29
|
-
|
31
|
+
describe 'when a dynamic match is found' do
|
30
32
|
let(:user_agent) { 'Mozilla/5.0 (iPhone 5S; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B466 Safari/600.1.4' }
|
31
33
|
let(:device_name) { 'iPhone 5S' }
|
32
34
|
|
33
35
|
it 'returns the full device name' do
|
34
|
-
|
36
|
+
extractor.call.must_equal device_name
|
35
37
|
end
|
36
38
|
|
37
39
|
end
|
38
40
|
|
39
41
|
end
|
40
42
|
|
41
|
-
|
43
|
+
describe 'when matching against static model' do
|
42
44
|
|
43
45
|
let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12A365 Weibo (iPhone7,2)' }
|
44
46
|
let(:device_name) { 'iPhone 6' }
|
@@ -51,7 +53,7 @@ describe DeviceDetector::ModelExtractor do
|
|
51
53
|
end
|
52
54
|
|
53
55
|
it 'returns the model name' do
|
54
|
-
|
56
|
+
extractor.call.must_equal device_name
|
55
57
|
end
|
56
58
|
|
57
59
|
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
describe DeviceDetector::VersionExtractor do
|
4
4
|
|
5
|
-
subject
|
5
|
+
subject { DeviceDetector::VersionExtractor.new(user_agent, regex_meta) }
|
6
|
+
|
7
|
+
alias :extractor :subject
|
6
8
|
|
7
9
|
describe '#call' do
|
8
10
|
|
9
|
-
|
11
|
+
describe 'extractor without version' do
|
10
12
|
|
11
13
|
let(:user_agent) { 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; Avant Browser; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)' }
|
12
14
|
|
@@ -19,12 +21,12 @@ RSpec.describe DeviceDetector::VersionExtractor do
|
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'returns nil' do
|
22
|
-
|
24
|
+
extractor.call.must_equal ''
|
23
25
|
end
|
24
26
|
|
25
27
|
end
|
26
28
|
|
27
|
-
|
29
|
+
describe 'regex with dynamic matching' do
|
28
30
|
|
29
31
|
let(:user_agent) { 'Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1b2) Gecko/20060821 BonEcho/2.0b2 (Debian-1.99+2.0b2+dfsg-1)' }
|
30
32
|
let(:version) { 'BonEcho (2.0)' }
|
@@ -37,17 +39,17 @@ RSpec.describe DeviceDetector::VersionExtractor do
|
|
37
39
|
end
|
38
40
|
|
39
41
|
it 'returns the correct version' do
|
40
|
-
|
42
|
+
extractor.call.must_equal version
|
41
43
|
end
|
42
44
|
|
43
45
|
it 'removes trailing white spaces' do
|
44
46
|
regex_meta['version'] = regex_meta['version'] + ' '
|
45
|
-
|
47
|
+
extractor.call.must_equal version
|
46
48
|
end
|
47
49
|
|
48
50
|
end
|
49
51
|
|
50
|
-
|
52
|
+
describe 'extractor with fixed version' do
|
51
53
|
|
52
54
|
let(:user_agent) { 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)' }
|
53
55
|
let(:regex_meta) do
|
@@ -58,18 +60,18 @@ RSpec.describe DeviceDetector::VersionExtractor do
|
|
58
60
|
end
|
59
61
|
|
60
62
|
it 'returns the correct version' do
|
61
|
-
|
63
|
+
extractor.call.must_equal '8.0'
|
62
64
|
end
|
63
65
|
|
64
66
|
end
|
65
67
|
|
66
|
-
|
68
|
+
describe 'unknown user agent' do
|
67
69
|
|
68
70
|
let(:user_agent) { 'garbage' }
|
69
71
|
let(:regex_meta) { {} }
|
70
72
|
|
71
73
|
it 'returns nil' do
|
72
|
-
|
74
|
+
extractor.call.must_be_nil
|
73
75
|
end
|
74
76
|
|
75
77
|
end
|
@@ -1,19 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
describe DeviceDetector do
|
4
4
|
|
5
|
-
subject
|
5
|
+
subject { DeviceDetector.new(user_agent) }
|
6
6
|
|
7
|
-
|
7
|
+
alias :client :subject
|
8
8
|
|
9
|
-
|
9
|
+
describe 'known user agent' do
|
10
|
+
|
11
|
+
describe 'desktop chrome browser' do
|
10
12
|
|
11
13
|
let(:user_agent) { 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69' }
|
12
14
|
|
13
15
|
describe '#name' do
|
14
16
|
|
15
17
|
it 'returns the name' do
|
16
|
-
|
18
|
+
client.name.must_equal 'Chrome'
|
17
19
|
end
|
18
20
|
|
19
21
|
end
|
@@ -21,7 +23,7 @@ RSpec.describe DeviceDetector do
|
|
21
23
|
describe '#full_version' do
|
22
24
|
|
23
25
|
it 'returns the full version' do
|
24
|
-
|
26
|
+
client.full_version.must_equal '30.0.1599.69'
|
25
27
|
end
|
26
28
|
|
27
29
|
end
|
@@ -29,7 +31,7 @@ RSpec.describe DeviceDetector do
|
|
29
31
|
describe '#os_name' do
|
30
32
|
|
31
33
|
it 'returns the operating system name' do
|
32
|
-
|
34
|
+
client.os_name.must_equal 'Mac'
|
33
35
|
end
|
34
36
|
|
35
37
|
end
|
@@ -37,7 +39,7 @@ RSpec.describe DeviceDetector do
|
|
37
39
|
describe '#os_full_version' do
|
38
40
|
|
39
41
|
it 'returns the operating system full version' do
|
40
|
-
|
42
|
+
client.os_full_version.must_equal '10.8.5'
|
41
43
|
end
|
42
44
|
|
43
45
|
end
|
@@ -45,7 +47,7 @@ RSpec.describe DeviceDetector do
|
|
45
47
|
describe '#known?' do
|
46
48
|
|
47
49
|
it 'returns true' do
|
48
|
-
|
50
|
+
client.known?.must_equal true
|
49
51
|
end
|
50
52
|
|
51
53
|
end
|
@@ -53,7 +55,7 @@ RSpec.describe DeviceDetector do
|
|
53
55
|
describe '#bot?' do
|
54
56
|
|
55
57
|
it 'returns false' do
|
56
|
-
|
58
|
+
client.bot?.must_equal false
|
57
59
|
end
|
58
60
|
|
59
61
|
end
|
@@ -61,29 +63,7 @@ RSpec.describe DeviceDetector do
|
|
61
63
|
describe '#bot_name' do
|
62
64
|
|
63
65
|
it 'returns nil' do
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'mobile iPhone 5S' do
|
72
|
-
|
73
|
-
let(:user_agent) { 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B440 [FBDV/iPhone6,1]' }
|
74
|
-
|
75
|
-
describe '#device_name' do
|
76
|
-
|
77
|
-
it 'returns device name' do
|
78
|
-
expect(client.device_name).to eq('iPhone 5S')
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
describe '#device_type' do
|
84
|
-
|
85
|
-
it 'returns the device type' do
|
86
|
-
expect(client.device_type).to eq('smartphone')
|
66
|
+
client.bot_name.must_be_nil
|
87
67
|
end
|
88
68
|
|
89
69
|
end
|
@@ -92,14 +72,14 @@ RSpec.describe DeviceDetector do
|
|
92
72
|
|
93
73
|
end
|
94
74
|
|
95
|
-
|
75
|
+
describe 'unknown user agent' do
|
96
76
|
|
97
77
|
let(:user_agent) { 'garbage123' }
|
98
78
|
|
99
79
|
describe '#name' do
|
100
80
|
|
101
81
|
it 'returns nil' do
|
102
|
-
|
82
|
+
client.name.must_be_nil
|
103
83
|
end
|
104
84
|
|
105
85
|
end
|
@@ -107,7 +87,7 @@ RSpec.describe DeviceDetector do
|
|
107
87
|
describe '#full_version' do
|
108
88
|
|
109
89
|
it 'returns nil' do
|
110
|
-
|
90
|
+
client.full_version.must_be_nil
|
111
91
|
end
|
112
92
|
|
113
93
|
end
|
@@ -115,7 +95,7 @@ RSpec.describe DeviceDetector do
|
|
115
95
|
describe '#os_name' do
|
116
96
|
|
117
97
|
it 'returns nil' do
|
118
|
-
|
98
|
+
client.os_name.must_be_nil
|
119
99
|
end
|
120
100
|
|
121
101
|
end
|
@@ -123,7 +103,7 @@ RSpec.describe DeviceDetector do
|
|
123
103
|
describe '#os_full_version' do
|
124
104
|
|
125
105
|
it 'returns nil' do
|
126
|
-
|
106
|
+
client.os_full_version.must_be_nil
|
127
107
|
end
|
128
108
|
|
129
109
|
end
|
@@ -131,7 +111,7 @@ RSpec.describe DeviceDetector do
|
|
131
111
|
describe '#known?' do
|
132
112
|
|
133
113
|
it 'returns false' do
|
134
|
-
|
114
|
+
client.known?.must_equal false
|
135
115
|
end
|
136
116
|
|
137
117
|
end
|
@@ -139,7 +119,7 @@ RSpec.describe DeviceDetector do
|
|
139
119
|
describe '#bot?' do
|
140
120
|
|
141
121
|
it 'returns false' do
|
142
|
-
|
122
|
+
client.bot?.must_equal false
|
143
123
|
end
|
144
124
|
|
145
125
|
end
|
@@ -147,21 +127,21 @@ RSpec.describe DeviceDetector do
|
|
147
127
|
describe '#bot_name' do
|
148
128
|
|
149
129
|
it 'returns nil' do
|
150
|
-
|
130
|
+
client.bot_name.must_be_nil
|
151
131
|
end
|
152
132
|
|
153
133
|
end
|
154
134
|
|
155
135
|
end
|
156
136
|
|
157
|
-
|
137
|
+
describe 'bot' do
|
158
138
|
|
159
139
|
let(:user_agent) { 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' }
|
160
140
|
|
161
141
|
describe '#name' do
|
162
142
|
|
163
143
|
it 'returns nil' do
|
164
|
-
|
144
|
+
client.name.must_be_nil
|
165
145
|
end
|
166
146
|
|
167
147
|
end
|
@@ -169,7 +149,7 @@ RSpec.describe DeviceDetector do
|
|
169
149
|
describe '#full_version' do
|
170
150
|
|
171
151
|
it 'returns nil' do
|
172
|
-
|
152
|
+
client.full_version.must_be_nil
|
173
153
|
end
|
174
154
|
|
175
155
|
end
|
@@ -177,7 +157,7 @@ RSpec.describe DeviceDetector do
|
|
177
157
|
describe '#os_name' do
|
178
158
|
|
179
159
|
it 'returns nil' do
|
180
|
-
|
160
|
+
client.os_name.must_be_nil
|
181
161
|
end
|
182
162
|
|
183
163
|
end
|
@@ -185,7 +165,7 @@ RSpec.describe DeviceDetector do
|
|
185
165
|
describe '#os_full_version' do
|
186
166
|
|
187
167
|
it 'returns nil' do
|
188
|
-
|
168
|
+
client.os_full_version.must_be_nil
|
189
169
|
end
|
190
170
|
|
191
171
|
end
|
@@ -193,7 +173,7 @@ RSpec.describe DeviceDetector do
|
|
193
173
|
describe '#known?' do
|
194
174
|
|
195
175
|
it 'returns false' do
|
196
|
-
|
176
|
+
client.known?.must_equal false
|
197
177
|
end
|
198
178
|
|
199
179
|
end
|
@@ -201,7 +181,7 @@ RSpec.describe DeviceDetector do
|
|
201
181
|
describe '#bot?' do
|
202
182
|
|
203
183
|
it 'returns true' do
|
204
|
-
|
184
|
+
client.bot?.must_equal true
|
205
185
|
end
|
206
186
|
|
207
187
|
end
|
@@ -209,7 +189,7 @@ RSpec.describe DeviceDetector do
|
|
209
189
|
describe '#bot_name' do
|
210
190
|
|
211
191
|
it 'returns the name of the bot' do
|
212
|
-
|
192
|
+
client.bot_name.must_equal 'Googlebot'
|
213
193
|
end
|
214
194
|
|
215
195
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
3
|
|
4
|
-
require '
|
5
|
-
require 'device_detector'
|
6
|
-
require 'pry'
|
4
|
+
require './lib/device_detector'
|
7
5
|
|
8
|
-
# Requires supporting files with custom matchers and macros, etc,
|
9
|
-
# in ./support/ and its subdirectories.
|
10
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
11
|
-
|
12
|
-
RSpec.configure do |config|
|
13
|
-
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: device_detector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mati Sójka
|
@@ -9,28 +9,36 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-03-
|
12
|
+
date: 2015-03-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: minitest
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "~>"
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '3.1'
|
21
18
|
- - ">="
|
22
19
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
20
|
+
version: '0'
|
24
21
|
type: :development
|
25
22
|
prerelease: false
|
26
23
|
version_requirements: !ruby/object:Gem::Requirement
|
27
24
|
requirements:
|
28
|
-
- - "
|
25
|
+
- - ">="
|
29
26
|
- !ruby/object:Gem::Version
|
30
|
-
version: '
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
31
39
|
- - ">="
|
32
40
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
41
|
+
version: '0'
|
34
42
|
- !ruby/object:Gem::Dependency
|
35
43
|
name: pry
|
36
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,6 +63,7 @@ extra_rdoc_files: []
|
|
55
63
|
files:
|
56
64
|
- ".gitignore"
|
57
65
|
- ".travis.yml"
|
66
|
+
- CHANGELOG.md
|
58
67
|
- Gemfile
|
59
68
|
- LICENSE.txt
|
60
69
|
- README.md
|
@@ -67,6 +76,7 @@ files:
|
|
67
76
|
- lib/device_detector/memory_cache.rb
|
68
77
|
- lib/device_detector/metadata_extractor.rb
|
69
78
|
- lib/device_detector/model_extractor.rb
|
79
|
+
- lib/device_detector/name_extractor.rb
|
70
80
|
- lib/device_detector/os.rb
|
71
81
|
- lib/device_detector/parser.rb
|
72
82
|
- lib/device_detector/version.rb
|
@@ -86,6 +96,7 @@ files:
|
|
86
96
|
- regexes/mobile_apps.yml
|
87
97
|
- regexes/oss.yml
|
88
98
|
- regexes/pim.yml
|
99
|
+
- spec/device_detector/concrete_user_agent_spec.rb
|
89
100
|
- spec/device_detector/device_spec.rb
|
90
101
|
- spec/device_detector/memory_cache_spec.rb
|
91
102
|
- spec/device_detector/model_extractor_spec.rb
|
@@ -117,6 +128,7 @@ signing_key:
|
|
117
128
|
specification_version: 4
|
118
129
|
summary: Precise and fast user agent parser and device detector
|
119
130
|
test_files:
|
131
|
+
- spec/device_detector/concrete_user_agent_spec.rb
|
120
132
|
- spec/device_detector/device_spec.rb
|
121
133
|
- spec/device_detector/memory_cache_spec.rb
|
122
134
|
- spec/device_detector/model_extractor_spec.rb
|