Skip to content

Commit f2be102

Browse files
committed
feta: add cache
1 parent 4ee4faf commit f2be102

File tree

7 files changed

+655
-0
lines changed

7 files changed

+655
-0
lines changed

cache/repository.go

+343
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
package cache
2+
3+
import (
4+
contracts "github.com/garavel-core/framework/contracts/cache"
5+
"github.com/garavel-core/framework/support/arr"
6+
"github.com/garavel-core/framework/support/helpers"
7+
)
8+
9+
type Repository struct {
10+
// The cache store implementation.
11+
store contracts.Store
12+
// The default number of seconds to store items.
13+
defaultCacheTime int
14+
// The event dispatcher implementation.
15+
// events contracts.Dispatcher
16+
}
17+
18+
// Create a new cache repository instance.
19+
func NewRepository(store contracts.Store) *Repository {
20+
return &Repository{store: store, defaultCacheTime: 3600}
21+
}
22+
23+
// Determine if an item exists in the cache.
24+
func (r *Repository) Has(key string) bool {
25+
return r.store.Get(key) != nil
26+
}
27+
28+
// Determine if an item doesn't exist in the cache.
29+
func (r *Repository) Missing(key string) bool {
30+
return !r.Has(key)
31+
}
32+
33+
// Retrieve an item from the cache by key.
34+
func (r *Repository) Get(key string, defaultValue ...any) any {
35+
value := r.store.Get(r.itemKey(key))
36+
37+
// If we could not find the cache value, we will fire the missed event and get
38+
// the default value for this cache value. This default could be a callback
39+
// so we will execute the value function which will resolve it if needed.
40+
if value == nil {
41+
// r.event(NewCacheMissed(key))
42+
43+
if defaultValue != nil && defaultValue[0] != nil {
44+
value = helpers.Value(defaultValue)
45+
}
46+
47+
} else {
48+
// r.event(NewCacheHit(key, value))
49+
}
50+
51+
return value
52+
}
53+
54+
// Retrieve multiple items from the cache by key.
55+
//
56+
// Items not found in the cache will have a null value.
57+
func (r *Repository) Many(keys any) map[string]any {
58+
if defaults, ok := keys.(map[string]any); ok {
59+
return arr.Map(r.store.Many(arr.Keys(defaults)), func(value any, key string) any {
60+
if value == nil {
61+
return defaults[key]
62+
}
63+
64+
return value
65+
})
66+
}
67+
68+
return r.store.Many(keys.([]string))
69+
}
70+
71+
// Obtains multiple cache items by their unique keys.
72+
func (r *Repository) GetMultiple(keys []string, defaultValue ...any) map[string]any {
73+
var value any
74+
75+
if len(defaultValue) != 0 {
76+
value = defaultValue[0]
77+
}
78+
79+
defaults := make(map[string]any, len(keys))
80+
81+
for _, key := range keys {
82+
defaults[key] = value
83+
}
84+
85+
return r.Many(defaults)
86+
}
87+
88+
// Retrieve an item from the cache and delete it.
89+
func (r *Repository) Pull(key string, defaultValue ...any) any {
90+
return helpers.Tap(r.Get(key, defaultValue...), func(_ any) {
91+
r.Forget(key)
92+
})
93+
}
94+
95+
// Store an item in the cache.
96+
func (r *Repository) Put(key string, value any, ttl ...any) bool {
97+
if ttl == nil || ttl[0] == nil {
98+
return r.Forever(key, value)
99+
}
100+
101+
seconds := r.getSeconds(ttl)
102+
103+
if seconds <= 0 {
104+
return r.Forget(key)
105+
}
106+
107+
result := r.store.Put(r.itemKey(key), value, seconds)
108+
109+
// if result {
110+
// r.event(NewKeyWritten(key, value, seconds))
111+
// }
112+
113+
return result
114+
}
115+
116+
// Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
117+
func (r *Repository) Set(key string, value any, ttl ...any) bool {
118+
return r.Put(key, value, ttl...)
119+
}
120+
121+
// Store multiple items in the cache for a given number of seconds.
122+
func (r *Repository) PutMany(values map[string]any, ttl ...any) bool {
123+
if ttl == nil || ttl[0] == nil {
124+
return r.putManyForever(values)
125+
}
126+
127+
seconds := r.getSeconds(ttl)
128+
129+
if seconds <= 0 {
130+
return r.DeleteMultiple(arr.Keys(values))
131+
}
132+
133+
result := r.store.PutMany(values, seconds)
134+
135+
// if result {
136+
// for key, value := range values {
137+
// r.event(NewKeyWritten(key, value, seconds))
138+
// }
139+
// }
140+
141+
return result
142+
}
143+
144+
// Store multiple items in the cache indefinitely.
145+
func (r *Repository) putManyForever(values map[string]any) bool {
146+
result := true
147+
148+
for key, value := range values {
149+
if !r.Forever(key, value) {
150+
result = false
151+
}
152+
}
153+
154+
return result
155+
}
156+
157+
// Persists a set of key => value pairs in the cache, with an optional TTL.
158+
func (r *Repository) SetMultiple(values map[string]any, ttl ...any) bool {
159+
return r.PutMany(values, ttl...)
160+
}
161+
162+
// Store an item in the cache if the key does not exist.
163+
func (r *Repository) Add(key string, value any, ttl ...any) bool {
164+
var seconds any
165+
166+
if ttl != nil && ttl[0] != nil {
167+
seconds = r.getSeconds(ttl)
168+
169+
if seconds.(int) <= 0 {
170+
return false
171+
}
172+
173+
// If the store has an "add" method we will call the method on the store so it
174+
// has a chance to override this logic. Some drivers better support the way
175+
// this operation should work with a total "atomic" implementation of it.
176+
if store, ok := any(r.store).(contracts.AtomicStore); ok {
177+
return store.Add(r.itemKey(key), value, seconds)
178+
}
179+
}
180+
181+
// If the value did not exist in the cache, we will put the value in the cache
182+
// so it exists for subsequent requests. Then, we will return true so it is
183+
// easy to know if the value gets added. Otherwise, we will return false.
184+
if r.Get(key) == nil {
185+
return r.Put(key, value, seconds)
186+
}
187+
188+
return false
189+
}
190+
191+
// Increment the value of an item in the cache.
192+
func (r *Repository) Increment(key string, value ...any) any {
193+
return r.store.Increment(key, value...)
194+
}
195+
196+
// Decrement the value of an item in the cache.
197+
func (r *Repository) Decrement(key string, value ...any) any {
198+
return r.store.Decrement(key, value...)
199+
}
200+
201+
// Store an item in the cache indefinitely.
202+
func (r *Repository) Forever(key string, value any) bool {
203+
result := r.store.Forever(r.itemKey(key), value)
204+
205+
// if result {
206+
// r.event(NewKeyWritten(key, value))
207+
// }
208+
209+
return result
210+
}
211+
212+
// Get an item from the cache, or execute the given Closure and store the result.
213+
func (r *Repository) Remember(key string, ttl any, callback func() any) any {
214+
value := r.Get(key)
215+
216+
// If the item exists in the cache we will just return this immediately and if
217+
// not we will execute the given Closure and cache the result of that for a
218+
// given number of seconds so it's available for all subsequent requests.
219+
if value != nil {
220+
return value
221+
}
222+
223+
value = callback()
224+
225+
r.Put(key, value, helpers.Value(ttl, value))
226+
227+
return value
228+
}
229+
230+
// Get an item from the cache, or execute the given Closure and store the result forever.
231+
func (r *Repository) Sear(key string, callback func() any) any {
232+
return r.RememberForever(key, callback)
233+
}
234+
235+
// Get an item from the cache, or execute the given Closure and store the result forever.
236+
func (r *Repository) RememberForever(key string, callback func() any) any {
237+
value := r.Get(key)
238+
239+
// If the item exists in the cache we will just return this immediately
240+
// and if not we will execute the given Closure and cache the result
241+
// of that forever so it is available for all subsequent requests.
242+
if value != nil {
243+
return value
244+
}
245+
246+
return helpers.Tap(callback(), func(value any) {
247+
r.Forever(key, value)
248+
})
249+
}
250+
251+
// Remove an item from the cache.
252+
func (r *Repository) Forget(key string) bool {
253+
return helpers.Tap(r.store.Forget(r.itemKey(key)), func(result bool) {
254+
// if result {
255+
// r.event(NewKeyForgotten(key))
256+
// }
257+
})
258+
}
259+
260+
// Delete an item from the cache by its unique key.
261+
func (r *Repository) Delete(key string) bool {
262+
return r.Forget(key)
263+
}
264+
265+
// Deletes multiple cache items in a single operation.
266+
func (r *Repository) DeleteMultiple(keys []string) bool {
267+
result := true
268+
269+
for _, key := range keys {
270+
if !r.Forget(key) {
271+
result = false
272+
}
273+
}
274+
275+
return result
276+
}
277+
278+
// Wipes clean the entire cache's keys.
279+
func (r *Repository) Clear() bool {
280+
return r.store.Flush()
281+
}
282+
283+
// Begin executing a new tags operation if the store supports it.
284+
// func (r *Repository) Tags(names ...string) (contracts.TaggableStore, error) {
285+
// store, ok := any(r.store).(contracts.TaggableStore);
286+
287+
// if !ok {
288+
// return nil, errors.New("This cache store does not support tagging.")
289+
// }
290+
291+
// cache := store.Tags(names...)
292+
293+
// if r.events != nil {
294+
// cache.SetEventDispatcher(r.events)
295+
// }
296+
297+
// return cache.SetDefaultCacheTime(r.defaultCacheTime), nil
298+
// }
299+
300+
// Format the key for a cache item.
301+
func (r *Repository) itemKey(key string) string {
302+
return key
303+
}
304+
305+
// Calculate the number of seconds for the given TTL.
306+
func (r *Repository) getSeconds(ttl any) int {
307+
// TODO
308+
return 0
309+
}
310+
311+
// Get the default cache time.
312+
func (r *Repository) GetDefaultCacheTime() int {
313+
return r.defaultCacheTime
314+
}
315+
316+
// Set the default cache time in seconds.
317+
func (r *Repository) SetDefaultCacheTime(seconds int) *Repository {
318+
r.defaultCacheTime = seconds
319+
320+
return r
321+
}
322+
323+
// Get the cache store implementation.
324+
func (r *Repository) GetStore() contracts.Store {
325+
return r.store
326+
}
327+
328+
// Fire an event for this cache instance.
329+
// func (r *Repository) event(event any) {
330+
// if r.events != nil {
331+
// r.events.dispatch(event)
332+
// }
333+
// }
334+
335+
// Get the event dispatcher instance.
336+
// func (r *Repository) GetEventDispatcher() contracts.Dispatcher {
337+
// return r.events;
338+
// }
339+
340+
// Set the event dispatcher instance.
341+
// func (r *Repository) SetEventDispatcher(events contracts.Dispatcher) {
342+
// r.events = events;
343+
// }

contracts/cache/atomic_store.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package cache
2+
3+
type AtomicStore interface {
4+
// Store an item in the cache if the key does not exist.
5+
Add(key string, value any, ttl ...any) bool
6+
}

0 commit comments

Comments
 (0)