Skip to content

Commit 53f01fb

Browse files
committed
refactor: wrap cache interactions
1 parent 97d24f6 commit 53f01fb

File tree

1 file changed

+41
-37
lines changed

1 file changed

+41
-37
lines changed

src/posit/connect/resources.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -104,35 +104,28 @@ def __init__(self, ctx: Context, path: str, pathinfo: str = "", uid: str = "guid
104104
self._ctx = ctx
105105
self._path = posixpath.join(path, pathinfo)
106106
self._uid = uid
107-
self._cache: Optional[List[T]] = None
108-
109-
def _get_or_fetch(self) -> List[T]:
110-
"""
111-
Fetch and cache the data from the API.
107+
self._cache = None
112108

113-
This method sends a GET request to the `_endpoint` and parses the response as a list of JSON objects.
114-
Each JSON object is used to instantiate an item of type `T` using the class specified by `_cls`.
115-
The results are cached after the first request and reused for subsequent access unless reloaded.
109+
def _make(self, result: dict) -> T:
110+
uid = result[self._uid]
111+
return self._create_instance(self._path, uid, **result)
116112

117-
Returns
118-
-------
119-
List[T]
120-
A list of items of type `T` representing the fetched data.
121-
"""
122-
if self._cache is not None:
123-
return self._cache
113+
def cached(self) -> bool:
114+
return self._cache is not None
124115

125-
endpoint = self._ctx.url + self._path
126-
response = self._ctx.session.get(endpoint)
127-
results = response.json()
116+
@property
117+
def _data(self) -> List[T]:
118+
if self._cache is None:
119+
self._cache = self.fetch()
120+
return self._cache
128121

129-
self._cache = []
130-
for result in results:
131-
uid = result[self._uid]
132-
instance = self._create_instance(self._path, uid, **result)
133-
self._cache.append(instance)
122+
@_data.setter
123+
def _data(self, value: List[T]):
124+
self._cache = value
134125

135-
return self._cache
126+
@_data.deleter
127+
def _data(self):
128+
self._cache = None
136129

137130
@overload
138131
def __getitem__(self, index: int) -> T: ...
@@ -141,20 +134,16 @@ def __getitem__(self, index: int) -> T: ...
141134
def __getitem__(self, index: slice) -> Sequence[T]: ...
142135

143136
def __getitem__(self, index):
144-
data = self._get_or_fetch()
145-
return data[index]
137+
return self._data[index]
146138

147139
def __len__(self) -> int:
148-
data = self._get_or_fetch()
149-
return len(data)
140+
return len(self._data)
150141

151142
def __str__(self) -> str:
152-
data = self._get_or_fetch()
153-
return str(data)
143+
return str(self._data)
154144

155145
def __repr__(self) -> str:
156-
data = self._get_or_fetch()
157-
return repr(data)
146+
return repr(self._data)
158147

159148
@abstractmethod
160149
def _create_instance(self, path: str, pathinfo: str, /, **kwargs: Any) -> T:
@@ -175,9 +164,25 @@ def reload(self) -> Self:
175164
ActiveSequence
176165
The current instance with cleared cache, ready to reload data on next access.
177166
"""
178-
self._cache = None
167+
del self._data
179168
return self
180169

170+
def fetch(self) -> List[T]:
171+
"""
172+
Fetch the collection.
173+
174+
Sends a GET request to fetch the collection from Connect.
175+
176+
Returns
177+
-------
178+
List[T]
179+
A list of items of type `T` representing the fetched data.
180+
"""
181+
endpoint = self._ctx.url + self._path
182+
response = self._ctx.session.get(endpoint)
183+
results = response.json()
184+
return [self._make(result) for result in results]
185+
181186

182187
class ActiveFinderMethods(ActiveSequence[T], ABC):
183188
"""Finder methods.
@@ -200,7 +205,7 @@ def find(self, uid) -> T:
200205
-------
201206
T
202207
"""
203-
if self._cache:
208+
if self.cached():
204209
# Check if the record already exists in the cache.
205210
# It is assumed that local cache scan is faster than an additional HTTP request.
206211
conditions = {self._uid: uid}
@@ -215,7 +220,7 @@ def find(self, uid) -> T:
215220

216221
# Invalidate the cache.
217222
# It is assumed that the cache is stale since a record exists on the server and not in the cache.
218-
self._cache = None
223+
self.reload()
219224

220225
return result
221226

@@ -234,5 +239,4 @@ def find_by(self, **conditions: Any) -> Optional[T]:
234239
Optional[T]
235240
The first record matching the conditions, or `None` if no match is found.
236241
"""
237-
data = self._get_or_fetch()
238-
return next((v for v in data if v.items() >= conditions.items()), None)
242+
return next((v for v in self._data if v.items() >= conditions.items()), None)

0 commit comments

Comments
 (0)