ring_cache 1.0.1 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +29 -5
- data/Rakefile +12 -0
- data/lib/ring_cache/key_not_found_error.rb +2 -0
- data/lib/ring_cache/version.rb +1 -1
- data/lib/ring_cache.rb +18 -9
- data/ring_cache.gemspec +2 -0
- data/test/test_helper.rb +1 -4
- data/test/test_ring_cache.rb +56 -2
- metadata +41 -18
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 19d4ddc995312c013a62b2634e04ab59c94ddd64
|
4
|
+
data.tar.gz: 8a1e8b35a96a5354136eaff89d2ecc7e8e937a53
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8eeba522cb421e174bbb2e64b551bc2794102b3d6a1d17dd8f377bb94ce582c21432aaa3ad4ef334cadd60cf467795570a3a5b0f40df5e5f87c65317d96badd
|
7
|
+
data.tar.gz: 81fe4a6ab3590fdeb59074fe46893d51cb8b77d895a3c088502b3975130f5de13ac44fb44b3e34c8996c96b581392bea249af04a0db7aae30a9f400c684bad7a
|
data/README.md
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
RingCache is an in-memory cache that emulates a ring buffer, in which older elements are evicted to make room for new ones. It is mostly useful in situations in which it is not worth it, or possible, to keep all accessed data in memory, and some elements are more frequently accessed than others.
|
4
4
|
|
5
|
-
As a ring buffer, it can work with a
|
5
|
+
As a ring buffer, it can work with a limited capacity. In addition, it allows the possibility of specifying a target hit rate, above which it will evict the elements that have not been accessed the longest. This should make it easier to optimize the amount of memory used when the hit rate becomes insensitive to the capacity over a given threshold—read [Pareto principle](https://en.wikipedia.org/wiki/Pareto_principle).
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
RingCache does not have any dependency apart from Ruby.
|
9
|
+
RingCache does not have any dependency apart from Ruby Standard Library.
|
10
10
|
|
11
11
|
To install with Bundle, add the following line to the Gemfile:
|
12
12
|
|
@@ -22,9 +22,7 @@ Otherwise, it can be installed with Rubygems as follows:
|
|
22
22
|
|
23
23
|
## Cache Initialization
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
The options related to the cache size are:
|
25
|
+
RingCache accepts the following options to configure the size of the cache:
|
28
26
|
|
29
27
|
* `capacity`: The maximum number of elements that the cache will hold. By default, the capacity is unlimited.
|
30
28
|
|
@@ -58,6 +56,8 @@ test = cache.read(:example)
|
|
58
56
|
|
59
57
|
Both keys and values can be any data type.
|
60
58
|
|
59
|
+
The `read` method returns `nil` by default when the cache does not contain the requested key. Alternatively, `read!` raises a `RingCache::KeyNotFoundError` exception when the requested element is not in the cache.
|
60
|
+
|
61
61
|
Use `fetch` as a shortcut to provide missing, more-costly-to-load data in a block:
|
62
62
|
|
63
63
|
```ruby
|
@@ -67,6 +67,30 @@ end
|
|
67
67
|
# => "Lorem ipsum"
|
68
68
|
```
|
69
69
|
|
70
|
+
By default, `fetch` will cache anything returned from the block. The option `cache_nil` allows specifying whether `nil` values should also be cache, and defaults to `true`:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
cache.fetch(:example, cache_nil: false) do
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
# => nil
|
77
|
+
|
78
|
+
cache.has_key?(:example)
|
79
|
+
# => false
|
80
|
+
```
|
81
|
+
|
82
|
+
If already within the block it is determined that the returned content should not be cached, it is possible to throw a `:dont_cache` symbol. Optionally, the symbol can be accompanied by the value that should be returned from the block—while not cached:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
cache_fetch(:example) do
|
86
|
+
throw :dont_cache, true
|
87
|
+
end
|
88
|
+
# => true
|
89
|
+
|
90
|
+
cache.has_key?(:example)
|
91
|
+
# => false
|
92
|
+
```
|
93
|
+
|
70
94
|
Use `evict` to remove an element from the cache:
|
71
95
|
|
72
96
|
```ruby
|
data/Rakefile
CHANGED
@@ -6,3 +6,15 @@ Rake::TestTask.new do |t|
|
|
6
6
|
t.test_files = FileList['test/test*.rb']
|
7
7
|
t.verbose = true
|
8
8
|
end
|
9
|
+
|
10
|
+
task :environment do
|
11
|
+
require_relative 'lib/ring_cache'
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Start a console with library loaded'
|
15
|
+
task :console => :environment do
|
16
|
+
require 'irb'
|
17
|
+
require 'irb/completion'
|
18
|
+
ARGV.clear
|
19
|
+
IRB.start
|
20
|
+
end
|
data/lib/ring_cache/version.rb
CHANGED
data/lib/ring_cache.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require 'ring_cache/version'
|
1
|
+
require_relative 'ring_cache/key_not_found_error'
|
2
|
+
require_relative 'ring_cache/version'
|
5
3
|
require 'set'
|
6
4
|
|
7
5
|
class RingCache
|
@@ -33,10 +31,15 @@ class RingCache
|
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
36
|
-
def fetch(key, &block)
|
34
|
+
def fetch(key, options = {}, &block)
|
35
|
+
cache_nil = options.fetch(:cache_nil, true)
|
36
|
+
|
37
37
|
unless (data = read(key))
|
38
|
-
data =
|
39
|
-
|
38
|
+
data = catch(:dont_cache) do
|
39
|
+
data_to_cache = block.call
|
40
|
+
write(key, data_to_cache) unless data_to_cache.nil? and !cache_nil
|
41
|
+
data_to_cache
|
42
|
+
end
|
40
43
|
end
|
41
44
|
data
|
42
45
|
end
|
@@ -53,7 +56,7 @@ class RingCache
|
|
53
56
|
has_key?(key) ? @cache[key][:last_accessed_at] : nil
|
54
57
|
end
|
55
58
|
|
56
|
-
def read(key)
|
59
|
+
def read!(key)
|
57
60
|
@access_count += 1
|
58
61
|
|
59
62
|
if @cache.has_key?(key)
|
@@ -82,10 +85,16 @@ class RingCache
|
|
82
85
|
|
83
86
|
data
|
84
87
|
else
|
85
|
-
|
88
|
+
fail KeyNotFoundError, "Cache does not have content indexed by #{key}"
|
86
89
|
end
|
87
90
|
end
|
88
91
|
|
92
|
+
def read(key)
|
93
|
+
read!(key)
|
94
|
+
rescue KeyNotFoundError
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
|
89
98
|
def reset
|
90
99
|
@cache = {}
|
91
100
|
@access_time_index = SortedSet.new
|
data/ring_cache.gemspec
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
3
|
-
|
4
|
-
require 'ring_cache'
|
1
|
+
require_relative '../lib/ring_cache'
|
5
2
|
require 'minitest'
|
6
3
|
require 'minitest/autorun'
|
7
4
|
require 'minitest/reporters'
|
data/test/test_ring_cache.rb
CHANGED
@@ -72,6 +72,45 @@ class TestRingCache < Minitest::Test
|
|
72
72
|
assert_equal 0.0, c.hit_rate
|
73
73
|
end
|
74
74
|
|
75
|
+
def test_dont_cache_throw
|
76
|
+
cache = RingCache.new
|
77
|
+
|
78
|
+
value = cache.fetch(:a) do
|
79
|
+
throw :dont_cache, [1, 2, 3]
|
80
|
+
end
|
81
|
+
assert_equal [1, 2, 3], value
|
82
|
+
refute cache.has_key?(:a)
|
83
|
+
|
84
|
+
value = cache.fetch(:a) do
|
85
|
+
throw :dont_cache
|
86
|
+
end
|
87
|
+
assert_nil value
|
88
|
+
refute cache.has_key?(:a)
|
89
|
+
|
90
|
+
value = cache.fetch(:a) do
|
91
|
+
[1, 2, 3]
|
92
|
+
end
|
93
|
+
assert cache.has_key?(:a)
|
94
|
+
assert_equal [1, 2, 3], value
|
95
|
+
assert_equal [1, 2, 3], cache.read(:a)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_read_excl
|
99
|
+
c = RingCache.new
|
100
|
+
assert_raises RingCache::KeyNotFoundError do
|
101
|
+
c.read!(:nope)
|
102
|
+
end
|
103
|
+
|
104
|
+
c.write(:nope, 1)
|
105
|
+
begin
|
106
|
+
c.read!(:nope)
|
107
|
+
rescue RingCache::KeyNotFoundError
|
108
|
+
assert false, 'RingCache::KeyNotFoundError exception raised'
|
109
|
+
end
|
110
|
+
|
111
|
+
assert_equal 1, c.read!(:nope)
|
112
|
+
end
|
113
|
+
|
75
114
|
def test_execute_method_on_retrieve
|
76
115
|
test_class = Class.new
|
77
116
|
test_class.class_eval do
|
@@ -180,8 +219,23 @@ class TestRingCache < Minitest::Test
|
|
180
219
|
duplicate_on_retrieve: true,
|
181
220
|
execute_on_retrieve: :reload
|
182
221
|
)
|
183
|
-
|
184
|
-
cache.write(:d,
|
222
|
+
|
223
|
+
cache.write(:d, nil)
|
224
|
+
assert cache.has_key?(:d)
|
185
225
|
assert_nil cache.read(:d)
|
226
|
+
|
227
|
+
data = cache.fetch(:e) { nil }
|
228
|
+
assert_nil data
|
229
|
+
assert cache.has_key?(:e)
|
230
|
+
assert_nil cache.read(:e)
|
231
|
+
|
232
|
+
data = cache.fetch(:f, cache_nil: true) { nil }
|
233
|
+
assert_nil data
|
234
|
+
assert cache.has_key?(:f)
|
235
|
+
assert_nil cache.read(:f)
|
236
|
+
|
237
|
+
data = cache.fetch(:g, cache_nil: false) { nil }
|
238
|
+
assert_nil data
|
239
|
+
refute cache.has_key?(:g)
|
186
240
|
end
|
187
241
|
end
|
metadata
CHANGED
@@ -1,46 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ring_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: '1.1'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Alvaro Redondo
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-06-20 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '1.6'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.6'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
44
67
|
- !ruby/object:Gem::Version
|
45
68
|
version: '0'
|
46
69
|
description: RingCache is an in-memory cache that emulates a ring buffer, in which
|
@@ -51,12 +74,13 @@ executables: []
|
|
51
74
|
extensions: []
|
52
75
|
extra_rdoc_files: []
|
53
76
|
files:
|
54
|
-
- .gitignore
|
77
|
+
- ".gitignore"
|
55
78
|
- Gemfile
|
56
79
|
- LICENSE.txt
|
57
80
|
- README.md
|
58
81
|
- Rakefile
|
59
82
|
- lib/ring_cache.rb
|
83
|
+
- lib/ring_cache/key_not_found_error.rb
|
60
84
|
- lib/ring_cache/version.rb
|
61
85
|
- ring_cache.gemspec
|
62
86
|
- test/random_data_generator.rb
|
@@ -67,27 +91,26 @@ files:
|
|
67
91
|
homepage: https://github.com/aredondo/ring_cache
|
68
92
|
licenses:
|
69
93
|
- MIT
|
94
|
+
metadata: {}
|
70
95
|
post_install_message:
|
71
96
|
rdoc_options: []
|
72
97
|
require_paths:
|
73
98
|
- lib
|
74
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
-
none: false
|
76
100
|
requirements:
|
77
|
-
- -
|
101
|
+
- - ">="
|
78
102
|
- !ruby/object:Gem::Version
|
79
103
|
version: '0'
|
80
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
105
|
requirements:
|
83
|
-
- -
|
106
|
+
- - ">="
|
84
107
|
- !ruby/object:Gem::Version
|
85
108
|
version: '0'
|
86
109
|
requirements: []
|
87
110
|
rubyforge_project:
|
88
|
-
rubygems_version:
|
111
|
+
rubygems_version: 2.4.5
|
89
112
|
signing_key:
|
90
|
-
specification_version:
|
113
|
+
specification_version: 4
|
91
114
|
summary: In-memory cache that emulates a ring buffer.
|
92
115
|
test_files:
|
93
116
|
- test/random_data_generator.rb
|