device_detector 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b1d7d2d1f51f584026667fa8fff86b9d4cf994d
4
- data.tar.gz: dc71e14d78d7432fd03ea583fbaaf039a1aa3242
3
+ metadata.gz: 3831c0674436e07a0133b2396d2662d9e7c557e7
4
+ data.tar.gz: 913f71fc60c565076298fd8eb9a6c93184e048b0
5
5
  SHA512:
6
- metadata.gz: 956b2f1762edd6106dd96a8c920b88d0e1b323552790ba46d533063028802c4ca91f739a4c49d95a4ade4f273f1abad48a2a73781177ccdfd776857c45e087cc
7
- data.tar.gz: 49a4bbc2a254452296f155e11837772afb3a4399308a18eb035c98ab0f7ead38ab850f0650b802043c4701afe077788ebae5a1ae33d52e4a2d2d6926281f6de6
6
+ metadata.gz: 9f1fb95f6cfda05b85d7ae536c592f59098ba36075eff80ddbaca323505c4177023fdf9b879e3a2c769e085c25df5f61d5dababa019f1a15afc67e84d6f147d8
7
+ data.tar.gz: c29d2e75eaf37b83201b95ceeed6118157388c58e340c0d489beca60b0936250002e0b72759295b27d80229af900d3674d511208d1cf325fd2fdbed54bfa92f2
data/README.md CHANGED
@@ -48,6 +48,19 @@ client.os_full_version # => '10_8_5'
48
48
 
49
49
  `DeviceDetector` will return `nil` on all attributes, if the `user_agent` is unknown.
50
50
 
51
+ ### Memory cache
52
+
53
+ `DeviceDetector` will cache up 5,000 user agent strings to boost parsing performance.
54
+ You can tune the amount of keys that will get saved in the cache:
55
+
56
+ ```ruby
57
+
58
+ # You have to call this code **before** you initialize the Detector
59
+ DeviceDetector.configure do |config|
60
+ config.max_cache_keys = 20_000 # if you have enough RAM, proceed with care
61
+ end
62
+ ```
63
+
51
64
  ## Contributing
52
65
 
53
66
  1. Fork it ( https://github.com/[my-github-username]/device_detector/fork )
@@ -5,6 +5,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
5
 
6
6
  require 'device_detector/version'
7
7
  require 'device_detector/version_extractor'
8
+ require 'device_detector/memory_cache'
8
9
  require 'device_detector/parser'
9
10
  require 'device_detector/bot'
10
11
  require 'device_detector/client'
@@ -46,6 +47,33 @@ class DeviceDetector
46
47
  bot.name
47
48
  end
48
49
 
50
+ class << self
51
+
52
+ class Configuration
53
+ attr_accessor :max_cache_keys
54
+
55
+ def to_hash
56
+ {
57
+ max_cache_keys: max_cache_keys
58
+ }
59
+ end
60
+ end
61
+
62
+ def config
63
+ @config ||= Configuration.new
64
+ end
65
+
66
+ def cache
67
+ @cache ||= MemoryCache.new(config.to_hash)
68
+ end
69
+
70
+ def configure(&block)
71
+ @config = Configuration.new
72
+ yield(config)
73
+ end
74
+
75
+ end
76
+
49
77
  private
50
78
 
51
79
  def bot
@@ -0,0 +1,56 @@
1
+ class DeviceDetector
2
+ class MemoryCache
3
+
4
+ DEFAULT_MAX_KEYS = 5000
5
+
6
+ attr_reader :data, :max_keys
7
+
8
+ def initialize(config)
9
+ @data = {}
10
+ @max_keys = config[:max_cache_keys] || DEFAULT_MAX_KEYS
11
+ @lock = Mutex.new
12
+ end
13
+
14
+ def set(key, value)
15
+ lock.synchronize do
16
+ purge_cache
17
+ data[String(key)] = value
18
+ end
19
+ end
20
+
21
+ def get(key)
22
+ data[String(key)]
23
+ end
24
+
25
+ def key?(string_key)
26
+ data.key?(string_key)
27
+ end
28
+
29
+ def get_or_set(key, value = nil)
30
+ string_key = String(key)
31
+
32
+ if key?(string_key)
33
+ get(string_key)
34
+ else
35
+ value = yield if block_given?
36
+ set(string_key, value)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :lock
43
+
44
+ def purge_cache
45
+ key_size = data.keys.size
46
+
47
+ if key_size >= max_keys
48
+ # always remove about 1/3 of keys to reduce garbage collecting
49
+ amount_of_keys = key_size / 3
50
+
51
+ data.keys.first(amount_of_keys).each { |key| data.delete(key) }
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -1,6 +1,8 @@
1
1
  class DeviceDetector
2
2
  class Parser < Struct.new(:user_agent)
3
3
 
4
+ ROOT = Pathname.new(File.expand_path('../../..', __FILE__))
5
+
4
6
  def name
5
7
  regex_meta['name']
6
8
  end
@@ -16,11 +18,13 @@ class DeviceDetector
16
18
  end
17
19
 
18
20
  def matching_regex
19
- regexes.find { |r| user_agent =~ Regexp.new(r['regex']) }
21
+ DeviceDetector.cache.get_or_set([self.class.name, user_agent]) do
22
+ regexes.find { |r| user_agent =~ Regexp.new(r['regex']) }
23
+ end
20
24
  end
21
25
 
22
26
  def regexes
23
- YAML.load(filepaths.map { |filepath| File.read(filepath) }.join)
27
+ self.class.regexes_for(filepaths)
24
28
  end
25
29
 
26
30
  def filenames
@@ -29,12 +33,15 @@ class DeviceDetector
29
33
 
30
34
  def filepaths
31
35
  filenames.map do |filename|
32
- File.join(root, 'regexes', filename)
36
+ File.join(ROOT, 'regexes', filename)
33
37
  end
34
38
  end
35
39
 
36
- def root
37
- Pathname.new(File.expand_path('../../..', __FILE__))
40
+ # This is a performance optimization.
41
+ # We cache the regexes on the class for better performance
42
+ # Thread-safety shouldn't be an issue, as we do only perform reads
43
+ def self.regexes_for(filepaths)
44
+ @regexes ||= YAML.load(filepaths.map { |filepath| File.read(filepath) }.join)
38
45
  end
39
46
 
40
47
  end
@@ -1,3 +1,3 @@
1
1
  class DeviceDetector
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DeviceDetector::MemoryCache do
4
+
5
+ let(:subject) { DeviceDetector::MemoryCache.new(config) }
6
+
7
+ let(:config) { {} }
8
+
9
+ describe '#set' do
10
+
11
+ context 'string key' do
12
+
13
+ let(:key) { 'string' }
14
+
15
+ it 'sets the value under the key' do
16
+ subject.set(key, 'value')
17
+
18
+ expect(subject.data[key]).to eq('value')
19
+ end
20
+
21
+ end
22
+
23
+ context 'array key' do
24
+
25
+ let(:key) { ['string1', 'string2'] }
26
+
27
+ it 'sets the value under the key' do
28
+ subject.set(key, 'value')
29
+
30
+ expect(subject.data[String(key)]).to eq('value')
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ describe '#get' do
38
+
39
+ context 'string key' do
40
+
41
+ let(:key) { 'string' }
42
+
43
+ it 'gets the value for the key' do
44
+ subject.data[key] = 'value'
45
+
46
+ expect(subject.get(key)).to eq('value')
47
+ end
48
+
49
+ end
50
+
51
+ context 'array key' do
52
+
53
+ let(:key) { ['string1', 'string2'] }
54
+
55
+ it 'gets the value for the key' do
56
+ subject.data[String(key)] = 'value'
57
+
58
+ expect(subject.get(key)).to eq('value')
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ describe '#get_or_set' do
66
+
67
+ let(:key) { 'string' }
68
+
69
+ context 'value already present' do
70
+
71
+ it 'gets the value for the key from cache' do
72
+ subject.data[key] = 'value'
73
+
74
+ block_called = false
75
+ value = subject.get_or_set(key) do
76
+ block_called = true
77
+ end
78
+
79
+ expect(value).to eq('value')
80
+ expect(block_called).to eq(false)
81
+ end
82
+
83
+ end
84
+
85
+ context 'value not yet present' do
86
+
87
+ it 'evaluates the block and sets the result' do
88
+ block_called = false
89
+ subject.get_or_set(key) do
90
+ block_called = true
91
+ end
92
+
93
+ expect(block_called).to eq(true)
94
+ expect(subject.data[key]).to eq(true)
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ describe 'cache purging' do
102
+
103
+ let(:config) { { max_cache_keys: 3 } }
104
+
105
+ it 'purges the cache when key size arrives at max' do
106
+ subject.set('1', 'foo')
107
+ subject.set('2', 'bar')
108
+ subject.set('3', 'baz')
109
+ subject.set('4', 'boz')
110
+
111
+ expect(subject.data.keys.size).to eq(3)
112
+ end
113
+
114
+ end
115
+
116
+ 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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mati Sójka
@@ -62,6 +62,7 @@ files:
62
62
  - lib/device_detector.rb
63
63
  - lib/device_detector/bot.rb
64
64
  - lib/device_detector/client.rb
65
+ - lib/device_detector/memory_cache.rb
65
66
  - lib/device_detector/os.rb
66
67
  - lib/device_detector/parser.rb
67
68
  - lib/device_detector/version.rb
@@ -75,6 +76,7 @@ files:
75
76
  - regexes/mobile_apps.yml
76
77
  - regexes/oss.yml
77
78
  - regexes/pim.yml
79
+ - spec/device_detector/memory_cache_spec.rb
78
80
  - spec/device_detector/version_extractor_spec.rb
79
81
  - spec/device_detector_spec.rb
80
82
  - spec/spec_helper.rb
@@ -103,6 +105,7 @@ signing_key:
103
105
  specification_version: 4
104
106
  summary: Universal Device Detection
105
107
  test_files:
108
+ - spec/device_detector/memory_cache_spec.rb
106
109
  - spec/device_detector/version_extractor_spec.rb
107
110
  - spec/device_detector_spec.rb
108
111
  - spec/spec_helper.rb