cache_stache 0.1.1 → 0.2.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: 6d78eea9e2e19c6f95024644eaea2dc942993225c42ca636473e19e46fd6f449
4
- data.tar.gz: 6156b5fe4adc1756b1ebf1fe92e413ab16bba3589557e26d17efe3ef2fa18dbf
3
+ metadata.gz: d75f242cecde072c5db6fe680a47fcb3b98165a4d12cf46738d64b0c0956c7ef
4
+ data.tar.gz: 86c3c5b676cb94ea1427cbed332f8fc97b2dac235ce22c031511a8ad1014d8b8
5
5
  SHA512:
6
- metadata.gz: b976afa17c53451ea8b6aa57e6d15d88f097f9fd72bc8bd420b91911f06af686f7f7693eee76f9981fd1644b3abd3694c942c6a1e28e764ec97b4148a8062f47
7
- data.tar.gz: d31764e21a67a0b2006a59593f1b4757f95a731817d3c8cf0d6af802bef0e7afcbc9e5d0b30f1069d77b6606b60a1f62b6f24315ee9fafb8b77e20a6c71a2ff1
6
+ metadata.gz: a4997e8c87c2cb1b974eeac87489b5ad6344adef9dd632d0c1f80429f4aae49edbe76ca4b70557d201a309575f8369a9f361207556c7bb6758630adf6240d174
7
+ data.tar.gz: 623d1edd5b5a304e7839133d9bc4ae9fc9d8f1d67ec76d893df77c6ff506e6c5322b37c657eac5f42d9cbe617f689def250b4974f150f0a0d668348f00766709
data/README.md CHANGED
@@ -63,9 +63,9 @@ All settings go in `config/initializers/cache_stache.rb`:
63
63
 
64
64
  ```ruby
65
65
  CacheStache.configure do |config|
66
- # Redis connection for storing cache metrics
67
- # Falls back to ENV["REDIS_URL"] if not set
68
- config.redis_url = ENV.fetch("CACHE_STACHE_REDIS_URL", ENV["REDIS_URL"])
66
+ # Redis connection for storing cache metrics.
67
+ # Can be a String (URL), Proc, or Redis-compatible object.
68
+ config.redis = ENV.fetch("CACHE_STACHE_REDIS_URL", ENV["REDIS_URL"])
69
69
 
70
70
  # Time bucket size
71
71
  config.bucket_seconds = 5.minutes
@@ -99,7 +99,7 @@ end
99
99
 
100
100
  | Setting | Default | What it does |
101
101
  |---------|---------|--------------|
102
- | `redis_url` | `ENV["CACHE_STACHE_REDIS_URL"]` or `ENV["REDIS_URL"]` | Redis connection URL |
102
+ | `redis` | `ENV["CACHE_STACHE_REDIS_URL"]` or `ENV["REDIS_URL"]` | Redis connection (String URL, Proc, or Redis object) |
103
103
  | `redis_pool_size` | 5 | Size of the Redis connection pool |
104
104
  | `bucket_seconds` | 5 minutes | Size of each time bucket |
105
105
  | `retention_seconds` | 7 days | How long to keep data |
@@ -26,7 +26,7 @@ module CacheStache
26
26
  def initialize(config = CacheStache.configuration)
27
27
  @config = config
28
28
  @pool = ConnectionPool.new(size: @config.redis_pool_size) do
29
- Redis.new(url: @config.redis_url)
29
+ @config.build_redis
30
30
  end
31
31
  end
32
32
 
@@ -5,8 +5,10 @@ require "active_support/core_ext/numeric/time"
5
5
 
6
6
  module CacheStache
7
7
  class Configuration
8
+ DEFAULT_REDIS_OPTIONS = {reconnect_attempts: 0}.freeze
9
+
8
10
  attr_accessor :bucket_seconds, :retention_seconds, :sample_rate, :enabled,
9
- :redis_url, :redis_pool_size, :use_rack_after_reply, :max_buckets
11
+ :redis, :redis_pool_size, :use_rack_after_reply, :max_buckets
10
12
  attr_reader :keyspaces
11
13
 
12
14
  def initialize
@@ -15,13 +17,33 @@ module CacheStache
15
17
  @sample_rate = 1.0
16
18
  @enabled = rails_env != "test"
17
19
  @use_rack_after_reply = false
18
- @redis_url = ENV.fetch("CACHE_STACHE_REDIS_URL") { ENV.fetch("REDIS_URL", "redis://localhost:6379/0") }
20
+ @redis = ENV.fetch("CACHE_STACHE_REDIS_URL") { ENV.fetch("REDIS_URL", "redis://localhost:6379/0") }
19
21
  @redis_pool_size = 5
20
22
  @max_buckets = 288
21
23
  @keyspaces = []
22
24
  @keyspace_cache = {}
23
25
  end
24
26
 
27
+ # Factory method to create a new Redis instance.
28
+ #
29
+ # Handles three options:
30
+ #
31
+ # Option Class Result
32
+ # :redis Proc -> redis.call
33
+ # :redis String -> Redis.new(url: redis)
34
+ # :redis Object -> redis (assumed to be a Redis-compatible client)
35
+ #
36
+ def build_redis
37
+ case redis
38
+ when Proc
39
+ redis.call
40
+ when String
41
+ ::Redis.new(DEFAULT_REDIS_OPTIONS.merge(url: redis))
42
+ else
43
+ redis
44
+ end
45
+ end
46
+
25
47
  def keyspace(name, &block)
26
48
  ks = Keyspace.new(name)
27
49
  builder = KeyspaceBuilder.new(ks)
@@ -43,8 +65,9 @@ module CacheStache
43
65
  def validate!
44
66
  raise Error, "bucket_seconds must be positive" unless bucket_seconds.to_i.positive?
45
67
  raise Error, "retention_seconds must be positive" unless retention_seconds.to_i.positive?
68
+ raise Error, "redis must be configured" if redis.nil?
69
+ raise Error, "redis must be a Proc, String (URL), or Redis-compatible object" unless valid_redis_option?
46
70
  raise Error, "redis_pool_size must be positive" unless redis_pool_size.to_i.positive?
47
- raise Error, "redis_url must be configured" if redis_url.to_s.strip.empty?
48
71
  raise Error, "sample_rate must be between 0 and 1" unless sample_rate&.between?(0, 1)
49
72
  raise Error, "max_buckets must be positive" unless max_buckets.to_i.positive?
50
73
 
@@ -64,6 +87,12 @@ module CacheStache
64
87
 
65
88
  private
66
89
 
90
+ def valid_redis_option?
91
+ return true if redis.is_a?(Proc)
92
+ return redis.to_s.strip.length > 0 if redis.is_a?(String)
93
+ true # Assume other objects are Redis-compatible clients
94
+ end
95
+
67
96
  def key_digest(key)
68
97
  # Use last 4 chars of a simple hash as cache key
69
98
  Digest::MD5.hexdigest(key.to_s)[-4..]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CacheStache
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -4,9 +4,15 @@
4
4
  # This file configures the CacheStache cache hit rate monitoring system.
5
5
 
6
6
  CacheStache.configure do |config|
7
- # Redis connection for storing cache metrics
8
- # Falls back to ENV["REDIS_URL"] if not set
9
- config.redis_url = ENV.fetch("CACHE_STACHE_REDIS_URL", ENV["REDIS_URL"])
7
+ # Redis connection for storing cache metrics.
8
+ # Can be a String (URL), Proc, or Redis-compatible object.
9
+ #
10
+ # Examples:
11
+ # config.redis = "redis://localhost:6379/0"
12
+ # config.redis = -> { Redis.new(url: ENV["REDIS_URL"]) }
13
+ # config.redis = ConnectionPool.new { Redis.new }
14
+ #
15
+ config.redis = ENV.fetch("CACHE_STACHE_REDIS_URL", ENV["REDIS_URL"])
10
16
 
11
17
  # Size of time buckets for aggregation (default: 5 minutes)
12
18
  config.bucket_seconds = 5.minutes
@@ -50,7 +50,7 @@ module CacheStacheTestHelpers
50
50
  # Build a test configuration with common defaults
51
51
  def build_test_config(keyspaces: {}, **options)
52
52
  CacheStache::Configuration.new.tap do |c|
53
- c.redis_url = CACHE_STACHE_TEST_REDIS_URL
53
+ c.redis = CACHE_STACHE_TEST_REDIS_URL
54
54
  c.bucket_seconds = options.fetch(:bucket_seconds, 300)
55
55
  c.retention_seconds = options.fetch(:retention_seconds, 3600)
56
56
  c.sample_rate = options.fetch(:sample_rate, 1.0)
@@ -87,7 +87,7 @@ RSpec.configure do |config|
87
87
  config.before do
88
88
  # Configure CacheStache to use test Redis
89
89
  CacheStache.configure do |c|
90
- c.redis_url = CACHE_STACHE_TEST_REDIS_URL
90
+ c.redis = CACHE_STACHE_TEST_REDIS_URL
91
91
  c.redis_pool_size = 1
92
92
  c.enabled = true
93
93
  end
@@ -19,11 +19,38 @@ RSpec.describe CacheStache::Configuration do
19
19
  it { expect(config.sample_rate).to eq(1.0) }
20
20
  it { expect(config.enabled).to be(true) }
21
21
  it { expect(config.use_rack_after_reply).to be(false) }
22
- it { expect(config.redis_url).to eq(default_redis_url) }
22
+ it { expect(config.redis).to eq(default_redis_url) }
23
23
  it { expect(config.redis_pool_size).to eq(5) }
24
24
  it { expect(config.keyspaces).to eq([]) }
25
25
  end
26
26
 
27
+ describe "#build_redis" do
28
+ it "calls the proc when redis is a Proc" do
29
+ redis_instance = instance_double(Redis)
30
+ config.redis = -> { redis_instance }
31
+
32
+ expect(config.build_redis).to eq(redis_instance)
33
+ end
34
+
35
+ it "creates a Redis instance when redis is a String URL" do
36
+ config.redis = "redis://localhost:6379/1"
37
+
38
+ expect(Redis).to receive(:new).with(
39
+ hash_including(url: "redis://localhost:6379/1")
40
+ ).and_call_original
41
+
42
+ result = config.build_redis
43
+ expect(result).to be_a(Redis)
44
+ end
45
+
46
+ it "returns the object directly when redis is an Object" do
47
+ redis_instance = instance_double(Redis)
48
+ config.redis = redis_instance
49
+
50
+ expect(config.build_redis).to eq(redis_instance)
51
+ end
52
+ end
53
+
27
54
  describe "#keyspace" do
28
55
  it "adds a keyspace with the given name" do
29
56
  config.keyspace(:views) do
@@ -135,12 +162,27 @@ RSpec.describe CacheStache::Configuration do
135
162
 
136
163
  describe "#validate!" do
137
164
  before do
138
- config.redis_url = "redis://localhost:6379/0"
165
+ config.redis = "redis://localhost:6379/0"
166
+ end
167
+
168
+ it "requires redis" do
169
+ config.redis = nil
170
+ expect { config.validate! }.to raise_error(CacheStache::Error, /redis must be configured/)
171
+ end
172
+
173
+ it "rejects empty string for redis" do
174
+ config.redis = " "
175
+ expect { config.validate! }.to raise_error(CacheStache::Error, /redis must be a Proc, String/)
139
176
  end
140
177
 
141
- it "requires redis_url" do
142
- config.redis_url = nil
143
- expect { config.validate! }.to raise_error(CacheStache::Error, /redis_url must be configured/)
178
+ it "accepts a Proc for redis" do
179
+ config.redis = -> { Redis.new }
180
+ expect { config.validate! }.not_to raise_error
181
+ end
182
+
183
+ it "accepts an Object for redis" do
184
+ config.redis = instance_double(Redis)
185
+ expect { config.validate! }.not_to raise_error
144
186
  end
145
187
 
146
188
  it "requires redis_pool_size to be positive" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_stache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - CacheStache contributors