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 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 fix capacity. In addition, it allows the possibility of specifying a target hit rate, above which it will evict the elements that were accessed last. 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.
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
- All options accepted by RingCache are set when initializing the cache.
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
@@ -0,0 +1,2 @@
1
+ class RingCache::KeyNotFoundError < RuntimeError
2
+ end
@@ -1,3 +1,3 @@
1
1
  class RingCache
2
- VERSION = '1.0.1'
2
+ VERSION = '1.1'
3
3
  end
data/lib/ring_cache.rb CHANGED
@@ -1,7 +1,5 @@
1
- lib = File.dirname(__FILE__)
2
- $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
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 = block.call
39
- write(key, data)
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
- nil
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
@@ -20,4 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.6'
22
22
  spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'minitest'
24
+ spec.add_development_dependency 'minitest-reporters'
23
25
  end
data/test/test_helper.rb CHANGED
@@ -1,7 +1,4 @@
1
- lib = File.expand_path('../lib', File.dirname(__FILE__))
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'
@@ -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
- data = nil
184
- cache.write(:d, data)
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.0.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: 2014-10-20 00:00:00.000000000 Z
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: 1.8.29
111
+ rubygems_version: 2.4.5
89
112
  signing_key:
90
- specification_version: 3
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