Make getAnnotation generic

This commit is contained in:
Tobe O 2019-04-08 15:53:07 -04:00
parent 1b1c190de4
commit 62709c0bbd
15 changed files with 41 additions and 37 deletions

View file

@ -1,3 +1,7 @@
# 2.0.0-alpha.24
* Add `AngelEnv` class to `core`.
* Deprecate `Angel.isProduction`, in favor of `AngelEnv`.
# 2.0.0-alpha.23 # 2.0.0-alpha.23
* `ResponseContext.render` sets `charset` to `utf8` in `contentType`. * `ResponseContext.render` sets `charset` to `utf8` in `contentType`.

View file

@ -75,9 +75,9 @@ class Controller {
methodName != 'call' && methodName != 'call' &&
methodName != 'equals' && methodName != 'equals' &&
methodName != '==') { methodName != '==') {
Expose exposeDecl = decl.function.annotations var exposeDecl = decl.function.annotations
.map((m) => m.reflectee) .map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null); .firstWhere((r) => r is Expose, orElse: () => null) as Expose;
if (exposeDecl == null) return; if (exposeDecl == null) return;

View file

@ -1,6 +1,7 @@
export 'anonymous_service.dart'; export 'anonymous_service.dart';
export 'controller.dart'; export 'controller.dart';
export 'driver.dart'; export 'driver.dart';
export 'env.dart';
export 'hooked_service.dart'; export 'hooked_service.dart';
export 'map_service.dart'; export 'map_service.dart';
export 'metadata.dart'; export 'metadata.dart';

View file

@ -1,15 +1,15 @@
import 'dart:io'; import 'dart:io';
/// A constant instance of [AngelEnv]. /// A constant instance of [AngelEnv].
const AngelEnv angelEnv = const AngelEnv(); const AngelEnvironment angelEnv = const AngelEnvironment();
/// Queries the environment's `ANGEL_ENV` value. /// Queries the environment's `ANGEL_ENV` value.
class AngelEnv { class AngelEnvironment {
final String _customValue; final String _customValue;
/// You can optionally provide a custom value, in order to override the system's /// You can optionally provide a custom value, in order to override the system's
/// value. /// value.
const AngelEnv([this._customValue]); const AngelEnvironment([this._customValue]);
/// Returns the value of the `ANGEL_ENV` variable; defaults to `'development'`. /// Returns the value of the `ANGEL_ENV` variable; defaults to `'development'`.
String get value => String get value =>

View file

@ -90,7 +90,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
/// Adds hooks to this instance. /// Adds hooks to this instance.
void addHooks(Angel app) { void addHooks(Angel app) {
Hooks hooks = getAnnotation(inner, Hooks, app.container.reflector); Hooks hooks = getAnnotation<Hooks>(inner, app.container.reflector);
List<HookedServiceEventListener<Id, Data, T>> before = [], after = []; List<HookedServiceEventListener<Id, Data, T>> before = [], after = [];
if (hooks != null) { if (hooks != null) {
@ -101,7 +101,7 @@ class HookedService<Id, Data, T extends Service<Id, Data>>
void applyListeners( void applyListeners(
Function fn, HookedServiceEventDispatcher<Id, Data, T> dispatcher, Function fn, HookedServiceEventDispatcher<Id, Data, T> dispatcher,
[bool isAfter]) { [bool isAfter]) {
Hooks hooks = getAnnotation(fn, Hooks, app.container.reflector); Hooks hooks = getAnnotation<Hooks>(fn, app.container.reflector);
final listeners = <HookedServiceEventListener<Id, Data, T>>[] final listeners = <HookedServiceEventListener<Id, Data, T>>[]
..addAll(isAfter == true ? after : before); ..addAll(isAfter == true ? after : before);

View file

@ -53,8 +53,8 @@ resolveInjection(requirement, InjectionRequest injection, RequestContext req,
requirement.length == 2 && requirement.length == 2 &&
requirement.first is String && requirement.first is String &&
requirement.last is Type) { requirement.last is Type) {
String key = requirement.first; var key = requirement.first;
Type type = requirement.last; var type = requirement.last;
if (req.params.containsKey(key) || if (req.params.containsKey(key) ||
req.app.configuration.containsKey(key) || req.app.configuration.containsKey(key) ||
_primitiveTypes.contains(type)) { _primitiveTypes.contains(type)) {

View file

@ -51,7 +51,7 @@ class MapService extends Service<String, Map<String, dynamic>> {
if (allowQuery == false || params == null || params['query'] is! Map) if (allowQuery == false || params == null || params['query'] is! Map)
return new Future.value(items); return new Future.value(items);
else { else {
Map query = params['query']; var query = params['query'] as Map;
return new Future.value(items.where((item) { return new Future.value(items.where((item) {
for (var key in query.keys) { for (var key in query.keys) {

View file

@ -99,7 +99,7 @@ class Routable extends Router<RequestHandler> {
final handlers = <RequestHandler>[]; final handlers = <RequestHandler>[];
// Merge @Middleware declaration, if any // Merge @Middleware declaration, if any
Middleware middlewareDeclaration = Middleware middlewareDeclaration =
getAnnotation(handler, Middleware, _container?.reflector); getAnnotation<Middleware>(handler, _container?.reflector);
if (middlewareDeclaration != null) { if (middlewareDeclaration != null) {
handlers.addAll(middlewareDeclaration.handlers); handlers.addAll(middlewareDeclaration.handlers);
} }

View file

@ -15,6 +15,7 @@ import 'package:mime/mime.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import 'controller.dart'; import 'controller.dart';
import 'env.dart';
import 'hooked_service.dart'; import 'hooked_service.dart';
import 'request_context.dart'; import 'request_context.dart';
import 'response_context.dart'; import 'response_context.dart';
@ -42,7 +43,6 @@ class Angel extends Routable {
MiddlewarePipeline>> handlerCache = new HashMap(); MiddlewarePipeline>> handlerCache = new HashMap();
Router<RequestHandler> _flattened; Router<RequestHandler> _flattened;
bool _isProduction;
Angel _parent; Angel _parent;
/// A global Map of converters that can transform responses bodies. /// A global Map of converters that can transform responses bodies.
@ -75,6 +75,8 @@ class Angel extends Routable {
/// A set of [Controller] objects that have been loaded into the application. /// A set of [Controller] objects that have been loaded into the application.
Map<Pattern, Controller> get controllers => _controllers; Map<Pattern, Controller> get controllers => _controllers;
/// Now *deprecated*, in favor of [AngelEnv] and [angelEnv].
///
/// Indicates whether the application is running in a production environment. /// Indicates whether the application is running in a production environment.
/// ///
/// The criteria for this is the `ANGEL_ENV` environment variable being set to /// The criteria for this is the `ANGEL_ENV` environment variable being set to
@ -82,10 +84,8 @@ class Angel extends Routable {
/// ///
/// This value is memoized the first time you call it, so do not change environment /// This value is memoized the first time you call it, so do not change environment
/// configuration at runtime! /// configuration at runtime!
bool get isProduction { @deprecated
return _isProduction ??= bool get isProduction => angelEnv.isProduction;
(Platform.environment['ANGEL_ENV'] == 'production');
}
/// Returns the parent instance of this application, if any. /// Returns the parent instance of this application, if any.
Angel get parent => _parent; Angel get parent => _parent;
@ -282,7 +282,7 @@ class Angel extends Routable {
return parent != null ? parent.findProperty(key) : null; return parent != null ? parent.findProperty(key) : null;
} }
/// Runs several optimizations, *if* [isProduction] is `true`. /// Runs several optimizations, *if* [angelEnv.isProduction] is `true`.
/// ///
/// * Preprocesses all dependency injection, and eliminates the burden of reflecting handlers /// * Preprocesses all dependency injection, and eliminates the burden of reflecting handlers
/// at run-time. /// at run-time.
@ -291,7 +291,6 @@ class Angel extends Routable {
/// You may [force] the optimization to run, if you are not running in production. /// You may [force] the optimization to run, if you are not running in production.
void optimizeForProduction({bool force: false}) { void optimizeForProduction({bool force: false}) {
if (isProduction == true || force == true) { if (isProduction == true || force == true) {
_isProduction = true;
_flattened ??= flatten(this); _flattened ??= flatten(this);
logger?.info('Angel is running in production mode.'); logger?.info('Angel is running in production mode.');
} }

View file

@ -195,12 +195,12 @@ class Service<Id, Data> extends Routable {
// Add global middleware if declared on the instance itself // Add global middleware if declared on the instance itself
Middleware before = Middleware before =
getAnnotation(service, Middleware, app.container.reflector); getAnnotation<Middleware>(service, app.container.reflector);
if (before != null) handlers.addAll(before.handlers); if (before != null) handlers.addAll(before.handlers);
Middleware indexMiddleware = Middleware indexMiddleware =
getAnnotation(service.index, Middleware, app.container.reflector); getAnnotation<Middleware>(service.index, app.container.reflector);
get('/', (req, res) { get('/', (req, res) {
return this.index(mergeMap([ return this.index(mergeMap([
{'query': req.queryParameters}, {'query': req.queryParameters},
@ -213,7 +213,7 @@ class Service<Id, Data> extends Routable {
..addAll((indexMiddleware == null) ? [] : indexMiddleware.handlers)); ..addAll((indexMiddleware == null) ? [] : indexMiddleware.handlers));
Middleware createMiddleware = Middleware createMiddleware =
getAnnotation(service.create, Middleware, app.container.reflector); getAnnotation<Middleware>(service.create, app.container.reflector);
post('/', (req, ResponseContext res) { post('/', (req, ResponseContext res) {
return req.parseBody().then((_) { return req.parseBody().then((_) {
return this return this
@ -236,7 +236,7 @@ class Service<Id, Data> extends Routable {
(createMiddleware == null) ? [] : createMiddleware.handlers)); (createMiddleware == null) ? [] : createMiddleware.handlers));
Middleware readMiddleware = Middleware readMiddleware =
getAnnotation(service.read, Middleware, app.container.reflector); getAnnotation<Middleware>(service.read, app.container.reflector);
get('/:id', (req, res) { get('/:id', (req, res) {
return this.read( return this.read(
@ -252,7 +252,7 @@ class Service<Id, Data> extends Routable {
..addAll((readMiddleware == null) ? [] : readMiddleware.handlers)); ..addAll((readMiddleware == null) ? [] : readMiddleware.handlers));
Middleware modifyMiddleware = Middleware modifyMiddleware =
getAnnotation(service.modify, Middleware, app.container.reflector); getAnnotation<Middleware>(service.modify, app.container.reflector);
patch('/:id', (req, res) { patch('/:id', (req, res) {
return req.parseBody().then((_) { return req.parseBody().then((_) {
return this.modify( return this.modify(
@ -271,7 +271,7 @@ class Service<Id, Data> extends Routable {
(modifyMiddleware == null) ? [] : modifyMiddleware.handlers)); (modifyMiddleware == null) ? [] : modifyMiddleware.handlers));
Middleware updateMiddleware = Middleware updateMiddleware =
getAnnotation(service.update, Middleware, app.container.reflector); getAnnotation<Middleware>(service.update, app.container.reflector);
post('/:id', (req, res) { post('/:id', (req, res) {
return req.parseBody().then((_) { return req.parseBody().then((_) {
return this.update( return this.update(
@ -306,7 +306,7 @@ class Service<Id, Data> extends Routable {
(updateMiddleware == null) ? [] : updateMiddleware.handlers)); (updateMiddleware == null) ? [] : updateMiddleware.handlers));
Middleware removeMiddleware = Middleware removeMiddleware =
getAnnotation(service.remove, Middleware, app.container.reflector); getAnnotation<Middleware>(service.remove, app.container.reflector);
delete('/', (req, res) { delete('/', (req, res) {
return this.remove( return this.remove(
null, null,

View file

@ -2,26 +2,26 @@ import 'package:angel_container/angel_container.dart';
final RegExp straySlashes = new RegExp(r'(^/+)|(/+$)'); final RegExp straySlashes = new RegExp(r'(^/+)|(/+$)');
matchingAnnotation(List<ReflectedInstance> metadata, Type T) { T matchingAnnotation<T>(List<ReflectedInstance> metadata) {
for (ReflectedInstance metaDatum in metadata) { for (ReflectedInstance metaDatum in metadata) {
if (metaDatum.type.reflectedType == T) { if (metaDatum.type.reflectedType == T) {
return metaDatum.reflectee; return metaDatum.reflectee as T;
} }
} }
return null; return null;
} }
getAnnotation(obj, Type T, Reflector reflector) { T getAnnotation<T>(obj, Reflector reflector) {
if (reflector == null) { if (reflector == null) {
return null; return null;
} else { } else {
if (obj is Function) { if (obj is Function) {
var methodMirror = reflector.reflectFunction(obj); var methodMirror = reflector.reflectFunction(obj);
return matchingAnnotation(methodMirror.annotations, T); return matchingAnnotation<T>(methodMirror.annotations);
} else { } else {
var classMirror = reflector.reflectClass(obj.runtimeType as Type); var classMirror = reflector.reflectClass(obj.runtimeType as Type);
return matchingAnnotation(classMirror.annotations, T); return matchingAnnotation<T>(classMirror.annotations);
} }
} }
} }

View file

@ -122,7 +122,7 @@ main() {
expect(rgx.firstMatch(response.body)?.start, equals(0)); expect(rgx.firstMatch(response.body)?.start, equals(0));
Map todo = json.decode(response.body.replaceAll(rgx, "")); var todo = json.decode(response.body.replaceAll(rgx, "")) as Map;
print("Todo: $todo"); print("Todo: $todo");
expect(todo['text'], equals("Hello")); expect(todo['text'], equals("Hello"));
expect(todo['over'], equals("world")); expect(todo['over'], equals("world"));

View file

@ -73,19 +73,19 @@ main() {
test("make in route", () async { test("make in route", () async {
var response = await client.get("$url/errands3"); var response = await client.get("$url/errands3");
String text = await json.decode(response.body); var text = await json.decode(response.body) as String;
expect(text, equals(TEXT)); expect(text, equals(TEXT));
}); });
test("make in controller", () async { test("make in controller", () async {
var response = await client.get("$url/errands4"); var response = await client.get("$url/errands4");
String text = await json.decode(response.body); var text = await json.decode(response.body) as String;
expect(text, equals(TEXT)); expect(text, equals(TEXT));
}); });
} }
void validateTodoSingleton(response) { void validateTodoSingleton(response) {
Map todo = json.decode(response.body.toString()); var todo = json.decode(response.body.toString()) as Map;
expect(todo["id"], equals(null)); expect(todo["id"], equals(null));
expect(todo["text"], equals(TEXT)); expect(todo["text"], equals(TEXT));
expect(todo["over"], equals(OVER)); expect(todo["over"], equals(OVER));

View file

@ -77,7 +77,7 @@ main() {
body: json.encode({"arbitrary": "data"}), body: json.encode({"arbitrary": "data"}),
headers: headers as Map<String, String>); headers: headers as Map<String, String>);
print(response.body); print(response.body);
Map result = json.decode(response.body); var result = json.decode(response.body) as Map;
expect(result["hello"], equals("hooked world")); expect(result["hello"], equals("hooked world"));
}); });
@ -95,7 +95,7 @@ main() {
var response = await client.get("$url/todos"); var response = await client.get("$url/todos");
print(response.body); print(response.body);
List result = json.decode(response.body); var result = json.decode(response.body) as List;
expect(result[0]["angel"], equals("framework")); expect(result[0]["angel"], equals("framework"));
}); });