zache 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 186b5dcd33ca13d027053e477dc378e8cd203d96e43257f591d8537d8b665c17
4
- data.tar.gz: c39d96fefe90f9b481bbd6acd49ac4d2cd7cd675d83462b53895a8fc2ef1f49a
3
+ metadata.gz: 8ad9aee02e0592400be73acb02cdf8878c314950df90fce1632ea00e7d07d396
4
+ data.tar.gz: 802eba8bf8ad6e027b17d85eb296957b0255fde72e9340713bad5b0b96e4dd67
5
5
  SHA512:
6
- metadata.gz: fd75a39fa850377dc87c9f6658e6bd03f49304a7b167ff6b6e59ba676ca6a6b727333a1cc83248fcd9b45dd3ad118911e092de60991d4fd4ed8c35a700a80dcc
7
- data.tar.gz: 110ad00c04c2d65c65bf9c875dd21c9b9e3254412eae28766cf53be3cad07ebe9e81a09de881f0aec1d2747fcf35ca0f461503bcc2a5c47dacd1f35dc29c47f3
6
+ metadata.gz: c756d6ba076c41371f3c47d1d2f825f06876ea9dea1d39a86f1d7f5f6b93a87d077427af2726e4dc211248e416b02dba26a130a54b1a40e74eeb8f3e64b982bc
7
+ data.tar.gz: af8e41d5048f8afa5275362d3e23da9b9276c6277f7f0db33d0353e40bac2e4223806da97f612621b5b587e9f8c964aa737310696a76b9829f242ec543b6fae1
data/.rultor.yml CHANGED
@@ -20,4 +20,7 @@ architect:
20
20
  - yegor256
21
21
  merge:
22
22
  commanders: []
23
+ script: |-
24
+ bundle install
25
+ rake
23
26
  deploy: {}
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![EO principles respected here](http://www.elegantobjects.org/badge.svg)](http://www.elegantobjects.org)
1
2
  [![DevOps By Rultor.com](http://www.rultor.com/b/yegor256/zache)](http://www.rultor.com/p/yegor256/zache)
2
3
  [![We recommend RubyMine](http://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/)
3
4
 
data/Rakefile CHANGED
@@ -44,6 +44,13 @@ Rake::TestTask.new(:test) do |test|
44
44
  test.verbose = false
45
45
  end
46
46
 
47
+ require 'rdoc/task'
48
+ RDoc::Task.new do |rdoc|
49
+ rdoc.main = 'README.md'
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.rdoc_files.include('README.md', 'lib/**/*.rb')
52
+ end
53
+
47
54
  require 'rubocop/rake_task'
48
55
  desc 'Run RuboCop on all directories'
49
56
  RuboCop::RakeTask.new(:rubocop) do |task|
data/lib/zache.rb CHANGED
@@ -22,18 +22,36 @@
22
22
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
  # SOFTWARE.
24
24
 
25
- # Cache.
25
+ # It is a very simple thread-safe in-memory cache with an ability to expire
26
+ # keys automatically, when their lifetime is over. Use it like this:
27
+ #
28
+ # require 'zache'
29
+ # zache = Zache.new
30
+ # # Expires in 5 minutes
31
+ # v = zache.get(:count, lifetime: 5 * 60) { expensive() }
32
+ #
33
+ # For more information read
34
+ # {README}[https://github.com/yegor256/zache/blob/master/README.md] file.
35
+ #
26
36
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
27
37
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
28
38
  # License:: MIT
29
39
  class Zache
40
+ # Makes a new object of the cache.
41
+ # "sync" is whether the hash is thread-safe (`true`)
42
+ # or not (`false`); it is recommended to leave this parameter untouched,
43
+ # unless you really know what you are doing.
30
44
  def initialize(sync: true)
31
45
  @hash = {}
32
46
  @sync = sync
33
47
  @mutex = Mutex.new
34
48
  end
35
49
 
50
+ # Gets the value from the cache by the provided key. If the value is not
51
+ # found in the cache, it will be calculated via the provided block. If
52
+ # the block is not given, an exception will be raised.
36
53
  def get(key, lifetime: 60 * 60)
54
+ raise 'A block is required' unless block_given?
37
55
  if @sync
38
56
  @mutex.synchronize do
39
57
  calc(key, lifetime) { yield }
@@ -43,11 +61,49 @@ class Zache
43
61
  end
44
62
  end
45
63
 
64
+ # Checks whether the value exists in the cache by the provided key. Returns
65
+ # TRUE if the value is here. If the key is already expired in the hash,
66
+ # it will be removed by this method and the result will be FALSE.
67
+ def exists?(key)
68
+ rec = @hash[key]
69
+ if key_expired?(key)
70
+ @hash.delete(key)
71
+ rec = nil
72
+ end
73
+ !rec.nil?
74
+ end
75
+
76
+ # Removes the value from the hash, by the provied key. If the key is absent
77
+ # and the block is provide, the block will be called.
78
+ def remove(key)
79
+ if @sync
80
+ @mutex.synchronize do
81
+ @hash.delete(key) { yield if block_given? }
82
+ end
83
+ else
84
+ @hash.delete(key) { yield if block_given? }
85
+ end
86
+ end
87
+
88
+ def clean
89
+ if @sync
90
+ @mutex.synchronize do
91
+ @hash.delete_if do |_key, value|
92
+ value[:start] < Time.now - value[:lifetime]
93
+ end
94
+ end
95
+ else
96
+ @hash.delete_if do |_key, value|
97
+ value[:start] < Time.now - value[:lifetime]
98
+ end
99
+ end
100
+ end
101
+
46
102
  private
47
103
 
48
104
  def calc(key, lifetime)
49
105
  rec = @hash[key]
50
- rec = nil if !rec.nil? && rec[:start] < Time.now - rec[:lifetime]
106
+ rec = nil if key_expired?(key)
51
107
  if rec.nil?
52
108
  @hash[key] = {
53
109
  value: yield,
@@ -57,4 +113,9 @@ class Zache
57
113
  end
58
114
  @hash[key][:value]
59
115
  end
116
+
117
+ def key_expired?(key)
118
+ rec = @hash[key]
119
+ !rec.nil? && rec[:start] < Time.now - rec[:lifetime]
120
+ end
60
121
  end
data/test/test_zache.rb CHANGED
@@ -52,4 +52,78 @@ class ZacheTest < Minitest::Test
52
52
  cache.get(:hey, lifetime: 0.0001) { Random.rand }
53
53
  end
54
54
  end
55
+
56
+ def test_key_exists
57
+ cache = Zache.new
58
+ cache.get(:hey) { Random.rand }
59
+ exists_result = cache.exists?(:hey)
60
+ not_exists_result = cache.exists?(:bye)
61
+ assert(exists_result == true)
62
+ assert(not_exists_result == false)
63
+ end
64
+
65
+ def test_remove_key
66
+ cache = Zache.new
67
+ cache.get(:hey) { Random.rand }
68
+ cache.get(:wey) { Random.rand }
69
+ assert(cache.exists?(:hey) == true)
70
+ assert(cache.exists?(:wey) == true)
71
+ cache.remove(:hey)
72
+ assert(cache.exists?(:hey) == false)
73
+ assert(cache.exists?(:wey) == true)
74
+ end
75
+
76
+ def test_remove_key_with_sync_false
77
+ cache = Zache.new(sync: false)
78
+ cache.get(:hey) { Random.rand }
79
+ cache.get(:wey) { Random.rand }
80
+ assert(cache.exists?(:hey) == true)
81
+ assert(cache.exists?(:wey) == true)
82
+ cache.remove(:hey)
83
+ assert(cache.exists?(:hey) == false)
84
+ assert(cache.exists?(:wey) == true)
85
+ end
86
+
87
+ def test_clean_with_threads
88
+ cache = Zache.new
89
+ Threads.new(300).assert(3000) do
90
+ cache.get(:hey) { Random.rand }
91
+ cache.get(:bye, lifetime: 0.01) { Random.rand }
92
+ sleep 0.1
93
+ cache.clean
94
+ end
95
+ assert(cache.exists?(:hey) == true)
96
+ assert(cache.exists?(:bye) == false)
97
+ end
98
+
99
+ def test_clean
100
+ cache = Zache.new
101
+ cache.get(:hey) { Random.rand }
102
+ cache.get(:bye, lifetime: 0.01) { Random.rand }
103
+ sleep 0.1
104
+ cache.clean
105
+ assert(cache.exists?(:hey) == true)
106
+ assert(cache.exists?(:bye) == false)
107
+ end
108
+
109
+ def test_clean_with_sync_false
110
+ cache = Zache.new(sync: false)
111
+ cache.get(:hey) { Random.rand }
112
+ cache.get(:bye, lifetime: 0.01) { Random.rand }
113
+ sleep 0.1
114
+ cache.clean
115
+ assert(cache.exists?(:hey) == true)
116
+ assert(cache.exists?(:bye) == false)
117
+ end
118
+
119
+ def test_remove_absent_key
120
+ cache = Zache.new
121
+ cache.remove(:hey)
122
+ end
123
+
124
+ def test_check_and_remove
125
+ cache = Zache.new
126
+ cache.get(:hey, lifetime: 0) { Random.rand }
127
+ assert(!cache.exists?(:hey))
128
+ end
55
129
  end
data/zache.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.rubygems_version = '2.3.3'
32
32
  s.required_ruby_version = '>=2.3'
33
33
  s.name = 'zache'
34
- s.version = '0.2.0'
34
+ s.version = '0.3.0'
35
35
  s.license = 'MIT'
36
36
  s.summary = 'In-memory Cache'
37
37
  s.description = 'Zero-footprint in-memory thread-safe cache'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zache
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
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-01 00:00:00.000000000 Z
11
+ date: 2018-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest