Module: SafeMemoize::PublicMethods
- Included in:
- InstanceMethods
- Defined in:
- lib/safe_memoize/public_methods.rb
Overview
Public instance methods mixed into every class that prepends SafeMemoize.
Instance Method Summary collapse
-
#clear_memo_hooks(hook_type = nil) ⇒ void
Removes all registered hooks, or only hooks of a specific type.
-
#dump_memo(method_name = nil) ⇒ Hash
Exports live cache entries as a plain hash suitable for serialization.
-
#load_memo(snapshot) ⇒ nil
Restores cache entries from a snapshot produced by #dump_memo.
-
#memo_age(method_name, *args, **kwargs) ⇒ Float?
Returns how many seconds ago the entry was cached, or +nil+ if not cached.
-
#memo_count(*method_name) ⇒ Integer
Returns the number of live cached entries for a method (or all methods).
-
#memo_inspect(method_name, *args, **kwargs) ⇒ Hash?
Returns a detailed snapshot of a single cached entry, or +nil+ if not cached.
-
#memo_keys(*method_name) ⇒ Array<Hash>
Returns metadata hashes describing each cached entry.
-
#memo_preload(method_name, *arg_sets) ⇒ Array
Calls the memoized method for each argument set and caches all results.
-
#memo_refresh(method_name, *args, **kwargs) ⇒ Object
Clears the cached entry and immediately re-calls the method to populate a fresh value.
-
#memo_stale?(method_name, *args, **kwargs) ⇒ Boolean
Returns +true+ if the entry exists but its TTL has elapsed.
-
#memo_touch(method_name, *args, ttl: nil, **kwargs) ⇒ Boolean
Resets the expiry clock on a live cached entry without recomputing its value.
-
#memo_ttl_remaining(method_name, *args, **kwargs) ⇒ Float?
Returns the number of seconds until the cached entry expires.
-
#memo_values(*method_name) ⇒ Array<Hash>
Returns metadata hashes including the cached value for each entry.
-
#memoized?(method_name, *args, **kwargs, &block) ⇒ Boolean
Returns +true+ if the given call is currently cached (and not expired).
-
#on_memo_evict {|cache_key, record| ... } ⇒ void
Registers a hook that fires when an LRU eviction occurs.
-
#on_memo_expire {|cache_key, record| ... } ⇒ void
Registers a hook that fires on every cache hit.
-
#on_memo_hit {|cache_key, record| ... } ⇒ void
Registers a hook that fires on every cache hit.
-
#on_memo_miss {|cache_key, record| ... } ⇒ void
Registers a hook that fires on every cache miss (before the value is stored).
-
#on_memo_store {|cache_key, record| ... } ⇒ void
Registers a hook that fires whenever a value is written to the cache (miss, #warm_memo, or #load_memo).
-
#reset_all_memos ⇒ void
Clears all cached entries for every method on this instance.
-
#reset_memo(method_name, *args, **kwargs) ⇒ void
Removes one or all cached entries for a method.
-
#warm_memo(method_name, *args, ttl: nil, **kwargs) { ... } ⇒ Object
Pre-populates a cache entry with the value returned by the block without calling the memoized method itself.
Instance Method Details
#clear_memo_hooks(hook_type = nil) ⇒ void
This method returns an undefined value.
Removes all registered hooks, or only hooks of a specific type.
151 152 153 154 155 |
# File 'lib/safe_memoize/public_methods.rb', line 151 def clear_memo_hooks(hook_type = nil) with_memo_lock do _clear_memo_hooks(hook_type) end end |
#dump_memo(method_name = nil) ⇒ Hash
Exports live cache entries as a plain hash suitable for serialization.
212 213 214 215 216 217 218 219 220 221 |
# File 'lib/safe_memoize/public_methods.rb', line 212 def dump_memo(method_name = nil) method_name = method_name&.to_sym with_memo_lock do cache = memo_cache_or_nil || {} entries = method_name ? cache.select { |key, _| key[0] == method_name } : cache.dup entries.select! { |_, record| memo_record_live?(record) } entries.transform_values { |record| memo_record_value(record) } end end |
#load_memo(snapshot) ⇒ nil
Restores cache entries from a snapshot produced by #dump_memo.
Existing entries are not cleared; snapshot keys are merged in. Each restored entry fires the +:on_store+ hook.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/safe_memoize/public_methods.rb', line 231 def load_memo(snapshot) raise ArgumentError, "snapshot must be a Hash" unless snapshot.is_a?(Hash) with_memo_lock do @__safe_memo_cache__ ||= {} snapshot.each do |cache_key, value| record = memo_record(value, expires_at: nil) @__safe_memo_cache__[cache_key] = record call_memo_hooks(:on_store, cache_key, record) end end nil end |
#memo_age(method_name, *args, **kwargs) ⇒ Float?
Returns how many seconds ago the entry was cached, or +nil+ if not cached.
298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/safe_memoize/public_methods.rb', line 298 def memo_age(method_name, *args, **kwargs) cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do record = memo_cache_record(cache_key) return nil unless record cached_at = record[:cached_at] return nil unless cached_at (Process.clock_gettime(Process::CLOCK_MONOTONIC) - cached_at).round(6) end end |
#memo_count(*method_name) ⇒ Integer
Returns the number of live cached entries for a method (or all methods).
51 52 53 54 55 56 57 |
# File 'lib/safe_memoize/public_methods.rb', line 51 def memo_count(*method_name) scoped_method = safe_memo_scoped_method(method_name) with_memo_lock do safe_memo_count_for(scoped_method) end end |
#memo_inspect(method_name, *args, **kwargs) ⇒ Hash?
Returns a detailed snapshot of a single cached entry, or +nil+ if not cached.
All reads are performed inside a single mutex hold.
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/safe_memoize/public_methods.rb', line 387 def memo_inspect(method_name, *args, **kwargs) method_name = method_name.to_sym cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do record = memo_cache_record(cache_key) return nil unless record now = Process.clock_gettime(Process::CLOCK_MONOTONIC) ttl_remaining = if record[:expires_at] remaining = record[:expires_at] - now (remaining > 0) ? remaining.round(6) : 0 end age = (now - record[:cached_at]).round(6) if record[:cached_at] metrics_key = safe_memo_cache_key(method_name, args, kwargs) entry_metrics = memo_metrics_store[metrics_key] || {hits: 0, misses: 0} custom_key = (cache_key.length == 2) ? cache_key[1] : nil lru_position = begin method_lru = lru_order_store[method_name] if method_lru&.key?(cache_key) keys = method_lru.keys keys.length - keys.index(cache_key) end end { cached: true, value: memo_record_value(record), hits: entry_metrics[:hits], misses: entry_metrics[:misses], ttl_remaining: ttl_remaining, age: age, custom_key: custom_key, lru_position: lru_position } end end |
#memo_keys(*method_name) ⇒ Array<Hash>
Returns metadata hashes describing each cached entry.
Each hash contains +:args+, +:kwargs+ (or +:custom_key+ for custom-keyed entries), and +:method+ when no +method_name+ filter is applied.
66 67 68 69 70 71 72 |
# File 'lib/safe_memoize/public_methods.rb', line 66 def memo_keys(*method_name) scoped_method = safe_memo_scoped_method(method_name) with_memo_lock do safe_memo_keys_for(scoped_method) end end |
#memo_preload(method_name, *arg_sets) ⇒ Array
Calls the memoized method for each argument set and caches all results.
Equivalent to calling the method for each arg set individually, but expressed as a single call for clarity.
200 201 202 203 204 205 |
# File 'lib/safe_memoize/public_methods.rb', line 200 def memo_preload(method_name, *arg_sets) method_name = method_name.to_sym arg_sets.map do |args| send(method_name, *Array(args)) end end |
#memo_refresh(method_name, *args, **kwargs) ⇒ Object
Clears the cached entry and immediately re-calls the method to populate a fresh value.
286 287 288 289 290 |
# File 'lib/safe_memoize/public_methods.rb', line 286 def memo_refresh(method_name, *args, **kwargs) method_name = method_name.to_sym reset_memo(method_name, *args, **kwargs) send(method_name, *args, **kwargs) end |
#memo_stale?(method_name, *args, **kwargs) ⇒ Boolean
Returns +true+ if the entry exists but its TTL has elapsed.
318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/safe_memoize/public_methods.rb', line 318 def memo_stale?(method_name, *args, **kwargs) cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do cache = memo_cache_or_nil return false unless cache record = cache[cache_key] return false unless record !memo_record_live?(record) end end |
#memo_touch(method_name, *args, ttl: nil, **kwargs) ⇒ Boolean
Resets the expiry clock on a live cached entry without recomputing its value.
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/safe_memoize/public_methods.rb', line 254 def memo_touch(method_name, *args, ttl: nil, **kwargs) method_name = method_name.to_sym cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do cache = memo_cache_or_nil return false unless cache record = cache[cache_key] return false unless record && memo_record_live?(record) now = Process.clock_gettime(Process::CLOCK_MONOTONIC) effective_ttl = if ttl ttl elsif record[:expires_at] && record[:cached_at] record[:expires_at] - record[:cached_at] end record[:expires_at] = effective_ttl ? now + effective_ttl : nil record[:cached_at] = now true end end |
#memo_ttl_remaining(method_name, *args, **kwargs) ⇒ Float?
Returns the number of seconds until the cached entry expires.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/safe_memoize/public_methods.rb', line 32 def memo_ttl_remaining(method_name, *args, **kwargs) cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do record = memo_cache_record(cache_key) return 0 unless record expires_at = record[:expires_at] return nil unless expires_at remaining = expires_at - Process.clock_gettime(Process::CLOCK_MONOTONIC) (remaining > 0) ? remaining.round(6) : 0 end end |
#memo_values(*method_name) ⇒ Array<Hash>
Returns metadata hashes including the cached value for each entry.
Each hash contains all fields from #memo_keys plus +:value+.
80 81 82 83 84 85 86 |
# File 'lib/safe_memoize/public_methods.rb', line 80 def memo_values(*method_name) scoped_method = safe_memo_scoped_method(method_name) with_memo_lock do safe_memo_values_for(scoped_method) end end |
#memoized?(method_name, *args, **kwargs, &block) ⇒ Boolean
Returns +true+ if the given call is currently cached (and not expired).
Always returns +false+ when a block is provided, because block-taking methods cannot be safely keyed by arguments alone.
15 16 17 18 19 20 21 22 23 |
# File 'lib/safe_memoize/public_methods.rb', line 15 def memoized?(method_name, *args, **kwargs, &block) return false if block cache_key = compute_cache_key(method_name, args, kwargs) with_memo_lock do memo_cache_hit?(cache_key) end end |
#on_memo_evict {|cache_key, record| ... } ⇒ void
This method returns an undefined value.
Registers a hook that fires when an LRU eviction occurs.
106 107 108 109 110 |
# File 'lib/safe_memoize/public_methods.rb', line 106 def on_memo_evict(&block) raise ArgumentError, "block required" unless block register_memo_hook(:on_evict, &block) end |
#on_memo_expire {|cache_key, record| ... } ⇒ void
This method returns an undefined value.
Registers a hook that fires on every cache hit.
95 96 97 98 99 |
# File 'lib/safe_memoize/public_methods.rb', line 95 def on_memo_expire(&block) raise ArgumentError, "block required" unless block register_memo_hook(:on_expire, &block) end |
#on_memo_hit {|cache_key, record| ... } ⇒ void
This method returns an undefined value.
Registers a hook that fires on every cache hit.
117 118 119 120 121 |
# File 'lib/safe_memoize/public_methods.rb', line 117 def on_memo_hit(&block) raise ArgumentError, "block required" unless block register_memo_hook(:on_hit, &block) end |
#on_memo_miss {|cache_key, record| ... } ⇒ void
This method returns an undefined value.
Registers a hook that fires on every cache miss (before the value is stored).
128 129 130 131 132 |
# File 'lib/safe_memoize/public_methods.rb', line 128 def on_memo_miss(&block) raise ArgumentError, "block required" unless block register_memo_hook(:on_miss, &block) end |
#on_memo_store {|cache_key, record| ... } ⇒ void
This method returns an undefined value.
Registers a hook that fires whenever a value is written to the cache (miss, #warm_memo, or #load_memo).
140 141 142 143 144 |
# File 'lib/safe_memoize/public_methods.rb', line 140 def on_memo_store(&block) raise ArgumentError, "block required" unless block register_memo_hook(:on_store, &block) end |
#reset_all_memos ⇒ void
This method returns an undefined value.
Clears all cached entries for every method on this instance. Each evicted entry fires the +:on_evict+ hook.
365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/safe_memoize/public_methods.rb', line 365 def reset_all_memos with_memo_lock do if defined?(@__safe_memo_cache__) && @__safe_memo_cache__ @__safe_memo_cache__.each do |key, record| call_memo_hooks(:on_evict, key, record) end end @__safe_memo_cache__ = {} lru_clear_all end end |
#reset_memo(method_name, *args, **kwargs) ⇒ void
This method returns an undefined value.
Removes one or all cached entries for a method.
When called with only +method_name+, all entries for that method are cleared. When called with +method_name+ and arguments, only the exact matching entry is cleared. Each evicted entry fires the +:on_evict+ hook.
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/safe_memoize/public_methods.rb', line 342 def reset_memo(method_name, *args, **kwargs) method_name = method_name.to_sym matcher = memo_matcher_for(method_name, args, kwargs) with_memo_lock do with_memo_cache do |cache| cache.delete_if do |key, record| if matcher.call(key) call_memo_hooks(:on_evict, key, record) true else false end end end end end |
#warm_memo(method_name, *args, ttl: nil, **kwargs) { ... } ⇒ Object
Pre-populates a cache entry with the value returned by the block without calling the memoized method itself.
Useful for warming caches from a serialized snapshot or an external source.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/safe_memoize/public_methods.rb', line 172 def warm_memo(method_name, *args, ttl: nil, **kwargs, &block) raise ArgumentError, "block required" unless block method_name = method_name.to_sym cache_key = compute_cache_key(method_name, args, kwargs) value = block.call with_memo_lock do @__safe_memo_cache__ ||= {} record = memo_record(value, expires_at: memo_expires_at(ttl)) @__safe_memo_cache__[cache_key] = record call_memo_hooks(:on_store, cache_key, record) end value end |