typed_cache 0.1.1 → 0.2.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +116 -19
  4. data/examples.md +106 -50
  5. data/lib/typed_cache/backends/memory.rb +9 -6
  6. data/lib/typed_cache/backends.rb +6 -8
  7. data/lib/typed_cache/cache_builder.rb +72 -19
  8. data/lib/typed_cache/cache_key.rb +2 -0
  9. data/lib/typed_cache/cache_ref.rb +4 -0
  10. data/lib/typed_cache/clock.rb +31 -14
  11. data/lib/typed_cache/decorator.rb +17 -0
  12. data/lib/typed_cache/{store → decorators}/instrumented.rb +19 -23
  13. data/lib/typed_cache/decorators.rb +7 -3
  14. data/lib/typed_cache/errors.rb +9 -1
  15. data/lib/typed_cache/instrumenter.rb +43 -0
  16. data/lib/typed_cache/instrumenters/active_support.rb +28 -0
  17. data/lib/typed_cache/instrumenters/mixins/namespaced_singleton.rb +52 -0
  18. data/lib/typed_cache/instrumenters/mixins.rb +8 -0
  19. data/lib/typed_cache/instrumenters/monitor.rb +27 -0
  20. data/lib/typed_cache/instrumenters/null.rb +26 -0
  21. data/lib/typed_cache/instrumenters.rb +38 -0
  22. data/lib/typed_cache/registry.rb +15 -0
  23. data/lib/typed_cache/store.rb +3 -0
  24. data/lib/typed_cache/version.rb +1 -1
  25. data/lib/typed_cache.rb +30 -14
  26. data/sig/generated/typed_cache/backends.rbs +2 -0
  27. data/sig/generated/typed_cache/cache_builder.rbs +13 -2
  28. data/sig/generated/typed_cache/clock.rbs +19 -9
  29. data/sig/generated/typed_cache/decorator.rbs +8 -0
  30. data/sig/generated/typed_cache/decorators/instrumented.rbs +35 -0
  31. data/sig/generated/typed_cache/decorators.rbs +2 -0
  32. data/sig/generated/typed_cache/errors.rbs +2 -0
  33. data/sig/generated/typed_cache/instrumenter.rbs +31 -0
  34. data/sig/generated/typed_cache/instrumenters/active_support.rbs +20 -0
  35. data/sig/generated/typed_cache/instrumenters/mixins/namespaced_singleton.rbs +33 -0
  36. data/sig/generated/typed_cache/instrumenters/mixins.rbs +8 -0
  37. data/sig/generated/typed_cache/instrumenters/monitor.rbs +19 -0
  38. data/sig/generated/typed_cache/instrumenters/null.rbs +21 -0
  39. data/sig/generated/typed_cache/instrumenters.rbs +24 -0
  40. data/sig/generated/typed_cache/registry.rbs +8 -0
  41. data/sig/generated/typed_cache/store/instrumented.rbs +2 -6
  42. data/sig/generated/typed_cache/store.rbs +3 -0
  43. data/sig/generated/typed_cache.rbs +6 -6
  44. data/typed_cache.gemspec +4 -3
  45. data.tar.gz.sig +0 -0
  46. metadata +25 -27
  47. metadata.gz.sig +0 -0
  48. data/lib/typed_cache/instrumentation.rb +0 -112
  49. data/sig/generated/typed_cache/instrumentation.rbs +0 -30
  50. data/sig/handwritten/gems/zeitwerk/2.7/zeitwerk.rbs +0 -9
@@ -0,0 +1,35 @@
1
+ # Generated from lib/typed_cache/decorators/instrumented.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ # Decorator that adds instrumentation to any Store implementation
5
+ # This decorator can wrap any store to add ActiveSupport::Notifications
6
+ # @rbs generic V
7
+ class Decorators::Instrumented[V]
8
+ include Decorator[V]
9
+
10
+ extend Forwardable
11
+
12
+ attr_reader store: TypedCache::Store[V]
13
+
14
+ attr_reader instrumenter: Instrumenter
15
+
16
+ # @rbs (Symbol, ?operation: String) ?{ (*untyped, **untyped) -> String } -> void
17
+ private def self.instrument: (Symbol, ?operation: String) ?{ (*untyped, **untyped) -> String } -> void
18
+
19
+ # : (TypedCache::Store[V], instrumenter: Instrumenter) -> void
20
+ def initialize: (TypedCache::Store[V], instrumenter: Instrumenter) -> void
21
+
22
+ # @rbs override
23
+ # : -> String
24
+ def store_type: ...
25
+
26
+ # @rbs override
27
+ # @rbs (key) -> CacheRef[V]
28
+ def ref: ...
29
+
30
+ # Additional methods that might exist on the wrapped store
31
+ def respond_to_missing?: (untyped method_name, ?untyped include_private) -> untyped
32
+
33
+ def method_missing: (untyped method_name, *untyped args) ?{ (?) -> untyped } -> untyped
34
+ end
35
+ end
@@ -13,12 +13,14 @@ module TypedCache
13
13
  # .with_decorator(:my_decorator)
14
14
  # .build.value
15
15
  module Decorators
16
+ # @api private
16
17
  # Default decorator set – starts with instrumentation only, but this registry
17
18
  # lets end-users register their own via `Decorators.register`.
18
19
  REGISTRY: untyped
19
20
 
20
21
  extend Forwardable
21
22
 
23
+ # @api private
22
24
  # @rbs () -> Registry[Store[untyped]]
23
25
  def self.registry: () -> Registry[Store[untyped]]
24
26
  end
@@ -3,6 +3,8 @@
3
3
  module TypedCache
4
4
  # Base error class for TypedCache operations
5
5
  class Error < StandardError
6
+ # @rbs (*untyped) -> void
7
+ def initialize: (*untyped) -> void
6
8
  end
7
9
 
8
10
  # Store operation errors (network, I/O, etc.)
@@ -0,0 +1,31 @@
1
+ # Generated from lib/typed_cache/instrumenter.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ # Instrumenter for cache operations
5
+ module Instrumenter
6
+ type event = Dry::Events::Event | ActiveSupport::Notifications::Event
7
+
8
+ # @rbs [R](String, String, **untyped) { -> R } -> R
9
+ def instrument: [R] (String, String, **untyped) { () -> R } -> R
10
+
11
+ # @rbs (String, **untyped) { (event) -> void } -> void
12
+ def subscribe: (String, **untyped) { (event) -> void } -> void
13
+
14
+ # : -> String
15
+ def namespace: () -> String
16
+
17
+ # @rbs (String, String, **untyped) -> Hash[Symbol, untyped]
18
+ def build_payload: (String, String, **untyped) -> Hash[Symbol, untyped]
19
+
20
+ # @rbs () -> bool
21
+ def enabled?: () -> bool
22
+
23
+ # @rbs (String) -> String
24
+ def event_name: (String) -> String
25
+
26
+ private
27
+
28
+ # @rbs () -> TypedCache::_TypedCacheInstrumentationConfig
29
+ def config: () -> TypedCache::_TypedCacheInstrumentationConfig
30
+ end
31
+ end
@@ -0,0 +1,20 @@
1
+ # Generated from lib/typed_cache/instrumenters/active_support.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ # Instrumenter for ActiveSupport::Notifications
6
+ class ActiveSupport
7
+ include Instrumenter
8
+
9
+ include Mixins::NamespacedSingleton
10
+
11
+ # @rbs override
12
+ # : [R] (String, String, Hash[Symbol, untyped]) { -> R } -> R
13
+ def instrument: ...
14
+
15
+ # @rbs override
16
+ # @rbs (String, **top) { (event) -> void } -> void
17
+ def subscribe: ...
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # Generated from lib/typed_cache/instrumenters/mixins/namespaced_singleton.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ module Mixins
6
+ module NamespacedSingleton
7
+ # @rbs () -> Array[Class[Instrumenter & NamespacedSingleton]]
8
+ def self.all: () -> Array[Class[Instrumenter & NamespacedSingleton]]
9
+
10
+ # @rbs (Class[Instrumenter & NamespacedSingleton]) -> void
11
+ def self.included: (Class[Instrumenter & NamespacedSingleton]) -> void
12
+
13
+ # @rbs override
14
+ # @rbs () -> String
15
+ def namespace: ...
16
+
17
+ # @rbs (String | Namespace) -> void
18
+ def initialize: (String | Namespace) -> void
19
+
20
+ module ClassMethods
21
+ # @rbs (String | Namespace) -> class
22
+ def new: (String | Namespace) -> class
23
+
24
+ # @rbs (String) -> maybe[class]
25
+ def get: (String) -> maybe[class]
26
+
27
+ # @rbs () -> Concurrent::Map[String, Class[Instrumenter & NamespacedSingleton]]
28
+ def namespace_cache: () -> Concurrent::Map[String, Class[Instrumenter & NamespacedSingleton]]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ # Generated from lib/typed_cache/instrumenters/mixins.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ module Mixins
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ # Generated from lib/typed_cache/instrumenters/monitor.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ class Monitor
6
+ include Instrumenter
7
+
8
+ include Mixins::NamespacedSingleton
9
+
10
+ # @rbs override
11
+ # : [R] (String, String, **untyped) { -> R } -> R
12
+ def instrument: ...
13
+
14
+ # @rbs override
15
+ # @rbs (String, **top) { (event) -> void } -> void
16
+ def subscribe: ...
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # Generated from lib/typed_cache/instrumenters/null.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ # A no-op implementation used when instrumentation is disabled.
6
+ # It fulfils the Instrumenter contract but simply yields.
7
+ class Null
8
+ include Instrumenter
9
+
10
+ include Mixins::NamespacedSingleton
11
+
12
+ # @rbs override
13
+ # [R] (String, String, **untyped) { -> R } -> R
14
+ def instrument: ...
15
+
16
+ # @rbs override
17
+ # @rbs (String, **top) { (event) -> void } -> void
18
+ def subscribe: ...
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # Generated from lib/typed_cache/instrumenters.rb with RBS::Inline
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ # @api private
6
+ # Registry mapping symbols to instrumenter classes. We can't reuse the generic
7
+ # Registry class directly because many instrumenters mix in `Singleton`,
8
+ # making `.new` inaccessible. Instead we implement a thin facade that
9
+ # returns either the singleton instance (preferred) or a fresh instance.
10
+ REGISTRY: Registry[Symbol, Class[Instrumenter]]
11
+
12
+ extend Forwardable
13
+
14
+ # @api private
15
+ # @rbs () -> Registry[Symbol, Class[Instrumenter]]
16
+ def self.registry: () -> Registry[Symbol, Class[Instrumenter]]
17
+
18
+ def resolve: (Symbol, **untyped) -> either[Error, Instrumenter]
19
+
20
+ def available: () -> Array[Symbol]
21
+
22
+ def registered?: (Symbol) -> Boolean
23
+ end
24
+ end
@@ -7,6 +7,12 @@ module TypedCache
7
7
  # @rbs (String, Hash[Symbol, Class[T]]) -> void
8
8
  def initialize: (String, Hash[Symbol, Class[T]]) -> void
9
9
 
10
+ # @rbs (Registry[T]) -> Registry[T]
11
+ def initialize_copy: (Registry[T]) -> Registry[T]
12
+
13
+ # @rbs () -> void
14
+ def clear: () -> void
15
+
10
16
  # @rbs (Symbol, *untyped, **untyped) -> either[Error, T]
11
17
  def resolve: (Symbol, *untyped, **untyped) -> either[Error, T]
12
18
 
@@ -21,5 +27,7 @@ module TypedCache
21
27
 
22
28
  # @rbs (Symbol) -> bool
23
29
  def registered?: (Symbol) -> bool
30
+
31
+ attr_reader registry: Hash[Symbol, Class[T]]
24
32
  end
25
33
  end
@@ -14,8 +14,8 @@ module TypedCache
14
14
  # @rbs (Symbol, ?operation: String) ?{ (*untyped, **untyped) -> String } -> void
15
15
  private def self.instrument: (Symbol, ?operation: String) ?{ (*untyped, **untyped) -> String } -> void
16
16
 
17
- # : (TypedCache::Store[V]) -> void
18
- def initialize: (TypedCache::Store[V]) -> void
17
+ # : (TypedCache::Store[V], instrumenter: Instrumenter) -> void
18
+ def initialize: (TypedCache::Store[V], instrumenter: Instrumenter) -> void
19
19
 
20
20
  # @rbs override
21
21
  # : -> String
@@ -25,10 +25,6 @@ module TypedCache
25
25
  # : -> String
26
26
  def store_type: ...
27
27
 
28
- # @rbs override
29
- # : (cache_key) -> either[Error, CacheRef[V]]
30
- def ref: ...
31
-
32
28
  # Additional methods that might exist on the wrapped store
33
29
  def respond_to_missing?: (untyped method_name, ?untyped include_private) -> untyped
34
30
 
@@ -72,6 +72,9 @@ module TypedCache
72
72
  # @rbs (cache_key) { () -> V } -> either[Error, Snapshot[V]]
73
73
  def fetch: (cache_key) { () -> V } -> either[Error, Snapshot[V]]
74
74
 
75
+ # @rbs () -> Instrumenter
76
+ def instrumenter: () -> Instrumenter
77
+
75
78
  # Returns the namespace for this store (for instrumentation/debugging)
76
79
  # @rbs () -> Namespace
77
80
  def namespace: () -> Namespace
@@ -23,12 +23,12 @@ module TypedCache
23
23
 
24
24
  def config: () -> _TypedCacheConfig
25
25
 
26
- # @rbs () -> singleton(Instrumentation)
27
- def self.instrumentation: () -> singleton(Instrumentation)
26
+ # @rbs () -> singleton(Backends)
27
+ def self.backends: () -> singleton(Backends)
28
28
 
29
- # @rbs () -> Registry[backend[untyped]]
30
- def self.backends: () -> Registry[backend[untyped]]
29
+ # @rbs () -> singleton(Decorators)
30
+ def self.decorators: () -> singleton(Decorators)
31
31
 
32
- # @rbs () -> Register[decorator[untyped]]
33
- def self.decorators: () -> Register[decorator[untyped]]
32
+ # @rbs () -> singleton(Instrumenters)
33
+ def self.instrumenters: () -> singleton(Instrumenters)
34
34
  end
data/typed_cache.gemspec CHANGED
@@ -29,13 +29,14 @@ Gem::Specification.new do |spec|
29
29
  spec.cert_chain = [Gem.default_cert_path]
30
30
 
31
31
  spec.required_ruby_version = '>= 3.2.0'
32
- spec.add_dependency('concurrent-ruby', '~> 1.3.5')
33
- spec.add_dependency('concurrent-ruby-edge', '~>0.7.2')
32
+
33
+ spec.add_dependency('concurrent-ruby', '~> 1.0')
34
+
34
35
  spec.add_dependency('dry-configurable', '~> 1.0')
36
+ spec.add_dependency('dry-monitor', '~>1.0')
35
37
  spec.add_dependency('dry-struct', '~> 1.0')
36
38
  spec.add_dependency('dry-types', '~>1.0')
37
39
  spec.add_dependency('multi_json', '~> 1.17')
38
- spec.add_dependency('zeitwerk', '~> 2.7')
39
40
 
40
41
  spec.extra_rdoc_files = Dir['README*', 'LICENSE*', 'examples*']
41
42
  spec.files = Dir['*.gemspec', 'lib/**/*', 'sig/**/*']
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Autumn Winter
@@ -35,7 +35,7 @@ cert_chain:
35
35
  uuhUq525FXA3gYeLAfnCSHNm7D1H7whsXYd3z+gKvEhSEQPb3mSBQ+31TDFmA5k7
36
36
  q7YL3dwhxKPXtnAboI+30XBImpdizFG9Nyqgzgj/kD0QwVzL0QsVdVWnNTJXrm4n
37
37
  -----END CERTIFICATE-----
38
- date: 2025-07-21 00:00:00.000000000 Z
38
+ date: 2025-08-06 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: concurrent-ruby
@@ -43,30 +43,30 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 1.3.5
46
+ version: '1.0'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 1.3.5
53
+ version: '1.0'
54
54
  - !ruby/object:Gem::Dependency
55
- name: concurrent-ruby-edge
55
+ name: dry-configurable
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 0.7.2
60
+ version: '1.0'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 0.7.2
67
+ version: '1.0'
68
68
  - !ruby/object:Gem::Dependency
69
- name: dry-configurable
69
+ name: dry-monitor
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
@@ -121,20 +121,6 @@ dependencies:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
123
  version: '1.17'
124
- - !ruby/object:Gem::Dependency
125
- name: zeitwerk
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: '2.7'
131
- type: :runtime
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '2.7'
138
124
  description: |
139
125
  TypedCache is a Ruby caching library designed to eliminate common caching pitfalls by providing a monadic, type-safe API that makes cache operations explicit and predictable. Cache interactions are first-class operations with
140
126
  comprehensive error handling and transparent state management. The library supports wrapping other caching libraries via custom backends and ActiveSupport::Cache is supported out of the box.
@@ -161,15 +147,21 @@ files:
161
147
  - lib/typed_cache/clock.rb
162
148
  - lib/typed_cache/decorator.rb
163
149
  - lib/typed_cache/decorators.rb
150
+ - lib/typed_cache/decorators/instrumented.rb
164
151
  - lib/typed_cache/either.rb
165
152
  - lib/typed_cache/errors.rb
166
- - lib/typed_cache/instrumentation.rb
153
+ - lib/typed_cache/instrumenter.rb
154
+ - lib/typed_cache/instrumenters.rb
155
+ - lib/typed_cache/instrumenters/active_support.rb
156
+ - lib/typed_cache/instrumenters/mixins.rb
157
+ - lib/typed_cache/instrumenters/mixins/namespaced_singleton.rb
158
+ - lib/typed_cache/instrumenters/monitor.rb
159
+ - lib/typed_cache/instrumenters/null.rb
167
160
  - lib/typed_cache/maybe.rb
168
161
  - lib/typed_cache/namespace.rb
169
162
  - lib/typed_cache/registry.rb
170
163
  - lib/typed_cache/snapshot.rb
171
164
  - lib/typed_cache/store.rb
172
- - lib/typed_cache/store/instrumented.rb
173
165
  - lib/typed_cache/version.rb
174
166
  - sig/generated/typed_cache.rbs
175
167
  - sig/generated/typed_cache/backend.rbs
@@ -182,9 +174,16 @@ files:
182
174
  - sig/generated/typed_cache/clock.rbs
183
175
  - sig/generated/typed_cache/decorator.rbs
184
176
  - sig/generated/typed_cache/decorators.rbs
177
+ - sig/generated/typed_cache/decorators/instrumented.rbs
185
178
  - sig/generated/typed_cache/either.rbs
186
179
  - sig/generated/typed_cache/errors.rbs
187
- - sig/generated/typed_cache/instrumentation.rbs
180
+ - sig/generated/typed_cache/instrumenter.rbs
181
+ - sig/generated/typed_cache/instrumenters.rbs
182
+ - sig/generated/typed_cache/instrumenters/active_support.rbs
183
+ - sig/generated/typed_cache/instrumenters/mixins.rbs
184
+ - sig/generated/typed_cache/instrumenters/mixins/namespaced_singleton.rbs
185
+ - sig/generated/typed_cache/instrumenters/monitor.rbs
186
+ - sig/generated/typed_cache/instrumenters/null.rbs
188
187
  - sig/generated/typed_cache/maybe.rbs
189
188
  - sig/generated/typed_cache/namespace.rbs
190
189
  - sig/generated/typed_cache/registry.rbs
@@ -192,14 +191,13 @@ files:
192
191
  - sig/generated/typed_cache/store.rbs
193
192
  - sig/generated/typed_cache/store/instrumented.rbs
194
193
  - sig/generated/typed_cache/version.rbs
195
- - sig/handwritten/gems/zeitwerk/2.7/zeitwerk.rbs
196
194
  - typed_cache.gemspec
197
195
  homepage: https://github.com/glossawy/typed_cache
198
196
  licenses:
199
197
  - Apache-2.0
200
198
  metadata:
201
199
  issue_tracker_uri: https://github.com/glossawy/typed_cache/issues
202
- changelog_uri: https://github.com/glossawy/typed_cache/blob/main/VERSIONS.adoc#011
200
+ changelog_uri: https://github.com/glossawy/typed_cache/blob/main/VERSIONS.adoc#020
203
201
  license_uri: https://github.com/glossawy/typed_cache/blob/main/LICENSE
204
202
  label: caching
205
203
  labels: typed_cache,ruby,caching,type-safety,rails,rbs
metadata.gz.sig CHANGED
Binary file
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'dry/configurable'
4
-
5
- module TypedCache
6
- # Instrumentation hooks for ActiveSupport::Notifications integration
7
- # All instrumentation is explicit and opt-in - no automatic behavior
8
- module Instrumentation
9
- class << self
10
- # @rbs! type config = TypedCache::_TypedCacheInstrumentationConfig
11
-
12
- # @rbs () -> config
13
- def config
14
- TypedCache.config.instrumentation
15
- end
16
-
17
- # Check if ActiveSupport::Notifications is available
18
- # @rbs () -> bool
19
- def notifications_available?
20
- defined?(ActiveSupport::Notifications)
21
- end
22
-
23
- # Main instrumentation method
24
- #: [T] (String, String, String, Hash[Symbol, untyped]) { -> T } -> T
25
- def instrument(operation, namespace, key, payload = {})
26
- return yield unless config.enabled && notifications_available?
27
-
28
- event_name = "#{operation}.#{config.namespace}"
29
- start_time = current_time
30
-
31
- begin
32
- result = yield
33
-
34
- # Determine success and extract metadata
35
- success, snapshot_data = extract_result_metadata(result)
36
-
37
- final_payload = base_payload(namespace, key, start_time).merge(payload).merge({
38
- success: success,
39
- **snapshot_data,
40
- })
41
-
42
- ActiveSupport::Notifications.instrument(event_name, final_payload) do
43
- # This block is called by subscribers who want the result
44
- result
45
- end
46
-
47
- result
48
- rescue => error
49
- error_payload = base_payload(namespace, key, start_time).merge(payload).merge({
50
- success: false,
51
- error: error.class.name,
52
- error_message: error.message,
53
- })
54
-
55
- ActiveSupport::Notifications.instrument(event_name, error_payload)
56
- raise
57
- end
58
- end
59
-
60
- private
61
-
62
- # Cross-platform current time helper (uses Time.current when available)
63
- #: -> Time
64
- def current_time
65
- if Time.respond_to?(:current)
66
- Time.current
67
- else
68
- Time.now
69
- end
70
- end
71
-
72
- # @rbs (String, String, Time) -> Hash[Symbol, untyped]
73
- def base_payload(namespace, key, start_time)
74
- {
75
- namespace: namespace,
76
- key: key,
77
- duration: (current_time - start_time) * 1000.0, # milliseconds
78
- store_type: nil, # Will be set by caller if available
79
- }
80
- end
81
-
82
- # @rbs (Either[StandardError, Snapshot]) -> [bool, Hash[Symbol, untyped]]
83
- def extract_result_metadata(result)
84
- case result
85
- when Either
86
- if result.right?
87
- snapshot = result.value
88
- if snapshot.is_a?(Snapshot)
89
- [true, {
90
- cache_hit: snapshot.from_cache?,
91
- cache_miss: !snapshot.from_cache?,
92
- source: snapshot.source,
93
- snapshot_age: snapshot.age,
94
- },]
95
- else
96
- [true, { cache_hit: false, cache_miss: true }]
97
- end
98
- else
99
- error_data = {
100
- cache_hit: false,
101
- cache_miss: true,
102
- error_type: result.error.class.name,
103
- }
104
- [false, error_data]
105
- end
106
- else
107
- [true, {}]
108
- end
109
- end
110
- end
111
- end
112
- end
@@ -1,30 +0,0 @@
1
- # Generated from lib/typed_cache/instrumentation.rb with RBS::Inline
2
-
3
- module TypedCache
4
- # Instrumentation hooks for ActiveSupport::Notifications integration
5
- # All instrumentation is explicit and opt-in - no automatic behavior
6
- module Instrumentation
7
- type config = TypedCache::_TypedCacheInstrumentationConfig
8
-
9
- # @rbs () -> config
10
- def self.config: () -> config
11
-
12
- # Check if ActiveSupport::Notifications is available
13
- # @rbs () -> bool
14
- def self.notifications_available?: () -> bool
15
-
16
- # Main instrumentation method
17
- # : [T] (String, String, String, Hash[Symbol, untyped]) { -> T } -> T
18
- def self.instrument: [T] (String, String, String, Hash[Symbol, untyped]) { () -> T } -> T
19
-
20
- # Cross-platform current time helper (uses Time.current when available)
21
- # : -> Time
22
- private def self.current_time: () -> Time
23
-
24
- # @rbs (String, String, Time) -> Hash[Symbol, untyped]
25
- private def self.base_payload: (String, String, Time) -> Hash[Symbol, untyped]
26
-
27
- # @rbs (Either[StandardError, Snapshot]) -> [bool, Hash[Symbol, untyped]]
28
- private def self.extract_result_metadata: (Either[StandardError, Snapshot]) -> [ bool, Hash[Symbol, untyped] ]
29
- end
30
- end
@@ -1,9 +0,0 @@
1
-
2
-
3
- module Zeitwerk
4
- class Loader
5
- def self.for_gem: -> instance
6
-
7
- def setup: -> void
8
- end
9
- end