philiprehberger-cache_kit 0.1.0 → 0.2.2

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: 969b99417559919665d91aea64820dd9373d787020b4d966b2443d0099864da0
4
- data.tar.gz: '09e639031c7c4b3864e45dfe35ece4e29d109d062c4b0ebca424f06269f5b14b'
3
+ metadata.gz: b4acd246ec80526d77eb23a49ecac10bcb00cbeaed209e279381099b14346f19
4
+ data.tar.gz: de8a6c3c962e0836c859dd97042b1885cc553e2edb41ee633707e431d3f73480
5
5
  SHA512:
6
- metadata.gz: f15036e4a4f6cd77fd5e9f6af3c8f668eb4dee397415bbdeeda94d8acac24e7e7f81cfc6cc06721b4621ea4f1f043b0f468d1c44bef745ec14b8c01600b545f9
7
- data.tar.gz: 771c1d2c89759a190c242c1cf6b12fca3673050d8ddca0e0cdee746d7daa73be7fe81b945dc945f69a7eae43aef3e991d32c4cb0c51d4304f79faa27af6ad0ba
6
+ metadata.gz: 9b3269a4eeebbb1b2020b421b8288e45a2aa78f48f61d55cc4fbf470b052137f85fea597ae215b83ea68ae862c8da326bbd6b4a82238d81511ce5a9f5a190572
7
+ data.tar.gz: '08dbc8a4505fbf216efede795a81ea828eb16e5f7a8a27793f8022225508ed58d8a11fbe68eeaae60212a0dbbb88136c087c538a2ef24bea82358063720de955'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2
4
+
5
+ - Add License badge to README
6
+ - Add bug_tracker_uri to gemspec
7
+
3
8
  All notable changes to this gem will be documented in this file.
4
9
 
5
10
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
@@ -7,6 +12,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
12
 
8
13
  ## [Unreleased]
9
14
 
15
+ ## [0.2.1] - 2026-03-13
16
+
17
+ ### Fixed
18
+ - Fix SyntaxError: setter method `[]=` cannot use endless method definition
19
+
20
+ ## [0.2.0] - 2026-03-13
21
+
22
+ ### Added
23
+ - `keys` method returns all non-expired cache keys
24
+ - `[]` and `[]=` hash-like accessors for get/set
25
+ - `stats` method returns size, hits, misses, and eviction counts
26
+ - `prune` method removes all expired entries and returns count removed
27
+ - Hit/miss/eviction tracking counters (thread-safe)
28
+
10
29
  ## [0.1.0] - 2026-03-10
11
30
 
12
31
  ### Added
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Tests](https://github.com/philiprehberger/rb-cache-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-cache-kit/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/philiprehberger-cache_kit.svg)](https://rubygems.org/gems/philiprehberger-cache_kit)
5
+ [![License](https://img.shields.io/github/license/philiprehberger/rb-cache-kit)](LICENSE)
5
6
 
6
7
  In-memory LRU cache with TTL, tags, and thread safety for Ruby.
7
8
 
@@ -68,6 +69,13 @@ cache.set("post:1", data3, tags: ["posts"])
68
69
  cache.invalidate_tag("users") # removes user:1 and user:2, keeps post:1
69
70
  ```
70
71
 
72
+ ### Hash-like Access
73
+
74
+ ```ruby
75
+ cache["user:1"] = { name: "Alice" }
76
+ cache["user:1"] # => { name: "Alice" }
77
+ ```
78
+
71
79
  ### LRU Eviction
72
80
 
73
81
  ```ruby
@@ -82,6 +90,27 @@ cache.get("a") # => nil
82
90
  cache.get("d") # => 4
83
91
  ```
84
92
 
93
+ ### Pruning Expired Entries
94
+
95
+ ```ruby
96
+ cache.set("a", 1, ttl: 1)
97
+ cache.set("b", 2, ttl: 1)
98
+ cache.set("c", 3)
99
+ # ... after TTL expires ...
100
+ cache.prune # => 2 (removed expired entries)
101
+ ```
102
+
103
+ ### Stats
104
+
105
+ ```ruby
106
+ cache.set("a", 1)
107
+ cache.get("a") # hit
108
+ cache.get("missing") # miss
109
+
110
+ cache.stats
111
+ # => { size: 1, hits: 1, misses: 1, evictions: 0 }
112
+ ```
113
+
85
114
  ## API
86
115
 
87
116
  | Method | Description |
@@ -95,6 +124,11 @@ cache.get("d") # => 4
95
124
  | `Store#clear` | Remove all entries |
96
125
  | `Store#size` | Number of entries |
97
126
  | `Store#key?(key)` | Check if a key exists and is not expired |
127
+ | `Store#keys` | Returns all non-expired keys |
128
+ | `Store#[](key)` | Hash-like read (alias for `get`) |
129
+ | `Store#[]=(key, value)` | Hash-like write (alias for `set` without TTL/tags) |
130
+ | `Store#stats` | Returns `{ size:, hits:, misses:, evictions: }` |
131
+ | `Store#prune` | Remove all expired entries, returns count removed |
98
132
 
99
133
  ## Development
100
134
 
@@ -10,6 +10,9 @@ module Philiprehberger
10
10
  @data = {}
11
11
  @order = []
12
12
  @mutex = Mutex.new
13
+ @hits = 0
14
+ @misses = 0
15
+ @evictions = 0
13
16
  end
14
17
 
15
18
  # Get a value by key. Returns nil if missing or expired.
@@ -17,18 +20,7 @@ module Philiprehberger
17
20
  # @param key [String] the cache key
18
21
  # @return the cached value, or nil
19
22
  def get(key)
20
- @mutex.synchronize do
21
- entry = @data[key]
22
- return nil unless entry
23
-
24
- if entry.expired?
25
- remove_entry(key)
26
- return nil
27
- end
28
-
29
- touch(key)
30
- entry.value
31
- end
23
+ @mutex.synchronize { fetch_entry(key) }
32
24
  end
33
25
 
34
26
  # Store a value.
@@ -65,16 +57,9 @@ module Philiprehberger
65
57
  computed
66
58
  end
67
59
 
68
- # Delete a specific key.
69
- #
70
- # @param key [String] the cache key
71
60
  # @return [Boolean] true if the key existed
72
61
  def delete(key)
73
- @mutex.synchronize do
74
- removed = @data.key?(key)
75
- remove_entry(key)
76
- removed
77
- end
62
+ @mutex.synchronize { @data.key?(key).tap { remove_entry(key) } }
78
63
  end
79
64
 
80
65
  # Invalidate all entries with a given tag.
@@ -90,14 +75,9 @@ module Philiprehberger
90
75
  end
91
76
  end
92
77
 
93
- # Clear all entries.
94
- #
95
78
  # @return [void]
96
79
  def clear
97
- @mutex.synchronize do
98
- @data.clear
99
- @order.clear
100
- end
80
+ @mutex.synchronize { @data.clear && @order.clear }
101
81
  end
102
82
 
103
83
  # @return [Integer] number of entries (including expired ones not yet evicted)
@@ -105,15 +85,39 @@ module Philiprehberger
105
85
  @mutex.synchronize { @data.size }
106
86
  end
107
87
 
108
- # @param key [String]
109
88
  # @return [Boolean] true if the key exists and is not expired
110
89
  def key?(key)
111
90
  @mutex.synchronize do
112
91
  entry = @data[key]
113
- return false unless entry
114
- return false if entry.expired?
92
+ entry ? !entry.expired? : false
93
+ end
94
+ end
95
+
96
+ # @return [Array<String>] list of valid keys
97
+ def keys
98
+ @mutex.synchronize { @data.reject { |_, entry| entry.expired? }.keys }
99
+ end
115
100
 
116
- true
101
+ # @param key [String]
102
+ def [](key) = get(key)
103
+
104
+ # @param key [String]
105
+ # @param value the value to cache
106
+ def []=(key, value)
107
+ set(key, value)
108
+ end
109
+
110
+ # @return [Hash] stats with :size, :hits, :misses, :evictions
111
+ def stats
112
+ @mutex.synchronize { { size: @data.size, hits: @hits, misses: @misses, evictions: @evictions } }
113
+ end
114
+
115
+ # @return [Integer] number of entries removed
116
+ def prune
117
+ @mutex.synchronize do
118
+ expired_keys = @data.select { |_, entry| entry.expired? }.keys
119
+ expired_keys.each { |k| remove_entry(k) }
120
+ expired_keys.size
117
121
  end
118
122
  end
119
123
 
@@ -124,9 +128,31 @@ module Philiprehberger
124
128
  @order.push(key)
125
129
  end
126
130
 
131
+ def fetch_entry(key)
132
+ entry = @data[key]
133
+ return record_miss unless entry
134
+
135
+ if entry.expired?
136
+ remove_entry(key)
137
+ return record_miss
138
+ end
139
+
140
+ @hits += 1
141
+ touch(key)
142
+ entry.value
143
+ end
144
+
145
+ def record_miss
146
+ @misses += 1
147
+ nil
148
+ end
149
+
127
150
  def evict
128
151
  oldest = @order.first
129
- remove_entry(oldest) if oldest
152
+ return unless oldest
153
+
154
+ remove_entry(oldest)
155
+ @evictions += 1
130
156
  end
131
157
 
132
158
  def remove_entry(key)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module CacheKit
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-cache_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-10 00:00:00.000000000 Z
11
+ date: 2026-03-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A lightweight, thread-safe in-memory LRU cache with TTL expiration and
14
14
  tag-based bulk invalidation for Ruby applications.