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.
@@ -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