Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update cache annotation #119

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/cached/example/cached_example/bin/example.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:convert';

import '../bin/user.dart';
import 'gen.dart';

/// Open gen.dart file to check how use [Cached] package,
Expand Down Expand Up @@ -43,4 +44,15 @@ void main(List<String> arguments) async {
print('\n==============================');

await streamSubscription.cancel();

final updatedUser = User('123', 'John Doe', 25);

final user = await gen.getUser('123');

await gen.updateUser(updatedUser, 'someParam');

final updatedUserResponse = await gen.getUser('123');

print('\nUser before update: ${user.name}'); // John
print('User after update: ${updatedUserResponse.name}'); // John Doe
}
31 changes: 31 additions & 0 deletions packages/cached/example/cached_example/bin/gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';

import 'package:cached_annotation/cached_annotation.dart';
import 'package:http/http.dart';
import '../bin/user.dart';

/// Do not forget add part keyword for file,
/// because then class not be generated.
Expand Down Expand Up @@ -128,6 +129,36 @@ abstract mixin class Gen implements _$Gen {
/// Method with this annotation will clear cached values for all methods.
@clearAllCached
void clearAllCache();

@Cached(
ttl: 30,
limit: 10,
)
Future<User> getUser(String id) async {
return User(id, 'John', 24);
}

@UpdateCache('getUser')
Future<User> updateUser(
@CacheKey(getUserKeyHash) User updatedUser,
@ignore String otherParamToBeIgnoredWhileGeneratingCacheKey,
) async {
return updatedUser;
}

@cached
int method() {
return 1;
}

@UpdateCache('method')
int updateMethodCache() {
return 2;
}
}

String getUserKeyHash(dynamic updatedUser) {
return updatedUser.id.hashCode.toString();
}

Future<bool> _shouldCache(Response response) async {
Expand Down
24 changes: 24 additions & 0 deletions packages/cached/example/cached_example/bin/user.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
final class User {
User(this.id, this.name, this.age);

final String id;
final String name;
final int age;

@override
String toString() {
return 'User{id: $id, name: $name, age: $age}';
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name &&
age == other.age;

@override
int get hashCode => id.hashCode ^ name.hashCode ^ age.hashCode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ Future<void> main(List<String> arguments) async {
// await gen.clearTodos();

await _fetchTodos(gen);

final number = await gen.getNumber();
print('Number: $number');
final updatedNumber = await gen.updateNumber(43);
print('Updated number: $updatedNumber');
final numberAfterUpdate = await gen.getNumber();
print('Number after update: $numberAfterUpdate');
}

Future<void> _fetchTodos(Gen gen) async {
Expand Down
20 changes: 20 additions & 0 deletions packages/cached/example/persistent_storage_example/bin/gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ abstract mixin class Gen implements _$Gen {
return decodedBody.map<Todo>((e) => Todo.fromJson(e)).toList();
}

@PersistentCached()
Future<int> getNumber() async {
return 42;
}

@DirectPersistentCached()
Future<int> getDirectNumber() async {
return 42;
}

@UpdateCache('getNumber')
Future<int> updateNumber(@ignore int number) async {
return number;
}

@UpdateCache('getDirectNumber')
Future<int> updateDirectNumber(@ignore int number) async {
return number;
}

/// To delete persisted data, you can also use [ClearAllCached],
/// [DeletesCache] annotations.
@ClearCached('getTodos')
Expand Down
15 changes: 15 additions & 0 deletions packages/cached/lib/src/models/class_with_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:cached/src/models/clear_cached_method.dart';
import 'package:cached/src/models/constructor.dart';
import 'package:cached/src/models/deletes_cache_method.dart';
import 'package:cached/src/models/streamed_cache_method.dart';
import 'package:cached/src/models/update_cache.dart';
import 'package:cached/src/utils/asserts.dart';
import 'package:cached/src/utils/utils.dart';
import 'package:cached_annotation/cached_annotation.dart';
Expand All @@ -28,6 +29,7 @@ class ClassWithCache {
required this.cachePeekMethods,
required this.deletesCacheMethods,
required this.getters,
required this.updateCacheMethods,
this.clearAllMethod,
});

Expand Down Expand Up @@ -146,6 +148,17 @@ class ClassWithCache {
[...methods, ...getters],
);

final updateCacheMethods = [...element.methods, ...element.accessors]
.where((element) => UpdateCacheMethod.getAnnotation(element) != null)
.map(
(e) => UpdateCacheMethod.fromElement(
e,
[...element.methods, ...element.accessors],
config,
),
)
.toList();

return ClassWithCache(
name: element.name,
useStaticCache:
Expand All @@ -158,6 +171,7 @@ class ClassWithCache {
cachePeekMethods: cachePeekMethods,
deletesCacheMethods: deletesCacheMethods,
getters: getters,
updateCacheMethods: updateCacheMethods,
);
}

Expand All @@ -171,4 +185,5 @@ class ClassWithCache {
final Iterable<DeletesCacheMethod> deletesCacheMethods;
final ClearAllCachedMethod? clearAllMethod;
final Iterable<CachedGetter> getters;
final Iterable<UpdateCacheMethod> updateCacheMethods;
}
141 changes: 141 additions & 0 deletions packages/cached/lib/src/models/update_cache.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:cached/src/config.dart';
import 'package:cached/src/models/cached_function.dart';
import 'package:cached/src/models/cached_getter.dart';
import 'package:cached/src/models/cached_method.dart';
import 'package:cached/src/models/check_if_should_cache_method.dart';
import 'package:cached/src/models/param.dart';
import 'package:cached/src/utils/asserts.dart';
import 'package:cached_annotation/cached_annotation.dart';
import 'package:source_gen/source_gen.dart';

class UpdateCacheMethod {
UpdateCacheMethod({
required this.target,
required this.params,
required this.name,
required this.isGetter,
required this.syncWrite,
required this.returnType,
required this.targetIsGenerator,
required this.isAsync,
required this.targetLimit,
required this.targetTtl,
required this.checkIfShouldCacheMethod,
required this.targetIsPersistentStorage,
required this.targetIsDirectPersistentStorage,
required this.targetIsLazyPersistentStorage,
});

factory UpdateCacheMethod.fromElement(
ExecutableElement element,
Iterable<ExecutableElement> allElements,
Config config,
) {
UpdateCacheMethod.assertIsValid(element);

final annotation = getAnnotation(element);

if (annotation == null) {
throw InvalidGenerationSourceError(
'[ERROR] Expected @UpdateCache annotation with parameters',
element: element,
);
}

final reader = ConstantReader(annotation);

final methodName = reader.read('methodName').stringValue;

if (methodName.isEmpty) {
throw InvalidGenerationSourceError(
'[ERROR] Method name cannot be empty',
element: element,
);
}

final targetElement = allElements.firstWhere(
(e) => e.name == methodName,
orElse: () => throw InvalidGenerationSourceError(
'[ERROR] Method "$methodName" do not exists',
element: element,
),
);

late final CachedFunction cachedFunction;

if (targetElement is MethodElement) {
cachedFunction = CachedMethod.fromElement(targetElement, config);
} else if (targetElement is PropertyAccessorElement) {
cachedFunction = CachedGetter.fromElement(targetElement, config);
} else {
throw InvalidGenerationSourceError(
'[ERROR] Unsupported element type ${targetElement.runtimeType}',
element: element,
);
}

final returnType =
element.returnType.getDisplayString(withNullability: false);

final params = element.parameters.map(
(e) => Param.fromElement(e, config),
);

final method = UpdateCacheMethod(
target: cachedFunction,
name: element.name,
isGetter: element is PropertyAccessorElement,
syncWrite: cachedFunction.syncWrite,
returnType: returnType,
isAsync: element.isAsynchronous,
targetIsGenerator: cachedFunction.isGenerator,
targetLimit: cachedFunction.limit,
targetTtl: cachedFunction.ttl,
checkIfShouldCacheMethod: cachedFunction.checkIfShouldCacheMethod,
targetIsPersistentStorage: cachedFunction.persistentStorage ?? false,
targetIsDirectPersistentStorage:
cachedFunction.directPersistentStorage ?? false,
targetIsLazyPersistentStorage:
cachedFunction.lazyPersistentStorage ?? false,
params: params,
);

return method;
}

final String name;
final bool isGetter;
final bool syncWrite;
final String returnType;
final bool isAsync;
final bool targetIsGenerator;
final int? targetLimit;
final int? targetTtl;
final CheckIfShouldCacheMethod? checkIfShouldCacheMethod;
final bool targetIsPersistentStorage;
final bool targetIsDirectPersistentStorage;
final bool targetIsLazyPersistentStorage;
final CachedFunction target;

static void assertIsValid(ExecutableElement element) {
assertMethodNotVoid(element);
assertMethodIsNotAbstract(element);
}

static DartObject? getAnnotation<T extends UpdateCache>(
ExecutableElement element,
) {
final methodAnnotationChecker = TypeChecker.fromRuntime(T);
return methodAnnotationChecker.firstAnnotationOf(element);
}

static bool hasCachedAnnotation<T extends UpdateCache>(
ExecutableElement element,
) {
return getAnnotation<T>(element) != null;
}

final Iterable<Param> params;
}
21 changes: 21 additions & 0 deletions packages/cached/lib/src/templates/class_template.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:cached/src/templates/clear_all_cached_method_template.dart';
import 'package:cached/src/templates/clear_cached_method_template.dart';
import 'package:cached/src/templates/deletes_cache_method_template.dart';
import 'package:cached/src/templates/streamed_method_template.dart';
import 'package:cached/src/templates/update_cache_template.dart';
import 'package:cached/src/utils/common_generator.dart';
import 'package:collection/collection.dart';

Expand Down Expand Up @@ -63,6 +64,8 @@ class ClassTemplate {

final deletesCacheMethodTemplates = _getDeleterCacheMethodTemplates();

final updateCacheMethodTemplates = _getUpdateCacheMethodTemplates();

return '''
class _${classWithCache.name} with ${classWithCache.name} implements _\$${classWithCache.name} {
_${classWithCache.name}(${constructorParamTemplates.generateThisParams()})${_initAsyncStorage()}
Expand Down Expand Up @@ -94,6 +97,8 @@ class ClassTemplate {
${clearAllMethodTemplate.generateMethod()}

${deletesCacheMethodTemplates.map((e) => e.generateMethod()).join('\n\n')}

${updateCacheMethodTemplates.map((e) => e.generate()).join('\n\n')}
}
''';
}
Expand Down Expand Up @@ -372,6 +377,22 @@ class ClassTemplate {
.toList();
}

Iterable<UpdateCacheMethodTemplate> _getUpdateCacheMethodTemplates() {
final updateCacheMethods = classWithCache.updateCacheMethods;
return updateCacheMethods.map((m) {
final streamedCacheMethods = classWithCache.streamedCacheMethods;
final isCacheStreamed = streamedCacheMethods.any(
(s) => s.targetMethodName == m.target.name,
);

return UpdateCacheMethodTemplate(
m,
useStaticCache: classWithCache.useStaticCache,
isCacheStreamed: isCacheStreamed,
);
});
}

bool _streamedMethodFilter(
DeletesCacheMethod method,
StreamedCacheMethod streamedMethod,
Expand Down
Loading
Loading