Skip to content

Add plugin/extension architecture: SafeMemoize::Extension API#51

Merged
eclectic-coding merged 3 commits into
mainfrom
feature/extension-api
May 28, 2026
Merged

Add plugin/extension architecture: SafeMemoize::Extension API#51
eclectic-coding merged 3 commits into
mainfrom
feature/extension-api

Conversation

@eclectic-coding
Copy link
Copy Markdown
Owner

Summary

  • Adds SafeMemoize::Extension — a mixin DSL for building extensions that add custom memoize options and global lifecycle handlers without monkey-patching SafeMemoize internals
  • memoize now accepts **extension_options; unknown kwargs are validated against registered extensions at definition time and raise ArgumentError if unclaimed
  • Extensions inject standard options (cache_bust:, ttl:, namespace:, store:, etc.) at memoize definition time via handles_option processor blocks
  • Global on_cache_event handlers fire after every matching lifecycle event across all memoized methods (main Ractor only)
  • Duck-type compatible — no extend SafeMemoize::Extension required
  • Also removes shipped items from ROADMAP ahead of the v1.3.0 release

Registry API

SafeMemoize.register_extension(:name, MyExtension)
SafeMemoize.unregister_extension(:name)
SafeMemoize.extensions / reset_extensions!

Test plan

  • bundle exec rake — 788 examples, 0 failures, lint clean
  • spec/extension_spec.rb — 25 new examples covering DSL, registry, custom option injection, global event dispatch, and duck-type compatibility

🤖 Generated with Claude Code

eclectic-coding and others added 3 commits May 28, 2026 16:50
Third-party gems can now extend memoize without monkey-patching:

  module MyExtension
    extend SafeMemoize::Extension

    handles_option :active_record_bust do |value, method_name, _opts|
      { cache_bust: -> { send(:updated_at) } }
    end

    on_cache_event :miss do |klass, method_name, _key, _record|
      MyMetrics.increment("cache.miss")
    end
  end

  SafeMemoize.register_extension(:active_record_bust, MyExtension)

SafeMemoize::Extension DSL (via extend):
  handles_option(name, &processor) — declare a custom memoize option;
    processor returns a Hash of standard options to inject at definition time
  on_cache_event(*types, &handler) — global lifecycle event handler;
    fires after every matching event across all memoized methods

Registry API on SafeMemoize module:
  register_extension(name, ext) / unregister_extension(name)
  extensions / reset_extensions!
  extension_for_option(name) / dispatch_extension_events(...)

memoize now accepts **extension_options for unknown kwargs; each key
is validated against registered extensions at definition time and the
injected standard options (ttl:, namespace:, cache_bust:, etc.) are
applied before the normal memoize logic runs.

Duck-type compatibility: any object responding to handled_options,
process_memoize_option, and dispatch_cache_event works without
requiring extend SafeMemoize::Extension.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shipped items (namespace, cross-instance sharing, cache_bust, extension
architecture) are removed. DSL refinements remains as the only planned
item — it requires community feedback before implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@eclectic-coding eclectic-coding merged commit d84f62a into main May 28, 2026
4 checks passed
@eclectic-coding eclectic-coding deleted the feature/extension-api branch May 28, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant