thermos 2.0.0 → 2.0.1
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 +4 -4
- data/README.md +213 -0
- data/lib/thermos/version.rb +1 -1
- metadata +15 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 96c092c9b446b9147dca86d7f6b655dcb8f6f00191a8260b0dbb6bd15bea56c1
|
|
4
|
+
data.tar.gz: e7b19005dffe9c4fbec290576e0d9094fb1d04021fd037e3148dc7bb57d99179
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5484c23bf0c7ff96d75158c37bc399cf06a45a25447347c2a56ffc0bc8d026742042655019dbfb3518fbb3ea9765431963db334973692e3ad4f9cf272c78710e
|
|
7
|
+
data.tar.gz: 39f11866f8de933f2e6f68174f43d4c4b88fc48b25c472bee5799447cdcc6d91c568eb31173bf004a41cccf0d0b882cd0368d299287b6487d2c6f7d99bccac8c
|
data/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Thermos
|
|
2
|
+
|
|
3
|
+
**Always-warm Rails caching that automatically rebuilds when your models change.**
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/rb/thermos)
|
|
6
|
+
[](https://codeclimate.com/github/athal7/thermos)
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
Thermos is a Rails caching library that keeps your cache always warm by rebuilding it in the background whenever ActiveRecord models change. No more stale data, no more cold cache penalties, no more `touch: true` on all your associations.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Always-warm cache** — Cache is rebuilt in the background when models change
|
|
14
|
+
- **No stale data** — Unlike TTL-based caching, data is only as stale as your job queue latency
|
|
15
|
+
- **No cold cache penalties** — Cache is pre-warmed, so users never wait for expensive queries
|
|
16
|
+
- **No `touch` callbacks needed** — Thermos watches model dependencies automatically
|
|
17
|
+
- **Works with any backend** — Sidekiq, Solid Queue, Resque, or any ActiveJob adapter
|
|
18
|
+
- **Works with any cache store** — Redis, Memcached, Solid Cache, or any Rails cache store
|
|
19
|
+
- **ETag support** — Works seamlessly with HTTP caching for browser and CDN caching
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
Add to your Gemfile:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem 'thermos'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then run:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bundle install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# In a controller - cache is automatically rebuilt when Category or its products change
|
|
39
|
+
json = Thermos.keep_warm(key: "category", model: Category, id: params[:id], deps: [:products]) do |id|
|
|
40
|
+
Category.includes(:products).find(id).to_json
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
render json: json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That's it! When any `Category` or associated `Product` is created, updated, or destroyed, Thermos automatically rebuilds the cache in the background.
|
|
47
|
+
|
|
48
|
+
## Why Thermos?
|
|
49
|
+
|
|
50
|
+
Most cache strategies have significant downsides:
|
|
51
|
+
|
|
52
|
+
| Strategy | Problem |
|
|
53
|
+
|----------|---------|
|
|
54
|
+
| **TTL-based** (expires_in) | Stale data until expiration |
|
|
55
|
+
| **Key-based** (cache_key) | Cold cache on first request after any change |
|
|
56
|
+
| **Touch callbacks** | Extra database writes on every association change |
|
|
57
|
+
|
|
58
|
+
Thermos solves all of these by rebuilding caches proactively in background jobs.
|
|
59
|
+
|
|
60
|
+
> "I just want to Thermos everything now!! Unbelievable improvement. It's like every dev's dream come true" — [@jono-booth](https://github.com/jono-booth)
|
|
61
|
+
|
|
62
|
+
## Prerequisites
|
|
63
|
+
|
|
64
|
+
Configure a [Rails Cache Store](https://guides.rubyonrails.org/caching_with_rails.html#configuration) that supports shared access across processes (Redis, Memcached, Solid Cache — not MemoryStore).
|
|
65
|
+
|
|
66
|
+
Thermos works with any ActiveJob adapter, including Rails 8's [Solid Queue](https://github.com/rails/solid_queue) and [Solid Cache](https://github.com/rails/solid_cache).
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
### keep_warm (Simple)
|
|
71
|
+
|
|
72
|
+
With `keep_warm`, the cached content is defined along with the cache block and dependencies definition. This is the simplest implementation, *but is only compatible with the [Active Job Inline Adapter](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html)*. See the next section about fill/drink for compatibility with other Active Job Adapters.
|
|
73
|
+
|
|
74
|
+
*API Controller*
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
json = Thermos.keep_warm(key: "api_categories_show", model: Category, id: params[:id], deps: [:category_items, :products]) do |id|
|
|
78
|
+
Category.find(id).to_json
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
render json: json
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
*Frontend Controller*
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
rendered_template = Thermos.keep_warm(key: "frontend_categories_show", model: Category, id: params[:id], deps: [:category_items, :products]) do |id|
|
|
88
|
+
@category = Category.includes(category_items: :product).find(id)
|
|
89
|
+
render_to_string :show
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
render rendered_template
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### fill / drink (Advanced)
|
|
96
|
+
|
|
97
|
+
For more control, define your cache once with `fill` and read it anywhere with `drink`. This is ideal for sharing cached data across multiple controllers or when using background job adapters other than inline.
|
|
98
|
+
|
|
99
|
+
*Rails Initializer*
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
Thermos.fill(key: "api_categories_show", model: Category, deps: [:category_items, :products]) do |id|
|
|
103
|
+
Category.find(id).to_json
|
|
104
|
+
end
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
*API Controller*
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
json = Thermos.drink(key: "api_categories_show", id: params[:id])
|
|
111
|
+
render json: json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Options
|
|
115
|
+
|
|
116
|
+
### lookup_key
|
|
117
|
+
|
|
118
|
+
If you want to be able to lookup by a key other than `id` (e.g. you use a slug in the params), you can specify the `lookup_key` as an argument to `keep_warm` or `fill`:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
Thermos.keep_warm(key: "api_categories_show", model: Category, id: params[:slug], lookup_key: :slug) do |slug|
|
|
122
|
+
Category.find_by(slug: slug).to_json
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
or
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
Thermos.fill(key: "api_categories_show", model: Category, lookup_key: :slug) do |slug|
|
|
130
|
+
Category.find_by(slug: slug).to_json
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### queue
|
|
135
|
+
|
|
136
|
+
If you want to specify a queue for the refill jobs to run other than the default queue, you can provide it to either way of using Thermos:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
Thermos.keep_warm(key: "api_categories_show", model: Category, queue: "low_priority") do |id|
|
|
140
|
+
Category.find(id).to_json
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
or
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
Thermos.fill(key: "api_categories_show", model: Category, queue: "low_priority") do |id|
|
|
148
|
+
Category.find(id).to_json
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
Thermos.drink(key: "api_categories_show", id: params[:slug])
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Indirect Relationships
|
|
155
|
+
|
|
156
|
+
You can specify indirect relationships as dependencies as well. For example, if `Store has_many categories`, and `Category has_many products`, but there is no relationship specified on the `Store` model to `Product`:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
Thermos.keep_warm(key: "api_stores_show", model: Store, id: params[:id], deps: [categories: [:products]]) do |id|
|
|
160
|
+
Store.find(id).to_json
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
*NOTE* in this example, a change to any model in the association chain will trigger a refill of the cache.
|
|
165
|
+
|
|
166
|
+
### filter
|
|
167
|
+
|
|
168
|
+
You can provide a filter to restrict whether a record gets rebuilt on model changes:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
filter = ->(model) { model.name.match("ball") }
|
|
172
|
+
Thermos.keep_warm(key: "api_categories_show", model: Category, id: params[:id], filter: filter) do |id|
|
|
173
|
+
Category.find(id).to_json
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Using with ETags
|
|
178
|
+
|
|
179
|
+
Thermos works seamlessly with Rails' HTTP caching via ETags, enabling browser and CDN caching of your responses. Since Thermos keeps your cache always warm and rebuilds it when models change, the cached value's digest will naturally change when the underlying data changes.
|
|
180
|
+
|
|
181
|
+
Use Rails' `stale?` helper with the cached value to enable conditional GET requests:
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
def show
|
|
185
|
+
json = Thermos.drink(key: "api_categories_show", id: params[:id])
|
|
186
|
+
|
|
187
|
+
if stale?(etag: json)
|
|
188
|
+
render json: json
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
When the cached value changes (triggered by model updates), the ETag will change, and clients will receive the new content. When the value hasn't changed, clients with a matching ETag will receive a `304 Not Modified` response.
|
|
194
|
+
|
|
195
|
+
This enables caching at multiple layers:
|
|
196
|
+
- **Browser cache**: Browsers store responses and revalidate with the server using the ETag, avoiding re-downloads of unchanged content
|
|
197
|
+
- **CDN cache**: CDNs can cache responses and serve them directly to users, only revalidating with your server when needed
|
|
198
|
+
|
|
199
|
+
Combined with Thermos, you get:
|
|
200
|
+
- **Always-warm application cache** (no cold cache penalties)
|
|
201
|
+
- **Reduced server load** (304 responses skip rendering)
|
|
202
|
+
- **Reduced bandwidth** (browsers and CDNs serve cached content)
|
|
203
|
+
- **Faster responses** (CDN edge locations serve content closer to users)
|
|
204
|
+
|
|
205
|
+
## Contributors
|
|
206
|
+
|
|
207
|
+
<a href="https://github.com/athal7/thermos/graphs/contributors">
|
|
208
|
+
<img src="https://contrib.rocks/image?repo=athal7/thermos" />
|
|
209
|
+
</a>
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
This project uses MIT-LICENSE.
|
data/lib/thermos/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thermos
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Thal
|
|
@@ -87,8 +87,12 @@ dependencies:
|
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '0'
|
|
89
89
|
description: |
|
|
90
|
-
Thermos is a library
|
|
91
|
-
in the background
|
|
90
|
+
Thermos is a Rails caching library that keeps your cache always warm by
|
|
91
|
+
automatically rebuilding it in the background when ActiveRecord models change.
|
|
92
|
+
No more stale data from TTL expiration, no more slow cold cache hits, and no
|
|
93
|
+
need to 'touch' associated models. Works with any ActiveJob backend (Sidekiq,
|
|
94
|
+
Solid Queue, etc.) and any cache store (Redis, Memcached, Solid Cache, etc.).
|
|
95
|
+
Perfect for API responses, JSON serialization, and view caching.
|
|
92
96
|
email:
|
|
93
97
|
- athal7@me.com
|
|
94
98
|
executables: []
|
|
@@ -96,6 +100,7 @@ extensions: []
|
|
|
96
100
|
extra_rdoc_files: []
|
|
97
101
|
files:
|
|
98
102
|
- MIT-LICENSE
|
|
103
|
+
- README.md
|
|
99
104
|
- Rakefile
|
|
100
105
|
- lib/thermos.rb
|
|
101
106
|
- lib/thermos/beverage.rb
|
|
@@ -160,7 +165,11 @@ files:
|
|
|
160
165
|
homepage: https://github.com/athal7/thermos
|
|
161
166
|
licenses:
|
|
162
167
|
- MIT
|
|
163
|
-
metadata:
|
|
168
|
+
metadata:
|
|
169
|
+
homepage_uri: https://github.com/athal7/thermos
|
|
170
|
+
source_code_uri: https://github.com/athal7/thermos
|
|
171
|
+
changelog_uri: https://github.com/athal7/thermos/releases
|
|
172
|
+
rubygems_mfa_required: 'true'
|
|
164
173
|
post_install_message:
|
|
165
174
|
rdoc_options: []
|
|
166
175
|
require_paths:
|
|
@@ -179,7 +188,8 @@ requirements: []
|
|
|
179
188
|
rubygems_version: 3.5.22
|
|
180
189
|
signing_key:
|
|
181
190
|
specification_version: 4
|
|
182
|
-
summary: Always-warm, auto-rebuilding
|
|
191
|
+
summary: Always-warm, auto-rebuilding Rails cache that updates in the background when
|
|
192
|
+
models change.
|
|
183
193
|
test_files:
|
|
184
194
|
- test/dependencies_test.rb
|
|
185
195
|
- test/dummy/README.rdoc
|