scoped_cache_keys 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b96bac82675a12d0d185510840185d0829a897d4f08242ccf94922cfa45d1afd
4
- data.tar.gz: 87efd06a51cf71fb41d3b8193ed8f5f56378951101c8ffa341910e82e26c2386
3
+ metadata.gz: 599264adc7feb1e042fde17a4473126615907cfd03e27172d9355f9c5dfc6308
4
+ data.tar.gz: bd9335f025963374103c7fd8a0a897606e977b02568ba1a5816cc55a7f8f9afd
5
5
  SHA512:
6
- metadata.gz: a62968c83bfddb74ad349eaf0865700e65186bbc28bdcfdefbf156dfb00956e2b611ff0a2f5c4ca9b92653fb0442b2f99f91ef6908b33cf6bef7f035b941e96d
7
- data.tar.gz: 3eb0de9f214f5855fc0d21cd3bbe1b8e504f68d428f92902ffbd6ea96895d12990cc275087e14eefc24fd595f5bb593fada8c27c8d1c676c3b91c350d612b90c
6
+ metadata.gz: 9740614f52aeb1c14a7462ac11c1c4a496146402a7973f76991651bd3480ebf2e95754af17962ebecb2f1dab544fe56c7555f86b0d578be7032f374acbe26bf2
7
+ data.tar.gz: 9d2dc9a88572ef4991280a50bd9c764041d96d9996f8bc548b07cde2ecc614d7e5a619979099cc92c33af497b79816cfc5c8691fa0f56bd681261bd49bc058fc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- scoped_cache_keys (0.2.0)
4
+ scoped_cache_keys (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Readme.md CHANGED
@@ -1,16 +1,92 @@
1
- Add scoped_cache_key / expire_scoped_cache_key to your models for caching/sweeping of model-related caches.
1
+ scoped_cache_keys
2
+ =================
3
+
4
+ Add `scoped_cache_key` / `expire_scoped_cache_key` to your models for caching/sweeping of model-related caches.
5
+
6
+ Overview
7
+ --------
8
+
9
+ `scoped_cache_keys` enables you to "scope" multiple cache entries together, such that they can be expired
10
+ together in a single operation.
11
+
12
+ This saves the trouble of having to delete several individual cache entries, and improves the consistency
13
+ of your cache entries by having them all expire together.
14
+
15
+ Model-related caches in a Rails application often consist of multiple cache entries, which should be
16
+ consistent with each other.
17
+
18
+ For instance, you might have a cache entry which is a key->value mapping for a model, but another
19
+ cache entry which is a value->key mapping.
2
20
 
3
21
  Install
4
- =======
22
+ -------
5
23
 
6
24
  ```bash
7
25
  gem "scoped_cache_keys"
8
26
  ```
9
27
 
10
28
  Usage
11
- =====
29
+ -----
30
+
31
+ To use `scoped_cached_keys`, use `include ScopedCacheKeys` in your model declaration:
32
+
33
+ ```ruby
34
+ class User < ActiveRecord::Base
35
+ include ScopedCacheKeys
36
+ ...
37
+ end
38
+ ```
39
+
40
+ ### scoped_cache_key ###
41
+
42
+ The `scoped_cache_key` method returns a key that will change whenever `expire_scoped_cache_key` is called.
43
+ The cache key will be scoped to the model object that `scoped_cache_key` is invoked on, along with
44
+ whatever scope is passed:
45
+
46
+ ```ruby
47
+ account.scoped_cache_key(:orders)
48
+ ```
49
+
50
+ Options: Any caching options that are passed to `Rails.cache` methods may be passed to `scoped_cache_key`.
51
+ For example:
52
+
53
+ ```ruby
54
+ account.scoped_cache_key(:orders, expires_in: 10.minutes)
55
+ ```
56
+
57
+ The key that is returned can be treated as a "base key," that is, concatenated with other strings to form
58
+ other cache keys. All such cache keys will become unreachable, and thus "expired," when
59
+ `expire_scoped_cache_key` is called.
60
+
61
+ ### expire_scoped_cache_key ###
62
+
63
+ The `expire_scoped_cache_key` method will expire a scoped cache key obtained with `scoped_cache_key`.
64
+ Example:
65
+
66
+ ```ruby
67
+ account.expire_scoped_cache_key(:orders)
68
+ ```
69
+
70
+ Options: The optional `delay` parameter specifies an amount of time to delay before actually expiring
71
+ the scoped cache key. Example:
72
+
73
+ ```ruby
74
+ account.expire_scoped_cache_key(:orders, delay: 5.seconds)
75
+ ```
76
+
77
+ The `delay` parameter can be useful for contending with database replication lag. When many concurrent
78
+ processes access the scoped cache entries, one or more may immediately try to rebuild the cache
79
+ the instant that `expire_scoped_cache_key` completes. This could happen so quickly that the database
80
+ replicas may not be caught up with the model changes that were just committed. Alternately, one
81
+ could force the models to be queried from the primary database to avoid such race conditions.
82
+
83
+ The expiration delay may also be set at a global level:
84
+
85
+ ```ruby
86
+ ScopedCacheKeys.expire_scoped_cache_key_delay = 10.seconds
87
+ ```
12
88
 
13
- Gives you a key that will change whenever expire_scoped_cache_key is called.
89
+ ### Example ###
14
90
 
15
91
  ```
16
92
  <% cache user.scoped_cache_key :products do %>
@@ -34,7 +110,7 @@ Gives you a key that will change whenever expire_scoped_cache_key is called.
34
110
  ```
35
111
 
36
112
  Author
37
- ======
113
+ ------
38
114
  [Zendesk](http://zendesk.com)<br/>
39
115
  michael@grosser.it<br/>
40
116
  License: MIT
@@ -1,3 +1,3 @@
1
1
  module ScopedCacheKeys
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -1,13 +1,26 @@
1
1
  require 'scoped_cache_keys/version'
2
2
 
3
3
  module ScopedCacheKeys
4
+ class << self
5
+ # If set, expire_scoped_cache_key will delay expiration by this many seconds
6
+ # globally.
7
+ attr_accessor :expire_scoped_cache_key_delay
8
+ end
9
+
4
10
  def scoped_cache_key(scope, options = nil)
5
11
  base_key = Rails.cache.fetch(build_scoped_cache_key([scope]), options) { Time.now.to_f }
6
12
  build_scoped_cache_key [scope, base_key]
7
13
  end
8
14
 
9
- def expire_scoped_cache_key(scope)
10
- Rails.cache.delete(build_scoped_cache_key(scope))
15
+ def expire_scoped_cache_key(scope, delay: nil)
16
+ delay ||= ScopedCacheKeys.expire_scoped_cache_key_delay
17
+ key = build_scoped_cache_key(scope)
18
+ if delay
19
+ entry = Rails.cache.read(key)
20
+ Rails.cache.write(key, entry, expires_in: delay) if entry
21
+ else
22
+ Rails.cache.delete(key)
23
+ end
11
24
  end
12
25
 
13
26
  private
@@ -45,6 +45,12 @@ describe ScopedCacheKeys do
45
45
  user.updated_at = 1.week.ago
46
46
  }.to_not change{ user.scoped_cache_key(:foo) }
47
47
  end
48
+
49
+ it "accepts cache options such as expired_in" do
50
+ key = user.scoped_cache_key(:foo, expires_in: 10.seconds)
51
+ Timecop.travel(11.seconds.from_now)
52
+ user.scoped_cache_key(:foo).should_not == key
53
+ end
48
54
  end
49
55
 
50
56
  describe "#expire_scoped_cache_key" do
@@ -59,5 +65,25 @@ describe ScopedCacheKeys do
59
65
  user.expire_scoped_cache_key :bar
60
66
  }.to_not change{ user.scoped_cache_key(:foo) }
61
67
  end
68
+
69
+ it "expires a specific scope after a specified delay" do
70
+ key = user.scoped_cache_key :foo
71
+ user.expire_scoped_cache_key :foo, delay: 5.seconds
72
+ Timecop.travel(4.seconds.from_now)
73
+ user.scoped_cache_key(:foo).should == key
74
+ Timecop.travel(2.seconds.from_now)
75
+ user.scoped_cache_key(:foo).should_not == key
76
+ end
77
+
78
+ it "expires a specific scope after a specified global delay" do
79
+ ScopedCacheKeys.expire_scoped_cache_key_delay = 10.seconds
80
+ key = user.scoped_cache_key :foo
81
+ user.expire_scoped_cache_key :foo
82
+ Timecop.travel(9.seconds.from_now)
83
+ user.scoped_cache_key(:foo).should == key
84
+ Timecop.travel(2.seconds.from_now)
85
+ user.scoped_cache_key(:foo).should_not == key
86
+ ScopedCacheKeys.expire_scoped_cache_key_delay = nil
87
+ end
62
88
  end
63
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scoped_cache_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-02 00:00:00.000000000 Z
11
+ date: 2022-12-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: michael@grosser.it