object-cache 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -3
- data/README.md +57 -4
- data/lib/object/cache.rb +22 -2
- data/lib/object/cache/version.rb +1 -1
- data/test/cache_test.rb +36 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d2f1ec4b3467a87297e809f094d3f75461b3fa9
|
4
|
+
data.tar.gz: bf7eef92986a7ddb52c5f7ec2aa3110ddbaa791d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 323e1806f012635837d50ac2111d607232714308376d16e9ab32718cc2217fe760d745298b3e20165b1d3e70f339b90acca9aada9ec81cf44f5b75e971403006
|
7
|
+
data.tar.gz: 7b0cd11b83029e6e2b9701804a7426679e6002578777192916301c75af7faaf9780a61acd5ef8a32a8df52de45e977a5d86be1954f1a56dfdbd876bb7ea15dc7
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,7 @@ Easy caching of Ruby objects, using [Redis](http://redis.io) as a backend store.
|
|
8
8
|
* [marshaling data](#marshaling-data)
|
9
9
|
* [ttl](#ttl)
|
10
10
|
* [namespaced keys](#namespaced-keys)
|
11
|
+
* [key prefixes](#key-prefixes)
|
11
12
|
* [redis replicas](#redis-replicas)
|
12
13
|
* [core extension](#core-extension)
|
13
14
|
* [License](#license)
|
@@ -128,6 +129,58 @@ merged together with the file name and line number of the cache request, so you
|
|
128
129
|
can re-use that same `email` namespace in different locations, without worrying
|
129
130
|
about any naming collisions.
|
130
131
|
|
132
|
+
#### key prefixes
|
133
|
+
|
134
|
+
By default, the eventual key ending up in Redis is a 6-character long digest,
|
135
|
+
based on the file name, line number, and optional key passed into the Cache
|
136
|
+
object:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
Cache.new { 'hello world' }
|
140
|
+
Cache.backend.keys # => ["22abcc"]
|
141
|
+
```
|
142
|
+
|
143
|
+
This makes working with keys quick and easy, without worying about conflicting
|
144
|
+
keys.
|
145
|
+
|
146
|
+
However, this does make it more difficult to selectively delete keys from the
|
147
|
+
backend, if you want to purge the cache of specific keys, before their TTL
|
148
|
+
expires.
|
149
|
+
|
150
|
+
To support this use-case, you can use the `key_prefix` attribute:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
Cache.new(key_prefix: 'hello') { 'hello world' }
|
154
|
+
Cache.backend.keys # => ["hello_22abcc"]
|
155
|
+
```
|
156
|
+
|
157
|
+
This allows you to selectively purge keys from Redis:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
Cache.backend.del('hello_')
|
161
|
+
```
|
162
|
+
|
163
|
+
You can also use the special value `:method_name` to dynamically set the key
|
164
|
+
prefix based on where the cached object was created:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
Cache.new(key_prefix: :method_name) { 'hello world' }
|
168
|
+
Cache.backend.keys # => ["test_key_prefix_method_name_22abcc"]
|
169
|
+
```
|
170
|
+
|
171
|
+
Or, use `:class_name` to group keys in the same class together:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
Cache.new(key_prefix: :class_name) { 'hello world' }
|
175
|
+
Cache.backend.keys # => ["CacheTest_22abcc"]
|
176
|
+
```
|
177
|
+
|
178
|
+
You can also define these options globally:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
Cache.default_key_prefix = :method_name
|
182
|
+
```
|
183
|
+
|
131
184
|
#### redis replicas
|
132
185
|
|
133
186
|
Before, we used the following setup to connect `Object::Cache` to a redis
|
@@ -137,7 +190,7 @@ backend:
|
|
137
190
|
Cache.backend = Redis.new
|
138
191
|
```
|
139
192
|
|
140
|
-
The Ruby Redis library has primary/replicas support [
|
193
|
+
The Ruby Redis library has primary/replicas support [built-in using Redis
|
141
194
|
Sentinel][sentinel].
|
142
195
|
|
143
196
|
If however, you have your own setup, and want the writes and reads to be
|
@@ -151,7 +204,7 @@ Cache.backend = { primary: Redis.new, replicas: [Redis.new, Redis.new] }
|
|
151
204
|
When writing the initial object to the backend, the `primary` Redis is used. On
|
152
205
|
subsequent requests, a random replica is used to retrieve the stored value.
|
153
206
|
|
154
|
-
The above example
|
207
|
+
The above example obviously only works if the replicas receive the written data
|
155
208
|
from the primary instance.
|
156
209
|
|
157
210
|
#### core extension
|
@@ -167,8 +220,8 @@ cache('hello', ttl: 60) { 'hello world' }
|
|
167
220
|
Cache.new('hello', ttl: 60) { 'hello world' }
|
168
221
|
```
|
169
222
|
|
170
|
-
|
171
|
-
|
223
|
+
You can also call this method directly on any instances inheriting from
|
224
|
+
`Object`:
|
172
225
|
|
173
226
|
```ruby
|
174
227
|
require 'object/cache/core_extension'
|
data/lib/object/cache.rb
CHANGED
@@ -10,6 +10,7 @@ class Cache
|
|
10
10
|
class << self
|
11
11
|
attr_accessor :backend
|
12
12
|
attr_accessor :default_ttl
|
13
|
+
attr_accessor :default_key_prefix
|
13
14
|
|
14
15
|
# new
|
15
16
|
#
|
@@ -43,10 +44,10 @@ class Cache
|
|
43
44
|
# Cache.new { item } # item is only stored once, and then always
|
44
45
|
# # retrieved, even if it is a different item
|
45
46
|
#
|
46
|
-
def new(key = nil, ttl: default_ttl)
|
47
|
+
def new(key = nil, ttl: default_ttl, key_prefix: default_key_prefix)
|
47
48
|
return yield unless replica
|
48
49
|
|
49
|
-
key =
|
50
|
+
key = build_key(key, key_prefix, Proc.new)
|
50
51
|
|
51
52
|
if (cached_value = replica.get(key)).nil?
|
52
53
|
yield.tap { |value| update_cache(key, value, ttl: ttl) }
|
@@ -94,5 +95,24 @@ class Cache
|
|
94
95
|
def replica
|
95
96
|
replicas.sample
|
96
97
|
end
|
98
|
+
|
99
|
+
def build_key(key, key_prefix, proc)
|
100
|
+
hash = Digest::SHA1.hexdigest([key, proc.source_location].flatten.join)[0..5]
|
101
|
+
prefix = build_key_prefix(key_prefix, proc)
|
102
|
+
|
103
|
+
[prefix, hash].compact.join('_')
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_key_prefix(key_prefix, proc)
|
107
|
+
case key_prefix
|
108
|
+
when :method_name
|
109
|
+
location = caller_locations.find { |l| "#{l.path}#{l.lineno}" == proc.source_location.join }
|
110
|
+
location&.base_label
|
111
|
+
when :class_name
|
112
|
+
proc.binding.receiver.class.to_s
|
113
|
+
else
|
114
|
+
key_prefix
|
115
|
+
end
|
116
|
+
end
|
97
117
|
end
|
98
118
|
end
|
data/lib/object/cache/version.rb
CHANGED
data/test/cache_test.rb
CHANGED
@@ -147,4 +147,40 @@ class CacheTest < Minitest::Test # rubocop:disable Metrics/ClassLength
|
|
147
147
|
assert_equal 1, primary.keys.count
|
148
148
|
assert_equal 0, replica.keys.count
|
149
149
|
end
|
150
|
+
|
151
|
+
def test_default_key_prefix_custom
|
152
|
+
Cache.default_key_prefix = 'hello'
|
153
|
+
|
154
|
+
Cache.new { 'hello world' }
|
155
|
+
assert_match(/^hello/, redis.keys.first)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_default_key_prefix_method_name
|
159
|
+
Cache.default_key_prefix = :method_name
|
160
|
+
|
161
|
+
Cache.new { 'hello world' }
|
162
|
+
assert_match(/^test_default_key_prefix_method_name/, redis.keys.first)
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_default_key_prefix_class_name
|
166
|
+
Cache.default_key_prefix = :class_name
|
167
|
+
|
168
|
+
Cache.new { 'hello world' }
|
169
|
+
assert_match(/^CacheTest/, redis.keys.first)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_key_prefix_custom
|
173
|
+
Cache.new(key_prefix: 'hello') { 'hello world' }
|
174
|
+
assert_match(/^hello/, redis.keys.first)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_key_prefix_method_name
|
178
|
+
Cache.new(key_prefix: :method_name) { 'hello world' }
|
179
|
+
assert_match(/^test_key_prefix_method_name/, redis.keys.first)
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_key_prefix_class_name
|
183
|
+
Cache.new(key_prefix: :class_name) { 'hello world' }
|
184
|
+
assert_match(/^CacheTest/, redis.keys.first)
|
185
|
+
end
|
150
186
|
end
|