readthis 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Readthis
2
- VERSION = '2.1.0'.freeze
4
+ VERSION = '2.2.0'
3
5
  end
@@ -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.1.0
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: 2017-10-07 00:00:00.000000000 Z
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: '0'
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: '0'
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.5'
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.5'
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: '0'
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: '0'
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.1'
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.1'
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.5.2.1
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: []
@@ -1,13 +0,0 @@
1
- module RedisMatchers
2
- extend RSpec::Matchers::DSL
3
-
4
- matcher :have_ttl do |expected|
5
- match do |cache|
6
- cache.pool.with do |client|
7
- expected.all? do |(key, value)|
8
- client.ttl(key) == value
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -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