safe_memoize 0.6.3 → 0.7.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: fcdb1584d3b3306021fa9e49007f686f561cd2658ea811e97fc18d89e36cb20e
4
- data.tar.gz: bc69db3f6d77baf031c6a1b92004ca2b4d5c60346b9546ec29388231e2d3d782
3
+ metadata.gz: 41e3b8e3232ef52d536166c4e701a99a8d7a1e86e2c6d07493d1838d904dbef6
4
+ data.tar.gz: dac922ad348d3ccf14d8946035b5a0e7709cacc899dcc6e0713da77c980ac6ca
5
5
  SHA512:
6
- metadata.gz: f1a8bb8cd2ee519a39b90ed96ebac7a700d759e05bc0561225663ee24b6f595660f25da36dda2db4509fb521a9048a38b904760a06e2d09c2347fd52f5011db0
7
- data.tar.gz: dc1d3b24ecc9fe4a747f1ba12f2a9af335d61f671f1261203a43ff831fc7460dce8e1adf2142c771ff93d5f278127f9a9a54fe408b4361a8a585cebce8ba65a0
6
+ metadata.gz: a7f263ac2b16ff248ccf6afa158aaaae5a7fd69cadef68ff6926c3d96257378df8d4295f2eac8278a68de12492128282c1ffea42da95c178de7e5619981c8634
7
+ data.tar.gz: b50610d8ac36d0c7ff4fe9ed174a7f7864654f4178bc5778a9555eda1d934320b7ee0bcf4de53919e9dfacc35d06d8320f0b9dc8e8a25d6fab6808dfb0c0aab4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.7.0] - 2026-05-18
4
+
5
+ - Add `memo_preload` to batch-warm multiple cache entries in one call
6
+ - `obj.memo_preload(:find, [1], [2], [3])` calls the memoized method for each arg set and caches all results
7
+ - Returns an array of results in the same order as the input arg sets
8
+ - Computes each entry only once — subsequent calls return from cache
9
+ - Add `on_memo_store` hook that fires whenever a value is written to the cache
10
+ - Fires on every cache miss (fast path and LRU path)
11
+ - Also fires when entries are written via `warm_memo` or `load_memo`
12
+ - Does not fire on cache hits or when a conditional `:if`/`:unless` prevents storing
13
+ - Fires on the calling instance for `shared: true` misses
14
+ - Completes the full lifecycle hook set: `on_store`, `on_hit`, `on_miss`, `on_expire`, `on_evict`
15
+ - Add per-method `cache_metrics_reset(:method)` to clear stats for a single method without wiping the rest
16
+ - `cache_metrics_reset` (no args) still clears all metrics as before
17
+ - Add `SafeMemoize.configure` for global default options
18
+ - `SafeMemoize.configure { |c| c.default_ttl = 60 }` applies a TTL to all subsequently memoized methods
19
+ - `SafeMemoize.configure { |c| c.default_max_size = 100 }` sets a global LRU size limit
20
+ - Per-call options (`ttl:`, `max_size:`) override the global defaults
21
+ - `SafeMemoize.reset_configuration!` restores defaults to `nil`
22
+ - Add `memo_touch` to reset the expiry clock on a cached entry without recomputing
23
+ - `memo_touch(:method, *args)` extends the entry's TTL from now using the original TTL window
24
+ - `memo_touch(:method, *args, ttl: 30)` sets a new TTL explicitly
25
+ - Returns `true` on success, `false` if the entry is not cached or already expired
26
+ - Add `shared_memo_age` class method to inspect how long ago a shared entry was cached
27
+ - Add `shared_memo_stale?` class method to check whether a shared entry's TTL has elapsed
28
+ - Update RBS type signatures for all new methods and the `Configuration` class
29
+ - Add `key:` option to `memoize` for class-level cache key generation
30
+ - `memoize :method, key: ->(a, b) { a }` defines a key generator at the class level — calls whose key block returns the same value share one cache entry
31
+ - Instance-level `memoize_with_custom_key` still takes priority over `key:`
32
+ - Composes with all existing options (`ttl:`, `max_size:`, `shared:`, `if:`, etc.)
33
+ - Raises `ArgumentError` if `key:` is not callable
34
+ - Add `shared:` support to `memoize_all` (was already functional via `**options` passthrough; now tested and documented)
35
+ - Add `memo_refresh` to force-recompute a cached entry and store the new value in one call
36
+ - Add `memo_age` to return how many seconds ago an entry was cached (`nil` if not cached or expired)
37
+ - Add `memo_stale?` to check whether a cached entry exists but its TTL has elapsed
38
+
3
39
  ## [0.6.3] - 2026-05-18
4
40
 
5
41
  - Upgrade `softprops/action-gh-release` from v2 to v3 to resolve Node.js 20 deprecation warning in release workflow
@@ -26,5 +26,11 @@ module SafeMemoize
26
26
  def _reset_cache_metrics
27
27
  @__safe_memo_metrics__ = {}
28
28
  end
29
+
30
+ def _reset_cache_metrics_for(method_name)
31
+ return unless defined?(@__safe_memo_metrics__) && @__safe_memo_metrics__
32
+
33
+ @__safe_memo_metrics__.delete_if { |key, _| key[0] == method_name }
34
+ end
29
35
  end
30
36
  end
@@ -22,7 +22,7 @@ module SafeMemoize
22
22
  end
23
23
 
24
24
  def memo_record(value, expires_at:)
25
- {value: value, expires_at: expires_at}
25
+ {value: value, expires_at: expires_at, cached_at: Process.clock_gettime(Process::CLOCK_MONOTONIC)}
26
26
  end
27
27
 
28
28
  def memo_record_value(record)
@@ -2,10 +2,14 @@
2
2
 
3
3
  module SafeMemoize
4
4
  module ClassMethods
5
- def memoize(method_name, ttl: nil, max_size: nil, ttl_refresh: false, if: nil, unless: nil, shared: false)
5
+ def memoize(method_name, ttl: nil, max_size: nil, ttl_refresh: false, if: nil, unless: nil, shared: false, key: nil)
6
6
  method_name = method_name.to_sym
7
7
  visibility = memoized_method_visibility(method_name)
8
8
 
9
+ config = SafeMemoize.configuration
10
+ ttl = config.default_ttl if ttl.nil?
11
+ max_size = config.default_max_size if max_size.nil?
12
+
9
13
  # :if and :unless are reserved Ruby keywords, so they can't be referenced
10
14
  # as local variables directly. binding.local_variable_get is the only way
11
15
  # to read keyword arguments with those names inside the method body.
@@ -38,6 +42,9 @@ module SafeMemoize
38
42
  end
39
43
  raise ArgumentError, ":if must be callable" if cond_if && !cond_if.respond_to?(:call)
40
44
  raise ArgumentError, ":unless must be callable" if cond_unless && !cond_unless.respond_to?(:call)
45
+ raise ArgumentError, ":key must be callable" if key && !key.respond_to?(:call)
46
+
47
+ __safe_memo_class_key_generators__[method_name] = key if key
41
48
 
42
49
  # Normalize to a single "should cache?" predicate
43
50
  condition = if cond_if
@@ -79,7 +86,7 @@ module SafeMemoize
79
86
  value = super(*args, **kwargs)
80
87
  elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
81
88
 
82
- new_record = {value: value, expires_at: memo_expires_at(ttl)}
89
+ new_record = memo_record(value, expires_at: memo_expires_at(ttl))
83
90
 
84
91
  if !condition || condition.call(value)
85
92
  if max_size
@@ -97,6 +104,7 @@ module SafeMemoize
97
104
  lru = klass.send(:__safe_memo_shared_lru_order__)[method_name] ||= {}
98
105
  lru[cache_key] = true
99
106
  end
107
+ call_memo_hooks(:on_store, cache_key, new_record)
100
108
  end
101
109
 
102
110
  record_cache_miss(method_name, args, kwargs, elapsed_time)
@@ -143,6 +151,7 @@ module SafeMemoize
143
151
  @__safe_memo_cache__ ||= {}
144
152
  @__safe_memo_cache__[cache_key] = new_record
145
153
  lru_touch(method_name, cache_key) if max_size
154
+ call_memo_hooks(:on_store, cache_key, new_record)
146
155
  end
147
156
  record_cache_miss(method_name, args, kwargs, elapsed_time)
148
157
  call_memo_hooks(:on_miss, cache_key, new_record)
@@ -166,6 +175,7 @@ module SafeMemoize
166
175
  with_memo_lock do
167
176
  record_cache_miss(method_name, args, kwargs, elapsed_time)
168
177
  new_record = memo_cache_record(cache_key)
178
+ call_memo_hooks(:on_store, cache_key, new_record)
169
179
  call_memo_hooks(:on_miss, cache_key, new_record)
170
180
  end
171
181
 
@@ -225,6 +235,45 @@ module SafeMemoize
225
235
  end
226
236
  end
227
237
 
238
+ def shared_memo_age(method_name, *args, **kwargs)
239
+ method_name = method_name.to_sym
240
+ cache_key = [method_name, args, kwargs]
241
+
242
+ __safe_memo_shared_mutex__.synchronize do
243
+ cache = @__safe_memo_shared_cache__
244
+ return nil unless cache
245
+
246
+ record = cache[cache_key]
247
+ return nil unless record
248
+
249
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
250
+ return nil if record[:expires_at] && record[:expires_at] <= now
251
+
252
+ cached_at = record[:cached_at]
253
+ return nil unless cached_at
254
+
255
+ (now - cached_at).round(6)
256
+ end
257
+ end
258
+
259
+ def shared_memo_stale?(method_name, *args, **kwargs)
260
+ method_name = method_name.to_sym
261
+ cache_key = [method_name, args, kwargs]
262
+
263
+ __safe_memo_shared_mutex__.synchronize do
264
+ cache = @__safe_memo_shared_cache__
265
+ return false unless cache
266
+
267
+ record = cache[cache_key]
268
+ return false unless record
269
+
270
+ expires_at = record[:expires_at]
271
+ return false unless expires_at
272
+
273
+ expires_at <= Process.clock_gettime(Process::CLOCK_MONOTONIC)
274
+ end
275
+ end
276
+
228
277
  def memoize_all(except: [], include_protected: false, include_private: false, **options)
229
278
  excluded = Array(except).map(&:to_sym)
230
279
 
@@ -253,6 +302,10 @@ module SafeMemoize
253
302
  @__safe_memo_shared_lru_order__ ||= {}
254
303
  end
255
304
 
305
+ def __safe_memo_class_key_generators__
306
+ @__safe_memo_class_key_generators__ ||= {}
307
+ end
308
+
256
309
  def memoized_method_visibility(method_name)
257
310
  return :private if private_method_defined?(method_name)
258
311
  return :protected if protected_method_defined?(method_name)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafeMemoize
4
+ class Configuration
5
+ attr_accessor :default_ttl, :default_max_size
6
+
7
+ def initialize
8
+ @default_ttl = nil
9
+ @default_max_size = nil
10
+ end
11
+ end
12
+ end
@@ -18,16 +18,13 @@ module SafeMemoize
18
18
  def compute_cache_key(method_name, args, kwargs)
19
19
  method_name = method_name.to_sym
20
20
 
21
- # Check if a custom key generator is registered
22
- custom_key_block = custom_key_store[method_name]
23
-
24
- if custom_key_block
25
- # Call the custom key generator with args and kwargs
26
- custom_key = custom_key_block.call(*args, **kwargs)
27
- # Wrap in a standard format: [method, custom_key]
28
- [method_name, custom_key]
21
+ # Instance-level key generator takes priority over class-level
22
+ key_block = custom_key_store[method_name] ||
23
+ self.class.send(:__safe_memo_class_key_generators__)[method_name]
24
+
25
+ if key_block
26
+ [method_name, key_block.call(*args, **kwargs)]
29
27
  else
30
- # Use default key generation
31
28
  safe_memo_cache_key(method_name, args, kwargs)
32
29
  end
33
30
  end
@@ -5,13 +5,13 @@ module SafeMemoize
5
5
  private
6
6
 
7
7
  def memo_hook_store
8
- @__safe_memo_hooks__ ||= {on_expire: [], on_evict: [], on_hit: [], on_miss: []}
8
+ @__safe_memo_hooks__ ||= {on_expire: [], on_evict: [], on_hit: [], on_miss: [], on_store: []}
9
9
  end
10
10
 
11
11
  def register_memo_hook(hook_type, &block)
12
12
  raise ArgumentError, "block required" unless block
13
13
 
14
- valid_hooks = [:on_expire, :on_evict, :on_hit, :on_miss]
14
+ valid_hooks = [:on_expire, :on_evict, :on_hit, :on_miss, :on_store]
15
15
  raise ArgumentError, "invalid hook type: #{hook_type}" unless valid_hooks.include?(hook_type)
16
16
 
17
17
  memo_hook_store[hook_type] << block
@@ -26,7 +26,7 @@ module SafeMemoize
26
26
  if hook_type
27
27
  memo_hook_store[hook_type] = []
28
28
  else
29
- @__safe_memo_hooks__ = {on_expire: [], on_evict: [], on_hit: [], on_miss: []}
29
+ @__safe_memo_hooks__ = {on_expire: [], on_evict: [], on_hit: [], on_miss: [], on_store: []}
30
30
  end
31
31
  end
32
32
  end
@@ -75,6 +75,12 @@ module SafeMemoize
75
75
  register_memo_hook(:on_miss, &block)
76
76
  end
77
77
 
78
+ def on_memo_store(&block)
79
+ raise ArgumentError, "block required" unless block
80
+
81
+ register_memo_hook(:on_store, &block)
82
+ end
83
+
78
84
  def clear_memo_hooks(hook_type = nil)
79
85
  with_memo_lock do
80
86
  _clear_memo_hooks(hook_type)
@@ -90,12 +96,21 @@ module SafeMemoize
90
96
 
91
97
  with_memo_lock do
92
98
  @__safe_memo_cache__ ||= {}
93
- @__safe_memo_cache__[cache_key] = memo_record(value, expires_at: memo_expires_at(ttl))
99
+ record = memo_record(value, expires_at: memo_expires_at(ttl))
100
+ @__safe_memo_cache__[cache_key] = record
101
+ call_memo_hooks(:on_store, cache_key, record)
94
102
  end
95
103
 
96
104
  value
97
105
  end
98
106
 
107
+ def memo_preload(method_name, *arg_sets)
108
+ method_name = method_name.to_sym
109
+ arg_sets.map do |args|
110
+ send(method_name, *Array(args))
111
+ end
112
+ end
113
+
99
114
  def dump_memo(method_name = nil)
100
115
  method_name = method_name&.to_sym
101
116
 
@@ -113,13 +128,74 @@ module SafeMemoize
113
128
  with_memo_lock do
114
129
  @__safe_memo_cache__ ||= {}
115
130
  snapshot.each do |cache_key, value|
116
- @__safe_memo_cache__[cache_key] = memo_record(value, expires_at: nil)
131
+ record = memo_record(value, expires_at: nil)
132
+ @__safe_memo_cache__[cache_key] = record
133
+ call_memo_hooks(:on_store, cache_key, record)
117
134
  end
118
135
  end
119
136
 
120
137
  nil
121
138
  end
122
139
 
140
+ def memo_touch(method_name, *args, ttl: nil, **kwargs)
141
+ method_name = method_name.to_sym
142
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
143
+
144
+ with_memo_lock do
145
+ cache = memo_cache_or_nil
146
+ return false unless cache
147
+
148
+ record = cache[cache_key]
149
+ return false unless record && memo_record_live?(record)
150
+
151
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
152
+
153
+ effective_ttl = if ttl
154
+ ttl
155
+ elsif record[:expires_at] && record[:cached_at]
156
+ record[:expires_at] - record[:cached_at]
157
+ end
158
+
159
+ record[:expires_at] = effective_ttl ? now + effective_ttl : nil
160
+ record[:cached_at] = now
161
+ true
162
+ end
163
+ end
164
+
165
+ def memo_refresh(method_name, *args, **kwargs)
166
+ method_name = method_name.to_sym
167
+ reset_memo(method_name, *args, **kwargs)
168
+ send(method_name, *args, **kwargs)
169
+ end
170
+
171
+ def memo_age(method_name, *args, **kwargs)
172
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
173
+
174
+ with_memo_lock do
175
+ record = memo_cache_record(cache_key)
176
+ return nil unless record
177
+
178
+ cached_at = record[:cached_at]
179
+ return nil unless cached_at
180
+
181
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - cached_at).round(6)
182
+ end
183
+ end
184
+
185
+ def memo_stale?(method_name, *args, **kwargs)
186
+ cache_key = safe_memo_cache_key(method_name, args, kwargs)
187
+
188
+ with_memo_lock do
189
+ cache = memo_cache_or_nil
190
+ return false unless cache
191
+
192
+ record = cache[cache_key]
193
+ return false unless record
194
+
195
+ !memo_record_live?(record)
196
+ end
197
+ end
198
+
123
199
  def reset_memo(method_name, *args, **kwargs)
124
200
  method_name = method_name.to_sym
125
201
 
@@ -30,9 +30,13 @@ module SafeMemoize
30
30
  cache_stats[:miss_rate]
31
31
  end
32
32
 
33
- def cache_metrics_reset
33
+ def cache_metrics_reset(method_name = nil)
34
34
  with_memo_lock do
35
- _reset_cache_metrics
35
+ if method_name
36
+ _reset_cache_metrics_for(method_name.to_sym)
37
+ else
38
+ _reset_cache_metrics
39
+ end
36
40
  end
37
41
  end
38
42
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SafeMemoize
4
- VERSION = "0.6.3"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/safe_memoize.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "safe_memoize/version"
4
+ require_relative "safe_memoize/configuration"
4
5
  require_relative "safe_memoize/class_methods"
5
6
  require_relative "safe_memoize/public_methods"
6
7
  require_relative "safe_memoize/cache_store_methods"
@@ -22,4 +23,16 @@ module SafeMemoize
22
23
  def self.prepended(base)
23
24
  base.extend(ClassMethods)
24
25
  end
26
+
27
+ def self.configure
28
+ yield configuration
29
+ end
30
+
31
+ def self.configuration
32
+ @configuration ||= Configuration.new
33
+ end
34
+
35
+ def self.reset_configuration!
36
+ @configuration = Configuration.new
37
+ end
25
38
  end
data/sig/safe_memoize.rbs CHANGED
@@ -5,7 +5,7 @@ module SafeMemoize
5
5
  type default_memo_key = [Symbol, Array[untyped], Hash[Symbol, untyped]]
6
6
  type custom_memo_key = [Symbol, untyped]
7
7
  type memo_key = default_memo_key | custom_memo_key
8
- type memo_record = { value: untyped, expires_at: Float? }
8
+ type memo_record = { value: untyped, expires_at: Float?, cached_at: Float }
9
9
 
10
10
  @__safe_memo_cache__: Hash[memo_key, memo_record]?
11
11
  @__safe_memo_mutex__: Mutex?
@@ -14,20 +14,33 @@ module SafeMemoize
14
14
  @__safe_memo_shared_lru_order__: Hash[Symbol, Hash[memo_key, true]]?
15
15
 
16
16
  def self.prepended: (Class base) -> void
17
+ def self.configure: () { (Configuration) -> void } -> void
18
+ def self.configuration: () -> Configuration
19
+ def self.reset_configuration!: () -> Configuration
20
+
21
+ class Configuration
22
+ attr_accessor default_ttl: Numeric?
23
+ attr_accessor default_max_size: Integer?
24
+
25
+ def initialize: () -> void
26
+ end
17
27
 
18
28
  module ClassMethods
19
- def memoize: (Symbol | String method_name, ?ttl: Numeric?, ?max_size: Integer?, ?ttl_refresh: bool, ?if: (^(untyped result) -> boolish)?, ?unless: (^(untyped result) -> boolish)?, ?shared: bool) -> void
20
- def memoize_all: (?except: Array[Symbol | String], ?include_protected: bool, ?include_private: bool, ?ttl: Numeric?, ?max_size: Integer?, ?if: (^(untyped result) -> boolish)?, ?unless: (^(untyped result) -> boolish)?) -> void
29
+ def memoize: (Symbol | String method_name, ?ttl: Numeric?, ?max_size: Integer?, ?ttl_refresh: bool, ?if: (^(untyped result) -> boolish)?, ?unless: (^(untyped result) -> boolish)?, ?shared: bool, ?key: (^(*untyped args, **untyped kwargs) -> untyped)?) -> void
30
+ def memoize_all: (?except: Array[Symbol | String], ?include_protected: bool, ?include_private: bool, ?ttl: Numeric?, ?max_size: Integer?, ?if: (^(untyped result) -> boolish)?, ?unless: (^(untyped result) -> boolish)?, ?shared: bool, ?key: (^(*untyped args, **untyped kwargs) -> untyped)?) -> void
21
31
  def reset_shared_memo: (Symbol | String method_name, *untyped args, **untyped kwargs) -> void
22
32
  def reset_all_shared_memos: () -> void
23
33
  def shared_memoized?: (Symbol | String method_name, *untyped args, **untyped kwargs) -> bool
24
34
  def shared_memo_count: (?Symbol | String method_name) -> Integer
35
+ def shared_memo_age: (Symbol | String method_name, *untyped args, **untyped kwargs) -> Float?
36
+ def shared_memo_stale?: (Symbol | String method_name, *untyped args, **untyped kwargs) -> bool
25
37
 
26
38
  private
27
39
 
28
40
  def __safe_memo_shared_cache__: () -> Hash[memo_key, memo_record]
29
41
  def __safe_memo_shared_mutex__: () -> Mutex
30
42
  def __safe_memo_shared_lru_order__: () -> Hash[Symbol, Hash[memo_key, true]]
43
+ def __safe_memo_class_key_generators__: () -> Hash[Symbol, Proc]
31
44
  def memoized_method_visibility: (Symbol method_name) -> Symbol
32
45
  end
33
46
 
@@ -43,10 +56,16 @@ module SafeMemoize
43
56
  def on_memo_evict: { (memo_key cache_key, memo_record record) -> untyped } -> void
44
57
  def on_memo_hit: { (memo_key cache_key, memo_record record) -> untyped } -> void
45
58
  def on_memo_miss: { (memo_key cache_key, memo_record record) -> untyped } -> void
59
+ def on_memo_store: { (memo_key cache_key, memo_record record) -> untyped } -> void
46
60
  def clear_memo_hooks: (Symbol? hook_type) -> void
47
61
  def warm_memo: (Symbol | String method_name, *untyped args, ?ttl: Numeric?, **untyped kwargs) { () -> untyped } -> untyped
62
+ def memo_preload: (Symbol | String method_name, *Array[untyped] arg_sets) -> Array[untyped]
48
63
  def dump_memo: (?Symbol | String method_name) -> Hash[memo_key, untyped]
49
64
  def load_memo: (Hash[memo_key, untyped] snapshot) -> nil
65
+ def memo_touch: (Symbol | String method_name, *untyped args, ?ttl: Numeric?, **untyped kwargs) -> bool
66
+ def memo_refresh: (Symbol | String method_name, *untyped args, **untyped kwargs) -> untyped
67
+ def memo_age: (Symbol | String method_name, *untyped args, **untyped kwargs) -> Float?
68
+ def memo_stale?: (Symbol | String method_name, *untyped args, **untyped kwargs) -> bool
50
69
  def reset_memo: (Symbol | String method_name, *untyped args, **untyped kwargs) -> void
51
70
  def reset_all_memos: () -> void
52
71
  end
@@ -92,11 +111,11 @@ module SafeMemoize
92
111
  end
93
112
 
94
113
  module HooksMethods
95
- @__safe_memo_hooks__: { on_expire: Array[Proc], on_evict: Array[Proc], on_hit: Array[Proc], on_miss: Array[Proc] }?
114
+ @__safe_memo_hooks__: { on_expire: Array[Proc], on_evict: Array[Proc], on_hit: Array[Proc], on_miss: Array[Proc], on_store: Array[Proc] }?
96
115
 
97
116
  private
98
117
 
99
- def memo_hook_store: () -> { on_expire: Array[Proc], on_evict: Array[Proc], on_hit: Array[Proc], on_miss: Array[Proc] }
118
+ def memo_hook_store: () -> { on_expire: Array[Proc], on_evict: Array[Proc], on_hit: Array[Proc], on_miss: Array[Proc], on_store: Array[Proc] }
100
119
  def register_memo_hook: (Symbol hook_type) { (memo_key cache_key, memo_record record) -> untyped } -> void
101
120
  def call_memo_hooks: (Symbol hook_type, memo_key cache_key, memo_record record) -> void
102
121
  def _clear_memo_hooks: (Symbol? hook_type) -> void
@@ -118,7 +137,7 @@ module SafeMemoize
118
137
  def cache_stats_for: (Symbol | String method_name) -> Hash[Symbol, untyped]
119
138
  def cache_hit_rate: () -> Float
120
139
  def cache_miss_rate: () -> Float
121
- def cache_metrics_reset: () -> void
140
+ def cache_metrics_reset: (?Symbol | String method_name) -> void
122
141
 
123
142
  private
124
143
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe_memoize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -50,6 +50,7 @@ files:
50
50
  - lib/safe_memoize/cache_record_methods.rb
51
51
  - lib/safe_memoize/cache_store_methods.rb
52
52
  - lib/safe_memoize/class_methods.rb
53
+ - lib/safe_memoize/configuration.rb
53
54
  - lib/safe_memoize/custom_key_methods.rb
54
55
  - lib/safe_memoize/hooks_methods.rb
55
56
  - lib/safe_memoize/inspection_methods.rb