zache 0.13.2 → 0.15.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 +2 -21
- data/.github/workflows/actionlint.yml +5 -23
- data/.github/workflows/codecov.yml +11 -26
- data/.github/workflows/copyrights.yml +6 -23
- data/.github/workflows/license.yml +5 -22
- data/.github/workflows/markdown-lint.yml +5 -22
- data/.github/workflows/pdd.yml +6 -23
- data/.github/workflows/rake.yml +8 -26
- data/.github/workflows/reuse.yml +19 -0
- data/.github/workflows/xcop.yml +5 -22
- data/.github/workflows/yamllint.yml +5 -22
- data/.gitignore +6 -2
- data/.rubocop.yml +20 -28
- data/.rultor.yml +5 -22
- data/Gemfile +16 -27
- data/Gemfile.lock +78 -40
- data/LICENSE.txt +1 -1
- data/LICENSES/MIT.txt +21 -0
- data/README.md +45 -19
- data/REUSE.toml +31 -0
- data/Rakefile +19 -34
- data/lib/zache.rb +144 -46
- data/logo.svg +1 -1
- data/test/test__helper.rb +24 -21
- data/test/test_zache.rb +162 -147
- data/zache.gemspec +5 -24
- metadata +7 -7
data/test/test_zache.rb
CHANGED
@@ -1,296 +1,311 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (
|
4
|
-
#
|
5
|
-
# Copyright (c) 2018-2024 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
|
-
|
40
|
-
first =
|
41
|
-
second =
|
42
|
-
|
43
|
-
assert_equal(1,
|
22
|
+
z = Zache.new(sync: false)
|
23
|
+
first = z.get(:hey, lifetime: 5) { rand }
|
24
|
+
second = z.get(:hey) { rand }
|
25
|
+
assert_equal(first, second)
|
26
|
+
assert_equal(1, z.size)
|
44
27
|
end
|
45
28
|
|
46
29
|
def test_caches_and_expires
|
47
|
-
|
48
|
-
first =
|
30
|
+
z = Zache.new
|
31
|
+
first = z.get(:hey, lifetime: 0.01) { rand }
|
49
32
|
sleep 0.1
|
50
|
-
second =
|
51
|
-
|
33
|
+
second = z.get(:hey) { rand }
|
34
|
+
refute_equal(first, second)
|
52
35
|
end
|
53
36
|
|
54
37
|
def test_calculates_age
|
55
|
-
|
56
|
-
|
38
|
+
z = Zache.new
|
39
|
+
z.get(:hey) { rand }
|
57
40
|
sleep 0.1
|
58
|
-
|
41
|
+
assert_operator(z.mtime(:hey), :<, Time.now - 0.05)
|
59
42
|
end
|
60
43
|
|
61
44
|
def test_caches_in_threads
|
62
|
-
|
45
|
+
z = Zache.new
|
63
46
|
Threads.new(10).assert(100) do
|
64
|
-
|
47
|
+
z.get(:hey, lifetime: 0.0001) { rand }
|
65
48
|
end
|
66
49
|
end
|
67
50
|
|
68
51
|
def test_key_exists
|
69
|
-
|
70
|
-
|
71
|
-
exists_result =
|
72
|
-
not_exists_result =
|
73
|
-
assert(exists_result
|
74
|
-
|
52
|
+
z = Zache.new
|
53
|
+
z.get(:hey) { rand }
|
54
|
+
exists_result = z.exists?(:hey)
|
55
|
+
not_exists_result = z.exists?(:bye)
|
56
|
+
assert(exists_result)
|
57
|
+
refute(not_exists_result)
|
75
58
|
end
|
76
59
|
|
77
60
|
def test_put_and_exists
|
78
|
-
|
79
|
-
|
61
|
+
z = Zache.new
|
62
|
+
z.put(:hey, 'hello', lifetime: 0.1)
|
80
63
|
sleep 0.2
|
81
|
-
|
64
|
+
refute(z.exists?(:hey))
|
82
65
|
end
|
83
66
|
|
84
67
|
def test_remove_key
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
assert(
|
89
|
-
assert(
|
90
|
-
|
91
|
-
|
92
|
-
assert(
|
68
|
+
z = Zache.new
|
69
|
+
z.get(:hey) { rand }
|
70
|
+
z.get(:wey) { rand }
|
71
|
+
assert(z.exists?(:hey))
|
72
|
+
assert(z.exists?(:wey))
|
73
|
+
z.remove(:hey)
|
74
|
+
refute(z.exists?(:hey))
|
75
|
+
assert(z.exists?(:wey))
|
93
76
|
end
|
94
77
|
|
95
78
|
def test_remove_by_block
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
assert(
|
79
|
+
z = Zache.new
|
80
|
+
z.get('first') { rand }
|
81
|
+
z.get('second') { rand }
|
82
|
+
z.remove_by { |k| k == 'first' }
|
83
|
+
refute(z.exists?('first'))
|
84
|
+
assert(z.exists?('second'))
|
102
85
|
end
|
103
86
|
|
104
87
|
def test_remove_key_with_sync_false
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
assert(
|
109
|
-
assert(
|
110
|
-
|
111
|
-
|
112
|
-
assert(
|
88
|
+
z = Zache.new(sync: false)
|
89
|
+
z.get(:hey) { rand }
|
90
|
+
z.get(:wey) { rand }
|
91
|
+
assert(z.exists?(:hey))
|
92
|
+
assert(z.exists?(:wey))
|
93
|
+
z.remove(:hey)
|
94
|
+
refute(z.exists?(:hey))
|
95
|
+
assert(z.exists?(:wey))
|
113
96
|
end
|
114
97
|
|
115
98
|
def test_clean_with_threads
|
116
|
-
|
99
|
+
z = Zache.new
|
117
100
|
Threads.new(300).assert(3000) do
|
118
|
-
|
119
|
-
|
101
|
+
z.get(:hey) { rand }
|
102
|
+
z.get(:bye, lifetime: 0.01) { rand }
|
120
103
|
sleep 0.1
|
121
|
-
|
104
|
+
z.clean
|
122
105
|
end
|
123
|
-
assert(
|
124
|
-
|
106
|
+
assert(z.exists?(:hey))
|
107
|
+
refute(z.exists?(:bye))
|
125
108
|
end
|
126
109
|
|
127
110
|
def test_clean
|
128
|
-
|
129
|
-
|
130
|
-
|
111
|
+
z = Zache.new
|
112
|
+
z.get(:hey) { rand }
|
113
|
+
z.get(:bye, lifetime: 0.01) { rand }
|
131
114
|
sleep 0.1
|
132
|
-
|
133
|
-
assert(
|
134
|
-
|
115
|
+
z.clean
|
116
|
+
assert(z.exists?(:hey))
|
117
|
+
refute(z.exists?(:bye))
|
135
118
|
end
|
136
119
|
|
137
120
|
def test_clean_size
|
138
|
-
|
139
|
-
|
121
|
+
z = Zache.new
|
122
|
+
z.get(:hey, lifetime: 0.01) { rand }
|
140
123
|
sleep 0.1
|
141
|
-
|
142
|
-
|
124
|
+
z.clean
|
125
|
+
assert_empty(z)
|
143
126
|
end
|
144
127
|
|
145
128
|
def test_clean_with_sync_false
|
146
|
-
|
147
|
-
|
148
|
-
|
129
|
+
z = Zache.new(sync: false)
|
130
|
+
z.get(:hey) { rand }
|
131
|
+
z.get(:bye, lifetime: 0.01) { rand }
|
149
132
|
sleep 0.1
|
150
|
-
|
151
|
-
assert(
|
152
|
-
|
133
|
+
z.clean
|
134
|
+
assert(z.exists?(:hey))
|
135
|
+
refute(z.exists?(:bye))
|
153
136
|
end
|
154
137
|
|
155
138
|
def test_remove_absent_key
|
156
|
-
|
157
|
-
|
139
|
+
z = Zache.new
|
140
|
+
z.remove(:hey)
|
158
141
|
end
|
159
142
|
|
160
143
|
def test_check_and_remove
|
161
|
-
|
162
|
-
|
163
|
-
|
144
|
+
z = Zache.new
|
145
|
+
z.get(:hey, lifetime: 0) { rand }
|
146
|
+
refute(z.exists?(:hey))
|
164
147
|
end
|
165
148
|
|
166
149
|
def test_remove_all_with_threads
|
167
|
-
|
150
|
+
z = Zache.new
|
168
151
|
Threads.new(10).assert(100) do |i|
|
169
|
-
|
170
|
-
assert(
|
171
|
-
|
152
|
+
z.get(:"hey#{i}") { rand }
|
153
|
+
assert(z.exists?(:"hey#{i}"))
|
154
|
+
z.remove_all
|
172
155
|
end
|
173
156
|
10.times do |i|
|
174
|
-
|
157
|
+
refute(z.exists?(:"hey#{i}"))
|
175
158
|
end
|
176
159
|
end
|
177
160
|
|
178
161
|
def test_remove_all_with_sync
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
162
|
+
z = Zache.new
|
163
|
+
z.get(:hey) { rand }
|
164
|
+
z.get(:bye) { rand }
|
165
|
+
z.remove_all
|
166
|
+
refute(z.exists?(:hey))
|
167
|
+
refute(z.exists?(:bye))
|
185
168
|
end
|
186
169
|
|
187
170
|
def test_remove_all_without_sync
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
171
|
+
z = Zache.new(sync: false)
|
172
|
+
z.get(:hey) { rand }
|
173
|
+
z.get(:bye) { rand }
|
174
|
+
z.remove_all
|
175
|
+
refute(z.exists?(:hey))
|
176
|
+
refute(z.exists?(:bye))
|
194
177
|
end
|
195
178
|
|
196
179
|
def test_puts_something_in
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
assert_equal(123,
|
180
|
+
z = Zache.new(sync: false)
|
181
|
+
z.get(:hey) { rand }
|
182
|
+
z.put(:hey, 123)
|
183
|
+
assert_equal(123, z.get(:hey))
|
201
184
|
end
|
202
185
|
|
203
186
|
def test_sync_zache_is_not_reentrant
|
204
|
-
|
187
|
+
z = Zache.new
|
205
188
|
assert_raises ThreadError do
|
206
|
-
|
189
|
+
z.get(:first) { z.get(:first) { 1 } }
|
207
190
|
end
|
208
191
|
end
|
209
192
|
|
193
|
+
def test_sync_zache_is_reentrant_for_different_keys
|
194
|
+
z = Zache.new
|
195
|
+
z.get(:first) { z.get(:second) { 1 } }
|
196
|
+
end
|
197
|
+
|
210
198
|
def test_calculates_only_once
|
211
|
-
|
199
|
+
z = Zache.new
|
212
200
|
long = Thread.start do
|
213
|
-
|
201
|
+
z.get(:x) do
|
214
202
|
sleep 0.5
|
215
203
|
'first'
|
216
204
|
end
|
217
205
|
end
|
218
206
|
sleep 0.1
|
219
|
-
assert(
|
220
|
-
|
221
|
-
|
207
|
+
assert(z.locked?(:x))
|
208
|
+
z.get(:x) { 'second' }
|
209
|
+
refute(z.locked?(:x))
|
222
210
|
long.kill
|
223
211
|
end
|
224
212
|
|
225
213
|
def test_checks_locked_status_from_inside
|
226
|
-
|
227
|
-
|
228
|
-
assert(
|
214
|
+
z = Zache.new
|
215
|
+
z.get(:x) do
|
216
|
+
assert(z.locked?(:x))
|
229
217
|
'done'
|
230
218
|
end
|
231
|
-
|
219
|
+
refute(z.locked?(:x))
|
232
220
|
end
|
233
221
|
|
234
222
|
def test_returns_dirty_result
|
235
|
-
|
236
|
-
|
223
|
+
z = Zache.new(dirty: true)
|
224
|
+
z.get(:x, lifetime: 0) { 1 }
|
237
225
|
long = Thread.start do
|
238
|
-
|
226
|
+
z.get(:x) do
|
239
227
|
sleep 1000
|
240
228
|
2
|
241
229
|
end
|
242
230
|
end
|
243
231
|
sleep 0.1
|
244
232
|
Timeout.timeout(1) do
|
245
|
-
assert(
|
246
|
-
assert(
|
247
|
-
assert_equal(1,
|
248
|
-
assert_equal(1,
|
233
|
+
assert(z.exists?(:x))
|
234
|
+
assert(z.expired?(:x))
|
235
|
+
assert_equal(1, z.get(:x))
|
236
|
+
assert_equal(1, z.get(:x) { 2 })
|
249
237
|
end
|
250
238
|
long.kill
|
251
239
|
end
|
252
240
|
|
253
241
|
def test_returns_dirty_result_when_not_locked
|
254
|
-
|
255
|
-
|
256
|
-
assert(
|
257
|
-
assert_equal(1,
|
258
|
-
assert_equal(2,
|
242
|
+
z = Zache.new(dirty: true)
|
243
|
+
z.get(:x, lifetime: 0) { 1 }
|
244
|
+
assert(z.exists?(:x))
|
245
|
+
assert_equal(1, z.get(:x))
|
246
|
+
assert_equal(2, z.get(:x) { 2 })
|
259
247
|
end
|
260
248
|
|
261
249
|
def test_fetches_multiple_keys_in_many_threads_in_dirty_mode
|
262
|
-
|
250
|
+
z = Zache.new(dirty: true)
|
263
251
|
set = Concurrent::Set.new
|
264
252
|
threads = 50
|
265
253
|
barrier = Concurrent::CyclicBarrier.new(threads)
|
266
254
|
Threads.new(threads).assert(threads * 2) do |i|
|
267
255
|
barrier.wait if i < threads
|
268
|
-
set <<
|
256
|
+
set << z.get(i, lifetime: 0.001) { i }
|
269
257
|
end
|
270
258
|
assert_equal(threads, set.size)
|
271
259
|
end
|
272
260
|
|
273
261
|
def test_fetches_multiple_keys_in_many_threads
|
274
|
-
|
262
|
+
z = Zache.new
|
275
263
|
set = Concurrent::Set.new
|
276
264
|
threads = 50
|
277
265
|
barrier = Concurrent::CyclicBarrier.new(threads)
|
278
266
|
Threads.new(threads).assert(threads * 2) do |i|
|
279
267
|
barrier.wait if i < threads
|
280
|
-
set <<
|
268
|
+
set << z.get(i) { i }
|
281
269
|
end
|
282
270
|
assert_equal(threads, set.size)
|
283
271
|
end
|
284
272
|
|
285
273
|
def test_fake_class_works
|
286
|
-
|
287
|
-
assert_equal(1,
|
274
|
+
z = Zache::Fake.new
|
275
|
+
assert_equal(1, z.get(:x) { 1 })
|
288
276
|
end
|
289
277
|
|
290
278
|
def test_rethrows
|
291
|
-
|
279
|
+
z = Zache.new
|
292
280
|
assert_raises RuntimeError do
|
293
|
-
|
281
|
+
z.get(:hey) { raise 'intentional' }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_returns_placeholder_in_eager_mode
|
286
|
+
z = Zache.new
|
287
|
+
a = z.get(:me, placeholder: 42, eager: true) do
|
288
|
+
sleep 0.1
|
289
|
+
43
|
294
290
|
end
|
291
|
+
assert_equal(42, a)
|
292
|
+
sleep 0.2
|
293
|
+
b = z.get(:me)
|
294
|
+
assert_equal(43, b)
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_returns_placeholder_and_releases_lock
|
298
|
+
z = Zache.new
|
299
|
+
z.get(:slow, placeholder: 42, eager: true) do
|
300
|
+
sleep 9999
|
301
|
+
end
|
302
|
+
sleep 0.1
|
303
|
+
assert_equal(555, z.get(:fast) { 555 })
|
304
|
+
end
|
305
|
+
|
306
|
+
private
|
307
|
+
|
308
|
+
def rand
|
309
|
+
SecureRandom.uuid
|
295
310
|
end
|
296
311
|
end
|
data/zache.gemspec
CHANGED
@@ -1,39 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# (
|
4
|
-
#
|
5
|
-
# Copyright (c) 2018-2024 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.15.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.15.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-15 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: Zero-footprint in-memory thread-safe cache
|
14
13
|
email: yegor256@gmail.com
|
@@ -25,6 +24,7 @@ files:
|
|
25
24
|
- ".github/workflows/markdown-lint.yml"
|
26
25
|
- ".github/workflows/pdd.yml"
|
27
26
|
- ".github/workflows/rake.yml"
|
27
|
+
- ".github/workflows/reuse.yml"
|
28
28
|
- ".github/workflows/xcop.yml"
|
29
29
|
- ".github/workflows/yamllint.yml"
|
30
30
|
- ".gitignore"
|
@@ -34,7 +34,9 @@ files:
|
|
34
34
|
- Gemfile
|
35
35
|
- Gemfile.lock
|
36
36
|
- LICENSE.txt
|
37
|
+
- LICENSES/MIT.txt
|
37
38
|
- README.md
|
39
|
+
- REUSE.toml
|
38
40
|
- Rakefile
|
39
41
|
- lib/zache.rb
|
40
42
|
- logo.svg
|
@@ -42,12 +44,11 @@ files:
|
|
42
44
|
- test/test__helper.rb
|
43
45
|
- test/test_zache.rb
|
44
46
|
- zache.gemspec
|
45
|
-
homepage:
|
47
|
+
homepage: https://github.com/yegor256/zache
|
46
48
|
licenses:
|
47
49
|
- MIT
|
48
50
|
metadata:
|
49
51
|
rubygems_mfa_required: 'true'
|
50
|
-
post_install_message:
|
51
52
|
rdoc_options:
|
52
53
|
- "--charset=UTF-8"
|
53
54
|
require_paths:
|
@@ -63,8 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
64
|
- !ruby/object:Gem::Version
|
64
65
|
version: '0'
|
65
66
|
requirements: []
|
66
|
-
rubygems_version: 3.
|
67
|
-
signing_key:
|
67
|
+
rubygems_version: 3.6.2
|
68
68
|
specification_version: 4
|
69
69
|
summary: In-memory Cache
|
70
70
|
test_files: []
|