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
@@ -1,22 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'dry/struct'
4
+ require 'dry/types'
5
+
3
6
  module TypedCache
4
- # A simple, testable wrapper around Time to provide a consistent way of
5
- # getting the current time, respecting ActiveSupport's time zone when available.
6
- class Clock
7
+ module Clock
8
+ # @rbs generic R
9
+ class Measured < Dry::Struct
10
+ # @rbs! def start: () -> Float
11
+ # @rbs! def end: () -> Float
12
+ # @rbs! def result: () -> [R]
13
+
14
+ attribute :start, Dry.Types::Float
15
+ attribute :end, Dry.Types::Float
16
+ attribute :result, Dry.Types.Instance(Object) #: [R]
17
+
18
+ # @rbs! def initialize: (start: Float, end: Float, result: [R]) -> void
19
+
20
+ #: -> Float
21
+ def duration
22
+ self.end - start
23
+ end
24
+ end
25
+
7
26
  class << self
8
- # Retrieves the current time. If ActiveSupport's `Time.current` is
9
- # available, it will be used to respect the configured timezone. Otherwise,
10
- # it falls back to the system's `Time.now`.
11
- #
12
- # @return [Time] The current time.
27
+ # @rbs [R]() { () -> R } -> Measured[R]
28
+ def measure(&)
29
+ start = now
30
+ result = yield
31
+ Measured.new(start:, end: now, result:)
32
+ end
33
+
13
34
  # @rbs () -> Time
14
- def moment
15
- if Time.respond_to?(:current)
16
- Time.current
17
- else
18
- Time.now
19
- end
35
+ def now
36
+ Time.now
20
37
  end
21
38
  end
22
39
  end
@@ -1,12 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  module TypedCache
4
6
  # Marker mixin for cache store decorators. A decorator behaves exactly like a
5
7
  # Store but must accept another Store instance in its constructor.
6
8
  # @rbs generic V
7
9
  module Decorator
10
+ extend Forwardable
11
+
8
12
  include Store #[V]
9
13
  # @rbs! include Store::_Store[V]
10
14
  # @rbs! include Store::_Decorator[V]
15
+
16
+ # @rbs!
17
+ # def store: -> Store[V]
18
+
19
+ Store.instance_methods(false).each do |method_name|
20
+ def_delegator :store, method_name
21
+ end
22
+
23
+ # @rbs override
24
+ #: (cache_key) -> either[Error, CacheRef[V]]
25
+ def ref(key)
26
+ CacheRef.new(self, key)
27
+ end
11
28
  end
12
29
  end
@@ -4,40 +4,42 @@ module TypedCache
4
4
  # Decorator that adds instrumentation to any Store implementation
5
5
  # This decorator can wrap any store to add ActiveSupport::Notifications
6
6
  # @rbs generic V
7
- class Store::Instrumented # rubocop:disable Style/ClassAndModuleChildren
7
+ class Decorators::Instrumented # rubocop:disable Style/ClassAndModuleChildren
8
8
  include Decorator #[V]
9
9
 
10
10
  extend Forwardable
11
11
 
12
12
  attr_reader :store #: TypedCache::Store[V]
13
+ attr_reader :instrumenter #: Instrumenter
13
14
 
14
15
  class << self
15
16
  private
16
17
 
17
18
  # @rbs (Symbol, ?operation: String) ?{ (*untyped, **untyped) -> String } -> void
18
19
  def instrument(method_name, operation: method_name.to_s, &key_selector)
19
- define_method(:"#{method_name}_with_instrumentation") do |*args, **kwargs, &block|
20
- key = key_selector.call(*args, **kwargs) if key_selector # rubocop:disable Performance/RedundantBlockCall
20
+ key_selector ||= ->(*_args, **_kwargs, &_block) { 'n/a' }
21
+ alias_prefix = method_name.to_s.delete('?!')
21
22
 
22
- Instrumentation.instrument(operation, namespace, key || 'n/a', store_type: store_type) do
23
- send(:"#{method_name}_without_instrumentation", *args, **kwargs, &block)
23
+ define_method(:"#{alias_prefix}_instrumentation_key", &key_selector)
24
+
25
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
26
+ def #{alias_prefix}_with_instrumentation(...)
27
+ key = #{alias_prefix}_instrumentation_key(...)
28
+ instrumenter.instrument(:"#{operation}", key, store_type: store_type) do
29
+ #{alias_prefix}_without_instrumentation(...)
30
+ end
24
31
  end
25
- end
32
+ RUBY
26
33
 
27
- alias_method(:"#{method_name}_without_instrumentation", method_name)
28
- alias_method(method_name, :"#{method_name}_with_instrumentation")
34
+ alias_method(:"#{alias_prefix}_without_instrumentation", method_name)
35
+ alias_method(method_name, :"#{alias_prefix}_with_instrumentation")
29
36
  end
30
37
  end
31
38
 
32
- #: (TypedCache::Store[V]) -> void
33
- def initialize(store)
39
+ #: (TypedCache::Store[V], instrumenter: Instrumenter) -> void
40
+ def initialize(store, instrumenter:)
34
41
  @store = store
35
- end
36
-
37
- # @rbs override
38
- #: -> String
39
- def namespace
40
- store.namespace
42
+ @instrumenter = instrumenter
41
43
  end
42
44
 
43
45
  # @rbs override
@@ -48,7 +50,7 @@ module TypedCache
48
50
  end
49
51
 
50
52
  # @rbs override
51
- #: (cache_key) -> either[Error, CacheRef[V]]
53
+ # @rbs (key) -> CacheRef[V]
52
54
  def ref(key)
53
55
  CacheRef.new(self, key)
54
56
  end
@@ -66,12 +68,6 @@ module TypedCache
66
68
  end
67
69
  end
68
70
 
69
- Store.instance_methods(false).each do |method_name|
70
- next if instance_methods(false).include?(method_name)
71
-
72
- def_delegator :store, method_name
73
- end
74
-
75
71
  # Instrument core operations with proper key extraction
76
72
  instrument(:get) { |key, *_| key }
77
73
  instrument(:set) { |key, *_| key }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'typed_cache/registry'
4
+
3
5
  module TypedCache
4
6
  # Holds store decorators (e.g., instrumentation wrappers) that can be composed
5
7
  # by the CacheBuilder. Decorators must conform to the same API as the wrapped
@@ -14,20 +16,22 @@ module TypedCache
14
16
  # .build.value
15
17
  #
16
18
  module Decorators
19
+ autoload :Instrumented, 'typed_cache/decorators/instrumented'
20
+
21
+ # @api private
17
22
  # Default decorator set – starts with instrumentation only, but this registry
18
23
  # lets end-users register their own via `Decorators.register`.
19
24
  REGISTRY = Registry.new('decorator', {
20
- instrumented: Store::Instrumented,
25
+ instrumented: Instrumented,
21
26
  }).freeze
22
27
 
23
- private_constant :REGISTRY
24
-
25
28
  class << self
26
29
  extend Forwardable
27
30
 
28
31
  # Delegate common registry helpers
29
32
  delegate [:resolve, :register, :available, :registered?] => :registry
30
33
 
34
+ # @api private
31
35
  # @rbs () -> Registry[Store[untyped]]
32
36
  def registry = REGISTRY
33
37
  end
@@ -2,7 +2,13 @@
2
2
 
3
3
  module TypedCache
4
4
  # Base error class for TypedCache operations
5
- class Error < StandardError; end
5
+ class Error < StandardError
6
+ # @rbs (*untyped) -> void
7
+ def initialize(*args)
8
+ super(*args)
9
+ set_backtrace(caller(2))
10
+ end
11
+ end
6
12
 
7
13
  # Store operation errors (network, I/O, etc.)
8
14
  class StoreError < Error
@@ -14,6 +20,8 @@ module TypedCache
14
20
  @operation = operation
15
21
  @key = key
16
22
  @original_error = original_error
23
+
24
+ set_backtrace(original_error.backtrace) if original_error
17
25
  end
18
26
 
19
27
  # @rbs () -> String
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedCache
4
+ # Instrumenter for cache operations
5
+ module Instrumenter
6
+ # @rbs! type event = Dry::Events::Event | ActiveSupport::Notifications::Event
7
+
8
+ # @rbs [R](String, String, **untyped) { -> R } -> R
9
+ def instrument(event_name, key, **payload)
10
+ raise NotImplementedError, "#{self.class} must implement #instrument"
11
+ end
12
+
13
+ # @rbs (String, **untyped) { (event) -> void } -> void
14
+ def subscribe(event_name, **filters, &block)
15
+ raise NotImplementedError, "#{self.class} must implement #subscribe"
16
+ end
17
+
18
+ #: -> String
19
+ def namespace
20
+ config.namespace
21
+ end
22
+
23
+ # @rbs (String, String, **untyped) -> Hash[Symbol, untyped]
24
+ def build_payload(operation, key, **payload)
25
+ { namespace:, key:, operation: }.merge(payload)
26
+ end
27
+
28
+ # @rbs () -> bool
29
+ def enabled? = config.enabled
30
+
31
+ # @rbs (String) -> String
32
+ def event_name(operation)
33
+ "#{namespace}.#{operation}"
34
+ end
35
+
36
+ private
37
+
38
+ # @rbs () -> TypedCache::_TypedCacheInstrumentationConfig
39
+ def config
40
+ TypedCache.config.instrumentation
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/notifications'
4
+
5
+ module TypedCache
6
+ module Instrumenters
7
+ # Instrumenter for ActiveSupport::Notifications
8
+ class ActiveSupport
9
+ include Instrumenter
10
+ include Mixins::NamespacedSingleton
11
+
12
+ # @rbs override
13
+ #: [R] (String, String, Hash[Symbol, untyped]) { -> R } -> R
14
+ def instrument(operation, key, **payload, &block)
15
+ return yield unless enabled?
16
+
17
+ payload = build_payload(operation, key, **payload)
18
+ ::ActiveSupport::Notifications.instrument(event_name(operation), **payload, &block)
19
+ end
20
+
21
+ # @rbs override
22
+ # @rbs (String, **top) { (event) -> void } -> void
23
+ def subscribe(operation, **filters, &block)
24
+ ::ActiveSupport::Notifications.monotonic_subscribe(event_name(operation), &block)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ module Mixins
6
+ module NamespacedSingleton
7
+ class << self
8
+ # @rbs () -> Array[Class[Instrumenter & NamespacedSingleton]]
9
+ def all = @all ||= [] # rubocop:disable ThreadSafety
10
+
11
+ # @rbs (Class[Instrumenter & NamespacedSingleton]) -> void
12
+ def included(base)
13
+ base.singleton_class.class_eval do
14
+ alias private_new new
15
+ private(:private_new)
16
+ end
17
+
18
+ base.extend(ClassMethods)
19
+
20
+ all << base
21
+ end
22
+ end
23
+
24
+ # @rbs override
25
+ # @rbs () -> String
26
+ def namespace
27
+ @namespace
28
+ end
29
+
30
+ # @rbs (String | Namespace) -> void
31
+ def initialize(namespace)
32
+ @namespace = namespace.to_s
33
+ end
34
+
35
+ module ClassMethods
36
+ # @rbs (String | Namespace) -> class
37
+ def new(namespace: TypedCache.config.instrumentation.namespace)
38
+ namespace_cache.compute_if_absent(namespace.to_s) { private_new(namespace) }
39
+ end
40
+
41
+ # @rbs (String) -> maybe[class]
42
+ def get(namespace)
43
+ namespace_cache.get(namespace.to_s)
44
+ end
45
+
46
+ # @rbs () -> Concurrent::Map[String, Class[Instrumenter & NamespacedSingleton]]
47
+ def namespace_cache = @namespace_cache ||= Concurrent::Map.new # rubocop:disable ThreadSafety
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TypedCache
4
+ module Instrumenters
5
+ module Mixins
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/monitor'
4
+
5
+ module TypedCache
6
+ module Instrumenters
7
+ class Monitor
8
+ include Instrumenter
9
+ include Mixins::NamespacedSingleton
10
+
11
+ # @rbs override
12
+ #: [R] (String, String, **untyped) { -> R } -> R
13
+ def instrument(operation, key, **payload, &block)
14
+ return yield unless enabled?
15
+
16
+ payload = build_payload(operation, key, **payload)
17
+ Dry::Monitor::Notifications.instrument(event_name(operation), payload, &block)
18
+ end
19
+
20
+ # @rbs override
21
+ # @rbs (String, **top) { (event) -> void } -> void
22
+ def subscribe(operation, **filters, &block)
23
+ Dry::Monitor::Notifications.subscribe(event_name(operation), **filters, &block)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module TypedCache
6
+ module Instrumenters
7
+ # A no-op implementation used when instrumentation is disabled.
8
+ # It fulfils the Instrumenter contract but simply yields.
9
+ class Null
10
+ include Instrumenter
11
+ include Mixins::NamespacedSingleton
12
+
13
+ # @rbs override
14
+ # [R] (String, String, **untyped) { -> R } -> R
15
+ def instrument(_operation, _key, **_payload)
16
+ yield
17
+ end
18
+
19
+ # @rbs override
20
+ # @rbs (String, **top) { (event) -> void } -> void
21
+ def subscribe(_event_name, **_filters, &_block)
22
+ # no-op
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'typed_cache/instrumenters/mixins'
4
+ require 'typed_cache/instrumenters/mixins/namespaced_singleton'
5
+
6
+ module TypedCache
7
+ module Instrumenters
8
+ autoload :ActiveSupport, 'typed_cache/instrumenters/active_support'
9
+ autoload :Monitor, 'typed_cache/instrumenters/monitor'
10
+ autoload :Null, 'typed_cache/instrumenters/null'
11
+
12
+ # @api private
13
+ # Registry mapping symbols to instrumenter classes. We can't reuse the generic
14
+ # Registry class directly because many instrumenters mix in `Singleton`,
15
+ # making `.new` inaccessible. Instead we implement a thin facade that
16
+ # returns either the singleton instance (preferred) or a fresh instance.
17
+ REGISTRY = Registry.new('instrumenter', {
18
+ dry: Monitor,
19
+ rails: ActiveSupport,
20
+ default: Null,
21
+ null: Null,
22
+ }) #: Registry[Symbol, Class[Instrumenter]]
23
+
24
+ class << self
25
+ extend Forwardable
26
+
27
+ # @api private
28
+ # @rbs () -> Registry[Symbol, Class[Instrumenter]]
29
+ def registry = REGISTRY
30
+
31
+ # @rbs! def resolve: (Symbol, **untyped) -> either[Error, Instrumenter]
32
+ # @rbs! def available: () -> Array[Symbol]
33
+ # @rbs! def registered?: (Symbol) -> Boolean
34
+
35
+ def_delegators :registry, :resolve, :available, :registered?
36
+ end
37
+ end
38
+ end
@@ -10,6 +10,17 @@ module TypedCache
10
10
  @registry = defaults.dup
11
11
  end
12
12
 
13
+ # @rbs (Registry[T]) -> Registry[T]
14
+ def initialize_copy(other)
15
+ super
16
+ @registry = other.registry.dup
17
+ end
18
+
19
+ # @rbs () -> void
20
+ def clear
21
+ @registry.clear
22
+ end
23
+
13
24
  # @rbs (Symbol, *untyped, **untyped) -> either[Error, T]
14
25
  def resolve(key, *, **, &)
15
26
  klass = @registry[key]
@@ -51,5 +62,9 @@ module TypedCache
51
62
  def registered?(key)
52
63
  @registry.key?(key)
53
64
  end
65
+
66
+ protected
67
+
68
+ attr_reader :registry #: Hash[Symbol, Class[T]]
54
69
  end
55
70
  end
@@ -98,6 +98,9 @@ module TypedCache
98
98
  end
99
99
  end
100
100
 
101
+ # @rbs () -> Instrumenter
102
+ def instrumenter = Instrumenters::Null.instance
103
+
101
104
  # Returns the namespace for this store (for instrumentation/debugging)
102
105
  # @rbs () -> Namespace
103
106
  def namespace
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TypedCache
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/typed_cache.rb CHANGED
@@ -1,18 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'zeitwerk'
4
- require 'dry-configurable'
5
- require 'concurrent-ruby'
3
+ require 'dry/configurable'
4
+ require 'dry/struct'
5
+ require 'dry/types'
6
6
 
7
- # Load core registry before Zeitwerk
8
- require_relative 'typed_cache/registry'
9
- require_relative 'typed_cache/errors'
7
+ require 'typed_cache/either'
8
+ require 'typed_cache/maybe'
10
9
 
11
- Zeitwerk::Loader.for_gem.setup
10
+ require 'typed_cache/cache_key'
11
+ require 'typed_cache/errors'
12
+ require 'typed_cache/namespace'
13
+ require 'typed_cache/registry'
14
+ require 'typed_cache/clock'
15
+
16
+ require 'typed_cache/store'
17
+ require 'typed_cache/instrumenter'
18
+ require 'typed_cache/backend'
19
+ require 'typed_cache/decorator'
20
+
21
+ require 'typed_cache/snapshot'
22
+ require 'typed_cache/cache_ref'
23
+
24
+ require 'typed_cache/cache_builder'
12
25
 
13
26
  module TypedCache
14
27
  extend Dry::Configurable
15
28
 
29
+ autoload :Backends, 'typed_cache/backends'
30
+ autoload :Decorators, 'typed_cache/decorators'
31
+ autoload :Instrumenters, 'typed_cache/instrumenters'
32
+
16
33
  # @rbs!
17
34
  # interface _TypedCacheInstrumentationConfig
18
35
  # def enabled: -> bool
@@ -33,6 +50,7 @@ module TypedCache
33
50
  setting :instrumentation do
34
51
  setting :enabled, default: false
35
52
  setting :namespace, default: 'typed_cache'
53
+ setting :instrumenter, default: :default
36
54
  end
37
55
 
38
56
  class << self
@@ -44,15 +62,13 @@ module TypedCache
44
62
 
45
63
  # @rbs! def config: -> _TypedCacheConfig
46
64
 
47
- # @rbs () -> singleton(Instrumentation)
48
- def instrumentation
49
- Instrumentation
50
- end
51
-
52
- # @rbs () -> Registry[backend[untyped]]
65
+ # @rbs () -> singleton(Backends)
53
66
  def backends = Backends
54
67
 
55
- # @rbs () -> Register[decorator[untyped]]
68
+ # @rbs () -> singleton(Decorators)
56
69
  def decorators = Decorators
70
+
71
+ # @rbs () -> singleton(Instrumenters)
72
+ def instrumenters = Instrumenters
57
73
  end
58
74
  end
@@ -2,11 +2,13 @@
2
2
 
3
3
  module TypedCache
4
4
  module Backends
5
+ # @api private
5
6
  # Backend registry using composition
6
7
  REGISTRY: untyped
7
8
 
8
9
  extend Forwardable
9
10
 
11
+ # @api private
10
12
  # : -> Registry
11
13
  def self.registry: () -> Registry
12
14
 
@@ -4,6 +4,14 @@ module TypedCache
4
4
  class CacheBuilder
5
5
  type config = TypedCache::typed_cache_config
6
6
 
7
+ type instrumenter_source = :default | :dry | :rails | Instrumenter
8
+
9
+ class BackendConfig < Dry::Struct
10
+ end
11
+
12
+ class DecoratorConfig < Dry::Struct
13
+ end
14
+
7
15
  # @rbs (config, Registry[backend[untyped]], Registry[decorator[untyped]]) -> void
8
16
  def initialize: (config, Registry[backend[untyped]], Registry[decorator[untyped]]) -> void
9
17
 
@@ -20,8 +28,9 @@ module TypedCache
20
28
  # @rbs (Symbol) -> self
21
29
  def with_decorator: (Symbol) -> self
22
30
 
23
- # @rbs () -> self
24
- def with_instrumentation: () -> self
31
+ # Adds instrumentation using the specified strategy.
32
+ # @rbs (instrumenter_source) -> either[Error, self]
33
+ def with_instrumentation: (instrumenter_source) -> either[Error, self]
25
34
 
26
35
  private
27
36
 
@@ -33,5 +42,7 @@ module TypedCache
33
42
 
34
43
  # @rbs (Store[V]) -> either[Error, Store[V]]
35
44
  def apply_decorators: (Store[V]) -> either[Error, Store[V]]
45
+
46
+ def apply_instrumentation: (untyped store) -> untyped
36
47
  end
37
48
  end
@@ -1,15 +1,25 @@
1
1
  # Generated from lib/typed_cache/clock.rb with RBS::Inline
2
2
 
3
3
  module TypedCache
4
- # A simple, testable wrapper around Time to provide a consistent way of
5
- # getting the current time, respecting ActiveSupport's time zone when available.
6
- class Clock
7
- # Retrieves the current time. If ActiveSupport's `Time.current` is
8
- # available, it will be used to respect the configured timezone. Otherwise,
9
- # it falls back to the system's `Time.now`.
10
- #
11
- # @return [Time] The current time.
4
+ module Clock
5
+ # @rbs generic R
6
+ class Measured[R] < Dry::Struct
7
+ def start: () -> Float
8
+
9
+ def end: () -> Float
10
+
11
+ def result: () -> [ R ]
12
+
13
+ def initialize: (start: Float, end: Float, result: [ R ]) -> void
14
+
15
+ # : -> Float
16
+ def duration: () -> Float
17
+ end
18
+
19
+ # @rbs [R]() { () -> R } -> Measured[R]
20
+ def self.measure: [R] () { () -> R } -> Measured[R]
21
+
12
22
  # @rbs () -> Time
13
- def self.moment: () -> Time
23
+ def self.now: () -> Time
14
24
  end
15
25
  end
@@ -5,10 +5,18 @@ module TypedCache
5
5
  # Store but must accept another Store instance in its constructor.
6
6
  # @rbs generic V
7
7
  module Decorator[V]
8
+ extend Forwardable
9
+
8
10
  include Store[V]
9
11
 
10
12
  include Store::_Store[V]
11
13
 
12
14
  include Store::_Decorator[V]
15
+
16
+ def store: () -> Store[V]
17
+
18
+ # @rbs override
19
+ # : (cache_key) -> either[Error, CacheRef[V]]
20
+ def ref: ...
13
21
  end
14
22
  end