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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ec41a8981323affe19dcf95567cd004f58c744e6bada1e4b629678eda393f7d
|
4
|
+
data.tar.gz: 35cc676a0564191003dec33b91c53e98fea1e1f90bdc89269fbbfa35c395b2eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51a2c6dd90dcf5757d8058021cae3eb1d3002c0441673f008ed1c0a1302b8b3b32ad21981cf21c909ee0bf2f6a67f59827397bb0169d0f33e9fd536d9a468cac
|
7
|
+
data.tar.gz: 5f9a98912d940cbc4fb357f65d273ad030b7f97200a374b31b7b246c66ac85a733cd4b9332b545ea03fe7f25053271dcc19a4919e9312c2e0f85ea348a7eefce
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/typed_cache/backend.rb
CHANGED
@@ -10,7 +10,85 @@ module TypedCache
|
|
10
10
|
# that actually persist data.
|
11
11
|
# @rbs generic V
|
12
12
|
module Backend
|
13
|
-
|
14
|
-
|
13
|
+
# @rbs! type cache_key = String | CacheKey
|
14
|
+
|
15
|
+
# @rbs!
|
16
|
+
# interface _Backend[V]
|
17
|
+
# def read: (cache_key, **top) -> V?
|
18
|
+
# def read_multi: (Array[cache_key], **top) -> Hash[cache_key, V]
|
19
|
+
# def write: (cache_key, V, **top) -> V
|
20
|
+
# def write_multi: (Hash[cache_key, V], **top) -> Hash[cache_key, V]
|
21
|
+
# def delete: (cache_key) -> V?
|
22
|
+
# def key?: (cache_key) -> bool
|
23
|
+
# def clear: () -> void
|
24
|
+
# def fetch: (cache_key, **top) { () -> V? } -> V?
|
25
|
+
# def fetch_multi: (Array[cache_key], **top) { (cache_key) -> V? } -> Hash[cache_key, V]
|
26
|
+
# end
|
27
|
+
|
28
|
+
# @rbs! include _Backend[V]
|
29
|
+
|
30
|
+
# @rbs override
|
31
|
+
# @rbs (cache_key, **top) -> V?
|
32
|
+
def read(key, **opts)
|
33
|
+
raise NotImplementedError, "#{self.class} must implement #read"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @rbs override
|
37
|
+
# @rbs (Array[cache_key], **top) -> Hash[cache_key, V]
|
38
|
+
def read_multi(keys, **opts)
|
39
|
+
keys.to_h { |key| [key, read(key, **opts)] }
|
40
|
+
end
|
41
|
+
|
42
|
+
# @rbs override
|
43
|
+
# @rbs (cache_key, V, **top) -> V
|
44
|
+
def write(key, value, **opts)
|
45
|
+
raise NotImplementedError, "#{self.class} must implement #write"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @rbs override
|
49
|
+
# @rbs (Hash[cache_key, V], **top) -> Hash[cache_key, V]
|
50
|
+
def write_multi(values, **opts)
|
51
|
+
values.transform_values { |value| write(value, **opts) }
|
52
|
+
end
|
53
|
+
|
54
|
+
# @rbs override
|
55
|
+
# @rbs (cache_key) -> V?
|
56
|
+
def delete(key, **opts)
|
57
|
+
raise NotImplementedError, "#{self.class} must implement #delete"
|
58
|
+
end
|
59
|
+
|
60
|
+
# @rbs override
|
61
|
+
# @rbs (cache_key) -> bool
|
62
|
+
def key?(key, **opts)
|
63
|
+
raise NotImplementedError, "#{self.class} must implement #key?"
|
64
|
+
end
|
65
|
+
|
66
|
+
# @rbs override
|
67
|
+
# @rbs () -> void
|
68
|
+
def clear(**opts)
|
69
|
+
raise NotImplementedError, "#{self.class} must implement #clear"
|
70
|
+
end
|
71
|
+
|
72
|
+
# @rbs override
|
73
|
+
# @rbs (cache_key, **top) { () -> V? } -> V?
|
74
|
+
def fetch(key, **opts, &block)
|
75
|
+
raise NotImplementedError, "#{self.class} must implement #fetch"
|
76
|
+
end
|
77
|
+
|
78
|
+
# @rbs override
|
79
|
+
# @rbs (Array[cache_key], **top) { (cache_key) -> V? } -> Hash[cache_key, V]
|
80
|
+
def fetch_multi(keys, **opts, &block)
|
81
|
+
keys.to_h { |key| [key, fetch(key, **opts, &block)] }
|
82
|
+
end
|
83
|
+
|
84
|
+
# @rbs override
|
85
|
+
# @rbs () -> String
|
86
|
+
def to_s = self.class.name
|
87
|
+
|
88
|
+
# @rbs override
|
89
|
+
# @rbs () -> String
|
90
|
+
def inspect = self.class.inspect
|
15
91
|
end
|
92
|
+
|
93
|
+
# @rbs! type backend[V] = Backend::_Backend[V]
|
16
94
|
end
|
@@ -7,152 +7,78 @@ module TypedCache
|
|
7
7
|
class ActiveSupport
|
8
8
|
include Backend #[V]
|
9
9
|
|
10
|
-
attr_reader :namespace #: Namespace
|
11
10
|
attr_reader :cache_store #: ::ActiveSupport::Cache::Store
|
12
11
|
attr_reader :default_options #: Hash[Symbol, top]
|
13
12
|
|
14
|
-
#: (
|
15
|
-
def initialize(
|
16
|
-
@namespace = namespace
|
13
|
+
#: (::ActiveSupport::Cache::Store, ?Hash[Symbol, top]) -> void
|
14
|
+
def initialize(cache_store, default_options = {})
|
17
15
|
@cache_store = cache_store
|
18
16
|
@default_options = default_options
|
19
17
|
end
|
20
18
|
|
21
19
|
# @rbs override
|
22
|
-
#: (cache_key, **top) ->
|
20
|
+
#: (cache_key, **top) -> V?
|
23
21
|
def read(key, **kwargs)
|
24
|
-
|
25
|
-
raw_value = cache_store.read(cache_key_str, default_options.merge(**kwargs))
|
26
|
-
return Either.left(CacheMissError.new(key)) if raw_value.nil?
|
27
|
-
|
28
|
-
Either.right(Snapshot.cached(key, raw_value))
|
29
|
-
rescue => e
|
30
|
-
Either.left(StoreError.new(:get, key, "Failed to read from cache: #{e.message}", e))
|
22
|
+
cache_store.read(key, default_options.merge(kwargs))
|
31
23
|
end
|
32
24
|
|
33
25
|
# @rbs override
|
34
|
-
#: (cache_key, V, **top) ->
|
26
|
+
#: (cache_key, V, **top) -> V
|
35
27
|
def write(key, value, **kwargs)
|
36
|
-
|
37
|
-
success = cache_store.write(cache_key_str, value, default_options.merge(**kwargs))
|
38
|
-
|
39
|
-
if success
|
40
|
-
Either.right(Snapshot.cached(key, value))
|
41
|
-
else
|
42
|
-
Either.left(StoreError.new(:set, key, 'Failed to write to cache', nil))
|
43
|
-
end
|
44
|
-
rescue => e
|
45
|
-
Either.left(StoreError.new(:set, key, "Failed to write to cache: #{e.message}", e))
|
28
|
+
cache_store.write(key, value, default_options.merge(kwargs))
|
46
29
|
end
|
47
30
|
|
48
31
|
# @rbs override
|
49
|
-
#: (Hash[cache_key, V], **top) ->
|
32
|
+
#: (Hash[cache_key, V], **top) -> Array[V]
|
50
33
|
def write_all(values, **kwargs)
|
51
|
-
|
52
|
-
Either.right(results.map { |key, value| Snapshot.cached(key, value) })
|
53
|
-
rescue => e
|
54
|
-
Either.left(StoreError.new(:set_all, values, "Failed to write to cache: #{e.message}", e))
|
34
|
+
cache_store.write_multi(values, default_options.merge(kwargs))
|
55
35
|
end
|
56
36
|
|
57
37
|
# @rbs override
|
58
|
-
#: (cache_key) ->
|
38
|
+
#: (cache_key) -> V?
|
59
39
|
def delete(key)
|
60
|
-
|
61
|
-
->(error) { Either.left(error) },
|
62
|
-
->(snapshot) {
|
63
|
-
cache_key_str = namespaced_key(key).to_s
|
64
|
-
cache_store.delete(cache_key_str, default_options)
|
65
|
-
Either.right(snapshot)
|
66
|
-
},
|
67
|
-
)
|
68
|
-
rescue => e
|
69
|
-
Either.left(StoreError.new(:delete, key, "Failed to delete from cache: #{e.message}", e))
|
40
|
+
cache_store.delete(key, default_options)
|
70
41
|
end
|
71
42
|
|
72
43
|
# @rbs override
|
73
|
-
#: (Array[cache_key], **top) ->
|
44
|
+
#: (Array[cache_key], **top) -> Hash[cache_key, V]
|
74
45
|
def read_all(keys, **kwargs)
|
75
|
-
|
76
|
-
Either.right(results.map { |key, value| [key, Snapshot.cached(key, value)] }.to_h)
|
46
|
+
cache_store.read_multi(*keys, default_options.merge(kwargs))
|
77
47
|
end
|
78
48
|
|
79
49
|
# @rbs override
|
80
|
-
#: (
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
computed_keys = Set.new #: Set[String]
|
86
|
-
results = cache_store.fetch_multi(*key_map.keys, default_options.merge(**kwargs)) do |key|
|
87
|
-
computed_keys << key
|
88
|
-
yield(key_map[key])
|
89
|
-
end
|
90
|
-
|
91
|
-
snapshots = [] #: Array[Snapshot[V]]
|
92
|
-
|
93
|
-
results.each do |key, value|
|
94
|
-
maybe_value = Maybe.wrap(value)
|
95
|
-
snapshots <<
|
96
|
-
if computed_keys.include?(key)
|
97
|
-
Snapshot.computed(key, maybe_value)
|
98
|
-
else
|
99
|
-
Snapshot.cached(key, maybe_value)
|
100
|
-
end
|
101
|
-
end
|
50
|
+
#: (cache_key, **top) { () -> V? } -> V?
|
51
|
+
def fetch(key, **kwargs, &block)
|
52
|
+
cache_store.fetch(key, default_options.merge(kwargs), &block)
|
53
|
+
end
|
102
54
|
|
103
|
-
|
104
|
-
|
105
|
-
|
55
|
+
# @rbs override
|
56
|
+
#: (Array[cache_key], **top) { (CacheKey) -> V? } -> Hash[cache_key, V]
|
57
|
+
def fetch_all(keys, **kwargs, &block)
|
58
|
+
cache_store.fetch_multi(*keys, default_options.merge(kwargs), &block)
|
106
59
|
end
|
107
60
|
|
108
61
|
# @rbs override
|
109
62
|
#: (cache_key) -> bool
|
110
63
|
def key?(key)
|
111
|
-
cache_store.exist?(
|
112
|
-
rescue => _e
|
113
|
-
false
|
64
|
+
cache_store.exist?(key, default_options)
|
114
65
|
end
|
115
66
|
|
116
67
|
# @rbs override
|
117
|
-
#: ->
|
68
|
+
#: -> void
|
118
69
|
def clear
|
119
|
-
|
120
|
-
namespace_prefix_patterns.each do |pattern|
|
121
|
-
cache_store.delete_matched(pattern, default_options)
|
122
|
-
end
|
123
|
-
elsif cache_store.respond_to?(:clear)
|
124
|
-
cache_store.clear(default_options)
|
125
|
-
end
|
126
|
-
Maybe.none
|
127
|
-
rescue => e
|
128
|
-
Maybe.some(e)
|
129
|
-
end
|
130
|
-
|
131
|
-
# @rbs override
|
132
|
-
#: -> String
|
133
|
-
def store_type
|
134
|
-
'active_support'
|
70
|
+
cache_store.clear(default_options)
|
135
71
|
end
|
136
72
|
|
137
73
|
#: (Hash[Symbol, top]) -> ActiveSupport[V]
|
138
74
|
def with_options(new_options)
|
139
|
-
self.class.new(
|
75
|
+
self.class.new(cache_store, new_options)
|
140
76
|
end
|
141
77
|
|
142
78
|
#: -> ::ActiveSupport::Cache::Store
|
143
79
|
def raw_cache
|
144
80
|
cache_store
|
145
81
|
end
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
# Regex patterns that match keys for this namespace (with trailing colon)
|
150
|
-
#: -> Array[Regexp]
|
151
|
-
def namespace_prefix_patterns
|
152
|
-
[
|
153
|
-
/\A#{Regexp.escape(namespace.to_s)}:/,
|
154
|
-
]
|
155
|
-
end
|
156
82
|
end
|
157
83
|
end
|
158
84
|
end
|
@@ -5,18 +5,6 @@ require 'singleton'
|
|
5
5
|
require 'concurrent/map'
|
6
6
|
|
7
7
|
module TypedCache
|
8
|
-
class MemoryStoreRegistry
|
9
|
-
include Singleton
|
10
|
-
extend Forwardable
|
11
|
-
|
12
|
-
def_delegators :@backing_store, :[], :[]=, :delete, :key?, :keys
|
13
|
-
|
14
|
-
#: -> void
|
15
|
-
def initialize
|
16
|
-
@backing_store = Concurrent::Map.new
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
module Backends
|
21
9
|
# A type-safe memory store implementation with built-in namespacing
|
22
10
|
# @rbs generic V
|
@@ -57,109 +45,70 @@ module TypedCache
|
|
57
45
|
end
|
58
46
|
private_constant :Entry
|
59
47
|
|
60
|
-
attr_reader :
|
61
|
-
attr_reader :backing_store #: hash_like[
|
48
|
+
attr_reader :ttl #: Integer
|
49
|
+
attr_reader :backing_store #: hash_like[String, Entry[V]]
|
62
50
|
|
63
|
-
#: (
|
64
|
-
def initialize(
|
65
|
-
@namespace = namespace
|
51
|
+
#: (ttl: Integer) -> void
|
52
|
+
def initialize(ttl: 600)
|
66
53
|
@ttl = ttl
|
67
|
-
@backing_store =
|
54
|
+
@backing_store = Concurrent::Map.new
|
68
55
|
end
|
69
56
|
|
70
57
|
# @rbs override
|
71
|
-
#: (cache_key, **top) ->
|
58
|
+
#: (cache_key, **top) -> V?
|
72
59
|
def read(key, **kwargs)
|
73
|
-
|
74
|
-
return Either.left(CacheMissError.new(key)) unless backing_store.key?(key)
|
60
|
+
purge_expired_keys
|
75
61
|
|
76
62
|
entry = backing_store[key]
|
77
|
-
|
78
|
-
if entry.expired?
|
79
|
-
backing_store.delete(key)
|
80
|
-
return Either.left(CacheMissError.new(key))
|
81
|
-
end
|
82
|
-
|
83
|
-
Either.right(Snapshot.cached(key, entry.value))
|
63
|
+
entry&.value
|
84
64
|
end
|
85
65
|
|
86
66
|
# @rbs override
|
87
|
-
#: (cache_key, V, expires_in: Integer, expires_at: Time, **top) ->
|
67
|
+
#: (cache_key, V, expires_in: Integer, expires_at: Time, **top) -> V
|
88
68
|
def write(key, value, expires_in: @ttl, expires_at: Clock.now + expires_in, **kwargs)
|
89
|
-
key = namespaced_key(key)
|
90
69
|
entry = Entry.new(value: value, expires_at: expires_at)
|
91
70
|
backing_store[key] = entry
|
92
|
-
|
93
|
-
rescue => e
|
94
|
-
Either.left(StoreError.new(
|
95
|
-
:set,
|
96
|
-
key,
|
97
|
-
"Failed to store value for key '#{key}': #{e.message}",
|
98
|
-
e,
|
99
|
-
))
|
71
|
+
value
|
100
72
|
end
|
101
73
|
|
102
74
|
# @rbs override
|
103
|
-
#: (cache_key) ->
|
75
|
+
#: (cache_key) -> V?
|
104
76
|
def delete(key)
|
105
|
-
key = namespaced_key(key)
|
106
77
|
entry = backing_store.delete(key)
|
107
|
-
|
108
|
-
Either.left(CacheMissError.new(key))
|
109
|
-
else
|
110
|
-
Either.right(Snapshot.cached(key, entry.value))
|
111
|
-
end
|
78
|
+
entry&.value
|
112
79
|
end
|
113
80
|
|
114
81
|
# @rbs override
|
115
82
|
#: (cache_key) -> bool
|
116
83
|
def key?(key)
|
117
|
-
|
118
|
-
return false unless backing_store.key?(key) && key.belongs_to?(namespace)
|
84
|
+
return false unless backing_store.key?(key)
|
119
85
|
|
120
86
|
entry = backing_store[key]
|
121
87
|
!entry.expired?
|
122
88
|
end
|
123
89
|
|
124
90
|
# @rbs override
|
125
|
-
#: ->
|
91
|
+
#: -> void
|
126
92
|
def clear
|
127
|
-
|
128
|
-
keys_to_delete.each { |key| backing_store.delete(key) }
|
129
|
-
Maybe.none
|
130
|
-
rescue => e
|
131
|
-
Maybe.some(e)
|
93
|
+
backing_store.clear
|
132
94
|
end
|
133
95
|
|
134
96
|
# @rbs override
|
135
|
-
#: ->
|
136
|
-
def
|
137
|
-
'memory'
|
138
|
-
end
|
139
|
-
|
140
|
-
#: -> Integer
|
141
|
-
def size
|
97
|
+
#: (cache_key, ttl: Integer, **top) { () -> V? } -> V?
|
98
|
+
def fetch(key, ttl: @ttl, **opts, &block)
|
142
99
|
purge_expired_keys
|
143
|
-
|
144
|
-
|
100
|
+
result = backing_store.compute_if_absent(key) do
|
101
|
+
Entry.new(value: yield(key), expires_at: Clock.now + ttl)
|
102
|
+
end
|
145
103
|
|
146
|
-
|
147
|
-
def keys
|
148
|
-
purge_expired_keys
|
149
|
-
backing_store.keys
|
150
|
-
.select { |k| k.belongs_to?(namespace) }
|
104
|
+
result.value
|
151
105
|
end
|
152
106
|
|
153
107
|
private
|
154
108
|
|
155
|
-
#: -> Hash[CacheKey, Entry[V]]
|
156
|
-
def namespaced_entries
|
157
|
-
backing_store.select { |key, _entry| key.belongs_to?(namespace) }
|
158
|
-
end
|
159
|
-
|
160
109
|
#: -> void
|
161
110
|
def purge_expired_keys
|
162
|
-
|
111
|
+
backing_store.each do |key, entry|
|
163
112
|
backing_store.delete(key) if entry.expired?
|
164
113
|
end
|
165
114
|
end
|
data/lib/typed_cache/backends.rb
CHANGED
@@ -11,7 +11,7 @@ module TypedCache
|
|
11
11
|
# Backend registry using composition
|
12
12
|
REGISTRY = Registry.new('backend', {
|
13
13
|
memory: Memory,
|
14
|
-
}).freeze
|
14
|
+
}).freeze #: Registry[Backend]
|
15
15
|
|
16
16
|
class << self
|
17
17
|
extend Forwardable
|
@@ -23,7 +23,7 @@ module TypedCache
|
|
23
23
|
|
24
24
|
# Convenience method delegating to registry
|
25
25
|
# @rbs!
|
26
|
-
# def resolve: (Symbol, *untyped, **untyped) -> either[Error,
|
26
|
+
# def resolve: (Symbol, *untyped, **untyped) -> either[Error, Backend[untyped]]
|
27
27
|
# def available: -> Array[Symbol]
|
28
28
|
# def register: (Symbol, Class) -> either[Error, void]
|
29
29
|
# def registered?: (Symbol) -> bool
|