diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
index 11a357af..fd29c844 100644
--- a/.idea/libraries/Dart_Packages.xml
+++ b/.idea/libraries/Dart_Packages.xml
@@ -5,14 +5,14 @@
-
+
-
+
@@ -26,21 +26,21 @@
-
+
-
+
-
+
@@ -58,10 +58,17 @@
+
+
+
+
+
+
+
-
+
@@ -89,7 +96,14 @@
-
+
+
+
+
+
+
+
+
@@ -110,7 +124,7 @@
-
+
@@ -138,7 +152,7 @@
-
+
@@ -173,7 +187,7 @@
-
+
@@ -215,7 +229,7 @@
-
+
@@ -226,6 +240,13 @@
+
+
+
+
+
+
+
@@ -264,7 +285,7 @@
-
+
@@ -278,14 +299,14 @@
-
+
-
+
@@ -296,10 +317,17 @@
+
+
+
+
+
+
+
-
+
@@ -331,6 +359,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -341,53 +383,59 @@
-
-
+
+
-
-
-
+
+
+
-
+
+
-
+
+
-
+
-
+
-
+
-
+
+
-
+
-
-
+
+
-
+
+
+
+
diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml
new file mode 100644
index 00000000..a1c33e62
--- /dev/null
+++ b/.idea/libraries/Dart_SDK.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/All_Tests.xml b/.idea/runConfigurations/All_Tests.xml
index ac11209e..c753608c 100644
--- a/.idea/runConfigurations/All_Tests.xml
+++ b/.idea/runConfigurations/All_Tests.xml
@@ -2,6 +2,7 @@
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 721e5e7e..3127f8de 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -2,8 +2,16 @@
+
+
+
+
+
+
+
+
@@ -24,24 +32,27 @@
-
-
+
+
-
-
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
@@ -49,8 +60,8 @@
-
-
+
+
@@ -59,39 +70,31 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -112,10 +115,8 @@
-
-
-
-
+
+
@@ -139,6 +140,9 @@
BEFORE
_onError
_fatalErrorStream.add
+ throw
+ upa
+ transform
@@ -153,13 +157,15 @@
-
-
-
+
+
+
+
+
@@ -170,8 +176,8 @@
DEFINITION_ORDER
-
-
+
+
@@ -193,6 +199,8 @@
+
+
@@ -275,8 +283,6 @@
-
-
@@ -291,13 +297,32 @@
-
+
+
+
+
+
+
+
+
+
+
+
@@ -332,7 +357,7 @@
-
+
@@ -352,11 +377,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -376,6 +401,7 @@
+
1481237183504
@@ -423,17 +449,17 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -455,7 +481,7 @@
-
+
@@ -467,29 +493,29 @@
-
+
-
-
+
-
+
-
+
+
+
+
-
-
@@ -517,7 +543,7 @@
-
+
@@ -584,7 +610,6 @@
-
@@ -627,9 +652,7 @@
-
-
-
+
@@ -662,7 +685,6 @@
-
@@ -712,9 +734,7 @@
-
-
-
+
@@ -751,9 +771,7 @@
-
-
-
+
@@ -786,16 +804,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -816,26 +824,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -843,7 +832,7 @@
-
+
@@ -851,7 +840,7 @@
-
+
@@ -877,10 +866,8 @@
-
-
-
-
+
+
@@ -896,16 +883,76 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/README.md b/README.md
index abb1495e..c605acd2 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# angel_framework
-[](https://pub.dartlang.org/packages/angel_framework)
+[](https://pub.dartlang.org/packages/angel_framework)
[](https://travis-ci.org/angel-dart/framework)
A high-powered HTTP server with support for dependency injection, sophisticated routing and more.
diff --git a/lib/hooks.dart b/lib/hooks.dart
index 684abc6b..71aa40d2 100644
--- a/lib/hooks.dart
+++ b/lib/hooks.dart
@@ -36,21 +36,75 @@ AngelConfigurer hookAllServices(callback(Service service)) {
HookedServiceEventListener toJson() => transform(god.serializeObject);
/// Mutates `e.data` or `e.result` using the given [transformer].
-HookedServiceEventListener transform(transformer(obj)) {
- normalize(obj) {
+///
+/// You can optionally provide a [condition], which can be:
+/// * A [Providers] instance, or String, to run only on certain clients
+/// * The type [Providers], in which case the transformer will run on every client, but *not* on server-side events.
+/// * A function: if the function returns `true` (sync or async, doesn't matter),
+/// then the transformer will run. If not, the event will be skipped.
+/// * An [Iterable] of the above three.
+///
+/// A provided function must take a [HookedServiceEvent] as its only parameter.
+HookedServiceEventListener transform(transformer(obj), [condition]) {
+ Iterable cond = condition is Iterable ? condition : [condition];
+
+ _condition(HookedServiceEvent e, condition) async {
+ if (condition is Function)
+ return await condition(e);
+ else if (condition == Providers)
+ return true;
+ else {
+ if (e.params?.containsKey('provider') == true) {
+ var provider = e.params['provider'] as Providers;
+ if (condition is Providers)
+ return condition == provider;
+ else
+ return condition.toString() == provider.via;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ normalize(HookedServiceEvent e, obj) async {
+ bool transform = true;
+
+ for (var c in cond) {
+ var r = await _condition(e, c);
+
+ if (r != true) {
+ transform = false;
+ break;
+ }
+ }
+
+ if (transform != true) {
+ if (obj == null)
+ return null;
+ else if (obj is Iterable)
+ return obj.toList();
+ else
+ return obj;
+ }
+
if (obj == null)
return null;
- else if (obj is Iterable)
- return obj.map(normalize).toList();
- else
+ else if (obj is Iterable) {
+ var r = [];
+
+ for (var o in obj) {
+ r.add(await normalize(e, o));
+ }
+
+ return r;
+ } else
return transformer(obj);
}
- return (HookedServiceEvent e) {
+ return (HookedServiceEvent e) async {
if (e.isBefore) {
- e.data = normalize(e.data);
- } else if (e.isAfter)
- e.result = normalize(e.result);
+ e.data = await normalize(e, e.data);
+ } else if (e.isAfter) e.result = await normalize(e, e.result);
};
}
@@ -77,8 +131,6 @@ HookedServiceEventListener toType(Type type) {
/// Only applies to the client-side.
HookedServiceEventListener remove(key, [remover(key, obj)]) {
return (HookedServiceEvent e) async {
- if (!e.isAfter) throw new StateError("'remove' only works on after hooks.");
-
_remover(key, obj) {
if (remover != null)
return remover(key, obj);
@@ -95,7 +147,7 @@ HookedServiceEventListener remove(key, [remover(key, obj)]) {
reflect(obj).setField(new Symbol(key), null);
return obj;
} catch (e) {
- throw new ArgumentError("Cannot remove key 'key' from $obj.");
+ throw new ArgumentError("Cannot remove key '$key' from $obj.");
}
}
}
@@ -206,10 +258,18 @@ HookedServiceEventListener addCreatedAt({
};
}
+/// Typo: Use [addUpdatedAt] instead.
+@deprecated
+HookedServiceEventListener addUpatedAt({
+ assign(obj, String now),
+ String key,
+}) =>
+ addUpdatedAt(assign: assign, key: key);
+
/// Serializes the current time to `e.data` or `e.result`.
/// You can provide an [assign] function to set the property on your object, and skip reflection.///
/// Default key: `createdAt`
-HookedServiceEventListener addUpatedAt({
+HookedServiceEventListener addUpdatedAt({
assign(obj, String now),
String key,
}) {
diff --git a/lib/src/http/map_service.dart b/lib/src/http/map_service.dart
index d06fbf73..a4b3909c 100644
--- a/lib/src/http/map_service.dart
+++ b/lib/src/http/map_service.dart
@@ -52,9 +52,11 @@ class MapService extends Service {
throw new AngelHttpException.badRequest(
message:
'MapService does not support `create` with ${data.runtimeType}.');
+ var now = new DateTime.now();
var result = data
..['id'] = items.length.toString()
- ..['createdAt'] = new DateTime.now();
+ ..['createdAt'] = now
+ ..['updatedAt'] = now;
items.add(result);
return result;
}
@@ -97,6 +99,14 @@ class MapService extends Service {
@override
Future