Fixed preprocessing?

This commit is contained in:
thosakwe 2016-12-30 21:00:52 -05:00
parent b05fc64f1f
commit ce28fa338b
2 changed files with 61 additions and 49 deletions

View file

@ -3,12 +3,11 @@ library angel_framework.http.controller;
import 'dart:async'; import 'dart:async';
import 'dart:mirrors'; import 'dart:mirrors';
import 'package:angel_route/angel_route.dart'; import 'package:angel_route/angel_route.dart';
import 'angel_http_exception.dart';
import 'metadata.dart'; import 'metadata.dart';
import 'request_context.dart'; import 'request_context.dart';
import 'response_context.dart'; import 'response_context.dart';
import 'routable.dart'; import 'routable.dart';
import 'server.dart' show Angel; import 'server.dart' show Angel, preInject;
/// Contains a list of the data required for a DI-enabled method to run. /// Contains a list of the data required for a DI-enabled method to run.
/// ///
@ -30,7 +29,6 @@ class Controller {
final bool debug; final bool debug;
List middleware = []; List middleware = [];
Map<String, Route> routeMappings = {}; Map<String, Route> routeMappings = {};
Expose exposeDecl;
Controller({this.debug: false}); Controller({this.debug: false});
@ -39,9 +37,7 @@ class Controller {
// Load global expose decl // Load global expose decl
ClassMirror classMirror = reflectClass(this.runtimeType); ClassMirror classMirror = reflectClass(this.runtimeType);
Expose exposeDecl = classMirror.metadata Expose exposeDecl = findExpose();
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null);
if (exposeDecl == null) { if (exposeDecl == null) {
throw new Exception( throw new Exception(
@ -83,7 +79,6 @@ class Controller {
var reflectedMethod = instanceMirror.getField(methodName).reflectee; var reflectedMethod = instanceMirror.getField(methodName).reflectee;
var middleware = []..addAll(handlers)..addAll(exposeDecl.middleware); var middleware = []..addAll(handlers)..addAll(exposeDecl.middleware);
var injection = new InjectionRequest();
// Check if normal // Check if normal
if (method.parameters.length == 2 && if (method.parameters.length == 2 &&
@ -95,26 +90,8 @@ class Controller {
return; return;
} }
// Load parameters
for (var parameter in method.parameters) {
var name = MirrorSystem.getName(parameter.simpleName);
var type = parameter.type.reflectedType;
if (type == RequestContext || type == ResponseContext) {
injection.required.add(type);
} else if (name == 'req') {
injection.required.add(RequestContext);
} else if (name == 'res') {
injection.required.add(ResponseContext);
} else if (type == dynamic) {
injection.required.add(name);
} else {
injection.required.add([name, type]);
}
}
routable.addRoute(exposeDecl.method, exposeDecl.path, routable.addRoute(exposeDecl.method, exposeDecl.path,
handleContained(reflectedMethod, injection), handleContained(reflectedMethod, preInject(reflectedMethod)),
middleware: middleware); middleware: middleware);
} }
}; };
@ -122,6 +99,12 @@ class Controller {
/// Used to add additional routes to the router from within a [Controller]. /// Used to add additional routes to the router from within a [Controller].
void configureRoutes(Routable routable) {} void configureRoutes(Routable routable) {}
/// Finds the [Expose] declaration for this class.
Expose findExpose() => reflectClass(runtimeType)
.metadata
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null);
} }
/// Handles a request with a DI-enabled handler. /// Handles a request with a DI-enabled handler.
@ -140,7 +123,7 @@ RequestHandler handleContained(handler, InjectionRequest injection) {
.containsKey(requirement)) .containsKey(requirement))
args.add(req.injections[requirement]); args.add(req.injections[requirement]);
else { else {
throw new Exception( throw new ArgumentError(
"Cannot resolve parameter '$requirement' within handler."); "Cannot resolve parameter '$requirement' within handler.");
} }
args.add(req.params[requirement]); args.add(req.params[requirement]);

View file

@ -126,6 +126,7 @@ class Angel extends AngelBase {
Future<HttpServer> startServer([InternetAddress address, int port]) async { Future<HttpServer> startServer([InternetAddress address, int port]) async {
final host = address ?? InternetAddress.LOOPBACK_IP_V4; final host = address ?? InternetAddress.LOOPBACK_IP_V4;
this.httpServer = await _serverGenerator(host, port ?? 0); this.httpServer = await _serverGenerator(host, port ?? 0);
preprocessRoutes();
return httpServer..listen(handleRequest); return httpServer..listen(handleRequest);
} }
@ -284,6 +285,27 @@ class Angel extends AngelBase {
} }
} }
/// Preprocesses all routes, and eliminates the burden of reflecting handlers
/// at run-time.
void preprocessRoutes() {
_add(v) {
if (v is Function && !_preContained.containsKey(v)) {
_preContained[v] = preInject(v);
}
}
void _walk(Router router) {
router.requestMiddleware.forEach((k, v) => _add(v));
router.middleware.forEach(_add);
router.routes
.where((r) => r is SymlinkRoute)
.map((SymlinkRoute r) => r.router)
.forEach(_walk);
}
_walk(this);
}
/// Run a function after injecting from service container. /// Run a function after injecting from service container.
/// If this function has been reflected before, then /// If this function has been reflected before, then
/// the execution will be faster, as the injection requirements were stored beforehand. /// the execution will be faster, as the injection requirements were stored beforehand.
@ -299,26 +321,7 @@ class Angel extends AngelBase {
/// Runs with DI, and *always* reflects. Prefer [runContained]. /// Runs with DI, and *always* reflects. Prefer [runContained].
Future runReflected( Future runReflected(
Function handler, RequestContext req, ResponseContext res) async { Function handler, RequestContext req, ResponseContext res) async {
ClosureMirror closureMirror = reflect(handler); var injection = preInjection(handler);
var injection = new InjectionRequest();
// Load parameters
for (var parameter in closureMirror.function.parameters) {
var name = MirrorSystem.getName(parameter.simpleName);
var type = parameter.type.reflectedType;
if (type == RequestContext || type == ResponseContext) {
injection.required.add(type);
} else if (name == 'req') {
injection.required.add(RequestContext);
} else if (name == 'res') {
injection.required.add(ResponseContext);
} else if (type == dynamic) {
injection.required.add(name);
} else {
injection.required.add([name, type]);
}
}
_preContained[handler] = injection; _preContained[handler] = injection;
return handleContained(handler, injection); return handleContained(handler, injection);
@ -330,13 +333,13 @@ class Angel extends AngelBase {
await configurer(this); await configurer(this);
if (configurer is Controller) if (configurer is Controller)
_onController.add(controllers[configurer.exposeDecl.path] = configurer); _onController.add(controllers[configurer.findExpose().path] = configurer);
} }
/// Fallback when an error is thrown while handling a request. /// Fallback when an error is thrown while handling a request.
void failSilently(HttpRequest request, ResponseContext res) {} void failSilently(HttpRequest request, ResponseContext res) {}
/// Starts the server. /// Starts the server, wrapped in a [runZoned] call.
void listen({InternetAddress address, int port: 3000}) { void listen({InternetAddress address, int port: 3000}) {
runZoned(() async { runZoned(() async {
await startServer(address, port); await startServer(address, port);
@ -433,3 +436,29 @@ class Angel extends AngelBase {
return app; return app;
} }
} }
/// Predetermines what needs to be injected for a handler to run.
InjectionRequest preInject(Function handler) {
ClosureMirror closureMirror = reflect(handler);
var injection = new InjectionRequest();
// Load parameters
for (var parameter in closureMirror.function.parameters) {
var name = MirrorSystem.getName(parameter.simpleName);
var type = parameter.type.reflectedType;
if (type == RequestContext || type == ResponseContext) {
injection.required.add(type);
} else if (name == 'req') {
injection.required.add(RequestContext);
} else if (name == 'res') {
injection.required.add(ResponseContext);
} else if (type == dynamic) {
injection.required.add(name);
} else {
injection.required.add([name, type]);
}
}
return injection;
}