Add safe_memoize_options and copy_on_read: true (v1.4.0)#59
Merged
Conversation
- UNSET sentinel on all memoize keyword args so class defaults can distinguish "not passed" from explicitly nil/false - safe_memoize_options(**opts) class macro: sets defaults for ttl:, max_size:, ttl_refresh:, if:, unless:, key:, cache_bust:, copy_on_read:, namespace:, and store:; per-call options override; raises ArgumentError for mode-switch options (shared:, fiber_local:, ractor_safe:, shared_cache:) - copy_on_read: true on memoize: returns dup / deep_dup of the cached value on every read across all paths (fast, locked, shared, fiber- local, external store); nil and frozen values pass through unchanged; incompatible with ractor_safe: (values are always frozen there) - 23 new examples; 100% line coverage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CHANGELOG: add Unreleased entries for both features - ROADMAP: mark both v1.4.0 items as Shipped - README: feature list entries, two new sections with examples, options reference table rows, safe_memoize_options API table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
safe_memoize_options(**opts)— class-level macro that sets default options for everymemoizecall on the class. Per-call options take precedence; class defaults take precedence over globalSafeMemoize.configuredefaults. Accepts allmemoizeoptions except mode-switch options (shared:,fiber_local:,ractor_safe:,shared_cache:), which must be specified per call.copy_on_read: trueonmemoize— returns adup(ordeep_dupwhen available) of the cached value on every read, preventing callers from mutating shared cached state. Works across all cache paths (per-instance, LRU, shared, fiber-local, external store). Incompatible withractor_safe:(ractor values are always frozen). Can be set as a class-wide default viasafe_memoize_options.Implementation notes
UNSET = Object.new.freezesentinel so everymemoizekeyword arg can distinguish "not passed" from explicitlynil/false, enabling clean three-tier precedence: per-call → class default → global config.dup_fnis a lambda closed over by everydefine_methodblock; applied at all return points in every cache path with zero overhead whencopy_on_read: false(the identity lambda).rescue TypeErrorindup_fn— all types that would raise are frozen in Ruby ≥ 3.3, sov.frozen?catches them first. Result: 100% line coverage.Test plan
bundle exec rake— 811 examples, 0 failures, lint clean, 100% coveragesafe_memoize_options: TTL default, max_size default, copy_on_read default, global config fallback, clearing defaults, disallowed mode-switch optionscopy_on_read:: object identity differs on every hit, cache protected from mutation, nil/frozen pass-through,max_size:locked path,shared:path,ttl:path,deep_dupdelegation,ractor_safe:incompatibility guard🤖 Generated with Claude Code