togglecraft 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6e77c8218c124ce83fbdc25f2f2975f50ca69fde64b3109116e639580ac5be42
4
+ data.tar.gz: 872451734b10596e4b11337275454a998605a7966ff7e9f5d575c4764ff2a89e
5
+ SHA512:
6
+ metadata.gz: 3353a6384c9c2ab564c5244f0c015a723c403287213e6fa6542c0b8cbfbb49bf804d303f3c1d51510c1a07858884b2626a0128a05744d2d0a7e394ad6f1f43b0
7
+ data.tar.gz: 6a31210c7fb15839f8b5bbb2b85b37c13129f00ea26a57796d885331a5cf981e6c4eefaf7ad69fdd882a05a29acb2b9175321784892a750d087ef9deba4c4e96
data/CHANGELOG.md ADDED
@@ -0,0 +1,108 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-10-26
9
+
10
+ ### Added
11
+
12
+ **Core Features:**
13
+ - Real-time feature flag evaluation with Server-Sent Events (SSE)
14
+ - Three flag types: boolean, multivariate, and percentage rollout
15
+ - Advanced targeting rules with 14 operators (equals, contains, regex, semver, etc.)
16
+ - Scheduled rollout stages with automatic percentage transitions
17
+ - Local flag evaluation for zero-latency performance
18
+ - Smart caching with TTL support (memory adapter included)
19
+ - Thread-safe connection pooling for multi-client efficiency
20
+ - Event system for real-time updates (ready, flags_updated, disconnected, error, reconnecting, rollout_stage_changed)
21
+
22
+ **Client Features:**
23
+ - `Client#enabled?` - Boolean flag evaluation
24
+ - `Client#variant` - Multivariate flag evaluation with weighted distribution
25
+ - `Client#in_percentage?` - Percentage rollout evaluation with consistent hashing
26
+ - `Client#wait_for_ready` - Synchronous initialization support
27
+ - `Client#on`/`once`/`off` - Event listener management
28
+ - Automatic reconnection with exponential backoff and hybrid strategy
29
+ - Graceful degradation with default values
30
+
31
+ **Connection Management:**
32
+ - Shared SSE connection pooling (default)
33
+ - Dedicated SSE connections (opt-in)
34
+ - Heartbeat mechanism with jitter (0-30s) to prevent thundering herd
35
+ - Zombie connection prevention (critical for billing accuracy)
36
+ - Automatic reconnection with exponential backoff
37
+ - Hybrid reconnection strategy (fast mode → slow mode)
38
+
39
+ **Developer Experience:**
40
+ - Comprehensive RSpec test suite (375 tests, 87.36% coverage)
41
+ - Full YARD documentation
42
+ - Thread-safe for Puma, Sidekiq, and multi-threaded environments
43
+ - Rails-friendly with initializer examples
44
+ - Zero-build philosophy compatibility
45
+ - Debug logging support
46
+
47
+ **Documentation:**
48
+ - Complete README with quick start guide
49
+ - API reference documentation
50
+ - Advanced configuration guide
51
+ - Framework integration examples (Rails, Sidekiq)
52
+ - Troubleshooting guide
53
+ - Security policy (SECURITY.md)
54
+ - Publishing guide (PUBLISHING.md)
55
+
56
+ ### Technical Highlights
57
+
58
+ - **Zombie Connection Prevention:** Heartbeat only sent when SSE connection is actually open, preventing billing for dead connections
59
+ - **Consistent Hashing:** Same user always gets same variant/percentage result across all SDKs
60
+ - **Jitter Support:** Version update fetching includes configurable jitter (default 1500ms) to prevent thundering herd
61
+ - **Thread Safety:** Uses `Concurrent::Map`, `Concurrent::AtomicBoolean`, and proper mutex locking throughout
62
+ - **Connection Pooling:** Multiple clients can share a single SSE connection for efficiency
63
+ - **Rollout Stage Polling:** Automatic detection of scheduled percentage increases (every 60s, configurable)
64
+
65
+ ### Dependencies
66
+
67
+ **Runtime:**
68
+ - `concurrent-ruby` ~> 1.2 (thread-safe data structures)
69
+ - `http` ~> 5.0 (HTTP client for SSE and heartbeat)
70
+
71
+ **Development:**
72
+ - `rspec` ~> 3.12 (testing framework)
73
+ - `rubocop` ~> 1.59 (linting)
74
+ - `simplecov` ~> 0.22 (code coverage)
75
+ - `webmock` ~> 3.19 (HTTP mocking)
76
+
77
+ ### Requirements
78
+
79
+ - Ruby 3.0 or higher
80
+ - Compatible with Rails 6.0+, Sinatra, Hanami, and standalone Ruby applications
81
+
82
+ ### Performance
83
+
84
+ - **Zero-latency local evaluation:** All flag evaluation happens locally after initial SSE connection
85
+ - **Minimal bandwidth:** Only flag changes trigger SSE messages
86
+ - **Efficient connection pooling:** Share one SSE connection across multiple client instances
87
+ - **Smart caching:** Reduces server load with configurable TTL
88
+
89
+ ### Security
90
+
91
+ - MFA required for RubyGems publishing (`rubygems_mfa_required: true`)
92
+ - No credentials in code (SDK key passed at initialization)
93
+ - HTTPS-only connections (enforced)
94
+ - Security policy documented in SECURITY.md
95
+
96
+ ---
97
+
98
+ ## [Unreleased]
99
+
100
+ ### Planned Features
101
+ - Redis cache adapter
102
+ - Custom cache adapter support
103
+ - Additional targeting operators
104
+ - Metrics and analytics integration
105
+
106
+ ---
107
+
108
+ [1.0.0]: https://github.com/togglecraft/ruby-sdk/releases/tag/v1.0.0
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ToggleCraft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
16
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # ToggleCraft Ruby SDK
2
+
3
+ A lightweight, real-time feature flag SDK for Ruby applications. Thread-safe, Rails-friendly, and built for production.
4
+
5
+ ## Why ToggleCraft?
6
+
7
+ - 🚀 **Real-time Updates** - Instant flag changes via Server-Sent Events
8
+ - 🧵 **Thread-Safe** - Built for Puma, Sidekiq, and multi-threaded environments
9
+ - 💾 **Smart Caching** - Works offline with memory or Redis support
10
+ - 🔒 **Production Ready** - 214 passing tests with comprehensive coverage
11
+ - 🌐 **Universal** - Works with Rails, Sinatra, Hanami, and standalone Ruby
12
+ - ♻️ **Auto-reconnection** - Resilient connection handling
13
+
14
+ ## Installation
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```ruby
19
+ gem 'togglecraft'
20
+ ```
21
+
22
+ Or install directly:
23
+
24
+ ```bash
25
+ gem install togglecraft
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ### Ruby
31
+
32
+ ```ruby
33
+ require 'togglecraft'
34
+
35
+ # Initialize client
36
+ client = ToggleCraft::Client.new(sdk_key: 'your-sdk-key')
37
+
38
+ # Connect and wait for flags
39
+ client.connect
40
+ client.wait_for_ready
41
+
42
+ # Use your flags
43
+ if client.enabled?('new-feature', user: { id: current_user.id })
44
+ # Feature is enabled
45
+ end
46
+ ```
47
+
48
+ ### Rails
49
+
50
+ ```ruby
51
+ # config/initializers/togglecraft.rb
52
+ Rails.application.config.togglecraft = ToggleCraft::Client.new(
53
+ sdk_key: ENV['TOGGLECRAFT_SDK_KEY'],
54
+ cache_adapter: :memory,
55
+ logger: Rails.logger,
56
+ debug: Rails.env.development?
57
+ )
58
+
59
+ Rails.application.config.togglecraft.connect
60
+
61
+ at_exit do
62
+ Rails.application.config.togglecraft.destroy
63
+ end
64
+ ```
65
+
66
+ Then use in your controllers:
67
+
68
+ ```ruby
69
+ class DashboardController < ApplicationController
70
+ def index
71
+ if togglecraft.enabled?('premium-dashboard', user: { id: current_user.id })
72
+ render :premium
73
+ else
74
+ render :standard
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def togglecraft
81
+ Rails.application.config.togglecraft
82
+ end
83
+ end
84
+ ```
85
+
86
+ ## Basic Usage
87
+
88
+ ### Boolean Flags
89
+
90
+ Simple on/off feature toggles:
91
+
92
+ ```ruby
93
+ if client.enabled?('dark-mode', user: { id: '123' })
94
+ enable_dark_mode
95
+ end
96
+ ```
97
+
98
+ ### Multivariate Flags
99
+
100
+ A/B/n testing with multiple variants:
101
+
102
+ ```ruby
103
+ variant = client.variant('checkout-flow', user: { id: '123' })
104
+
105
+ case variant
106
+ when 'one-page'
107
+ render_one_page_checkout
108
+ when 'multi-step'
109
+ render_multi_step_checkout
110
+ else
111
+ render_default_checkout
112
+ end
113
+ ```
114
+
115
+ ### Percentage Rollouts
116
+
117
+ Gradual feature rollouts:
118
+
119
+ ```ruby
120
+ if client.in_percentage?('new-algorithm', user: { id: '123' })
121
+ use_new_algorithm
122
+ else
123
+ use_legacy_algorithm
124
+ end
125
+ ```
126
+
127
+ ## Configuration
128
+
129
+ ```ruby
130
+ client = ToggleCraft::Client.new(
131
+ # Required
132
+ sdk_key: 'your-sdk-key', # Get from ToggleCraft dashboard
133
+
134
+ # Optional - Common settings
135
+ enable_cache: true, # Enable caching (default: true)
136
+ cache_adapter: :memory, # :memory or :redis (default: :memory)
137
+ cache_ttl: 300, # Cache TTL in seconds (default: 5 minutes)
138
+ debug: false # Enable debug logging (default: false)
139
+ )
140
+ ```
141
+
142
+ **Need more control?** See [Advanced Configuration →](docs/ADVANCED.md)
143
+
144
+ ## Evaluation Context
145
+
146
+ The context object provides data for targeting rules:
147
+
148
+ ```ruby
149
+ context = {
150
+ user: {
151
+ id: 'user-123', # Required for consistent evaluation
152
+ email: 'user@example.com',
153
+ plan: 'premium',
154
+ # Add any custom attributes you need
155
+ role: 'admin',
156
+ company_id: 'acme-corp'
157
+ },
158
+ request: {
159
+ ip: '192.168.1.1',
160
+ country: 'US'
161
+ },
162
+ device: {
163
+ type: 'mobile',
164
+ os: 'iOS'
165
+ }
166
+ }
167
+
168
+ client.enabled?('premium-feature', context)
169
+ ```
170
+
171
+ You can add any custom properties - the SDK evaluates all attributes using dot notation (e.g., `user.role`, `request.country`).
172
+
173
+ ## Framework Integration
174
+
175
+ ### Rails
176
+
177
+ ```ruby
178
+ # config/initializers/togglecraft.rb
179
+ Rails.application.config.togglecraft = ToggleCraft::Client.new(
180
+ sdk_key: ENV['TOGGLECRAFT_SDK_KEY'],
181
+ cache_adapter: :memory,
182
+ logger: Rails.logger
183
+ )
184
+ ```
185
+
186
+ [Full Rails Integration Guide →](docs/FRAMEWORKS.md#rails)
187
+
188
+ ### Sidekiq
189
+
190
+ ```ruby
191
+ class FeatureWorker
192
+ include Sidekiq::Worker
193
+
194
+ def togglecraft
195
+ @togglecraft ||= ToggleCraft::Client.new(
196
+ sdk_key: ENV['TOGGLECRAFT_SDK_KEY'],
197
+ share_connection: true # Share connection with other workers
198
+ )
199
+ end
200
+
201
+ def perform(user_id)
202
+ togglecraft.connect unless togglecraft.connected?
203
+
204
+ if togglecraft.enabled?('batch-processing', user: { id: user_id })
205
+ # Use new batch processing
206
+ end
207
+ end
208
+ end
209
+ ```
210
+
211
+ [Full Sidekiq Integration Guide →](docs/FRAMEWORKS.md#sidekiq)
212
+
213
+ ## Event Handling
214
+
215
+ Listen for real-time updates:
216
+
217
+ ```ruby
218
+ # Flags are ready
219
+ client.on(:ready) do
220
+ puts 'Client ready!'
221
+ end
222
+
223
+ # Flags updated in real-time
224
+ client.on(:flags_updated) do |flags|
225
+ puts "Flags updated: #{flags.keys.join(', ')}"
226
+ end
227
+
228
+ # Connection lost
229
+ client.on(:disconnected) do
230
+ puts 'Disconnected - using cached flags'
231
+ end
232
+
233
+ # Error occurred
234
+ client.on(:error) do |error|
235
+ logger.error "ToggleCraft error: #{error}"
236
+ end
237
+ ```
238
+
239
+ ## Error Handling
240
+
241
+ Always provide default values and handle errors gracefully:
242
+
243
+ ```ruby
244
+ # Safe evaluation with defaults
245
+ is_enabled = client.enabled?('feature', context, default: false)
246
+ # Returns false if flag doesn't exist or on error
247
+
248
+ # Handle connection errors
249
+ begin
250
+ client.connect
251
+ rescue StandardError => e
252
+ logger.error "Failed to connect: #{e}"
253
+ # App still works with cached values or defaults
254
+ end
255
+ ```
256
+
257
+ ## Best Practices
258
+
259
+ 1. **Initialize Once** - Create a single client instance and reuse it
260
+ 2. **Always Provide Context** - Include at least `user.id` for consistent evaluation
261
+ 3. **Use Default Values** - Handle missing flags gracefully
262
+ 4. **Clean Up on Shutdown** - Call `client.destroy` when closing
263
+
264
+ ```ruby
265
+ # On application shutdown
266
+ at_exit do
267
+ client.destroy
268
+ end
269
+ ```
270
+
271
+ ## API Reference
272
+
273
+ **Core Methods:**
274
+ - `enabled?(flag_key, context = {}, default: false)` - Check boolean flags
275
+ - `variant(flag_key, context = {}, default: nil)` - Get multivariate variant
276
+ - `in_percentage?(flag_key, context = {}, default: false)` - Check percentage rollout
277
+
278
+ **Connection:**
279
+ - `connect` - Connect to SSE server
280
+ - `disconnect` - Disconnect from server
281
+ - `ready?` - Check if client has flags loaded
282
+ - `wait_for_ready(timeout: 5)` - Wait for client to be ready
283
+
284
+ [Full API Documentation →](docs/API.md)
285
+
286
+ ## Advanced Features
287
+
288
+ Power users can customize:
289
+ - Connection pooling and reconnection strategies
290
+ - Scheduled rollout stages with automatic transitions
291
+ - Redis cache adapter
292
+ - Hybrid reconnection with exponential backoff
293
+
294
+ [Advanced Features Guide →](docs/ADVANCED.md)
295
+
296
+ ## Troubleshooting
297
+
298
+ **Client not connecting?**
299
+ - Verify your SDK key is correct
300
+ - Check that you called `connect` before using the client
301
+ - Ensure your firewall allows connections to `sse.togglecraft.io`
302
+
303
+ **Flags not updating?**
304
+ - Verify the SSE connection is established (`client.connected?`)
305
+ - Check the logs for error messages
306
+ - Enable debug mode: `debug: true`
307
+
308
+ [More Troubleshooting →](docs/TROUBLESHOOTING.md)
309
+
310
+ ## Requirements
311
+
312
+ - **Ruby 3.0+**
313
+ - **Dependencies:**
314
+ - `concurrent-ruby` (~> 1.2) - Thread-safe data structures
315
+ - `http` (~> 5.0) - HTTP client for API requests
316
+ - `semantic` (~> 1.6) - Semantic version comparison
317
+
318
+ ## Thread Safety
319
+
320
+ This SDK is fully thread-safe and production-ready for:
321
+ - **Puma** - Multi-threaded Rails server
322
+ - **Sidekiq** - Background job processing
323
+ - **Any multi-threaded Ruby environment**
324
+
325
+ All critical sections use `Concurrent::Map`, `Mutex`, and `Concurrent::AtomicBoolean` for thread safety.
326
+
327
+ ## Documentation
328
+
329
+ - [API Reference](docs/API.md) - Complete API documentation
330
+ - [Advanced Features](docs/ADVANCED.md) - Connection pooling, rollout stages, custom configuration
331
+ - [Framework Integration](docs/FRAMEWORKS.md) - Rails, Sidekiq, and other framework guides
332
+ - [Troubleshooting](docs/TROUBLESHOOTING.md) - Common issues and solutions
333
+ - [Security](SECURITY.md) - Security best practices and policies
334
+
335
+ ## License
336
+
337
+ MIT
338
+
339
+ ## Contributing
340
+
341
+ Contributions are welcome! Please open an issue or submit a pull request on GitHub.
342
+
343
+ ## Support
344
+
345
+ - **Documentation:** [GitHub Repository](https://github.com/togglecraft/ruby-sdk)
346
+ - **Issues:** [GitHub Issues](https://github.com/togglecraft/ruby-sdk/issues)
347
+ - **Security:** See [SECURITY.md](SECURITY.md) for reporting vulnerabilities
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'cache_adapters/memory_adapter'
4
+
5
+ module ToggleCraft
6
+ # Cache manager with TTL support and multiple adapters
7
+ class Cache
8
+ attr_reader :adapter, :default_ttl
9
+
10
+ # Initialize the cache
11
+ # @param adapter [Symbol, Object] The cache adapter (:memory or custom adapter instance)
12
+ # @param ttl [Integer] Default TTL in seconds (default: 300 = 5 minutes)
13
+ def initialize(adapter: :memory, ttl: 300)
14
+ @default_ttl = ttl
15
+ @adapter = case adapter
16
+ when :memory
17
+ CacheAdapters::MemoryAdapter.new
18
+ else
19
+ adapter
20
+ end
21
+ @cleanup_thread = nil
22
+ start_cleanup
23
+ end
24
+
25
+ # Get a value from cache
26
+ # Returns nil if key doesn't exist or is expired
27
+ # @param key [String] The cache key
28
+ # @return [Object, nil] The cached value or nil
29
+ def get(key)
30
+ entry = @adapter.get(key)
31
+ return nil unless entry
32
+ return nil if expired?(entry)
33
+
34
+ entry[:value]
35
+ end
36
+
37
+ # Set a value in cache with optional TTL override
38
+ # @param key [String] The cache key
39
+ # @param value [Object] The value to cache
40
+ # @param ttl [Integer, nil] Optional TTL override in seconds
41
+ def set(key, value, ttl: nil)
42
+ entry = {
43
+ value: value,
44
+ timestamp: Time.now.to_f,
45
+ ttl: ttl || @default_ttl
46
+ }
47
+ @adapter.set(key, entry)
48
+ end
49
+
50
+ # Delete a value from cache
51
+ # @param key [String] The cache key
52
+ # @return [Boolean] true if deleted, false otherwise
53
+ def delete(key)
54
+ @adapter.delete(key)
55
+ end
56
+
57
+ # Clear all cache entries
58
+ def clear
59
+ @adapter.clear
60
+ end
61
+
62
+ # Check if a key exists in cache (and is not expired)
63
+ # @param key [String] The cache key
64
+ # @return [Boolean]
65
+ def has?(key)
66
+ entry = @adapter.get(key)
67
+ return false unless entry
68
+ return false if expired?(entry)
69
+
70
+ true
71
+ end
72
+
73
+ # Delete all keys matching a prefix
74
+ # @param prefix [String] The prefix to match
75
+ def clear_by_prefix(prefix)
76
+ keys_to_delete = @adapter.keys.select { |key| key.start_with?(prefix) }
77
+ keys_to_delete.each { |key| delete(key) }
78
+ end
79
+
80
+ # Get all cached flags (non-expired)
81
+ # @return [Hash] Hash of flag_key => flag_data
82
+ def all_flags
83
+ flags = {}
84
+ @adapter.keys.each do |key|
85
+ next unless key.start_with?('flag:')
86
+
87
+ flag_key = key.sub('flag:', '')
88
+ value = get(key)
89
+ flags[flag_key] = value if value
90
+ end
91
+ flags
92
+ end
93
+
94
+ # Store all flags at once
95
+ # @param flags [Hash] Hash of flag_key => flag_data
96
+ # @param ttl [Integer, nil] Optional TTL override
97
+ def set_all_flags(flags, ttl: nil)
98
+ flags.each do |flag_key, flag_data|
99
+ set("flag:#{flag_key}", flag_data, ttl: ttl)
100
+ end
101
+ end
102
+
103
+ # Clean up resources and stop background cleanup
104
+ def destroy
105
+ stop_cleanup
106
+ clear
107
+ end
108
+
109
+ private
110
+
111
+ # Check if an entry is expired
112
+ # @param entry [Hash] The cache entry
113
+ # @return [Boolean]
114
+ def expired?(entry)
115
+ return false unless entry[:timestamp] && entry[:ttl]
116
+
117
+ Time.now.to_f - entry[:timestamp] > entry[:ttl]
118
+ end
119
+
120
+ # Start periodic cleanup of expired entries
121
+ def start_cleanup
122
+ return if @cleanup_thread
123
+
124
+ @cleanup_thread = Thread.new do
125
+ loop do
126
+ sleep 60 # Run cleanup every minute
127
+ cleanup_expired
128
+ rescue StandardError => e
129
+ warn "[ToggleCraft Cache] Cleanup thread error: #{e.message}"
130
+ end
131
+ end
132
+ @cleanup_thread.abort_on_exception = false
133
+ end
134
+
135
+ # Clean up expired entries
136
+ def cleanup_expired
137
+ @adapter.keys.each do |key|
138
+ entry = @adapter.get(key)
139
+ delete(key) if entry && expired?(entry)
140
+ end
141
+ end
142
+
143
+ # Stop the cleanup thread
144
+ def stop_cleanup
145
+ return unless @cleanup_thread
146
+
147
+ @cleanup_thread.kill
148
+ @cleanup_thread = nil
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent'
4
+
5
+ module ToggleCraft
6
+ module CacheAdapters
7
+ # In-memory cache adapter using thread-safe Concurrent::Map
8
+ # Suitable for single-process applications
9
+ class MemoryAdapter
10
+ def initialize
11
+ @store = Concurrent::Map.new
12
+ end
13
+
14
+ # Get a value from the store
15
+ # @param key [String] The cache key
16
+ # @return [Object, nil] The stored value or nil
17
+ def get(key)
18
+ @store[key]
19
+ end
20
+
21
+ # Set a value in the store
22
+ # @param key [String] The cache key
23
+ # @param value [Object] The value to store
24
+ def set(key, value)
25
+ @store[key] = value
26
+ end
27
+
28
+ # Delete a value from the store
29
+ # @param key [String] The cache key
30
+ # @return [Boolean] true if deleted, false otherwise
31
+ def delete(key)
32
+ !@store.delete(key).nil?
33
+ end
34
+
35
+ # Clear all values from the store
36
+ def clear
37
+ @store.clear
38
+ end
39
+
40
+ # Check if a key exists in the store
41
+ # @param key [String] The cache key
42
+ # @return [Boolean]
43
+ def has?(key)
44
+ @store.key?(key)
45
+ end
46
+
47
+ # Get all keys in the store
48
+ # @return [Array<String>]
49
+ def keys
50
+ @store.keys
51
+ end
52
+ end
53
+ end
54
+ end