readthis 2.1.0 → 2.2.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 +5 -5
- data/LICENSE.txt +22 -0
- data/README.md +1 -1
- data/lib/active_support/cache/readthis_store.rb +3 -1
- data/lib/readthis.rb +2 -0
- data/lib/readthis/cache.rb +66 -26
- data/lib/readthis/entity.rb +2 -0
- data/lib/readthis/errors.rb +2 -0
- data/lib/readthis/expanders.rb +53 -4
- data/lib/readthis/passthrough.rb +19 -1
- data/lib/readthis/scripts.rb +3 -1
- data/lib/readthis/serializers.rb +44 -3
- data/lib/readthis/version.rb +3 -1
- data/readthis.gemspec +27 -0
- metadata +32 -48
- data/spec/matchers/redis_matchers.rb +0 -13
- data/spec/readthis/cache_spec.rb +0 -418
- data/spec/readthis/entity_spec.rb +0 -143
- data/spec/readthis/expanders_spec.rb +0 -40
- data/spec/readthis/passthrough_spec.rb +0 -16
- data/spec/readthis/scripts_spec.rb +0 -31
- data/spec/readthis/serializers_spec.rb +0 -96
- data/spec/readthis_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -28
data/lib/readthis/version.rb
CHANGED
data/readthis.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'readthis/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'readthis'
|
9
|
+
spec.version = Readthis::VERSION
|
10
|
+
spec.authors = ['Parker Selbert']
|
11
|
+
spec.email = ['parker@sorentwo.com']
|
12
|
+
spec.summary = 'Pooled active support compliant caching with redis'
|
13
|
+
spec.homepage = 'https://github.com/sorentwo/readthis'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z readthis.gemspec lib script README.md LICENSE.txt`.split("\x0")
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_dependency 'connection_pool', '~> 2.1'
|
20
|
+
spec.add_dependency 'redis', '>= 3.0', '< 5.0'
|
21
|
+
|
22
|
+
spec.add_development_dependency 'activesupport', '> 4.0'
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
24
|
+
spec.add_development_dependency 'hiredis', '~> 0.6'
|
25
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.7'
|
27
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: readthis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Parker Selbert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: connection_pool
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: redis
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -30,20 +44,6 @@ dependencies:
|
|
30
44
|
- - "<"
|
31
45
|
- !ruby/object:Gem::Version
|
32
46
|
version: '5.0'
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: connection_pool
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '2.1'
|
40
|
-
type: :runtime
|
41
|
-
prerelease: false
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - "~>"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '2.1'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: activesupport
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,58 +62,58 @@ dependencies:
|
|
62
62
|
name: bundler
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - "
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
67
|
+
version: '1.16'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - "
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
74
|
+
version: '1.16'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: hiredis
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '0.
|
81
|
+
version: '0.6'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: '0.
|
88
|
+
version: '0.6'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: rake
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - "
|
93
|
+
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
95
|
+
version: '12.3'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- - "
|
100
|
+
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '
|
102
|
+
version: '12.3'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: rspec
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: '3.
|
109
|
+
version: '3.7'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: '3.
|
116
|
+
version: '3.7'
|
117
117
|
description:
|
118
118
|
email:
|
119
119
|
- parker@sorentwo.com
|
@@ -121,6 +121,7 @@ executables: []
|
|
121
121
|
extensions: []
|
122
122
|
extra_rdoc_files: []
|
123
123
|
files:
|
124
|
+
- LICENSE.txt
|
124
125
|
- README.md
|
125
126
|
- lib/active_support/cache/readthis_store.rb
|
126
127
|
- lib/readthis.rb
|
@@ -132,16 +133,8 @@ files:
|
|
132
133
|
- lib/readthis/scripts.rb
|
133
134
|
- lib/readthis/serializers.rb
|
134
135
|
- lib/readthis/version.rb
|
136
|
+
- readthis.gemspec
|
135
137
|
- script/mexpire.lua
|
136
|
-
- spec/matchers/redis_matchers.rb
|
137
|
-
- spec/readthis/cache_spec.rb
|
138
|
-
- spec/readthis/entity_spec.rb
|
139
|
-
- spec/readthis/expanders_spec.rb
|
140
|
-
- spec/readthis/passthrough_spec.rb
|
141
|
-
- spec/readthis/scripts_spec.rb
|
142
|
-
- spec/readthis/serializers_spec.rb
|
143
|
-
- spec/readthis_spec.rb
|
144
|
-
- spec/spec_helper.rb
|
145
138
|
homepage: https://github.com/sorentwo/readthis
|
146
139
|
licenses:
|
147
140
|
- MIT
|
@@ -162,17 +155,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
155
|
version: '0'
|
163
156
|
requirements: []
|
164
157
|
rubyforge_project:
|
165
|
-
rubygems_version: 2.
|
158
|
+
rubygems_version: 2.7.3
|
166
159
|
signing_key:
|
167
160
|
specification_version: 4
|
168
161
|
summary: Pooled active support compliant caching with redis
|
169
|
-
test_files:
|
170
|
-
- spec/matchers/redis_matchers.rb
|
171
|
-
- spec/readthis/cache_spec.rb
|
172
|
-
- spec/readthis/entity_spec.rb
|
173
|
-
- spec/readthis/expanders_spec.rb
|
174
|
-
- spec/readthis/passthrough_spec.rb
|
175
|
-
- spec/readthis/scripts_spec.rb
|
176
|
-
- spec/readthis/serializers_spec.rb
|
177
|
-
- spec/readthis_spec.rb
|
178
|
-
- spec/spec_helper.rb
|
162
|
+
test_files: []
|
data/spec/readthis/cache_spec.rb
DELETED
@@ -1,418 +0,0 @@
|
|
1
|
-
require 'matchers/redis_matchers'
|
2
|
-
|
3
|
-
RSpec.describe Readthis::Cache do
|
4
|
-
include RedisMatchers
|
5
|
-
|
6
|
-
let(:cache) { Readthis::Cache.new }
|
7
|
-
|
8
|
-
after do
|
9
|
-
cache.clear
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '#initialize' do
|
13
|
-
it 'makes options available' do
|
14
|
-
cache = Readthis::Cache.new(namespace: 'cache', expires_in: 1)
|
15
|
-
|
16
|
-
expect(cache.options).to eq(namespace: 'cache', expires_in: 1)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe '#pool' do
|
21
|
-
it 'uses the passed redis configuration' do
|
22
|
-
cache = Readthis::Cache.new(redis: { driver: :hiredis })
|
23
|
-
|
24
|
-
cache.pool.with do |client|
|
25
|
-
expect(client._client.driver).to be(Redis::Connection::Hiredis)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#write' do
|
31
|
-
it 'stores strings in the cache' do
|
32
|
-
cache.write('some-key', 'some-value')
|
33
|
-
|
34
|
-
expect(cache.read('some-key')).to eq('some-value')
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'stores values within a namespace' do
|
38
|
-
cache.write('some-key', 'some-value', namespace: 'cache')
|
39
|
-
|
40
|
-
expect(cache.read('some-key')).to be_nil
|
41
|
-
expect(cache.read('some-key', namespace: 'cache')).to eq('some-value')
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'roundtrips values as their original type' do
|
45
|
-
object = { a: 1, b: 2 }
|
46
|
-
|
47
|
-
cache.write('obj-key', object)
|
48
|
-
|
49
|
-
expect(cache.read('obj-key')).to eq(object)
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'uses a custom expiration' do
|
53
|
-
cache = Readthis::Cache.new(expires_in: 10)
|
54
|
-
|
55
|
-
cache.write('some-key', 'some-value')
|
56
|
-
cache.write('other-key', 'other-value', expires_in: 1)
|
57
|
-
|
58
|
-
expect(cache.read('some-key')).not_to be_nil
|
59
|
-
expect(cache.read('other-key')).not_to be_nil
|
60
|
-
|
61
|
-
expect(cache).to have_ttl('some-key' => 10, 'other-key' => 1)
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'rounds floats to a valid expiration value' do
|
65
|
-
cache = Readthis::Cache.new
|
66
|
-
|
67
|
-
cache.write('some-key', 'some-value', expires_in: 0.1)
|
68
|
-
|
69
|
-
expect(cache).to have_ttl('some-key' => 1)
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'expands non-string keys' do
|
73
|
-
key_obj = double(cache_key: 'custom')
|
74
|
-
|
75
|
-
cache.write(key_obj, 'some-value')
|
76
|
-
|
77
|
-
expect(cache.read('custom')).to eq('some-value')
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'stores russian symbols by russian key' do
|
81
|
-
cache.write('вгдмн', 'вгдмн')
|
82
|
-
|
83
|
-
expect(cache.read('вгдмн')).to eq('вгдмн')
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe '#read' do
|
88
|
-
it 'gracefully handles nil options' do
|
89
|
-
expect { cache.read('whatever', nil) }.not_to raise_error
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'can refresh the expiration of an entity' do
|
93
|
-
cache = Readthis::Cache.new(refresh: true)
|
94
|
-
|
95
|
-
cache.write('some-key', 'some-value', expires_in: 1)
|
96
|
-
|
97
|
-
cache.read('some-key', expires_in: 2)
|
98
|
-
expect(cache).to have_ttl('some-key' => 2)
|
99
|
-
|
100
|
-
cache.read('some-key', expires_in: 0.1)
|
101
|
-
expect(cache).to have_ttl('some-key' => 1)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
describe 'serializers' do
|
106
|
-
after do
|
107
|
-
Readthis.serializers.reset!
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'uses globally configured serializers' do
|
111
|
-
custom = Class.new do
|
112
|
-
def self.dump(value)
|
113
|
-
value
|
114
|
-
end
|
115
|
-
|
116
|
-
def self.load(value)
|
117
|
-
value
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
Readthis.serializers << custom
|
122
|
-
|
123
|
-
cache.write('customized', 'some value', marshal: custom)
|
124
|
-
|
125
|
-
expect(cache.read('customized')).to eq('some value')
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
describe 'compression' do
|
130
|
-
it 'roundtrips entries when compression is enabled' do
|
131
|
-
com_cache = Readthis::Cache.new(compress: true, compression_threshold: 8)
|
132
|
-
raw_cache = Readthis::Cache.new
|
133
|
-
value = 'enough text that it should be compressed'
|
134
|
-
|
135
|
-
com_cache.write('compressed', value)
|
136
|
-
|
137
|
-
expect(com_cache.read('compressed')).to eq(value)
|
138
|
-
expect(raw_cache.read('compressed')).to eq(value)
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'roundtrips entries with option overrides' do
|
142
|
-
cache = Readthis::Cache.new(compress: false)
|
143
|
-
value = 'enough text that it should be compressed'
|
144
|
-
|
145
|
-
cache.write('comp-round', value, marshal: JSON, compress: true, threshold: 8)
|
146
|
-
|
147
|
-
expect(cache.read('comp-round')).to eq(value)
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'roundtrips bulk entries when compression is enabled' do
|
151
|
-
cache = Readthis::Cache.new(compress: true, compression_threshold: 8)
|
152
|
-
value = 'also enough text to compress'
|
153
|
-
|
154
|
-
cache.write('comp-a', value)
|
155
|
-
cache.write('comp-b', value)
|
156
|
-
|
157
|
-
expect(cache.read_multi('comp-a', 'comp-b')).to eq(
|
158
|
-
'comp-a' => value,
|
159
|
-
'comp-b' => value
|
160
|
-
)
|
161
|
-
|
162
|
-
expect(cache.fetch_multi('comp-a', 'comp-b')).to eq(
|
163
|
-
'comp-a' => value,
|
164
|
-
'comp-b' => value
|
165
|
-
)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
describe '#fetch' do
|
170
|
-
it 'gets an existing value' do
|
171
|
-
cache.write('great-key', 'great')
|
172
|
-
expect(cache.fetch('great-key')).to eq('great')
|
173
|
-
end
|
174
|
-
|
175
|
-
it 'sets the value from the provided block' do
|
176
|
-
value = 'value for you'
|
177
|
-
cache.fetch('missing-key') { value }
|
178
|
-
expect(cache.read('missing-key')).to eq(value)
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'returns computed value when using passthrough marshalling' do
|
182
|
-
cache = Readthis::Cache.new(marshal: Readthis::Passthrough)
|
183
|
-
result = cache.fetch('missing-key') { 'value for you' }
|
184
|
-
expect(result).to eq('value for you')
|
185
|
-
end
|
186
|
-
|
187
|
-
it 'does not set for a missing key without a block' do
|
188
|
-
expect(cache.fetch('missing-key')).to be_nil
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'forces a cache miss when `force` is passed' do
|
192
|
-
cache.write('short-key', 'stuff')
|
193
|
-
cache.fetch('short-key', force: true) { 'other stuff' }
|
194
|
-
|
195
|
-
expect(cache.read('short-key')).to eq('other stuff')
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'gets an existing value when `options` are passed as nil' do
|
199
|
-
cache.write('great-key', 'great')
|
200
|
-
expect(cache.fetch('great-key', nil)).to eq('great')
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'serves computed content when the cache is down and tolerance is enabled' do
|
204
|
-
Readthis.fault_tolerant = true
|
205
|
-
|
206
|
-
allow(cache.pool).to receive(:with).and_raise(Redis::CannotConnectError)
|
207
|
-
|
208
|
-
computed = cache.fetch('error-key') { 'computed' }
|
209
|
-
|
210
|
-
expect(computed).to eq('computed')
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
describe '#read_multi' do
|
215
|
-
it 'maps multiple values to keys' do
|
216
|
-
cache.write('a', 1)
|
217
|
-
cache.write('b', 2)
|
218
|
-
cache.write('c', '3')
|
219
|
-
cache.write('d', nil)
|
220
|
-
|
221
|
-
expect(cache.read_multi('a', 'b', 'c', 'd')).to eq(
|
222
|
-
'a' => 1,
|
223
|
-
'b' => 2,
|
224
|
-
'c' => '3'
|
225
|
-
)
|
226
|
-
end
|
227
|
-
|
228
|
-
it 'can be configured to retain keys with nil values' do
|
229
|
-
cache.write('a', nil)
|
230
|
-
cache.write('b', nil)
|
231
|
-
|
232
|
-
expect(cache.read_multi('a', 'b', retain_nils: true)).to eq(
|
233
|
-
'a' => nil,
|
234
|
-
'b' => nil
|
235
|
-
)
|
236
|
-
end
|
237
|
-
|
238
|
-
it 'respects namespacing' do
|
239
|
-
cache.write('d', 1, namespace: 'cache')
|
240
|
-
cache.write('e', 2, namespace: 'cache')
|
241
|
-
|
242
|
-
expect(cache.read_multi('d', 'e', namespace: 'cache')).to eq(
|
243
|
-
'd' => 1,
|
244
|
-
'e' => 2
|
245
|
-
)
|
246
|
-
end
|
247
|
-
|
248
|
-
it 'returns {} with no keys' do
|
249
|
-
expect(cache.read_multi(namespace: 'cache')).to eq({})
|
250
|
-
end
|
251
|
-
|
252
|
-
it 'refreshes each key that is read' do
|
253
|
-
cache = Readthis::Cache.new(refresh: true)
|
254
|
-
|
255
|
-
cache.write('a', 1, expires_in: 1)
|
256
|
-
cache.write('b', 2, expires_in: 1)
|
257
|
-
|
258
|
-
cache.read_multi('a', 'b', expires_in: 2)
|
259
|
-
|
260
|
-
expect(cache).to have_ttl('a' => 2, 'b' => 2)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
describe '#write_multi' do
|
265
|
-
it 'writes multiple key value pairs simultaneously' do
|
266
|
-
response = cache.write_multi('a' => 1, 'b' => 2)
|
267
|
-
|
268
|
-
expect(response).to be_truthy
|
269
|
-
expect(cache.read('a')).to eq(1)
|
270
|
-
expect(cache.read('b')).to eq(2)
|
271
|
-
end
|
272
|
-
|
273
|
-
it 'respects passed options' do
|
274
|
-
cache.write_multi(
|
275
|
-
{ 'a' => 1, 'b' => 2 },
|
276
|
-
namespace: 'multi',
|
277
|
-
expires_in: 1
|
278
|
-
)
|
279
|
-
|
280
|
-
expect(cache.read('a')).to be_nil
|
281
|
-
expect(cache.read('a', namespace: 'multi')).to eq(1)
|
282
|
-
|
283
|
-
expect(cache).to have_ttl('multi:a' => 1)
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
describe '#fetch_multi' do
|
288
|
-
it 'reads multiple values, filling in missing keys from a block' do
|
289
|
-
cache.write('a', 1)
|
290
|
-
cache.write('c', 3)
|
291
|
-
|
292
|
-
results = cache.fetch_multi('a', 'b', 'c') { |key| key + key }
|
293
|
-
|
294
|
-
expect(results).to eq(
|
295
|
-
'a' => 1,
|
296
|
-
'b' => 'bb',
|
297
|
-
'c' => 3
|
298
|
-
)
|
299
|
-
|
300
|
-
expect(cache.read('b')).to eq('bb')
|
301
|
-
end
|
302
|
-
|
303
|
-
it 'uses passed options' do
|
304
|
-
cache.write('a', 1, namespace: 'alph')
|
305
|
-
|
306
|
-
results = cache.fetch_multi('a', 'b', namespace: 'alph') { |key| key }
|
307
|
-
|
308
|
-
expect(results).to eq(
|
309
|
-
'a' => 1,
|
310
|
-
'b' => 'b'
|
311
|
-
)
|
312
|
-
|
313
|
-
expect(cache.read('b')).to be_nil
|
314
|
-
expect(cache.read('b', namespace: 'alph')).not_to be_nil
|
315
|
-
end
|
316
|
-
|
317
|
-
it 'return empty results without keys' do
|
318
|
-
results = cache.fetch_multi(namespace: 'alph') { |key| key }
|
319
|
-
expect(results).to eq({})
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
describe '#exist?' do
|
324
|
-
it 'is true when the key has been set' do
|
325
|
-
cache.write('existing-key', 'stuff')
|
326
|
-
expect(cache.exist?('existing-key')).to be_truthy
|
327
|
-
end
|
328
|
-
|
329
|
-
it 'is false when the key has not been set' do
|
330
|
-
expect(cache.exist?('random-key')).to be_falsey
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
describe '#delete' do
|
335
|
-
it 'deletes an existing key' do
|
336
|
-
cache.write('not-long', 'for this world')
|
337
|
-
|
338
|
-
expect(cache.delete('not-long')).to be_truthy
|
339
|
-
expect(cache.read('not-long')).to be_nil
|
340
|
-
end
|
341
|
-
|
342
|
-
it 'safely returns false if nothing is deleted' do
|
343
|
-
expect(cache.delete('no-such-key')).to be_falsy
|
344
|
-
end
|
345
|
-
end
|
346
|
-
|
347
|
-
describe '#delete_matched' do
|
348
|
-
it 'deletes all matching keys' do
|
349
|
-
cache.write('tomcat', 'cat')
|
350
|
-
cache.write('wildcat', 'cat')
|
351
|
-
cache.write('bobcat', 'cat')
|
352
|
-
cache.write('cougar', 'cat')
|
353
|
-
|
354
|
-
expect(cache.delete_matched('tomcat')).to eq(1)
|
355
|
-
expect(cache.read('tomcat')).to be_nil
|
356
|
-
expect(cache.read('bobcat')).not_to be_nil
|
357
|
-
expect(cache.read('wildcat')).not_to be_nil
|
358
|
-
|
359
|
-
expect(cache.delete_matched('*cat', count: 1)).to eq(2)
|
360
|
-
expect(cache.read('wildcat')).to be_nil
|
361
|
-
expect(cache.read('bobcat')).to be_nil
|
362
|
-
|
363
|
-
expect(cache.delete_matched('*cat')).to eq(0)
|
364
|
-
end
|
365
|
-
|
366
|
-
it 'respects namespacing when matching keys' do
|
367
|
-
cache.write('tomcat', 'cat', namespace: 'feral')
|
368
|
-
|
369
|
-
expect(cache.delete_matched('tom*', namespace: 'feral')).to eq(1)
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
describe '#increment' do
|
374
|
-
it 'atomically increases the stored integer' do
|
375
|
-
cache.write('counter', 10)
|
376
|
-
expect(cache.increment('counter')).to eq(11)
|
377
|
-
expect(cache.read('counter')).to eq(11)
|
378
|
-
end
|
379
|
-
|
380
|
-
it 'defaults a missing key to 1' do
|
381
|
-
expect(cache.increment('unknown')).to eq(1)
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
describe '#decrement' do
|
386
|
-
it 'decrements a stored integer' do
|
387
|
-
cache.write('counter', 20)
|
388
|
-
expect(cache.decrement('counter')).to eq(19)
|
389
|
-
end
|
390
|
-
|
391
|
-
it 'defaults a missing key to -1' do
|
392
|
-
expect(cache.decrement('unknown')).to eq(-1)
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
describe 'instrumentation' do
|
397
|
-
it 'instruments cache invokations' do
|
398
|
-
require 'active_support/notifications'
|
399
|
-
|
400
|
-
notes = ActiveSupport::Notifications
|
401
|
-
cache = Readthis::Cache.new
|
402
|
-
events = []
|
403
|
-
|
404
|
-
notes.subscribe(/cache_*/) do |*args|
|
405
|
-
events << ActiveSupport::Notifications::Event.new(*args)
|
406
|
-
end
|
407
|
-
|
408
|
-
cache.write('a', 'a')
|
409
|
-
cache.read('a')
|
410
|
-
|
411
|
-
expect(events.length).to eq(2)
|
412
|
-
expect(events.map(&:name)).to eq %w[
|
413
|
-
cache_write.active_support
|
414
|
-
cache_read.active_support
|
415
|
-
]
|
416
|
-
end
|
417
|
-
end
|
418
|
-
end
|