Skip to content

Commit 7392ca9

Browse files
committed
feat: move UseRepoMixin hooks to detecated record
1 parent e711714 commit 7392ca9

File tree

6 files changed

+112
-74
lines changed

6 files changed

+112
-74
lines changed

lib/src/cache/mixins/query_mixins.dart

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'dart:typed_data';
55
import 'package:fuzzy_bolt/fuzzy_bolt.dart';
66
import 'package:get_it/get_it.dart';
77
import 'package:grumpy_annotations/grumpy_annotations.dart';
8-
import 'package:memory_cache/memory_cache.dart';
98
import 'package:meta/meta.dart';
109
import 'package:grumpy/grumpy.dart';
1110

@@ -42,8 +41,6 @@ mixin QueryMixin<T> on Repo<T>, RepoLifecycleHooksMixin<T>, TelemetryMixin {
4241

4342
int _dataVersion = 0;
4443

45-
/// Legacy in-memory cache used when pipeline is unavailable.
46-
final cache = MemoryCache();
4744
final Map<StorageKey, Future<Object?>> _inflight =
4845
<StorageKey, Future<Object?>>{};
4946

@@ -106,29 +103,6 @@ mixin QueryMixin<T> on Repo<T>, RepoLifecycleHooksMixin<T>, TelemetryMixin {
106103

107104
onData((_) {
108105
_dataVersion++;
109-
if (invalidateCacheOnNewData) {
110-
cache.invalidate();
111-
log('Cache cleared due to new data. (v=$_dataVersion)');
112-
}
113-
});
114-
115-
onLoading(() {
116-
if (invalidateCacheOnLoading) {
117-
cache.invalidate();
118-
log('Cache cleared due to loading state.');
119-
}
120-
});
121-
122-
onError((error, stackTrace) {
123-
if (invalidateCacheOnError) {
124-
cache.invalidate();
125-
log('Cache cleared due to error: $error');
126-
}
127-
});
128-
129-
onDisposed(() {
130-
cache.invalidate();
131-
log('Cache cleared on dispose.');
132106
});
133107

134108
_installed = true;

lib/src/repo/mixins/use_repo_mixin.dart

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@ import 'package:grumpy_annotations/grumpy_annotations.dart';
55
import 'package:meta/meta.dart';
66
import 'package:grumpy/grumpy.dart';
77

8+
/// Provides use hooks for watching and accessing data within [UseRepoMixin._onDependenciesReady].
9+
typedef UseHooks = ({
10+
/// A function that allows you to watch a [Repo] of type [R] managing data of type [S] and
11+
/// returns a tuple containing the data from the repo and the repo itself.
12+
///
13+
/// Throws a [NoRepoDataError] if the repo's state does not contain data.
14+
Future<(S, R)> Function<S, R extends Repo<S>>() repo,
15+
});
16+
817
/// A mixin that provides functionality to watch and use multiple [Repo] instances.
918
///
1019
/// {@category repo}
11-
1220
mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
1321
final _subs = <StreamSubscription>[];
1422
final _watchedRepos = <Type, Repo>{};
1523
final _pendingRepoResolutions = <Type, Future<Repo<dynamic>>>{};
24+
final _watchedStreams = <Stream, StreamSubscription>{};
1625

1726
bool _installed = false;
1827
int _stateChangeVersion = 0;
@@ -71,7 +80,7 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
7180
log('All dependencies are ready. Rebuilding data...');
7281
nextError = null;
7382
nextLoading = null;
74-
nextData = await onDependenciesReady();
83+
nextData = await _onDependenciesReady();
7584
log('Dependencies ready, obtained new data.');
7685
}
7786
} on NoRepoDataError catch (e, st) {
@@ -103,7 +112,7 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
103112
Future<void> _discover() async {
104113
log('Discovering dependencies...');
105114
try {
106-
final result = await onDependenciesReady();
115+
final result = await _onDependenciesReady();
107116

108117
// if we already get data in the discovery phase, store it.
109118
_lastData = result;
@@ -116,6 +125,11 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
116125
_lastError = await onDependencyError(e, st);
117126
// Ignore errors during the initial discovery phase.
118127
// we are just trying to discover repos here.
128+
} finally {
129+
log(
130+
'Dependency discovery complete. Currently watching ${_watchedRepos.length} repos.',
131+
);
132+
await dependenciesChanged();
119133
}
120134
}
121135

@@ -150,13 +164,17 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
150164
_subs.clear();
151165
});
152166
onDisposed(_watchedRepos.clear);
167+
onDisposed(_watchedStreams.clear);
153168
}
154169

155170
/// Watches a [Repo] of type [R] managing data of type [S] and
156171
/// returns a tuple containing the data from the repo and the repo itself.
157172
///
158173
/// Throws a [NoRepoDataError] if the repo's state does not contain data.
159-
Future<(S, R)> useRepo<S, R extends Repo<S>>() async {
174+
@Deprecated('Use the provided use arg in onDependenciesReady instead')
175+
Future<(S, R)> useRepo<S, R extends Repo<S>>() => _useRepo<S, R>();
176+
177+
Future<(S, R)> _useRepo<S, R extends Repo<S>>() async {
160178
if (!_installed) {
161179
throw StateError(
162180
'UseRepoMixin not installed. Call installUseRepoHooks in the constructor.',
@@ -180,7 +198,7 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
180198
return _watchedRepos[R] as R;
181199
}
182200

183-
log('Discovered new dependency. Now watching repo of type $R');
201+
log('Discovered new dependency. Now watching ${repo.logTag}');
184202
_watchedRepos[R] = repo;
185203

186204
var ignoredInitialReplay = false;
@@ -207,17 +225,19 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
207225
return (repo.state.requireData, repo);
208226
}
209227

228+
FutureOr<D> _onDependenciesReady() => onDependenciesReady((repo: _useRepo));
229+
210230
/// A callback function that is called when all watched repositories are ready.
211-
/// Call [useRepo] within this function to access repositories required to build the value.
231+
/// Call [use.useRepo] within this function to access repositories required to build the value.
212232
///
213-
/// [onDependenciesReady] is called whenever any of the watched repositories emit a new state and *all* watched
233+
/// [_onDependenciesReady] is called whenever any of the watched repositories emit a new state and *all* watched
214234
/// repositories have a state of [RepoDataState].
215235
///
216236
/// If this function throws an exception, the error will be handled by [onDependencyError].
217-
FutureOr<D> onDependenciesReady();
237+
FutureOr<D> onDependenciesReady(UseHooks use);
218238

219239
/// A callback function that is called when any of the watched repositories emit an error state
220-
/// or when an exception is thrown during the execution of [onDependenciesReady].
240+
/// or when an exception is thrown during the execution of [_onDependenciesReady].
221241
///
222242
/// Takes precedence over [onDependenciesLoading].
223243
FutureOr<E> onDependencyError(Object error, StackTrace? stackTrace);
@@ -262,17 +282,6 @@ mixin UseRepoMixin<D, E, L> on LifecycleMixin, LifecycleHooksMixin {
262282
}
263283
}
264284

265-
/// A typedef for [UseRepoMixin.useRepo].
266-
///
267-
/// **Example:**
268-
/// ```dart
269-
/// final (user, userRepo) = await useRepo<User, UserRepo>();
270-
/// ```
271-
///
272-
/// {@category repo}
273-
274-
typedef UseRepo = Future<(T, R)> Function<T, R extends Repo<T>>();
275-
276285
/// A mixin that provides a deferred repository implementation using [UseRepoMixin].
277286
///
278287
/// The [DeferredRepoMixin] allows you to create a repository that builds its state
@@ -298,15 +307,15 @@ mixin DeferredRepoMixin<T> on Repo<T>, UseRepoMixin<void, void, void> {
298307

299308
@mustCallSuper
300309
@override
301-
FutureOr<void> onDependenciesReady() async {
302-
final data = await build();
310+
FutureOr<void> onDependenciesReady(UseHooks use) async {
311+
final data = await build(use);
303312

304313
this.data(data);
305314
}
306315

307316
/// A builder function that constructs the state of this repo of type [T].
308317
///
309-
/// When implementing this method, you can call [useRepo] to access other repositories
318+
/// When implementing this method, you can call [UseHooks.repo] to access other repositories
310319
/// that this repository depends on.
311-
FutureOr<T> build();
320+
FutureOr<T> build(UseHooks use);
312321
}

lib/src/telemetry/infra/services/noop_analytics_service.dart

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:async';
2+
13
import 'package:grumpy/grumpy.dart';
24

35
/// A no-operation implementation of [AnalyticsService].
@@ -9,10 +11,46 @@ class NoopAnalyticsService extends AnalyticsService {
911
NoopAnalyticsService() : super.internal();
1012

1113
@override
12-
noSuchMethod(Invocation invocation) {
13-
log('NoopAnalyticsService: ${invocation.memberName} called.');
14+
String get logTag => 'NoopAnalyticsService';
15+
16+
@override
17+
FutureOr<void> destroy() {}
18+
19+
@override
20+
Future<void> groupUser(String groupId, {Map<String, dynamic>? traits}) async {
21+
log('groupUser called.');
1422
}
1523

1624
@override
17-
String get logTag => 'NoopAnalyticsService';
25+
Future<void> identifyUser(
26+
String userId, {
27+
Map<String, dynamic>? traits,
28+
}) async {
29+
log('identifyUser called.');
30+
}
31+
32+
@override
33+
Future<void> recordNavigation(
34+
String from,
35+
String to, {
36+
Map<String, dynamic>? properties,
37+
}) async {
38+
log('recordNavigation called.');
39+
}
40+
41+
@override
42+
Future<void> recordPageView(
43+
String pageName, {
44+
Map<String, dynamic>? properties,
45+
}) async {
46+
log('recordPageView called.');
47+
}
48+
49+
@override
50+
Future<void> trackEvent(
51+
String name, {
52+
Map<String, dynamic>? properties,
53+
}) async {
54+
log('trackEvent called.');
55+
}
1856
}

lib/src/telemetry/infra/services/noop_telemetry_service.dart

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,44 @@ class NoopTelemetryService extends TelemetryService {
1010
/// A no-operation implementation of [TelemetryService].
1111
NoopTelemetryService() : super.internal();
1212

13-
@override
14-
noSuchMethod(Invocation invocation) {
15-
log('NoopTelemetryService: ${invocation.memberName} called.');
16-
}
17-
1813
@override
1914
Future<T> runSpan<T>(
2015
String name,
2116
FutureOr<T> Function() callback, {
2217
Map<String, dynamic>? attributes,
2318
}) async {
24-
log(
25-
'NoopTelemetryService: runSpan $name called with attributes: $attributes',
26-
);
19+
log('runSpan $name called with attributes: $attributes');
2720

2821
return callback();
2922
}
3023

3124
@override
3225
String get logTag => 'NoopTelemetryService';
26+
27+
@override
28+
void addSpanAttribute(String key, String value) {
29+
log('addSpanAttribute called with key: $key, value: $value');
30+
}
31+
32+
@override
33+
FutureOr<void> destroy() {}
34+
35+
@override
36+
Future<void> recordEvent(
37+
String name, {
38+
Map<String, dynamic>? attributes,
39+
}) async {
40+
log('recordEvent called with name: $name, attributes: $attributes');
41+
}
42+
43+
@override
44+
Future<void> recordException(
45+
Object error, [
46+
StackTrace? stackTrace,
47+
Map<String, dynamic>? attributes,
48+
]) async {
49+
log(
50+
'recordException called with error: $error, stackTrace: $stackTrace, attributes: $attributes',
51+
);
52+
}
3353
}

pubspec.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ dependencies:
1515
url: https://github.com/necodeIT/grumpy_annotations.git
1616
json_annotation: ^4.8.0
1717
logging: ^1.3.0
18-
memory_cache: ^1.2.0
1918
meta: ^1.17.0
2019
routingkit: ^5.1.2
2120
rxdart: ^0.28.0
@@ -27,5 +26,3 @@ dev_dependencies:
2726

2827
lints: ^6.0.0
2928
test: ^1.25.6
30-
31-

test/repo/harness/use_repo_mixin_test_harness.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ class UseRepoConsumer
1919
Object? lastError;
2020

2121
@override
22-
FutureOr<String> onDependenciesReady() async {
23-
final (count, _) = await useRepo<int, IntRepo>();
24-
final (label, _) = await useRepo<String, StringRepo>();
22+
FutureOr<String> onDependenciesReady(UseHooks use) async {
23+
final (count, _) = await use.repo<int, IntRepo>();
24+
final (label, _) = await use.repo<String, StringRepo>();
2525
return '$count-$label';
2626
}
2727

@@ -59,10 +59,10 @@ class SlowSnapshotUseRepoConsumer
5959
final Completer<void> firstSnapshotCaptured = Completer<void>();
6060

6161
@override
62-
FutureOr<String> onDependenciesReady() async {
62+
FutureOr<String> onDependenciesReady(UseHooks use) async {
6363
readyCalls++;
64-
final (count, _) = await useRepo<int, IntRepo>();
65-
final (label, _) = await useRepo<String, StringRepo>();
64+
final (count, _) = await use.repo<int, IntRepo>();
65+
final (label, _) = await use.repo<String, StringRepo>();
6666
final snapshot = '$count-$label';
6767
lastSnapshot = snapshot;
6868
if (!firstSnapshotCaptured.isCompleted) {
@@ -93,7 +93,7 @@ class UninitializedConsumer
9393
LifecycleHooksMixin,
9494
UseRepoMixin<void, void, void> {
9595
@override
96-
FutureOr<void> onDependenciesReady() {}
96+
FutureOr<void> onDependenciesReady(UseHooks use) {}
9797

9898
@override
9999
FutureOr<void> onDependencyError(Object _, StackTrace? _) {}
@@ -117,9 +117,9 @@ class DeferredCombinedRepo extends Repo<String>
117117
}
118118

119119
@override
120-
FutureOr<String> build() async {
121-
final (count, _) = await useRepo<int, IntRepo>();
122-
final (label, _) = await useRepo<String, StringRepo>();
120+
FutureOr<String> build(UseHooks use) async {
121+
final (count, _) = await use.repo<int, IntRepo>();
122+
final (label, _) = await use.repo<String, StringRepo>();
123123
return '$count-$label';
124124
}
125125

0 commit comments

Comments
 (0)