typed_cache 0.3.1 → 0.4.0.pre.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/typed_cache/backend.rb +80 -2
- data/lib/typed_cache/backends/active_support.rb +24 -98
- data/lib/typed_cache/backends/memory.rb +22 -73
- data/lib/typed_cache/backends.rb +2 -2
- data/lib/typed_cache/cache_builder.rb +131 -46
- data/lib/typed_cache/cache_key.rb +1 -1
- data/lib/typed_cache/cache_ref.rb +24 -55
- data/lib/typed_cache/decorator.rb +22 -12
- data/lib/typed_cache/decorators/instrumented.rb +12 -19
- data/lib/typed_cache/decorators.rb +7 -1
- data/lib/typed_cache/either.rb +28 -0
- data/lib/typed_cache/instrumenters.rb +6 -2
- data/lib/typed_cache/maybe.rb +28 -0
- data/lib/typed_cache/namespace.rb +1 -3
- data/lib/typed_cache/snapshot.rb +6 -0
- data/lib/typed_cache/store.rb +130 -78
- data/lib/typed_cache/version.rb +1 -1
- data/lib/typed_cache.rb +5 -2
- data/rbi/typed_cache/backend.rbi +44 -2
- data/rbi/typed_cache/backends/active_support.rbi +1 -1
- data/rbi/typed_cache/backends/memory.rbi +1 -1
- data/rbi/typed_cache/cache_builder.rbi +29 -10
- data/rbi/typed_cache/cache_key.rbi +14 -0
- data/rbi/typed_cache/cache_ref.rbi +6 -15
- data/rbi/typed_cache/decorator.rbi +23 -37
- data/rbi/typed_cache/decorators/instrumented.rbi +1 -1
- data/rbi/typed_cache/either.rbi +24 -0
- data/rbi/typed_cache/maybe.rbi +24 -0
- data/rbi/typed_cache/namespace.rbi +14 -0
- data/rbi/typed_cache/snapshot.rbi +6 -0
- data/rbi/typed_cache/store.rbi +26 -24
- data/rbi/typed_cache.rbi +1 -1
- data/sig/generated/typed_cache/backend.rbs +68 -2
- data/sig/generated/typed_cache/backends/active_support.rbs +13 -21
- data/sig/generated/typed_cache/backends/memory.rbs +10 -30
- data/sig/generated/typed_cache/backends.rbs +2 -2
- data/sig/generated/typed_cache/cache_builder.rbs +57 -16
- data/sig/generated/typed_cache/cache_ref.rbs +16 -37
- data/sig/generated/typed_cache/decorator.rbs +18 -6
- data/sig/generated/typed_cache/decorators/instrumented.rbs +3 -7
- data/sig/generated/typed_cache/decorators.rbs +9 -1
- data/sig/generated/typed_cache/either.rbs +24 -0
- data/sig/generated/typed_cache/instrumenters.rbs +4 -3
- data/sig/generated/typed_cache/maybe.rbs +24 -0
- data/sig/generated/typed_cache/namespace.rbs +0 -2
- data/sig/generated/typed_cache/snapshot.rbs +6 -0
- data/sig/generated/typed_cache/store.rbs +49 -42
- data/sig/generated/typed_cache.rbs +6 -2
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
@@ -13,117 +13,202 @@ require 'dry/struct'
|
|
13
13
|
require 'dry/types'
|
14
14
|
|
15
15
|
module TypedCache
|
16
|
-
|
17
|
-
|
16
|
+
# @rbs!
|
17
|
+
# interface _CacheBuilder
|
18
|
+
# def build(namespace: Namespace) -> either[Error, Store[untyped]]
|
19
|
+
# def build!(namespace: Namespace) -> Store[untyped]
|
20
|
+
# def build_backend -> either[Error, Backend[untyped]]
|
21
|
+
# def build_backend! -> Backend[untyped]
|
22
|
+
# end
|
23
|
+
|
24
|
+
# @rbs!
|
25
|
+
# interface _CacheDefinition
|
26
|
+
# def with_backend(Symbol, *untyped, **untyped) -> (_CacheDefinition & _CacheBuilder)
|
27
|
+
# def with_decorator(Symbol, **untyped) -> self
|
28
|
+
# def with_instrumentation(Symbol) -> self
|
29
|
+
# end
|
30
|
+
|
31
|
+
class BackendConfig < Dry::Struct
|
32
|
+
attribute :name, Dry.Types::Symbol
|
33
|
+
attribute :args, Dry.Types::Array.of(Dry.Types::Any)
|
34
|
+
attribute :options, Dry.Types::Hash.map(Dry.Types::Symbol, Dry.Types::Any)
|
35
|
+
end
|
36
|
+
|
37
|
+
class DecoratorConfig < Dry::Struct
|
38
|
+
attribute :name, Dry.Types::Symbol
|
39
|
+
attribute :options, Dry.Types::Hash.map(Dry.Types::Symbol, Dry.Types::Any)
|
40
|
+
end
|
41
|
+
|
42
|
+
class CacheDefinition
|
43
|
+
# @rbs! include _CacheDefinition
|
44
|
+
|
18
45
|
# @rbs! type instrumenter_source = :default | :dry | :rails | Instrumenter
|
19
46
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
47
|
+
attr_reader :backend_config #: BackendConfig?
|
48
|
+
attr_reader :decorator_configs #: Array[DecoratorConfig]
|
49
|
+
attr_reader :instrumenter_source #: instrumenter_source
|
50
|
+
|
51
|
+
# @rbs (?BackendConfig?, ?Array[DecoratorConfig], ?instrumenter_source) -> void
|
52
|
+
def initialize(backend_config = nil, decorator_configs = [], instrumenter_source = nil)
|
53
|
+
@backend_config = backend_config
|
54
|
+
@decorator_configs = decorator_configs
|
55
|
+
@instrumenter_source = instrumenter_source
|
56
|
+
end
|
57
|
+
|
58
|
+
# @rbs override
|
59
|
+
# @rbs (Symbol, *untyped, **untyped) -> self
|
60
|
+
def with_backend(name, *args, **options)
|
61
|
+
self.class.new(BackendConfig.new(name:, args:, options:), @decorator_configs)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @rbs override
|
65
|
+
# @rbs (Symbol, **untyped) -> self
|
66
|
+
def with_decorator(name, **options)
|
67
|
+
self.class.new(@backend_config, @decorator_configs + [DecoratorConfig.new(name:, options:)])
|
24
68
|
end
|
25
69
|
|
26
|
-
|
27
|
-
|
28
|
-
|
70
|
+
# @rbs override
|
71
|
+
# @rbs (instrumenter_source) -> self
|
72
|
+
def with_instrumentation(source = :default)
|
73
|
+
self.class.new(@backend_config, @decorator_configs, source)
|
29
74
|
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class CacheBuilder
|
78
|
+
# @rbs! include _CacheBuilder
|
79
|
+
# @rbs! include _CacheDefinition
|
30
80
|
|
31
|
-
# @rbs (
|
32
|
-
def initialize(
|
33
|
-
@config = config
|
81
|
+
# @rbs (?_CacheDefinition, ?Registry[backend[untyped]], ?Registry[decorator[untyped]]) -> void
|
82
|
+
def initialize(cache_definition = CacheDefinition.new, backend_registry = Backends, decorator_registry = Decorators)
|
34
83
|
@backend_registry = backend_registry
|
35
84
|
@decorator_registry = decorator_registry
|
36
85
|
|
37
|
-
@
|
38
|
-
@decorator_configs = []
|
86
|
+
@cache_definition = cache_definition
|
39
87
|
end
|
40
88
|
|
41
|
-
# Builds the cache
|
42
|
-
# @rbs (?Namespace) -> either[Error, Store[
|
43
|
-
def build(namespace = Namespace.
|
89
|
+
# Builds the cache using the given namespace, defaulting to the root namespace
|
90
|
+
# @rbs (?Namespace) -> either[Error, Store[untyped]]
|
91
|
+
def build(namespace = Namespace.root)
|
44
92
|
validate_and_build(namespace)
|
45
93
|
end
|
46
94
|
|
95
|
+
# @rbs (Namespace) -> Store[untyped]
|
96
|
+
def build!(namespace = Namespace.root)
|
97
|
+
build(namespace).right_or_raise!
|
98
|
+
end
|
99
|
+
|
100
|
+
# Constructs only a typed backend from the registry without a Store wrapper
|
101
|
+
# @rbs () -> either[Error, Backend[untyped]]
|
102
|
+
def build_backend
|
103
|
+
create_backend
|
104
|
+
end
|
105
|
+
|
106
|
+
# Constructs only a typed backend from the registry without a Store wrapper,
|
107
|
+
# raises an error if the backend cannot be constructed
|
108
|
+
#
|
109
|
+
# @rbs () -> Backend[untyped]
|
110
|
+
def build_backend!
|
111
|
+
create_backend.right_or_raise!
|
112
|
+
end
|
113
|
+
|
47
114
|
# Familiar Ruby fluent interface - always succeeds
|
48
115
|
# Invalid configurations are caught during build()
|
49
116
|
# @rbs (Symbol, *untyped, **untyped) -> self
|
50
117
|
def with_backend(name, *args, **options)
|
51
|
-
|
52
|
-
|
118
|
+
self.class.new(
|
119
|
+
@cache_definition.with_backend(name, *args, **options),
|
120
|
+
@backend_registry,
|
121
|
+
@decorator_registry,
|
122
|
+
)
|
53
123
|
end
|
54
124
|
|
55
125
|
# Adds an arbitrary decorator by registry key
|
56
126
|
# @rbs (Symbol) -> self
|
57
127
|
def with_decorator(name, **options)
|
58
|
-
|
59
|
-
|
128
|
+
self.class.new(
|
129
|
+
@cache_definition.with_decorator(name, **options),
|
130
|
+
@backend_registry,
|
131
|
+
@decorator_registry,
|
132
|
+
)
|
60
133
|
end
|
61
134
|
|
62
135
|
# Adds instrumentation using the specified strategy.
|
63
136
|
# @rbs (instrumenter_source) -> either[Error, self]
|
64
137
|
def with_instrumentation(source = :default)
|
65
|
-
|
66
|
-
|
138
|
+
self.class.new(
|
139
|
+
@cache_definition.with_instrumentation(source),
|
140
|
+
@backend_registry,
|
141
|
+
@decorator_registry,
|
142
|
+
)
|
67
143
|
end
|
68
144
|
|
69
145
|
private
|
70
146
|
|
71
147
|
# @rbs (Namespace) -> either[Error, Store[V]]
|
72
148
|
def validate_and_build(namespace)
|
73
|
-
|
74
|
-
apply_decorators(
|
75
|
-
apply_instrumentation(
|
149
|
+
create_backend.bind do |backend|
|
150
|
+
apply_decorators(backend).bind do |decorated_backend|
|
151
|
+
apply_instrumentation(decorated_backend).bind do |instrumented_backend|
|
152
|
+
Either.right(Store.new(namespace, instrumented_backend))
|
153
|
+
end
|
76
154
|
end
|
77
155
|
end
|
78
156
|
end
|
79
157
|
|
80
|
-
# @rbs (Namespace) -> either[Error,
|
81
|
-
def
|
82
|
-
|
158
|
+
# @rbs (Namespace) -> either[Error, Backend[untyped]]
|
159
|
+
def create_backend
|
160
|
+
backend_config = @cache_definition.backend_config
|
161
|
+
|
162
|
+
return Either.left(ArgumentError.new('Backend not configured')) unless backend_config
|
83
163
|
|
84
164
|
# Prepend namespace to the arguments for the backend constructor
|
85
|
-
@backend_registry.resolve(
|
165
|
+
@backend_registry.resolve(backend_config.name, *backend_config.args, **backend_config.options)
|
86
166
|
end
|
87
167
|
|
88
|
-
# @rbs (
|
89
|
-
def apply_decorators(
|
90
|
-
|
168
|
+
# @rbs (Backend[untyped]) -> either[Error, Decorator[untyped]]
|
169
|
+
def apply_decorators(backend)
|
170
|
+
decorator_configs = @cache_definition.decorator_configs
|
171
|
+
|
172
|
+
return Either.right(backend) if decorator_configs.empty?
|
91
173
|
|
92
|
-
names =
|
174
|
+
names = decorator_configs.map(&:name)
|
93
175
|
|
94
176
|
name_counts = names.tally
|
95
177
|
duplicates = name_counts.keys.select { |name| name_counts[name] > 1 }
|
96
178
|
return Either.left(ArgumentError.new("Duplicate decorator: #{duplicates.join(", ")}")) if duplicates.any?
|
97
179
|
|
98
|
-
|
99
|
-
result.bind do |
|
100
|
-
@decorator_registry.resolve(decorator_config.name,
|
180
|
+
decorator_configs.reduce(Either.right(backend)) do |result, decorator_config|
|
181
|
+
result.bind do |current_backend|
|
182
|
+
@decorator_registry.resolve(decorator_config.name, current_backend, **decorator_config.options)
|
101
183
|
end
|
102
184
|
end
|
103
185
|
rescue => e
|
104
186
|
Either.left(StoreError.new(:decorator_application, 'decorator', "Failed to apply decorator: #{e.message}", e))
|
105
187
|
end
|
106
188
|
|
107
|
-
|
108
|
-
|
189
|
+
# @rbs (Backend[untyped]) -> either[Error, Backend[untyped]]
|
190
|
+
def apply_instrumentation(backend)
|
191
|
+
instrumenter_source = @cache_definition.instrumenter_source
|
192
|
+
|
193
|
+
return Either.right(backend) unless instrumenter_source
|
109
194
|
|
110
195
|
instrumenter =
|
111
|
-
case
|
196
|
+
case instrumenter_source
|
112
197
|
when Symbol
|
113
|
-
Instrumenters.resolve(
|
198
|
+
Instrumenters.resolve(instrumenter_source)
|
114
199
|
when Instrumenter
|
115
|
-
Either.right(
|
200
|
+
Either.right(instrumenter_source)
|
116
201
|
else
|
117
202
|
Either.left(TypedCache::TypeError.new(
|
118
203
|
':default | :dry | :rails | Instrumenter',
|
119
|
-
|
120
|
-
|
121
|
-
"Invalid instrumenter source: #{
|
204
|
+
instrumenter_source.class.name,
|
205
|
+
instrumenter_source,
|
206
|
+
"Invalid instrumenter source: #{instrumenter_source.inspect}",
|
122
207
|
))
|
123
208
|
end
|
124
209
|
|
125
210
|
instrumenter.bind do |i|
|
126
|
-
@decorator_registry.resolve(:instrumented,
|
211
|
+
@decorator_registry.resolve(:instrumented, backend, instrumenter: i)
|
127
212
|
end
|
128
213
|
end
|
129
214
|
end
|
@@ -21,7 +21,7 @@ module TypedCache
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Gets a value from the cache as a snapshot
|
24
|
-
#: -> either[Error, Snapshot[V]]
|
24
|
+
#: -> either[Error, Snapshot[maybe[V]]]
|
25
25
|
def read
|
26
26
|
store.read(key)
|
27
27
|
end
|
@@ -33,14 +33,14 @@ module TypedCache
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Deletes the value from the cache and returns the deleted value as a snapshot
|
36
|
-
#: -> either[Error,
|
36
|
+
#: -> either[Error, maybe[V]]
|
37
37
|
def delete
|
38
38
|
store.delete(key)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Fetches a value from cache, computing and storing it if not found
|
42
42
|
# The snapshot indicates whether the value came from cache or was computed
|
43
|
-
#: () { -> V } -> either[Error, Snapshot[V]]
|
43
|
+
#: () { -> V? } -> either[Error, Snapshot[maybe[V]]]
|
44
44
|
def fetch(&block)
|
45
45
|
store.fetch(key, &block)
|
46
46
|
end
|
@@ -60,24 +60,26 @@ module TypedCache
|
|
60
60
|
# Maps over the cached value if it exists, preserving snapshot metadata
|
61
61
|
#: [R] () { (V) -> R } -> either[Error, Snapshot[R]]
|
62
62
|
def map(&block)
|
63
|
-
read.map { |snapshot| snapshot.map(&block) }
|
63
|
+
read.map { |snapshot| snapshot.map { |mb| mb.map(&block) } }
|
64
64
|
end
|
65
65
|
|
66
|
-
# Binds over the cached value, allowing for monadic composition with snapshots
|
67
|
-
#: [R] () { (V) -> either[Error, R] } -> either[Error, Snapshot[R]]
|
68
|
-
def bind(&block)
|
69
|
-
read.bind { |snapshot| snapshot.bind(&block) }
|
70
|
-
end
|
71
|
-
|
72
|
-
alias flat_map bind
|
73
|
-
|
74
66
|
# Updates the cached value using the provided block
|
75
67
|
# Returns the updated value as a snapshot with source=:updated
|
76
|
-
#: () { (V) -> V } -> either[Error, Snapshot[V]]
|
68
|
+
#: () { (V) -> V? } -> either[Error, Snapshot[maybe[V]]]
|
77
69
|
def update(&block)
|
78
70
|
read.bind do |snapshot|
|
79
|
-
new_value =
|
80
|
-
|
71
|
+
new_value = snapshot.value.bind do |value|
|
72
|
+
new_value = yield(value)
|
73
|
+
Maybe.wrap(new_value)
|
74
|
+
end
|
75
|
+
|
76
|
+
if new_value.some?
|
77
|
+
write(new_value.value).map do |snapshot|
|
78
|
+
snapshot.map { new_value }
|
79
|
+
end
|
80
|
+
else
|
81
|
+
delete.map { snapshot }
|
82
|
+
end
|
81
83
|
rescue => e
|
82
84
|
Either.left(StoreError.new(
|
83
85
|
:update,
|
@@ -88,28 +90,9 @@ module TypedCache
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
|
-
# Returns the cached value or a default if the cache is empty/errored
|
92
|
-
#: (V) -> V
|
93
|
-
def value_or(default)
|
94
|
-
read.fold(
|
95
|
-
->(_error) { default },
|
96
|
-
->(snapshot) { snapshot.value },
|
97
|
-
)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Returns a Maybe containing the cached value, or None if not present
|
101
|
-
# This provides a more functional approach than value_or
|
102
|
-
#: -> maybe[V]
|
103
|
-
def value_maybe
|
104
|
-
read.fold(
|
105
|
-
->(_error) { Maybe.none },
|
106
|
-
->(snapshot) { Maybe.some(snapshot.value) },
|
107
|
-
)
|
108
|
-
end
|
109
|
-
|
110
93
|
# Computes and caches a value if the cache is currently empty
|
111
94
|
# Returns existing snapshot if present, computed snapshot if cache miss, error otherwise
|
112
|
-
#: () { -> V } -> either[Error, Snapshot[V]]
|
95
|
+
#: () { -> V? } -> either[Error, Snapshot[maybe[V]]]
|
113
96
|
def compute_if_absent(&block)
|
114
97
|
fetch(&block).fold(
|
115
98
|
->(error) {
|
@@ -124,26 +107,6 @@ module TypedCache
|
|
124
107
|
)
|
125
108
|
end
|
126
109
|
|
127
|
-
# Creates a new CacheRef with the same store but different key
|
128
|
-
#: [R] (String) -> CacheRef[R]
|
129
|
-
def with_key(new_key)
|
130
|
-
CacheRef.new(store, store.namespace.key(new_key))
|
131
|
-
end
|
132
|
-
|
133
|
-
# Creates a scoped CacheRef by appending to the current key path
|
134
|
-
#: [R] (String) -> CacheRef[R]
|
135
|
-
def scope(scope_key)
|
136
|
-
new_namespace = store.namespace.nested(key.key)
|
137
|
-
new_store = store.with_namespace(new_namespace)
|
138
|
-
CacheRef.new(new_store, new_namespace.key(scope_key))
|
139
|
-
end
|
140
|
-
|
141
|
-
# Pattern matching support for Either[Error, Snapshot[V]] results
|
142
|
-
#: [R] (^(Error) -> R, ^(Snapshot[V]) -> R) -> R
|
143
|
-
def fold(left_fn, right_fn)
|
144
|
-
read.fold(left_fn, right_fn)
|
145
|
-
end
|
146
|
-
|
147
110
|
# Convenience method to work with the snapshot directly
|
148
111
|
#: [R] () { (Snapshot[V]) -> R } -> either[Error, R]
|
149
112
|
def with_snapshot(&block)
|
@@ -155,5 +118,11 @@ module TypedCache
|
|
155
118
|
def with(&block)
|
156
119
|
read.map { |snapshot| yield(snapshot.value) }
|
157
120
|
end
|
121
|
+
|
122
|
+
# @rbs () -> String
|
123
|
+
def to_s = "CacheRef(#{key} from #{store.namespace})"
|
124
|
+
|
125
|
+
# @rbs () -> String
|
126
|
+
def inspect = "CacheRef(#{key}, #{store.inspect})"
|
158
127
|
end
|
159
128
|
end
|
@@ -9,21 +9,21 @@ module TypedCache
|
|
9
9
|
module Decorator
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
include Store #[V]
|
13
|
-
# @rbs! include Store::_Store[V]
|
14
|
-
# @rbs! include Store::_Decorator[V]
|
15
|
-
|
16
12
|
# @rbs!
|
17
|
-
#
|
13
|
+
# interface _Decorator[V]
|
14
|
+
# def initialize: (Backend[V]) -> void
|
15
|
+
# end
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
include Backend #[V]
|
18
|
+
# @rbs! include Backend::_Backend[V]
|
19
|
+
# @rbs! include _Decorator[V]
|
22
20
|
|
23
21
|
# @rbs override
|
24
|
-
|
25
|
-
def
|
26
|
-
|
22
|
+
# @rbs () -> Backend[V]
|
23
|
+
def backend = raise NotImplementedError, "#{self.class} must implement #backend"
|
24
|
+
|
25
|
+
Backend.instance_methods(false).each do |method_name|
|
26
|
+
def_delegator :backend, method_name
|
27
27
|
end
|
28
28
|
|
29
29
|
# @rbs override
|
@@ -31,7 +31,17 @@ module TypedCache
|
|
31
31
|
def initialize_copy(other)
|
32
32
|
super
|
33
33
|
|
34
|
-
@
|
34
|
+
@backend = other.backend.clone
|
35
35
|
end
|
36
|
+
|
37
|
+
# @rbs override
|
38
|
+
# @rbs () -> String
|
39
|
+
def to_s = "#{self.class.name}(#{backend})"
|
40
|
+
|
41
|
+
# @rbs override
|
42
|
+
# @rbs () -> String
|
43
|
+
def inspect = "Decorator(#{self.class.name}, #{backend.inspect})"
|
36
44
|
end
|
45
|
+
|
46
|
+
# @rbs! type decorator[V] = Decorator::_Decorator[V] & Backend[V]
|
37
47
|
end
|
@@ -9,7 +9,7 @@ module TypedCache
|
|
9
9
|
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :backend #: Backend[V]
|
13
13
|
attr_reader :instrumenter #: Instrumenter
|
14
14
|
|
15
15
|
class << self
|
@@ -27,7 +27,7 @@ module TypedCache
|
|
27
27
|
return #{alias_prefix}_without_instrumentation(...) if @in_instrumentation
|
28
28
|
|
29
29
|
key = #{alias_prefix}_instrumentation_key(...)
|
30
|
-
instrumenter.instrument(:"#{operation}", key
|
30
|
+
instrumenter.instrument(:"#{operation}", key) do
|
31
31
|
@in_instrumentation = true
|
32
32
|
|
33
33
|
#{alias_prefix}_without_instrumentation(...)
|
@@ -42,9 +42,9 @@ module TypedCache
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
#: (
|
46
|
-
def initialize(
|
47
|
-
@
|
45
|
+
#: (Backend[V], instrumenter: Instrumenter) -> void
|
46
|
+
def initialize(backend, instrumenter:)
|
47
|
+
@backend = backend
|
48
48
|
@instrumenter = instrumenter
|
49
49
|
|
50
50
|
# Avoid instrumenting the cache calls themselves, fetch_all may call fetch for example
|
@@ -58,21 +58,14 @@ module TypedCache
|
|
58
58
|
@instrumenter = other.instrumenter
|
59
59
|
end
|
60
60
|
|
61
|
-
# @rbs override
|
62
|
-
#: -> String
|
63
|
-
def store_type
|
64
|
-
# Use polymorphism - delegate to the wrapped store
|
65
|
-
"instrumented(#{store.store_type})"
|
66
|
-
end
|
67
|
-
|
68
61
|
# Additional methods that might exist on the wrapped store
|
69
62
|
def respond_to_missing?(method_name, include_private = false)
|
70
|
-
|
63
|
+
backend.respond_to?(method_name, include_private) || super
|
71
64
|
end
|
72
65
|
|
73
66
|
def method_missing(method_name, *args, &block)
|
74
|
-
if
|
75
|
-
|
67
|
+
if backend.respond_to?(method_name)
|
68
|
+
backend.send(method_name, *args, &block)
|
76
69
|
else
|
77
70
|
super
|
78
71
|
end
|
@@ -80,12 +73,12 @@ module TypedCache
|
|
80
73
|
|
81
74
|
# Instrument core operations with proper key extraction
|
82
75
|
instrument(:read) { |key, *_| key }
|
83
|
-
instrument(:
|
84
|
-
instrument(:write)
|
85
|
-
instrument(:
|
76
|
+
instrument(:read_multi) { |keys, *_| keys.map(&:to_s).join('_') }
|
77
|
+
instrument(:write) { |key, *_| key }
|
78
|
+
instrument(:write_multi) { |values, *_| values.map { |key, _| key.to_s }.join('_') }
|
86
79
|
instrument(:delete) { |key, *_| key }
|
87
80
|
instrument(:fetch) { |key, *_| key }
|
88
|
-
instrument(:
|
81
|
+
instrument(:fetch_multi) { |keys, *_| keys.map(&:to_s).join('_') }
|
89
82
|
instrument(:key?) { |key, *_| key }
|
90
83
|
instrument(:clear) { 'all' }
|
91
84
|
end
|
@@ -23,7 +23,7 @@ module TypedCache
|
|
23
23
|
# lets end-users register their own via `Decorators.register`.
|
24
24
|
REGISTRY = Registry.new('decorator', {
|
25
25
|
instrumented: Instrumented,
|
26
|
-
}).freeze
|
26
|
+
}).freeze #: Registry[Decorator]
|
27
27
|
|
28
28
|
class << self
|
29
29
|
extend Forwardable
|
@@ -31,6 +31,12 @@ module TypedCache
|
|
31
31
|
# Delegate common registry helpers
|
32
32
|
delegate [:resolve, :register, :available, :registered?] => :registry
|
33
33
|
|
34
|
+
# @rbs!
|
35
|
+
# def resolve: (Symbol, *untyped, **untyped) -> either[Error, Decorator[untyped]]
|
36
|
+
# def available: -> Array[Symbol]
|
37
|
+
# def register: (Symbol, Class) -> either[Error, void]
|
38
|
+
# def registered?: (Symbol) -> bool
|
39
|
+
|
34
40
|
# @api private
|
35
41
|
# @rbs () -> Registry[Store[untyped]]
|
36
42
|
def registry = REGISTRY
|
data/lib/typed_cache/either.rb
CHANGED
@@ -86,6 +86,20 @@ module TypedCache
|
|
86
86
|
def deconstruct_keys(keys)
|
87
87
|
{ error: }
|
88
88
|
end
|
89
|
+
|
90
|
+
# @rbs (other: Object) -> bool
|
91
|
+
def ==(other)
|
92
|
+
other.is_a?(Left) && other.error == error
|
93
|
+
end
|
94
|
+
|
95
|
+
# @rbs () -> Integer
|
96
|
+
def hash = [Left, error].hash
|
97
|
+
|
98
|
+
# @rbs () -> String
|
99
|
+
def to_s = "Left(#{error})"
|
100
|
+
|
101
|
+
# @rbs () -> String
|
102
|
+
def inspect = "Left(#{error.inspect})"
|
89
103
|
end
|
90
104
|
|
91
105
|
# @rbs generic out R
|
@@ -139,5 +153,19 @@ module TypedCache
|
|
139
153
|
def deconstruct_keys(keys)
|
140
154
|
{ value: }
|
141
155
|
end
|
156
|
+
|
157
|
+
# @rbs (other: Object) -> bool
|
158
|
+
def ==(other)
|
159
|
+
other.is_a?(Right) && other.value == value
|
160
|
+
end
|
161
|
+
|
162
|
+
# @rbs () -> Integer
|
163
|
+
def hash = [Right, value].hash
|
164
|
+
|
165
|
+
# @rbs () -> String
|
166
|
+
def to_s = "Right(#{value})"
|
167
|
+
|
168
|
+
# @rbs () -> String
|
169
|
+
def inspect = "Right(#{value.inspect})"
|
142
170
|
end
|
143
171
|
end
|
@@ -19,7 +19,7 @@ module TypedCache
|
|
19
19
|
rails: ActiveSupport,
|
20
20
|
default: Null,
|
21
21
|
null: Null,
|
22
|
-
}) #: Registry[
|
22
|
+
}) #: Registry[Instrumenter]
|
23
23
|
|
24
24
|
class << self
|
25
25
|
extend Forwardable
|
@@ -28,8 +28,12 @@ module TypedCache
|
|
28
28
|
# @rbs () -> Registry[Symbol, Class[Instrumenter]]
|
29
29
|
def registry = REGISTRY
|
30
30
|
|
31
|
+
# @rbs (Symbol, ?namespace: String, **untyped) -> either[Error, Instrumenter]
|
32
|
+
def resolve(name, namespace: TypedCache.config.instrumentation.namespace, **options)
|
33
|
+
registry.resolve(name, namespace: namespace, **options)
|
34
|
+
end
|
35
|
+
|
31
36
|
# @rbs! def register: (Symbol, Class[Instrumenter]) -> void
|
32
|
-
# @rbs! def resolve: (Symbol, **untyped) -> either[Error, Instrumenter]
|
33
37
|
# @rbs! def available: () -> Array[Symbol]
|
34
38
|
# @rbs! def registered?: (Symbol) -> Boolean
|
35
39
|
|
data/lib/typed_cache/maybe.rb
CHANGED
@@ -78,6 +78,20 @@ module TypedCache
|
|
78
78
|
def deconstruct_keys(keys)
|
79
79
|
{ value: }
|
80
80
|
end
|
81
|
+
|
82
|
+
# @rbs (other: Object) -> bool
|
83
|
+
def ==(other)
|
84
|
+
other.is_a?(Some) && other.value == value
|
85
|
+
end
|
86
|
+
|
87
|
+
# @rbs () -> Integer
|
88
|
+
def hash = [Some, value].hash
|
89
|
+
|
90
|
+
# @rbs () -> String
|
91
|
+
def to_s = "Some(#{value})"
|
92
|
+
|
93
|
+
# @rbs () -> String
|
94
|
+
def inspect = "Some(#{value.inspect})"
|
81
95
|
end
|
82
96
|
|
83
97
|
class Nothing
|
@@ -106,5 +120,19 @@ module TypedCache
|
|
106
120
|
# @rbs override
|
107
121
|
#: -> V
|
108
122
|
def value_or_raise! = raise TypedCache::TypeError, 'Nothing has no value'
|
123
|
+
|
124
|
+
# @rbs (other: Object) -> bool
|
125
|
+
def ==(other)
|
126
|
+
other.is_a?(Nothing)
|
127
|
+
end
|
128
|
+
|
129
|
+
# @rbs () -> Integer
|
130
|
+
def hash = [Nothing].hash
|
131
|
+
|
132
|
+
# @rbs () -> String
|
133
|
+
def to_s = 'Nothing'
|
134
|
+
|
135
|
+
# @rbs () -> String
|
136
|
+
def inspect = to_s
|
109
137
|
end
|
110
138
|
end
|
@@ -164,7 +164,7 @@ module TypedCache
|
|
164
164
|
#
|
165
165
|
# @rbs () -> String
|
166
166
|
def inspect
|
167
|
-
"
|
167
|
+
"Namespace(#{@namespace})"
|
168
168
|
end
|
169
169
|
|
170
170
|
# @rbs () -> Integer
|
@@ -177,8 +177,6 @@ module TypedCache
|
|
177
177
|
other.is_a?(self.class) && other.to_s == to_s
|
178
178
|
end
|
179
179
|
|
180
|
-
alias eql? ==
|
181
|
-
|
182
180
|
private
|
183
181
|
|
184
182
|
# @rbs (String) -> String
|
data/lib/typed_cache/snapshot.rb
CHANGED
@@ -57,6 +57,12 @@ module TypedCache
|
|
57
57
|
|
58
58
|
alias flat_map bind
|
59
59
|
|
60
|
+
# @rbs () -> String
|
61
|
+
def to_s = "Snapshot(#{key} retrieved at #{retrieved_at} from #{source} with value #{value})"
|
62
|
+
|
63
|
+
# @rbs () -> String
|
64
|
+
def inspect = "Snapshot(#{key}, #{value.inspect}, #{source}, #{retrieved_at})"
|
65
|
+
|
60
66
|
class << self
|
61
67
|
# Creates a snapshot for a cached value
|
62
68
|
#: [V] (CacheKey, V) -> Snapshot[V]
|