cache_stache 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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +231 -0
  3. data/app/assets/stylesheets/cache_stache/application.css +5 -0
  4. data/app/assets/stylesheets/cache_stache/pico.css +4 -0
  5. data/app/controllers/cache_stache/application_controller.rb +11 -0
  6. data/app/controllers/cache_stache/dashboard_controller.rb +32 -0
  7. data/app/helpers/cache_stache/application_helper.rb +37 -0
  8. data/app/views/cache_stache/dashboard/index.html.erb +154 -0
  9. data/app/views/cache_stache/dashboard/keyspace.html.erb +83 -0
  10. data/app/views/layouts/cache_stache/application.html.erb +14 -0
  11. data/config/routes.rb +6 -0
  12. data/lib/cache_stache/cache_client.rb +202 -0
  13. data/lib/cache_stache/configuration.rb +87 -0
  14. data/lib/cache_stache/engine.rb +17 -0
  15. data/lib/cache_stache/instrumentation.rb +142 -0
  16. data/lib/cache_stache/keyspace.rb +28 -0
  17. data/lib/cache_stache/rack_after_reply_middleware.rb +22 -0
  18. data/lib/cache_stache/railtie.rb +30 -0
  19. data/lib/cache_stache/stats_query.rb +89 -0
  20. data/lib/cache_stache/version.rb +5 -0
  21. data/lib/cache_stache/web.rb +69 -0
  22. data/lib/cache_stache/window_options.rb +34 -0
  23. data/lib/cache_stache.rb +37 -0
  24. data/lib/generators/cache_stache/install_generator.rb +21 -0
  25. data/lib/generators/cache_stache/templates/README +35 -0
  26. data/lib/generators/cache_stache/templates/cache_stache.rb +43 -0
  27. data/spec/cache_stache_helper.rb +148 -0
  28. data/spec/dummy_app/Rakefile +5 -0
  29. data/spec/dummy_app/app/assets/config/manifest.js +1 -0
  30. data/spec/dummy_app/config/application.rb +31 -0
  31. data/spec/dummy_app/config/boot.rb +3 -0
  32. data/spec/dummy_app/config/environment.rb +5 -0
  33. data/spec/dummy_app/config/routes.rb +7 -0
  34. data/spec/integration/dashboard_controller_spec.rb +94 -0
  35. data/spec/integration/full_cache_flow_spec.rb +202 -0
  36. data/spec/integration/instrumentation_spec.rb +259 -0
  37. data/spec/integration/rack_after_reply_spec.rb +47 -0
  38. data/spec/integration/rake_tasks_spec.rb +17 -0
  39. data/spec/spec_helper.rb +64 -0
  40. data/spec/unit/cache_client_spec.rb +278 -0
  41. data/spec/unit/configuration_spec.rb +209 -0
  42. data/spec/unit/keyspace_spec.rb +93 -0
  43. data/spec/unit/stats_query_spec.rb +367 -0
  44. data/tasks/cache_stache.rake +74 -0
  45. metadata +226 -0
@@ -0,0 +1,367 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../cache_stache_helper"
4
+
5
+ RSpec.describe CacheStache::StatsQuery do
6
+ include ActiveSupport::Testing::TimeHelpers
7
+
8
+ subject(:query) { described_class.new(window: window) }
9
+
10
+ let(:window) { 1.hour }
11
+ let(:config) do
12
+ build_test_config(
13
+ keyspaces: {
14
+ views: {label: "View Fragments", match: /^views\//},
15
+ models: {label: "Model Cache", match: /model/}
16
+ }
17
+ )
18
+ end
19
+ let(:cache_client) { CacheStache::CacheClient.new(config) }
20
+
21
+ before do
22
+ allow(CacheStache).to receive(:configuration).and_return(config)
23
+ end
24
+
25
+ describe "#initialize" do
26
+ it { expect(query.window).to eq(3600) }
27
+
28
+ it "accepts resolution parameter" do
29
+ q = described_class.new(window: 1.hour, resolution: 12)
30
+ expect(q.resolution).to eq(12)
31
+ end
32
+ end
33
+
34
+ describe "#execute" do
35
+ let(:base_time) { Time.current }
36
+
37
+ before do
38
+ # Freeze time and create test data
39
+ travel_to base_time
40
+
41
+ # Create buckets with stats
42
+ bucket_ts = (base_time.to_i / 300) * 300
43
+
44
+ # Recent bucket (within window)
45
+ cache_client.increment_stats(bucket_ts, {
46
+ "overall:hits" => 10,
47
+ "overall:misses" => 5,
48
+ "views:hits" => 8,
49
+ "views:misses" => 2,
50
+ "models:hits" => 2,
51
+ "models:misses" => 3
52
+ })
53
+
54
+ # Another recent bucket
55
+ cache_client.increment_stats(bucket_ts - 300, {
56
+ "overall:hits" => 15,
57
+ "overall:misses" => 10,
58
+ "views:hits" => 12,
59
+ "views:misses" => 8,
60
+ "models:hits" => 3,
61
+ "models:misses" => 2
62
+ })
63
+
64
+ # Old bucket (outside window)
65
+ cache_client.increment_stats(bucket_ts - 7200, {
66
+ "overall:hits" => 100,
67
+ "overall:misses" => 50
68
+ })
69
+ end
70
+
71
+ after do
72
+ travel_back
73
+ end
74
+
75
+ it "returns overall statistics" do
76
+ results = query.execute
77
+
78
+ expect(results[:overall][:hits]).to eq(25)
79
+ expect(results[:overall][:misses]).to eq(15)
80
+ expect(results[:overall][:total_operations]).to eq(40)
81
+ expect(results[:overall][:hit_rate_percent]).to eq(62.5)
82
+ end
83
+
84
+ it "returns keyspace statistics" do
85
+ results = query.execute
86
+
87
+ expect(results[:keyspaces][:views]).to include(
88
+ label: "View Fragments",
89
+ pattern: /^views\//,
90
+ hits: 20,
91
+ misses: 10,
92
+ total_operations: 30,
93
+ hit_rate_percent: 66.67
94
+ )
95
+
96
+ expect(results[:keyspaces][:models]).to include(
97
+ label: "Model Cache",
98
+ pattern: /model/,
99
+ hits: 5,
100
+ misses: 5,
101
+ total_operations: 10,
102
+ hit_rate_percent: 50.0
103
+ )
104
+ end
105
+
106
+ it "includes bucket details" do
107
+ results = query.execute
108
+
109
+ expect(results[:buckets].size).to eq(2)
110
+ expect(results[:buckets][0]).to include(:timestamp, :time, :stats)
111
+ expect(results[:buckets][0][:stats]).to include("overall:hits", "overall:misses")
112
+ end
113
+
114
+ it "includes metadata" do
115
+ results = query.execute
116
+
117
+ expect(results[:window_seconds]).to eq(3600)
118
+ expect(results[:bucket_count]).to eq(2)
119
+ end
120
+
121
+ it "excludes buckets outside the window" do
122
+ results = query.execute
123
+
124
+ total_hits = results[:overall][:hits]
125
+ # Should not include the 100 hits from old bucket
126
+ expect(total_hits).to eq(25)
127
+ end
128
+
129
+ context "with no data" do
130
+ before do
131
+ flush_cache_stache_redis
132
+ end
133
+
134
+ it "returns zeros for overall stats" do
135
+ results = query.execute
136
+
137
+ expect(results[:overall]).to eq(
138
+ hits: 0,
139
+ misses: 0,
140
+ total_operations: 0,
141
+ hit_rate_percent: 0.0
142
+ )
143
+ end
144
+
145
+ it "returns zeros for keyspace stats" do
146
+ results = query.execute
147
+
148
+ expect(results[:keyspaces][:views][:total_operations]).to eq(0)
149
+ expect(results[:keyspaces][:views][:hit_rate_percent]).to eq(0.0)
150
+ end
151
+
152
+ it "has zero buckets" do
153
+ results = query.execute
154
+ expect(results[:bucket_count]).to eq(0)
155
+ end
156
+ end
157
+
158
+ context "with only hits" do
159
+ before do
160
+ flush_cache_stache_redis
161
+ bucket_ts = (base_time.to_i / 300) * 300
162
+ cache_client.increment_stats(bucket_ts, {
163
+ "overall:hits" => 20,
164
+ "overall:misses" => 0
165
+ })
166
+ end
167
+
168
+ it "calculates 100% hit rate" do
169
+ results = query.execute
170
+ expect(results[:overall][:hit_rate_percent]).to eq(100.0)
171
+ end
172
+ end
173
+
174
+ context "with only misses" do
175
+ before do
176
+ flush_cache_stache_redis
177
+ bucket_ts = (base_time.to_i / 300) * 300
178
+ cache_client.increment_stats(bucket_ts, {
179
+ "overall:hits" => 0,
180
+ "overall:misses" => 20
181
+ })
182
+ end
183
+
184
+ it "calculates 0% hit rate" do
185
+ results = query.execute
186
+ expect(results[:overall][:hit_rate_percent]).to eq(0.0)
187
+ end
188
+ end
189
+
190
+ context "with different window sizes" do
191
+ it "respects custom window parameter" do
192
+ short_query = described_class.new(window: 5.minutes)
193
+ results = short_query.execute
194
+
195
+ # Should only include most recent bucket
196
+ expect(results[:bucket_count]).to be <= 2
197
+ end
198
+
199
+ it "works with long windows" do
200
+ long_query = described_class.new(window: 7.days)
201
+ results = long_query.execute
202
+
203
+ expect(results[:window_seconds]).to eq(7.days.to_i)
204
+ end
205
+
206
+ it "truncates to max_buckets for very long windows" do
207
+ # With 5-minute buckets, 7 days would be 2,016 buckets
208
+ # but max_buckets defaults to 288, so we expect truncation
209
+ long_query = described_class.new(window: 7.days)
210
+
211
+ max_buckets = CacheStache.configuration.max_buckets
212
+
213
+ # Expect a warning to be logged when truncation occurs
214
+ expect(Rails.logger).to receive(:warn).with(
215
+ /CacheStache: Truncating bucket range from \d+ to #{max_buckets} buckets/
216
+ )
217
+
218
+ results = long_query.execute
219
+
220
+ # Should be capped at max_buckets (default: 288)
221
+ expect(results[:bucket_count]).to be <= max_buckets
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "#execute with default labels" do
227
+ let(:config) do
228
+ build_test_config(
229
+ keyspaces: {
230
+ profiles: {match: /^profile:/}
231
+ }
232
+ )
233
+ end
234
+
235
+ it "returns humanized labels when none are provided" do
236
+ bucket_ts = (Time.current.to_i / config.bucket_seconds) * config.bucket_seconds
237
+ cache_client.increment_stats(bucket_ts, {
238
+ "overall:hits" => 1,
239
+ "overall:misses" => 0,
240
+ "profiles:hits" => 1,
241
+ "profiles:misses" => 0
242
+ })
243
+
244
+ results = query.execute
245
+
246
+ expect(results[:keyspaces][:profiles][:label]).to eq("Profiles")
247
+ end
248
+ end
249
+
250
+ describe "zero-omission optimization" do
251
+ let(:base_time) { Time.current }
252
+
253
+ before do
254
+ travel_to base_time
255
+ end
256
+
257
+ after do
258
+ travel_back
259
+ end
260
+
261
+ it "treats missing stat fields as 0 when aggregating across buckets" do
262
+ bucket_ts = (base_time.to_i / 300) * 300
263
+
264
+ # Bucket 1: only has hits for overall and views
265
+ cache_client.increment_stats(bucket_ts, {
266
+ "overall:hits" => 5,
267
+ "views:hits" => 8
268
+ })
269
+
270
+ # Bucket 2: only has misses for overall and models
271
+ cache_client.increment_stats(bucket_ts - 300, {
272
+ "overall:misses" => 3,
273
+ "models:misses" => 2
274
+ })
275
+
276
+ results = query.execute
277
+
278
+ # Overall should aggregate: hits=5 (0 from bucket2), misses=3 (0 from bucket1)
279
+ expect(results[:overall][:hits]).to eq(5)
280
+ expect(results[:overall][:misses]).to eq(3)
281
+ expect(results[:overall][:total_operations]).to eq(8)
282
+ expect(results[:overall][:hit_rate_percent]).to eq(62.5)
283
+
284
+ # Views should aggregate: hits=8 (0 from bucket2), misses=0 (0 from bucket2)
285
+ expect(results[:keyspaces][:views][:hits]).to eq(8)
286
+ expect(results[:keyspaces][:views][:misses]).to eq(0)
287
+ expect(results[:keyspaces][:views][:total_operations]).to eq(8)
288
+ expect(results[:keyspaces][:views][:hit_rate_percent]).to eq(100.0)
289
+
290
+ # Models should aggregate: hits=0 (0 from bucket1), misses=2 (0 from bucket1)
291
+ expect(results[:keyspaces][:models][:hits]).to eq(0)
292
+ expect(results[:keyspaces][:models][:misses]).to eq(2)
293
+ expect(results[:keyspaces][:models][:total_operations]).to eq(2)
294
+ expect(results[:keyspaces][:models][:hit_rate_percent]).to eq(0.0)
295
+ end
296
+ end
297
+
298
+ describe "private methods" do
299
+ describe "#calculate_overall_stats" do
300
+ it "aggregates across buckets correctly" do
301
+ buckets = [
302
+ {stats: {"overall:hits" => 5.0, "overall:misses" => 3.0}},
303
+ {stats: {"overall:hits" => 10.0, "overall:misses" => 7.0}}
304
+ ]
305
+
306
+ stats = query.send(:calculate_overall_stats, buckets)
307
+
308
+ expect(stats[:hits]).to eq(15)
309
+ expect(stats[:misses]).to eq(10)
310
+ expect(stats[:total_operations]).to eq(25)
311
+ expect(stats[:hit_rate_percent]).to eq(60.0)
312
+ end
313
+
314
+ it "rounds floats to integers for operations" do
315
+ buckets = [
316
+ {stats: {"overall:hits" => 5.7, "overall:misses" => 3.2}}
317
+ ]
318
+
319
+ stats = query.send(:calculate_overall_stats, buckets)
320
+
321
+ expect(stats[:hits]).to eq(6)
322
+ expect(stats[:misses]).to eq(3)
323
+ end
324
+ end
325
+
326
+ describe "#calculate_keyspace_stats" do
327
+ it "aggregates keyspace stats correctly" do
328
+ views_keyspace = CacheStache::Keyspace.new(:views).tap do |k|
329
+ k.label = "Views"
330
+ k.pattern = /^views\//
331
+ end
332
+ allow(config).to receive(:keyspaces).and_return([views_keyspace])
333
+
334
+ buckets = [
335
+ {stats: {"views:hits" => 8.0, "views:misses" => 2.0}},
336
+ {stats: {"views:hits" => 12.0, "views:misses" => 8.0}}
337
+ ]
338
+
339
+ stats = query.send(:calculate_keyspace_stats, buckets)
340
+
341
+ expect(stats[:views]).to eq(
342
+ label: "Views",
343
+ pattern: /^views\//,
344
+ hits: 20,
345
+ misses: 10,
346
+ total_operations: 30,
347
+ hit_rate_percent: 66.67
348
+ )
349
+ end
350
+ end
351
+
352
+ describe "#format_bucket" do
353
+ it "formats bucket with timestamp and time" do
354
+ bucket = {
355
+ timestamp: 1_700_000_000,
356
+ stats: {"overall:hits" => 10.0}
357
+ }
358
+
359
+ formatted = query.send(:format_bucket, bucket)
360
+
361
+ expect(formatted[:timestamp]).to eq(1_700_000_000)
362
+ expect(formatted[:time]).to eq(Time.at(1_700_000_000).utc)
363
+ expect(formatted[:stats]).to eq({"overall:hits" => 10.0})
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :cache_stache do
4
+ desc "Prune old CacheStache data from Redis"
5
+ task prune: :environment do
6
+ require "redis"
7
+
8
+ config = CacheStache.configuration
9
+ redis = Redis.new(url: config.redis_url)
10
+
11
+ pattern = "cache_stache:v1:#{config.rails_env}:*"
12
+ cutoff_time = Time.current.to_i - config.retention_seconds
13
+
14
+ pruned_count = 0
15
+ redis.scan_each(match: pattern) do |key|
16
+ # Extract timestamp from key
17
+ timestamp = key.split(":").last.to_i
18
+
19
+ if timestamp < cutoff_time
20
+ redis.del(key)
21
+ pruned_count += 1
22
+ end
23
+ end
24
+
25
+ puts "CacheStache: Pruned #{pruned_count} old bucket(s)"
26
+ ensure
27
+ redis&.close
28
+ end
29
+
30
+ desc "Display CacheStache configuration"
31
+ task config: :environment do
32
+ config = CacheStache.configuration
33
+
34
+ puts "CacheStache Configuration:"
35
+ puts " Redis URL: #{config.redis_url}"
36
+ puts " Bucket Size: #{config.bucket_seconds} seconds (#{config.bucket_seconds / 60} minutes)"
37
+ puts " Retention: #{config.retention_seconds} seconds (#{config.retention_seconds / 86400} days)"
38
+ puts " Sample Rate: #{(config.sample_rate * 100).round}%"
39
+ puts " Enabled: #{config.enabled}"
40
+ puts " Environment: #{config.rails_env}"
41
+ puts "\nKeyspaces:"
42
+ if config.keyspaces.any?
43
+ config.keyspaces.each do |ks|
44
+ puts " - #{ks.name}: #{ks.label}"
45
+ end
46
+ else
47
+ puts " (none configured)"
48
+ end
49
+ end
50
+
51
+ desc "Show current CacheStache stats"
52
+ task stats: :environment do
53
+ query = CacheStache::StatsQuery.new(window: 1.hour)
54
+ results = query.execute
55
+
56
+ puts "CacheStache Stats (last hour):"
57
+ puts "\nOverall:"
58
+ puts " Total Operations: #{results[:overall][:total_operations]}"
59
+ puts " Hits: #{results[:overall][:hits]}"
60
+ puts " Misses: #{results[:overall][:misses]}"
61
+ puts " Hit Rate: #{results[:overall][:hit_rate_percent]}%"
62
+
63
+ if results[:keyspaces].any?
64
+ puts "\nKeyspaces:"
65
+ results[:keyspaces].each do |name, stats|
66
+ puts " #{stats[:label]}:"
67
+ puts " Operations: #{stats[:total_operations]}"
68
+ puts " Hit Rate: #{stats[:hit_rate_percent]}%"
69
+ end
70
+ end
71
+
72
+ puts "\nBuckets: #{results[:bucket_count]}"
73
+ end
74
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_stache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - CacheStache contributors
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: connection_pool
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: redis
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: sprockets-rails
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rspec-rails
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rails-controller-testing
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: simplecov
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: standard
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ description: CacheStache tracks cache hit/miss rates and exposes a small Rails engine
153
+ UI.
154
+ executables: []
155
+ extensions: []
156
+ extra_rdoc_files: []
157
+ files:
158
+ - README.md
159
+ - app/assets/stylesheets/cache_stache/application.css
160
+ - app/assets/stylesheets/cache_stache/pico.css
161
+ - app/controllers/cache_stache/application_controller.rb
162
+ - app/controllers/cache_stache/dashboard_controller.rb
163
+ - app/helpers/cache_stache/application_helper.rb
164
+ - app/views/cache_stache/dashboard/index.html.erb
165
+ - app/views/cache_stache/dashboard/keyspace.html.erb
166
+ - app/views/layouts/cache_stache/application.html.erb
167
+ - config/routes.rb
168
+ - lib/cache_stache.rb
169
+ - lib/cache_stache/cache_client.rb
170
+ - lib/cache_stache/configuration.rb
171
+ - lib/cache_stache/engine.rb
172
+ - lib/cache_stache/instrumentation.rb
173
+ - lib/cache_stache/keyspace.rb
174
+ - lib/cache_stache/rack_after_reply_middleware.rb
175
+ - lib/cache_stache/railtie.rb
176
+ - lib/cache_stache/stats_query.rb
177
+ - lib/cache_stache/version.rb
178
+ - lib/cache_stache/web.rb
179
+ - lib/cache_stache/window_options.rb
180
+ - lib/generators/cache_stache/install_generator.rb
181
+ - lib/generators/cache_stache/templates/README
182
+ - lib/generators/cache_stache/templates/cache_stache.rb
183
+ - spec/cache_stache_helper.rb
184
+ - spec/dummy_app/Rakefile
185
+ - spec/dummy_app/app/assets/config/manifest.js
186
+ - spec/dummy_app/config/application.rb
187
+ - spec/dummy_app/config/boot.rb
188
+ - spec/dummy_app/config/environment.rb
189
+ - spec/dummy_app/config/routes.rb
190
+ - spec/integration/dashboard_controller_spec.rb
191
+ - spec/integration/full_cache_flow_spec.rb
192
+ - spec/integration/instrumentation_spec.rb
193
+ - spec/integration/rack_after_reply_spec.rb
194
+ - spec/integration/rake_tasks_spec.rb
195
+ - spec/spec_helper.rb
196
+ - spec/unit/cache_client_spec.rb
197
+ - spec/unit/configuration_spec.rb
198
+ - spec/unit/keyspace_spec.rb
199
+ - spec/unit/stats_query_spec.rb
200
+ - tasks/cache_stache.rake
201
+ homepage: https://github.com/speedshop/cache_stache
202
+ licenses:
203
+ - MIT
204
+ metadata:
205
+ source_code_uri: https://github.com/speedshop/cache_stache
206
+ changelog_uri: https://github.com/speedshop/cache_stache/blob/main/CHANGELOG.md
207
+ bug_tracker_uri: https://github.com/speedshop/cache_stache/issues
208
+ rubygems_mfa_required: 'true'
209
+ rdoc_options: []
210
+ require_paths:
211
+ - lib
212
+ required_ruby_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '3.1'
217
+ required_rubygems_version: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ requirements: []
223
+ rubygems_version: 3.6.9
224
+ specification_version: 4
225
+ summary: CacheStache tracks cache hit rates for Rails apps.
226
+ test_files: []