cache_keeper 0.1.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 +7 -0
- data/.github/workflows/ci.yml +38 -0
- data/.gitignore +10 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +197 -0
- data/MIT-LICENSE +20 -0
- data/README.md +69 -0
- data/app/jobs/cache_keeper/base_job.rb +16 -0
- data/app/jobs/cache_keeper/refresh_job.rb +7 -0
- data/app/models/cache_keeper/cached_method/refreshable.rb +11 -0
- data/app/models/cache_keeper/cached_method.rb +49 -0
- data/app/serializers/cache_keeper/cached_method_serializer.rb +17 -0
- data/app/serializers/cache_keeper/whatever_serializer.rb +15 -0
- data/bin/rails +13 -0
- data/bin/release +13 -0
- data/bin/test +11 -0
- data/cache_keeper.gemspec +18 -0
- data/lib/cache_keeper/caching.rb +20 -0
- data/lib/cache_keeper/configuration.rb +20 -0
- data/lib/cache_keeper/engine.rb +22 -0
- data/lib/cache_keeper/manager.rb +42 -0
- data/lib/cache_keeper/replace_method.rb +48 -0
- data/lib/cache_keeper/version.rb +3 -0
- data/lib/cache_keeper.rb +13 -0
- data/test/cache_helper.rb +29 -0
- data/test/dummy/Rakefile +3 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/cable.js +13 -0
- data/test/dummy/app/assets/javascripts/channels/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/recordings_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/recording.rb +13 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/config/application.rb +21 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +46 -0
- data/test/dummy/config/environments/production.rb +94 -0
- data/test/dummy/config/environments/test.rb +51 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +6 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/migrate/20200504213548_create_recordings.rb +7 -0
- data/test/dummy/db/schema.rb +19 -0
- data/test/dummy/db/seeds.rb +7 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/lib/tasks/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/robots.txt +1 -0
- data/test/dummy/test/application_system_test_case.rb +5 -0
- data/test/dummy/test/controllers/.keep +0 -0
- data/test/dummy/test/fixtures/.keep +0 -0
- data/test/dummy/test/fixtures/files/.keep +0 -0
- data/test/dummy/test/helpers/.keep +0 -0
- data/test/dummy/test/integration/.keep +0 -0
- data/test/dummy/test/mailers/.keep +0 -0
- data/test/dummy/test/models/.keep +0 -0
- data/test/dummy/test/system/.keep +0 -0
- data/test/dummy/test/test_helper.rb +10 -0
- data/test/dummy/vendor/.keep +0 -0
- data/test/engine_test.rb +11 -0
- data/test/manager_test.rb +8 -0
- data/test/models/cached_method_test.rb +20 -0
- data/test/serializers/cached_method_serializer_test.rb +36 -0
- data/test/serializers/whatever_serializer_test.rb +26 -0
- data/test/test_helper.rb +10 -0
- metadata +231 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a594d57869401830f847424c24851913039af6979f2e5f9665d77b96bb2c955e
|
4
|
+
data.tar.gz: c80f3edb0b84157a3e49ef8438347931c515ecffe2a55c9a08a630b2fca1c6c0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 46806eb4e857047be29c784b9c8455a2ce92ac79ebe19fac9bcec704fa9c29c48b63ebf546e78d615cf8d429044bc7557dbac7e94abc939b1288353f856729b3
|
7
|
+
data.tar.gz: b1902aa78ec63d686c7435924c412c997828c117cc498a6a474d0b00c3175c621542d5677a74bb97172523d0a24df38ee916ab8c0a0edafc42047a31dfa87a21
|
@@ -0,0 +1,38 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
tests:
|
5
|
+
strategy:
|
6
|
+
matrix:
|
7
|
+
ruby-version:
|
8
|
+
- "2.7"
|
9
|
+
- "3.0"
|
10
|
+
rails-version:
|
11
|
+
- "6.1"
|
12
|
+
- "7.0"
|
13
|
+
- "main"
|
14
|
+
|
15
|
+
env:
|
16
|
+
RAILS_VERSION: "${{ matrix.rails-version }}"
|
17
|
+
|
18
|
+
name: ${{ format('Tests (Ruby {0}, Rails {1})', matrix.ruby-version, matrix.rails-version) }}
|
19
|
+
runs-on: ubuntu-latest
|
20
|
+
continue-on-error: true
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v1
|
24
|
+
|
25
|
+
- name: Install Ruby
|
26
|
+
uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
ruby-version: ${{ matrix.ruby-version }}
|
29
|
+
bundler-cache: true
|
30
|
+
|
31
|
+
- name: Run tests
|
32
|
+
run: |
|
33
|
+
bin/test test/**/*_test.rb
|
34
|
+
|
35
|
+
- name: Fail when generated changes are not checked-in
|
36
|
+
run: |
|
37
|
+
git update-index --refresh
|
38
|
+
git diff-index --quiet HEAD --
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cache_keeper (0.1.0)
|
5
|
+
rails (>= 6.1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actioncable (7.1.1)
|
11
|
+
actionpack (= 7.1.1)
|
12
|
+
activesupport (= 7.1.1)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
zeitwerk (~> 2.6)
|
16
|
+
actionmailbox (7.1.1)
|
17
|
+
actionpack (= 7.1.1)
|
18
|
+
activejob (= 7.1.1)
|
19
|
+
activerecord (= 7.1.1)
|
20
|
+
activestorage (= 7.1.1)
|
21
|
+
activesupport (= 7.1.1)
|
22
|
+
mail (>= 2.7.1)
|
23
|
+
net-imap
|
24
|
+
net-pop
|
25
|
+
net-smtp
|
26
|
+
actionmailer (7.1.1)
|
27
|
+
actionpack (= 7.1.1)
|
28
|
+
actionview (= 7.1.1)
|
29
|
+
activejob (= 7.1.1)
|
30
|
+
activesupport (= 7.1.1)
|
31
|
+
mail (~> 2.5, >= 2.5.4)
|
32
|
+
net-imap
|
33
|
+
net-pop
|
34
|
+
net-smtp
|
35
|
+
rails-dom-testing (~> 2.2)
|
36
|
+
actionpack (7.1.1)
|
37
|
+
actionview (= 7.1.1)
|
38
|
+
activesupport (= 7.1.1)
|
39
|
+
nokogiri (>= 1.8.5)
|
40
|
+
rack (>= 2.2.4)
|
41
|
+
rack-session (>= 1.0.1)
|
42
|
+
rack-test (>= 0.6.3)
|
43
|
+
rails-dom-testing (~> 2.2)
|
44
|
+
rails-html-sanitizer (~> 1.6)
|
45
|
+
actiontext (7.1.1)
|
46
|
+
actionpack (= 7.1.1)
|
47
|
+
activerecord (= 7.1.1)
|
48
|
+
activestorage (= 7.1.1)
|
49
|
+
activesupport (= 7.1.1)
|
50
|
+
globalid (>= 0.6.0)
|
51
|
+
nokogiri (>= 1.8.5)
|
52
|
+
actionview (7.1.1)
|
53
|
+
activesupport (= 7.1.1)
|
54
|
+
builder (~> 3.1)
|
55
|
+
erubi (~> 1.11)
|
56
|
+
rails-dom-testing (~> 2.2)
|
57
|
+
rails-html-sanitizer (~> 1.6)
|
58
|
+
activejob (7.1.1)
|
59
|
+
activesupport (= 7.1.1)
|
60
|
+
globalid (>= 0.3.6)
|
61
|
+
activemodel (7.1.1)
|
62
|
+
activesupport (= 7.1.1)
|
63
|
+
activerecord (7.1.1)
|
64
|
+
activemodel (= 7.1.1)
|
65
|
+
activesupport (= 7.1.1)
|
66
|
+
timeout (>= 0.4.0)
|
67
|
+
activestorage (7.1.1)
|
68
|
+
actionpack (= 7.1.1)
|
69
|
+
activejob (= 7.1.1)
|
70
|
+
activerecord (= 7.1.1)
|
71
|
+
activesupport (= 7.1.1)
|
72
|
+
marcel (~> 1.0)
|
73
|
+
activesupport (7.1.1)
|
74
|
+
base64
|
75
|
+
bigdecimal
|
76
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
77
|
+
connection_pool (>= 2.2.5)
|
78
|
+
drb
|
79
|
+
i18n (>= 1.6, < 2)
|
80
|
+
minitest (>= 5.1)
|
81
|
+
mutex_m
|
82
|
+
tzinfo (~> 2.0)
|
83
|
+
base64 (0.1.1)
|
84
|
+
bigdecimal (3.1.4)
|
85
|
+
builder (3.2.4)
|
86
|
+
concurrent-ruby (1.2.2)
|
87
|
+
connection_pool (2.4.1)
|
88
|
+
crass (1.0.6)
|
89
|
+
date (3.3.3)
|
90
|
+
drb (2.1.1)
|
91
|
+
ruby2_keywords
|
92
|
+
erubi (1.12.0)
|
93
|
+
globalid (1.2.1)
|
94
|
+
activesupport (>= 6.1)
|
95
|
+
i18n (1.14.1)
|
96
|
+
concurrent-ruby (~> 1.0)
|
97
|
+
io-console (0.6.0)
|
98
|
+
irb (1.8.3)
|
99
|
+
rdoc
|
100
|
+
reline (>= 0.3.8)
|
101
|
+
loofah (2.21.4)
|
102
|
+
crass (~> 1.0.2)
|
103
|
+
nokogiri (>= 1.12.0)
|
104
|
+
mail (2.8.1)
|
105
|
+
mini_mime (>= 0.1.1)
|
106
|
+
net-imap
|
107
|
+
net-pop
|
108
|
+
net-smtp
|
109
|
+
marcel (1.0.2)
|
110
|
+
mini_mime (1.1.5)
|
111
|
+
minitest (5.20.0)
|
112
|
+
mutex_m (0.1.2)
|
113
|
+
net-imap (0.4.2)
|
114
|
+
date
|
115
|
+
net-protocol
|
116
|
+
net-pop (0.1.2)
|
117
|
+
net-protocol
|
118
|
+
net-protocol (0.2.1)
|
119
|
+
timeout
|
120
|
+
net-smtp (0.4.0)
|
121
|
+
net-protocol
|
122
|
+
nio4r (2.5.9)
|
123
|
+
nokogiri (1.15.4-arm64-darwin)
|
124
|
+
racc (~> 1.4)
|
125
|
+
nokogiri (1.15.4-x86_64-linux)
|
126
|
+
racc (~> 1.4)
|
127
|
+
psych (5.1.1.1)
|
128
|
+
stringio
|
129
|
+
racc (1.7.1)
|
130
|
+
rack (3.0.8)
|
131
|
+
rack-session (2.0.0)
|
132
|
+
rack (>= 3.0.0)
|
133
|
+
rack-test (2.1.0)
|
134
|
+
rack (>= 1.3)
|
135
|
+
rackup (2.1.0)
|
136
|
+
rack (>= 3)
|
137
|
+
webrick (~> 1.8)
|
138
|
+
rails (7.1.1)
|
139
|
+
actioncable (= 7.1.1)
|
140
|
+
actionmailbox (= 7.1.1)
|
141
|
+
actionmailer (= 7.1.1)
|
142
|
+
actionpack (= 7.1.1)
|
143
|
+
actiontext (= 7.1.1)
|
144
|
+
actionview (= 7.1.1)
|
145
|
+
activejob (= 7.1.1)
|
146
|
+
activemodel (= 7.1.1)
|
147
|
+
activerecord (= 7.1.1)
|
148
|
+
activestorage (= 7.1.1)
|
149
|
+
activesupport (= 7.1.1)
|
150
|
+
bundler (>= 1.15.0)
|
151
|
+
railties (= 7.1.1)
|
152
|
+
rails-dom-testing (2.2.0)
|
153
|
+
activesupport (>= 5.0.0)
|
154
|
+
minitest
|
155
|
+
nokogiri (>= 1.6)
|
156
|
+
rails-html-sanitizer (1.6.0)
|
157
|
+
loofah (~> 2.21)
|
158
|
+
nokogiri (~> 1.14)
|
159
|
+
railties (7.1.1)
|
160
|
+
actionpack (= 7.1.1)
|
161
|
+
activesupport (= 7.1.1)
|
162
|
+
irb
|
163
|
+
rackup (>= 1.0.0)
|
164
|
+
rake (>= 12.2)
|
165
|
+
thor (~> 1.0, >= 1.2.2)
|
166
|
+
zeitwerk (~> 2.6)
|
167
|
+
rake (13.0.6)
|
168
|
+
rdoc (6.5.0)
|
169
|
+
psych (>= 4.0.0)
|
170
|
+
reline (0.3.9)
|
171
|
+
io-console (~> 0.5)
|
172
|
+
ruby2_keywords (0.0.5)
|
173
|
+
sqlite3 (1.6.7-arm64-darwin)
|
174
|
+
sqlite3 (1.6.7-x86_64-linux)
|
175
|
+
stringio (3.0.8)
|
176
|
+
thor (1.3.0)
|
177
|
+
timeout (0.4.0)
|
178
|
+
tzinfo (2.0.6)
|
179
|
+
concurrent-ruby (~> 1.0)
|
180
|
+
webrick (1.8.1)
|
181
|
+
websocket-driver (0.7.6)
|
182
|
+
websocket-extensions (>= 0.1.0)
|
183
|
+
websocket-extensions (0.1.5)
|
184
|
+
zeitwerk (2.6.12)
|
185
|
+
|
186
|
+
PLATFORMS
|
187
|
+
arm64-darwin-22
|
188
|
+
x86_64-linux
|
189
|
+
|
190
|
+
DEPENDENCIES
|
191
|
+
cache_keeper!
|
192
|
+
rails (>= 6.1)
|
193
|
+
rake
|
194
|
+
sqlite3
|
195
|
+
|
196
|
+
BUNDLED WITH
|
197
|
+
2.4.19
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2017 David Heinemeier Hansson, Basecamp
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
<h1 align="center">
|
2
|
+
CacheKeeper
|
3
|
+
<br>
|
4
|
+
</h1>
|
5
|
+
|
6
|
+
<h3 align="center">Keep cached methods always fresh in your Rails application.</h3>
|
7
|
+
|
8
|
+
<p align="center">
|
9
|
+
<img alt="Build" src="https://img.shields.io/github/actions/workflow/status/martinzamuner/cache_keeper/ci.yml?branch=main">
|
10
|
+
<img alt="Gem" src="https://img.shields.io/gem/v/cache_keeper">
|
11
|
+
<img alt="rails version" src="https://img.shields.io/badge/rails-%3E%3D%206.1.0-informational">
|
12
|
+
<img alt="License" src="https://img.shields.io/github/license/martinzamuner/cache_keeper">
|
13
|
+
</p>
|
14
|
+
|
15
|
+
CacheKeeper allows you to mark any method to be kept fresh in your Rails cache. It uses ActiveJob to refresh the cache in the background.
|
16
|
+
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add CacheKeeper to your Gemfile:
|
21
|
+
|
22
|
+
```sh
|
23
|
+
bundle add cache_keeper
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
CacheKeeper provides a `caches` method that will cache the result of the methods you give it:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
caches :slow_method, :really_slow_method, expires_in: 1.hour
|
33
|
+
caches :incredibly_slow_method, expires_in: 2.hours, must_revalidate: true
|
34
|
+
```
|
35
|
+
|
36
|
+
It is automatically available in your ActiveRecord models and in your controllers. You can also use it in any other class by including `CacheKeeper::Caching`.
|
37
|
+
|
38
|
+
By default, it will immediately run the method call if it hasn't been cached before. The next time it is called, it will return the cached value if it hasn't expired yet. If it has expired, it will enqueue a job to refresh the cache in the background and return the stale value in the meantime. You can avoid returning stale values by setting `must_revalidate: true` in the options.
|
39
|
+
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
CacheKeeper can be configured in an initializer, in any environment file or in your `config/application.rb` file. The following options are available:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
Rails.application.configure do
|
47
|
+
# If a stale entry is requested, refresh immediately instead of enqueuing a refresh job.
|
48
|
+
# Default: false
|
49
|
+
config.cache_keeper.must_revalidate = true
|
50
|
+
|
51
|
+
# The queue to use for the refresh jobs.
|
52
|
+
# Default: nil (uses the default queue)
|
53
|
+
config.cache_keeper.queues.refresh = :low_priority
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
|
58
|
+
## Development
|
59
|
+
|
60
|
+
<details>
|
61
|
+
<summary>Running the tests</summary><br>
|
62
|
+
|
63
|
+
- You can run the whole suite with `./bin/test test/**/*_test.rb`
|
64
|
+
</details>
|
65
|
+
|
66
|
+
|
67
|
+
## License
|
68
|
+
|
69
|
+
CacheKeeper is released under the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CacheKeeper::BaseJob < ActiveJob::Base
|
2
|
+
discard_on StandardError
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
# Monkey patch ActiveJob::Core#serialize_arguments to use CacheKeeper::WhateverSerializer
|
7
|
+
# in case there's no serializer for the argument. I'm doing it this way because I don't
|
8
|
+
# want to register the serializer as it would affect the whole application.
|
9
|
+
def serialize_arguments(arguments)
|
10
|
+
arguments.map do |argument|
|
11
|
+
ActiveJob::Arguments.send :serialize_argument, argument
|
12
|
+
rescue ActiveJob::SerializationError
|
13
|
+
CacheKeeper::WhateverSerializer.serialize argument
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CacheKeeper::CachedMethod::Refreshable
|
2
|
+
def refresh(instance)
|
3
|
+
Rails.cache.fetch(cache_key, expires_in: expires_in) do
|
4
|
+
instance.send alias_for_original_method
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def refresh_later(instance)
|
9
|
+
CacheKeeper::RefreshJob.perform_later self, instance
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class CacheKeeper::CachedMethod
|
2
|
+
include Refreshable
|
3
|
+
|
4
|
+
attr_accessor :klass, :method_name, :options
|
5
|
+
|
6
|
+
def initialize(klass, method_name, options = {})
|
7
|
+
self.klass = klass
|
8
|
+
self.method_name = method_name
|
9
|
+
self.options = options.with_indifferent_access
|
10
|
+
end
|
11
|
+
|
12
|
+
def alias_for_original_method
|
13
|
+
:"__#{method_name}__hooked__"
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(instance)
|
17
|
+
if cache_entry.blank?
|
18
|
+
refresh instance
|
19
|
+
elsif cache_entry.expired?
|
20
|
+
if must_revalidate?
|
21
|
+
refresh instance
|
22
|
+
else
|
23
|
+
refresh_later instance
|
24
|
+
|
25
|
+
cache_entry.value
|
26
|
+
end
|
27
|
+
else
|
28
|
+
cache_entry.value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def cache_entry
|
35
|
+
Rails.cache.send :read_entry, Rails.cache.send(:normalize_key, cache_key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_key
|
39
|
+
["CacheKeeper", klass, method_name]
|
40
|
+
end
|
41
|
+
|
42
|
+
def expires_in
|
43
|
+
options[:expires_in]
|
44
|
+
end
|
45
|
+
|
46
|
+
def must_revalidate?
|
47
|
+
options[:must_revalidate].nil? ? CacheKeeper.configuration.must_revalidate : options[:must_revalidate]
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CacheKeeper::CachedMethodSerializer < ActiveJob::Serializers::ObjectSerializer
|
2
|
+
def serialize?(argument)
|
3
|
+
argument.is_a? CacheKeeper::CachedMethod
|
4
|
+
end
|
5
|
+
|
6
|
+
def serialize(cached_method)
|
7
|
+
super(
|
8
|
+
"klass" => cached_method.klass,
|
9
|
+
"method_name" => cached_method.method_name,
|
10
|
+
"options" => cached_method.options
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def deserialize(hash)
|
15
|
+
CacheKeeper::CachedMethod.new hash["klass"], hash["method_name"], hash["options"]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CacheKeeper::WhateverSerializer < ActiveJob::Serializers::ObjectSerializer
|
2
|
+
def serialize?(argument)
|
3
|
+
true
|
4
|
+
end
|
5
|
+
|
6
|
+
def serialize(whatever)
|
7
|
+
super(
|
8
|
+
"klass" => whatever.class.to_s
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def deserialize(hash)
|
13
|
+
hash["klass"].constantize.new
|
14
|
+
end
|
15
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
6
|
+
APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
|
7
|
+
|
8
|
+
# Set up gems listed in the Gemfile.
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
11
|
+
|
12
|
+
require 'rails/all'
|
13
|
+
require 'rails/engine/commands'
|
data/bin/release
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
VERSION=$1
|
4
|
+
|
5
|
+
printf "module CacheKeeper\n VERSION = \"$VERSION\"\nend\n" > ./lib/cache_keeper/version.rb
|
6
|
+
bundle
|
7
|
+
git add Gemfile.lock lib/cache_keeper/version.rb
|
8
|
+
git commit -m "Bump version for $VERSION"
|
9
|
+
git push
|
10
|
+
git tag v$VERSION
|
11
|
+
git push --tags
|
12
|
+
gem build cache_keeper.gemspec
|
13
|
+
gem push "cache_keeper-$VERSION.gem" --host https://rubygems.org
|
data/bin/test
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.expand_path("../test", __dir__)
|
3
|
+
|
4
|
+
puts "Installing Ruby dependencies"
|
5
|
+
`bundle install`
|
6
|
+
|
7
|
+
puts "Preparing test database"
|
8
|
+
`cd test/dummy; ./bin/rails db:test:prepare`
|
9
|
+
|
10
|
+
require "bundler/setup"
|
11
|
+
require "rails/plugin/test"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "lib/cache_keeper/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "cache_keeper"
|
5
|
+
s.version = CacheKeeper::VERSION
|
6
|
+
s.authors = ["Martin Zamuner"]
|
7
|
+
s.email = "martinzamuner@gmail.com"
|
8
|
+
s.summary = "Keep a fresh copy of any method in your cache"
|
9
|
+
s.homepage = "https://github.com/martinzamuner/cache_keeper"
|
10
|
+
s.license = "MIT"
|
11
|
+
|
12
|
+
s.required_ruby_version = ">= 2.5.0"
|
13
|
+
|
14
|
+
s.add_dependency "rails", ">= 6.1.0"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CacheKeeper::Caching
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
def self.caches(*method_names, **options)
|
6
|
+
method_names.each do |method_name|
|
7
|
+
CacheKeeper.manager.handle self, method_name, options
|
8
|
+
|
9
|
+
# If the method is already defined, we need to hook it
|
10
|
+
method_added method_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.method_added(method_name)
|
15
|
+
super
|
16
|
+
|
17
|
+
CacheKeeper.manager.activate_if_handling self, method_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CacheKeeper::Configuration
|
2
|
+
DEFAULT_MUST_REVALIDATE = false
|
3
|
+
DEFAULT_QUEUES = {}
|
4
|
+
|
5
|
+
def must_revalidate
|
6
|
+
return rails_config[:must_revalidate] unless rails_config[:must_revalidate].nil?
|
7
|
+
|
8
|
+
DEFAULT_MUST_REVALIDATE
|
9
|
+
end
|
10
|
+
|
11
|
+
def queues
|
12
|
+
rails_config[:queues] || DEFAULT_QUEUES
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def rails_config
|
18
|
+
Rails.application.config.cache_keeper
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CacheKeeper::Engine < ::Rails::Engine
|
2
|
+
isolate_namespace CacheKeeper
|
3
|
+
|
4
|
+
config.cache_keeper = ActiveSupport::OrderedOptions.new
|
5
|
+
config.cache_keeper.queues = ActiveSupport::InheritableOptions.new
|
6
|
+
|
7
|
+
initializer "cache_keeper.active_job_serializer" do |app|
|
8
|
+
config.to_prepare do
|
9
|
+
Rails.application.config.active_job.custom_serializers << CacheKeeper::CachedMethodSerializer
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer "cache_keeper.caching_methods" do |app|
|
14
|
+
ActiveSupport.on_load :action_controller do
|
15
|
+
ActionController::Base.send :include, CacheKeeper::Caching
|
16
|
+
end
|
17
|
+
|
18
|
+
ActiveSupport.on_load :active_record do
|
19
|
+
include CacheKeeper::Caching
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class CacheKeeper::Manager
|
2
|
+
attr_accessor :cached_methods
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
self.cached_methods = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def find(klass, method_name)
|
9
|
+
cached_methods.find do |cached_method|
|
10
|
+
cached_method.klass == klass && cached_method.method_name == method_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def handled?(klass, method_name)
|
15
|
+
find(klass, method_name).present?
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle(klass, method_name, options)
|
19
|
+
CacheKeeper::CachedMethod.new(klass, method_name, options).tap do |cached_method|
|
20
|
+
cached_methods << cached_method
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def activate_if_handling(klass, method_name)
|
25
|
+
cached_method = find(klass, method_name) or return
|
26
|
+
|
27
|
+
return unless requires_activation?(cached_method)
|
28
|
+
|
29
|
+
CacheKeeper::ReplaceMethod.replace(cached_method) do
|
30
|
+
cached_method.call(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def requires_activation?(cached_method)
|
37
|
+
return false if cached_method.klass.instance_methods.exclude?(cached_method.method_name) && cached_method.klass.private_instance_methods.exclude?(cached_method.method_name)
|
38
|
+
return false if cached_method.klass.private_instance_methods.include?(cached_method.alias_for_original_method)
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
end
|