zache 0.13.1 → 0.14.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 +4 -4
- data/.0pdd.yml +3 -0
- data/.github/workflows/actionlint.yml +25 -0
- data/.github/workflows/codecov.yml +13 -8
- data/.github/workflows/copyrights.yml +15 -0
- data/.github/workflows/license.yml +42 -0
- data/.github/workflows/markdown-lint.yml +23 -0
- data/.github/workflows/pdd.yml +8 -4
- data/.github/workflows/rake.yml +10 -6
- data/.github/workflows/reuse.yml +19 -0
- data/.github/workflows/xcop.yml +6 -2
- data/.github/workflows/yamllint.yml +19 -0
- data/.gitignore +6 -3
- data/.rubocop.yml +21 -6
- data/.rultor.yml +6 -1
- data/Gemfile +16 -27
- data/Gemfile.lock +115 -0
- data/LICENSE.txt +1 -1
- data/LICENSES/MIT.txt +21 -0
- data/README.md +55 -26
- data/REUSE.toml +31 -0
- data/Rakefile +19 -34
- data/lib/zache.rb +123 -34
- data/logo.svg +1 -1
- data/test/test__helper.rb +24 -21
- data/test/test_zache.rb +91 -82
- data/zache.gemspec +5 -24
- metadata +13 -7
data/test/test_zache.rb
CHANGED
@@ -1,147 +1,138 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (
|
4
|
-
#
|
5
|
-
# Copyright (c) 2018-2023 Yegor Bugayenko
|
6
|
-
#
|
7
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
-
# of this software and associated documentation files (the 'Software'), to deal
|
9
|
-
# in the Software without restriction, including without limitation the rights
|
10
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
-
# copies of the Software, and to permit persons to whom the Software is
|
12
|
-
# furnished to do so, subject to the following conditions:
|
13
|
-
#
|
14
|
-
# The above copyright notice and this permission notice shall be included in all
|
15
|
-
# copies or substantial portions of the Software.
|
16
|
-
#
|
17
|
-
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
-
# SOFTWARE.
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
24
5
|
|
6
|
+
require 'concurrent'
|
25
7
|
require 'minitest/autorun'
|
8
|
+
require 'securerandom'
|
26
9
|
require 'threads'
|
27
10
|
require 'timeout'
|
28
|
-
require 'concurrent'
|
29
11
|
require_relative '../lib/zache'
|
12
|
+
require_relative 'test__helper'
|
30
13
|
|
31
14
|
Thread.report_on_exception = true
|
32
15
|
|
33
16
|
# Cache test.
|
34
17
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
35
|
-
# Copyright:: Copyright (c) 2018-
|
18
|
+
# Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko
|
36
19
|
# License:: MIT
|
37
20
|
class ZacheTest < Minitest::Test
|
38
21
|
def test_caches
|
39
22
|
cache = Zache.new(sync: false)
|
40
|
-
first = cache.get(:hey, lifetime: 5) {
|
41
|
-
second = cache.get(:hey) {
|
42
|
-
|
23
|
+
first = cache.get(:hey, lifetime: 5) { rand }
|
24
|
+
second = cache.get(:hey) { rand }
|
25
|
+
assert_equal(first, second)
|
43
26
|
assert_equal(1, cache.size)
|
44
27
|
end
|
45
28
|
|
46
29
|
def test_caches_and_expires
|
47
30
|
cache = Zache.new
|
48
|
-
first = cache.get(:hey, lifetime: 0.01) {
|
31
|
+
first = cache.get(:hey, lifetime: 0.01) { rand }
|
49
32
|
sleep 0.1
|
50
|
-
second = cache.get(:hey) {
|
51
|
-
|
33
|
+
second = cache.get(:hey) { rand }
|
34
|
+
refute_equal(first, second)
|
52
35
|
end
|
53
36
|
|
54
37
|
def test_calculates_age
|
55
38
|
cache = Zache.new
|
56
|
-
cache.get(:hey) {
|
39
|
+
cache.get(:hey) { rand }
|
57
40
|
sleep 0.1
|
58
|
-
|
41
|
+
assert_operator(cache.mtime(:hey), :<, Time.now - 0.05)
|
59
42
|
end
|
60
43
|
|
61
44
|
def test_caches_in_threads
|
62
45
|
cache = Zache.new
|
63
46
|
Threads.new(10).assert(100) do
|
64
|
-
cache.get(:hey, lifetime: 0.0001) {
|
47
|
+
cache.get(:hey, lifetime: 0.0001) { rand }
|
65
48
|
end
|
66
49
|
end
|
67
50
|
|
68
51
|
def test_key_exists
|
69
52
|
cache = Zache.new
|
70
|
-
cache.get(:hey) {
|
53
|
+
cache.get(:hey) { rand }
|
71
54
|
exists_result = cache.exists?(:hey)
|
72
55
|
not_exists_result = cache.exists?(:bye)
|
73
|
-
assert(exists_result
|
74
|
-
|
56
|
+
assert(exists_result)
|
57
|
+
refute(not_exists_result)
|
75
58
|
end
|
76
59
|
|
77
60
|
def test_put_and_exists
|
78
61
|
cache = Zache.new
|
79
62
|
cache.put(:hey, 'hello', lifetime: 0.1)
|
80
63
|
sleep 0.2
|
81
|
-
|
64
|
+
refute(cache.exists?(:hey))
|
82
65
|
end
|
83
66
|
|
84
67
|
def test_remove_key
|
85
68
|
cache = Zache.new
|
86
|
-
cache.get(:hey) {
|
87
|
-
cache.get(:wey) {
|
88
|
-
assert(cache.exists?(:hey)
|
89
|
-
assert(cache.exists?(:wey)
|
69
|
+
cache.get(:hey) { rand }
|
70
|
+
cache.get(:wey) { rand }
|
71
|
+
assert(cache.exists?(:hey))
|
72
|
+
assert(cache.exists?(:wey))
|
90
73
|
cache.remove(:hey)
|
91
|
-
|
92
|
-
assert(cache.exists?(:wey)
|
74
|
+
refute(cache.exists?(:hey))
|
75
|
+
assert(cache.exists?(:wey))
|
93
76
|
end
|
94
77
|
|
95
78
|
def test_remove_by_block
|
96
79
|
cache = Zache.new
|
97
|
-
cache.get('first') {
|
98
|
-
cache.get('second') {
|
80
|
+
cache.get('first') { rand }
|
81
|
+
cache.get('second') { rand }
|
99
82
|
cache.remove_by { |k| k == 'first' }
|
100
|
-
|
101
|
-
assert(cache.exists?('second')
|
83
|
+
refute(cache.exists?('first'))
|
84
|
+
assert(cache.exists?('second'))
|
102
85
|
end
|
103
86
|
|
104
87
|
def test_remove_key_with_sync_false
|
105
88
|
cache = Zache.new(sync: false)
|
106
|
-
cache.get(:hey) {
|
107
|
-
cache.get(:wey) {
|
108
|
-
assert(cache.exists?(:hey)
|
109
|
-
assert(cache.exists?(:wey)
|
89
|
+
cache.get(:hey) { rand }
|
90
|
+
cache.get(:wey) { rand }
|
91
|
+
assert(cache.exists?(:hey))
|
92
|
+
assert(cache.exists?(:wey))
|
110
93
|
cache.remove(:hey)
|
111
|
-
|
112
|
-
assert(cache.exists?(:wey)
|
94
|
+
refute(cache.exists?(:hey))
|
95
|
+
assert(cache.exists?(:wey))
|
113
96
|
end
|
114
97
|
|
115
98
|
def test_clean_with_threads
|
116
99
|
cache = Zache.new
|
117
100
|
Threads.new(300).assert(3000) do
|
118
|
-
cache.get(:hey) {
|
119
|
-
cache.get(:bye, lifetime: 0.01) {
|
101
|
+
cache.get(:hey) { rand }
|
102
|
+
cache.get(:bye, lifetime: 0.01) { rand }
|
120
103
|
sleep 0.1
|
121
104
|
cache.clean
|
122
105
|
end
|
123
|
-
assert(cache.exists?(:hey)
|
124
|
-
|
106
|
+
assert(cache.exists?(:hey))
|
107
|
+
refute(cache.exists?(:bye))
|
125
108
|
end
|
126
109
|
|
127
110
|
def test_clean
|
128
111
|
cache = Zache.new
|
129
|
-
cache.get(:hey) {
|
130
|
-
cache.get(:bye, lifetime: 0.01) {
|
112
|
+
cache.get(:hey) { rand }
|
113
|
+
cache.get(:bye, lifetime: 0.01) { rand }
|
114
|
+
sleep 0.1
|
115
|
+
cache.clean
|
116
|
+
assert(cache.exists?(:hey))
|
117
|
+
refute(cache.exists?(:bye))
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_clean_size
|
121
|
+
cache = Zache.new
|
122
|
+
cache.get(:hey, lifetime: 0.01) { rand }
|
131
123
|
sleep 0.1
|
132
124
|
cache.clean
|
133
|
-
|
134
|
-
assert(cache.exists?(:bye) == false)
|
125
|
+
assert_empty(cache)
|
135
126
|
end
|
136
127
|
|
137
128
|
def test_clean_with_sync_false
|
138
129
|
cache = Zache.new(sync: false)
|
139
|
-
cache.get(:hey) {
|
140
|
-
cache.get(:bye, lifetime: 0.01) {
|
130
|
+
cache.get(:hey) { rand }
|
131
|
+
cache.get(:bye, lifetime: 0.01) { rand }
|
141
132
|
sleep 0.1
|
142
133
|
cache.clean
|
143
|
-
assert(cache.exists?(:hey)
|
144
|
-
|
134
|
+
assert(cache.exists?(:hey))
|
135
|
+
refute(cache.exists?(:bye))
|
145
136
|
end
|
146
137
|
|
147
138
|
def test_remove_absent_key
|
@@ -151,43 +142,43 @@ class ZacheTest < Minitest::Test
|
|
151
142
|
|
152
143
|
def test_check_and_remove
|
153
144
|
cache = Zache.new
|
154
|
-
cache.get(:hey, lifetime: 0) {
|
155
|
-
|
145
|
+
cache.get(:hey, lifetime: 0) { rand }
|
146
|
+
refute(cache.exists?(:hey))
|
156
147
|
end
|
157
148
|
|
158
149
|
def test_remove_all_with_threads
|
159
150
|
cache = Zache.new
|
160
151
|
Threads.new(10).assert(100) do |i|
|
161
|
-
cache.get("hey#{i}"
|
162
|
-
assert(cache.exists?("hey#{i}"
|
152
|
+
cache.get(:"hey#{i}") { rand }
|
153
|
+
assert(cache.exists?(:"hey#{i}"))
|
163
154
|
cache.remove_all
|
164
155
|
end
|
165
156
|
10.times do |i|
|
166
|
-
|
157
|
+
refute(cache.exists?(:"hey#{i}"))
|
167
158
|
end
|
168
159
|
end
|
169
160
|
|
170
161
|
def test_remove_all_with_sync
|
171
162
|
cache = Zache.new
|
172
|
-
cache.get(:hey) {
|
173
|
-
cache.get(:bye) {
|
163
|
+
cache.get(:hey) { rand }
|
164
|
+
cache.get(:bye) { rand }
|
174
165
|
cache.remove_all
|
175
|
-
|
176
|
-
|
166
|
+
refute(cache.exists?(:hey))
|
167
|
+
refute(cache.exists?(:bye))
|
177
168
|
end
|
178
169
|
|
179
170
|
def test_remove_all_without_sync
|
180
171
|
cache = Zache.new(sync: false)
|
181
|
-
cache.get(:hey) {
|
182
|
-
cache.get(:bye) {
|
172
|
+
cache.get(:hey) { rand }
|
173
|
+
cache.get(:bye) { rand }
|
183
174
|
cache.remove_all
|
184
|
-
|
185
|
-
|
175
|
+
refute(cache.exists?(:hey))
|
176
|
+
refute(cache.exists?(:bye))
|
186
177
|
end
|
187
178
|
|
188
179
|
def test_puts_something_in
|
189
180
|
cache = Zache.new(sync: false)
|
190
|
-
cache.get(:hey) {
|
181
|
+
cache.get(:hey) { rand }
|
191
182
|
cache.put(:hey, 123)
|
192
183
|
assert_equal(123, cache.get(:hey))
|
193
184
|
end
|
@@ -208,19 +199,19 @@ class ZacheTest < Minitest::Test
|
|
208
199
|
end
|
209
200
|
end
|
210
201
|
sleep 0.1
|
211
|
-
|
202
|
+
assert_predicate(cache, :locked?)
|
212
203
|
cache.get(:x) { 'second' }
|
213
|
-
|
204
|
+
refute_predicate(cache, :locked?)
|
214
205
|
long.kill
|
215
206
|
end
|
216
207
|
|
217
208
|
def test_checks_locked_status_from_inside
|
218
209
|
cache = Zache.new
|
219
210
|
cache.get(:x) do
|
220
|
-
|
211
|
+
assert_predicate(cache, :locked?)
|
221
212
|
'done'
|
222
213
|
end
|
223
|
-
|
214
|
+
refute_predicate(cache, :locked?)
|
224
215
|
end
|
225
216
|
|
226
217
|
def test_returns_dirty_result
|
@@ -285,4 +276,22 @@ class ZacheTest < Minitest::Test
|
|
285
276
|
cache.get(:hey) { raise 'intentional' }
|
286
277
|
end
|
287
278
|
end
|
279
|
+
|
280
|
+
def test_returns_placeholder_in_eager_mode
|
281
|
+
cache = Zache.new
|
282
|
+
a = cache.get(:me, placeholder: 42, eager: true) do
|
283
|
+
sleep 0.1
|
284
|
+
43
|
285
|
+
end
|
286
|
+
assert_equal(42, a)
|
287
|
+
sleep 0.2
|
288
|
+
b = cache.get(:me)
|
289
|
+
assert_equal(43, b)
|
290
|
+
end
|
291
|
+
|
292
|
+
private
|
293
|
+
|
294
|
+
def rand
|
295
|
+
SecureRandom.uuid
|
296
|
+
end
|
288
297
|
end
|
data/zache.gemspec
CHANGED
@@ -1,39 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (
|
4
|
-
#
|
5
|
-
# Copyright (c) 2018-2023 Yegor Bugayenko
|
6
|
-
#
|
7
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
-
# of this software and associated documentation files (the 'Software'), to deal
|
9
|
-
# in the Software without restriction, including without limitation the rights
|
10
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
-
# copies of the Software, and to permit persons to whom the Software is
|
12
|
-
# furnished to do so, subject to the following conditions:
|
13
|
-
#
|
14
|
-
# The above copyright notice and this permission notice shall be included in all
|
15
|
-
# copies or substantial portions of the Software.
|
16
|
-
#
|
17
|
-
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
-
# SOFTWARE.
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
|
4
|
+
# SPDX-License-Identifier: MIT
|
24
5
|
|
25
6
|
require 'English'
|
26
7
|
Gem::Specification.new do |s|
|
27
8
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
28
|
-
s.required_ruby_version = '>=2.5'
|
9
|
+
s.required_ruby_version = '>= 2.5'
|
29
10
|
s.name = 'zache'
|
30
|
-
s.version = '0.
|
11
|
+
s.version = '0.14.0' # Version should be updated before release
|
31
12
|
s.license = 'MIT'
|
32
13
|
s.summary = 'In-memory Cache'
|
33
14
|
s.description = 'Zero-footprint in-memory thread-safe cache'
|
34
15
|
s.authors = ['Yegor Bugayenko']
|
35
16
|
s.email = 'yegor256@gmail.com'
|
36
|
-
s.homepage = '
|
17
|
+
s.homepage = 'https://github.com/yegor256/zache'
|
37
18
|
s.files = `git ls-files`.split($RS)
|
38
19
|
s.rdoc_options = ['--charset=UTF-8']
|
39
20
|
s.extra_rdoc_files = ['README.md']
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-04-12 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: Zero-footprint in-memory thread-safe cache
|
14
13
|
email: yegor256@gmail.com
|
@@ -18,17 +17,26 @@ extra_rdoc_files:
|
|
18
17
|
- README.md
|
19
18
|
files:
|
20
19
|
- ".0pdd.yml"
|
20
|
+
- ".github/workflows/actionlint.yml"
|
21
21
|
- ".github/workflows/codecov.yml"
|
22
|
+
- ".github/workflows/copyrights.yml"
|
23
|
+
- ".github/workflows/license.yml"
|
24
|
+
- ".github/workflows/markdown-lint.yml"
|
22
25
|
- ".github/workflows/pdd.yml"
|
23
26
|
- ".github/workflows/rake.yml"
|
27
|
+
- ".github/workflows/reuse.yml"
|
24
28
|
- ".github/workflows/xcop.yml"
|
29
|
+
- ".github/workflows/yamllint.yml"
|
25
30
|
- ".gitignore"
|
26
31
|
- ".pdd"
|
27
32
|
- ".rubocop.yml"
|
28
33
|
- ".rultor.yml"
|
29
34
|
- Gemfile
|
35
|
+
- Gemfile.lock
|
30
36
|
- LICENSE.txt
|
37
|
+
- LICENSES/MIT.txt
|
31
38
|
- README.md
|
39
|
+
- REUSE.toml
|
32
40
|
- Rakefile
|
33
41
|
- lib/zache.rb
|
34
42
|
- logo.svg
|
@@ -36,12 +44,11 @@ files:
|
|
36
44
|
- test/test__helper.rb
|
37
45
|
- test/test_zache.rb
|
38
46
|
- zache.gemspec
|
39
|
-
homepage:
|
47
|
+
homepage: https://github.com/yegor256/zache
|
40
48
|
licenses:
|
41
49
|
- MIT
|
42
50
|
metadata:
|
43
51
|
rubygems_mfa_required: 'true'
|
44
|
-
post_install_message:
|
45
52
|
rdoc_options:
|
46
53
|
- "--charset=UTF-8"
|
47
54
|
require_paths:
|
@@ -57,8 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
64
|
- !ruby/object:Gem::Version
|
58
65
|
version: '0'
|
59
66
|
requirements: []
|
60
|
-
rubygems_version: 3.2
|
61
|
-
signing_key:
|
67
|
+
rubygems_version: 3.6.2
|
62
68
|
specification_version: 4
|
63
69
|
summary: In-memory Cache
|
64
70
|
test_files: []
|