philiprehberger-expiring_map 0.3.0 → 0.4.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: 5749cba84d6dc29c0db85c7c0112f4e8d58ae3c86b506c265d2fec2ea854c407
4
- data.tar.gz: dc3f78c07157a42550dd77b156d73f6d7ffc4d24dfa4cb957cfb6f59932d88d6
3
+ metadata.gz: c9113519082c9f4bc5b232176d347f5bf07ffd282a63f27be15a68443a6aee90
4
+ data.tar.gz: 7b90b98bab68612c39f4723c4d7673a1149155c50531972754eb7ae0daea841c
5
5
  SHA512:
6
- metadata.gz: f76d3842b3da9209a8060e6fd6bf885993a158319681effd26a9a5185c67c2bf8b35cf67c28c56589aceff5e5753f05908192f3adaacda130487e36f24a13b67
7
- data.tar.gz: 43f6e2b1c3e93a476f4f2cc039b7ea40685d59ab0d74a0a60eaa7983e84eced5c362c0379b1fd29d5b9086a89768db6dc523ddf042093006e1fb6211bf614006
6
+ metadata.gz: f55f4d5d64f2b81cd57519784e8fdce758360f72c9cd85c6789f29bea0950de80eaf3a649b1d0c070c890179db3a126893e9933aa39f9e41817fcccc765c3e34
7
+ data.tar.gz: 40c275b4ef7abd9dfc531bcf6ef19e44d812e25b1eb5158c4d4b2cdea07f6e0952dff5e6543a6fad9cb67ea4d2a6389dfbda2c6c2ce4dee867173d86f374fb44
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-04-27
11
+
12
+ ### Added
13
+ - `Map#purge_expired!` — actively sweep expired entries and return the count removed; fires `on_expire` callbacks. Useful for explicit cleanup in long-running processes that read sparingly.
14
+ - `Map#expired?(key)` — predicate for whether an entry is present-but-expired without deleting it or firing `on_expire`. Distinct from `get(key).nil?`, which can't tell "missing" from "expired".
15
+
10
16
  ## [0.3.0] - 2026-04-17
11
17
 
12
18
  ### Added
data/README.md CHANGED
@@ -121,6 +121,33 @@ cache.each { |key, value| puts "#{key}: #{value}" }
121
121
  cache.select { |_k, v| v > 10 }
122
122
  ```
123
123
 
124
+ ### Manual Sweep
125
+
126
+ Most read methods (`get`, `size`, `each`, `keys`, `values`) sweep expired
127
+ entries lazily on access. Long-running processes that read sparingly can
128
+ accumulate stale entries between reads — call `purge_expired!` to reclaim
129
+ memory and fire `on_expire` callbacks for everything that has expired:
130
+
131
+ ```ruby
132
+ cache.set(:a, 1, ttl: 0.01)
133
+ cache.set(:b, 2, ttl: 10)
134
+ sleep 0.02
135
+
136
+ cache.purge_expired! # => 1 (number of entries removed)
137
+ cache.keys # => [:b]
138
+ ```
139
+
140
+ `expired?(key)` returns whether a present key has elapsed its TTL — without
141
+ deleting the entry or firing `on_expire`. Distinct from `get(key).nil?`,
142
+ which can't tell "missing" from "expired":
143
+
144
+ ```ruby
145
+ cache.set(:dead, 'gone', ttl: 0.01)
146
+ sleep 0.02
147
+ cache.expired?(:dead) # => true
148
+ cache.expired?(:nope) # => false (missing, not expired)
149
+ ```
150
+
124
151
  ## API
125
152
 
126
153
  | Method | Description |
@@ -142,6 +169,8 @@ cache.select { |_k, v| v > 10 }
142
169
  | `#on_expire { \|k, v\| }` | Register expiration callback |
143
170
  | `#clear` | Remove all entries |
144
171
  | `#each { \|k, v\| }` | Iterate over non-expired entries |
172
+ | `#purge_expired!` | Actively sweep expired entries; fires `on_expire`; returns the count removed |
173
+ | `#expired?(key)` | Whether the entry at `key` is present-but-expired (does not delete or fire `on_expire`) |
145
174
 
146
175
  ## Development
147
176
 
@@ -268,6 +268,39 @@ module Philiprehberger
268
268
  end
269
269
  end
270
270
 
271
+ # Actively sweep expired entries and return how many were removed.
272
+ #
273
+ # Most read methods (`get`, `size`, `each`, `keys`, `values`) sweep lazily,
274
+ # so long-running processes that read sparingly may accumulate stale
275
+ # entries before the next read triggers cleanup. Call this to reclaim
276
+ # memory and fire `on_expire` callbacks for everything that has expired.
277
+ #
278
+ # @return [Integer] number of entries removed
279
+ def purge_expired!
280
+ @mutex.synchronize do
281
+ before = @store.size
282
+ sweep_expired
283
+ before - @store.size
284
+ end
285
+ end
286
+
287
+ # Whether the entry at +key+ has expired.
288
+ #
289
+ # Returns +false+ when the key is missing or still valid; returns +true+
290
+ # when the entry exists but its TTL has elapsed. Unlike `get`, this does
291
+ # not delete the entry or fire `on_expire`.
292
+ #
293
+ # @param key [Object] the key
294
+ # @return [Boolean]
295
+ def expired?(key)
296
+ @mutex.synchronize do
297
+ entry = @store[key]
298
+ return false unless entry
299
+
300
+ entry.expired?
301
+ end
302
+ end
303
+
271
304
  private
272
305
 
273
306
  # Remove all expired entries, firing callbacks
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module ExpiringMap
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-expiring_map
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
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-04-18 00:00:00.000000000 Z
11
+ date: 2026-04-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A thread-safe hash map where each key has its own TTL, with automatic
14
14
  expiration, max size eviction, expiration callbacks, and Enumerable support.