diff --git a/.idea/runConfigurations/cache_service_dart.xml b/.idea/runConfigurations/cache_service_dart.xml
new file mode 100644
index 00000000..0e7d0ce4
--- /dev/null
+++ b/.idea/runConfigurations/cache_service_dart.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index de2210c9..0ab8dc43 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,3 @@
-language: dart
\ No newline at end of file
+language: dart
+dart:
+ - dev
\ No newline at end of file
diff --git a/example/cache_service.dart b/example/cache_service.dart
new file mode 100644
index 00000000..433ffb45
--- /dev/null
+++ b/example/cache_service.dart
@@ -0,0 +1,25 @@
+import 'package:angel_cache/angel_cache.dart';
+import 'package:angel_framework/angel_framework.dart';
+
+main() async {
+ var app = new Angel()..lazyParseBodies = true;
+
+ app.use(
+ '/api/todos',
+ new CacheService(
+ database: new AnonymousService(
+ index: ([params]) {
+ print('Fetched directly from the underlying service at ${new DateTime.now()}!');
+ return ['foo', 'bar', 'baz'];
+ },
+ read: (id, [params]) {
+ return {id: '$id at ${new DateTime.now()}'};
+ }
+ ),
+ ),
+ );
+
+ var http = new AngelHttp(app);
+ var server = await http.startServer('127.0.0.1', 3000);
+ print('Listening at http://${server.address.address}:${server.port}');
+}
diff --git a/lib/src/cache_service.dart b/lib/src/cache_service.dart
index 9f79435f..78ca1fce 100644
--- a/lib/src/cache_service.dart
+++ b/lib/src/cache_service.dart
@@ -1,4 +1,5 @@
import 'dart:async';
+import 'package:collection/collection.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:meta/meta.dart';
@@ -15,28 +16,118 @@ class CacheService extends Service {
/// If not provided, this defaults to a [MapService].
final Service cache;
- CacheService({@required this.database, Service cache})
+ final bool ignoreQuery;
+
+ final Duration timeout;
+
+ final Map _cache = {};
+ _CachedItem _indexed;
+
+ CacheService(
+ {@required this.database,
+ Service cache,
+ this.ignoreQuery: false,
+ this.timeout})
: this.cache = cache ?? new MapService() {
assert(database != null);
}
+ Future _getCached(Map params, _CachedItem get(), Future getFresh(),
+ Future getCached(), Future save(data, DateTime now)) async {
+ var cached = get();
+ //print('$params => $cached');
+ var now = new DateTime.now().toUtc();
+
+ if (cached != null) {
+ // If the entry has expired, don't send from the cache
+ var expired =
+ timeout != null && now.difference(cached.timestamp) >= timeout;
+
+ if (timeout == null || !expired) {
+ // Read from the cache if necessary
+ var queryEqual = ignoreQuery == true ||
+ (params != null &&
+ cached.params != null &&
+ const MapEquality()
+ .equals(params['query'], cached.params['query']));
+ if (queryEqual) {
+ return await getCached();
+ }
+ }
+ }
+
+ // If we haven't fetched from the cache by this point,
+ // let's fetch from the database.
+ var data = await getFresh();
+ await save(data, now);
+ return data;
+ }
+
+ @override
+ Future index([Map params]) {
+ return _getCached(
+ params,
+ () => _indexed,
+ () => database.index(params),
+ () => _indexed.data,
+ (data, now) async {
+ _indexed = new _CachedItem(params, now, data);
+ return data;
+ },
+ );
+ }
+
+ @override
+ Future read(id, [Map params]) async {
+ return _getCached(
+ params,
+ () => _cache[id],
+ () => database.read(id, params),
+ () => cache.read(id),
+ (data, now) async {
+ _cache[id] = new _CachedItem(params, now);
+ return await cache.modify(id, data);
+ },
+ );
+ }
+
@override
Future create(data, [Map params]) {
+ _indexed = null;
return database.create(data, params);
}
@override
Future modify(id, data, [Map params]) {
+ _indexed = null;
+ _cache.remove(id);
return database.modify(id, data, params);
}
@override
Future update(id, data, [Map params]) {
+ _indexed = null;
+ _cache.remove(id);
return database.modify(id, data, params);
}
@override
Future remove(id, [Map params]) {
+ _indexed = null;
+ _cache.remove(id);
return database.remove(id, params);
}
}
+
+class _CachedItem {
+ final params;
+ final DateTime timestamp;
+ final data;
+
+ _CachedItem(this.params, this.timestamp, [this.data]);
+
+ @override
+ String toString() {
+ return '$timestamp:$params:$data';
+ }
+}