v1.3.0
SafeMemoize 1.3.0
Added
-
SafeMemoize::Extension— mixin for building SafeMemoize extensions. Extend it in any module to get a DSL for declaring custommemoizeoptions and global lifecycle event handlers without monkey-patching SafeMemoize internals.handles_option(name, &processor)— declares a custom keyword argument thatmemoizewill accept; the processor block is called at definition time with(value, method_name, all_extension_options)and must return aHashof standard memoize options to inject (e.g.{cache_bust: ...},{ttl: 60},{namespace: "v2"}).on_cache_event(*event_types, &handler)— registers a global lifecycle handler that fires after every matching event (:on_hit,:on_miss,:on_store,:on_expire,:on_evict) across all memoized methods on all classes; handler receives(klass, method_name, cache_key, record); runs on the main Ractor only.- Duck-type compatible — any object responding to
handled_options,process_memoize_option, anddispatch_cache_eventworks withoutextend SafeMemoize::Extension.
-
SafeMemoize.register_extension(name, extension)— registers an extension under a symbolic name. -
SafeMemoize.unregister_extension(name)— removes an extension. -
SafeMemoize.extensions— returns a snapshot of the registry. -
SafeMemoize.reset_extensions!— clears the registry (test teardown). -
SafeMemoize.extension_for_option(option_name)— returns the registered extension that handles the named option, ornil. -
memoizenow accepts**extension_optionsfor any unknown keyword argument; each key is validated against registered extensions at call time and raisesArgumentErrorif no extension claims it, preserving the existing strict-options behaviour for typos. -
cache_bust: callableoption onmemoize— automatic cache invalidation driven by a version token. A callable (Proc, lambda, or Symbol naming an instance method) is invoked on the instance at every cache lookup; the returned token is folded into the cache key alongside the normal arguments. When the token changes (e.g. an ActiveRecordupdated_atadvances after asave), the old key no longer matches any entry — the method body is recomputed and stored under the new key without any explicitreset_memocall. Accepts a zero-argument callable invoked viainstance_exec(giving access toself, instance variables, and methods) or aSymbolnaming an instance method. Returns any comparable value as the token: aTime,Integer,String,Array, etc. Old token entries accumulate as stale; pair withttl:or a store adapter's eviction to bound memory. Incompatible withkey:. Composes withnamespace:,ttl:,if:,unless:, andshared_cache:. -
shared_cache: "name"option onmemoize— routes all reads and writes through a globally-registered namedStores::Baseinstance, enabling cross-class cache sharing. Any number of unrelated classes can share the same backing store by referencing the same name. The store is resolved atmemoizedefinition time viaSafeMemoize.shared_cache("name"), which auto-creates aStores::Memoryinstance on first access; supply a custom adapter (Redis, RailsCache, etc.) by callingSafeMemoize.register_shared_cache("name", store)before any class that references the name is loaded. Incompatible withshared:,store:,fiber_local:,ractor_safe:, andmax_size:; composes naturally withnamespace:,ttl:,if:,unless:, andkey:. -
SafeMemoize.shared_cache(name)— returns theStores::Baseinstance for the given name, creating a newStores::Memoryif none is registered. -
SafeMemoize.register_shared_cache(name, store)— registers a customStores::Baseinstance under a name; must be called before any class that uses that name viashared_cache:is loaded. -
SafeMemoize.clear_shared_cache(name)— callsclearon the named store, evicting all entries. No-op for unregistered names. -
SafeMemoize.drop_shared_cache(name)— removes the named store from the registry; subsequentshared_cache(name)calls will auto-create a newMemorystore. -
SafeMemoize.shared_caches— returns a dup of the current registry as aHash{String => Stores::Base}. -
SafeMemoize.reset_shared_caches!— clears the entire registry; useful in test-suiteafterhooks to prevent state leaking between examples. -
namespace:option onmemoize— a String prefix scoped to a single method; prepended to the cache key's first element so that entries with different namespaces never collide, even when sharing the same store or the same per-instance hash. Must be a non-empty string without:. Useful for versioning one method independently of its peers. -
.safe_memoize_namespace/.safe_memoize_namespace=— class-level namespace attribute; applies to everymemoizecall on the class that does not specify its ownnamespace:option. Takes precedence over the globalSafeMemoize::Configuration#namespace. -
SafeMemoize::Configuration#namespace— global namespace prefix applied to everymemoizecall site that has no per-method or class-level namespace set. Set viaSafeMemoize.configure { |c| c.namespace = "v1" }. Useful for versioned deployments and multi-tenant setups. Cleared byreset_configuration!. -
Resolution priority: per-method
namespace:> class.safe_memoize_namespace> globalConfiguration#namespace. -
All introspection methods (
memoized?,memo_count,memo_keys,memo_values,reset_memo,reset_all_memos,dump_memo,cache_stats_for,cache_metrics_reset, shared-cache equivalents, etc.) accept the bare method name regardless of which namespace tier is active; the:methodfield in projections always returns the bare method name. -
Ractor-safe: namespace resolution uses
instance_variable_get(read-only) so worker Ractors can callcompute_cache_keywithout triggering unshareable class-level ivar initialization.