From 1c5e5726882f68901a19f1dcd335e5b721609476 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Tue, 28 Nov 2017 16:07:14 -0500 Subject: [PATCH] wildcard patch --- lib/src/grammar.dart | 98 ++++++++++++++++++++----------------- lib/src/route.dart | 14 +++--- lib/src/router.dart | 13 ++--- lib/src/routing_result.dart | 2 +- pubspec.yaml | 2 +- test/params_test.dart | 4 +- test/server_test.dart | 2 +- test/wildcard_test.dart | 24 +++++++++ 8 files changed, 94 insertions(+), 65 deletions(-) create mode 100644 test/wildcard_test.dart diff --git a/lib/src/grammar.dart b/lib/src/grammar.dart index 2d8e316d..6c061e00 100644 --- a/lib/src/grammar.dart +++ b/lib/src/grammar.dart @@ -1,6 +1,6 @@ part of angel_route.src.router; -class _RouteGrammar { +class RouteGrammar { static final Parser notSlash = match(new RegExp(r'[^/]+')).value((r) => r.span.text); static final Parser regExp = new _RegExpParser(); @@ -8,27 +8,27 @@ class _RouteGrammar { match(new RegExp(r':([A-Za-z0-9_]+)')) .value((r) => r.span.text.substring(1)); - static final Parser<_ParameterSegment> parameterSegment = chain([ + static final Parser parameterSegment = chain([ parameterName, match('?').value((r) => true).opt(), regExp.opt(), ]).map((r) { - var s = new _ParameterSegment(r.value[0], r.value[2]); - return r.value[1] == true ? new _OptionalSegment(s) : s; + var s = new ParameterSegment(r.value[0], r.value[2]); + return r.value[1] == true ? new OptionalSegment(s) : s; }); - static final Parser<_WildcardSegment> wildcardSegment = - match('*').value((r) => new _WildcardSegment()); + static final Parser wildcardSegment = + match('*').value((r) => new WildcardSegment()); - static final Parser<_ConstantSegment> constantSegment = - notSlash.map((r) => new _ConstantSegment(r.value)); + static final Parser constantSegment = + notSlash.map((r) => new ConstantSegment(r.value)); - static final Parser<_RouteSegment> routeSegment = + static final Parser routeSegment = any([parameterSegment, wildcardSegment, constantSegment]); - static final Parser<_RouteDefinition> routeDefinition = routeSegment + static final Parser routeDefinition = routeSegment .separatedBy(match('/')) - .map((r) => new _RouteDefinition(r.value ?? [])) + .map((r) => new RouteDefinition(r.value ?? [])) .surroundedBy(match('/').star().opt()); } @@ -43,34 +43,37 @@ class _RegExpParser extends Parser { } } -class _RouteDefinition { - final List<_RouteSegment> segments; +class RouteDefinition { + final List segments; - _RouteDefinition(this.segments); + RouteDefinition(this.segments); - Parser> compile() { - Parser> out; + Parser> compile() { + Parser> out; - for (var s in segments) { + for (int i = 0; i < segments.length; i++) { + var s = segments[i]; + bool isLast = i == segments.length - 1; if (out == null) - out = s.compile(); + out = s.compile(isLast); else - out = s.compileNext(out.then(match('/')).index(0)); + out = s.compileNext(out.then(match('/')).index(0), isLast); } return out; } } -abstract class _RouteSegment { - Parser> compile(); - Parser> compileNext(Parser> p); +abstract class RouteSegment { + Parser> compile(bool isLast); + Parser> compileNext( + Parser> p, bool isLast); } -class _ConstantSegment extends _RouteSegment { +class ConstantSegment extends RouteSegment { final String text; - _ConstantSegment(this.text); + ConstantSegment(this.text); @override String toString() { @@ -78,41 +81,44 @@ class _ConstantSegment extends _RouteSegment { } @override - Parser> compile() { + Parser> compile(bool isLast) { return match(text).value((r) => {}); } @override - Parser> compileNext(Parser> p) { - return p.then(compile()).index(0); + Parser> compileNext( + Parser> p, bool isLast) { + return p.then(compile(isLast)).index(0); } } -class _WildcardSegment extends _RouteSegment { +class WildcardSegment extends RouteSegment { @override String toString() { return 'Wildcard segment'; } - Parser> _compile() { + Parser> _compile(bool isLast) { + if (isLast) return match(new RegExp(r'.*')); return match(new RegExp(r'[^/]*')); } @override - Parser> compile() { - return _compile().map((r) => {}); + Parser> compile(bool isLast) { + return _compile(isLast).map((r) => {}); } @override - Parser> compileNext(Parser> p) { - return p.then(_compile()).index(0); + Parser> compileNext( + Parser> p, bool isLast) { + return p.then(_compile(isLast)).index(0); } } -class _OptionalSegment extends _ParameterSegment { - final _ParameterSegment parameter; +class OptionalSegment extends ParameterSegment { + final ParameterSegment parameter; - _OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp); + OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp); @override String toString() { @@ -120,12 +126,13 @@ class _OptionalSegment extends _ParameterSegment { } @override - Parser> compile() { - return super.compile().opt(); + Parser> compile(bool isLast) { + return super.compile(isLast).opt(); } @override - Parser> compileNext(Parser> p) { + Parser> compileNext( + Parser> p, bool isLast) { return p.then(_compile().opt()).map((r) { if (r.value[1] == null) return r.value[0]; return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])}); @@ -133,11 +140,11 @@ class _OptionalSegment extends _ParameterSegment { } } -class _ParameterSegment extends _RouteSegment { +class ParameterSegment extends RouteSegment { final String name; final RegExp regExp; - _ParameterSegment(this.name, this.regExp); + ParameterSegment(this.name, this.regExp); @override String toString() { @@ -145,19 +152,20 @@ class _ParameterSegment extends _RouteSegment { return 'Param: $name'; } - Parser> _compile() { + Parser> _compile() { return regExp != null ? match(regExp).value((r) => r.span.text) - : _RouteGrammar.notSlash; + : RouteGrammar.notSlash; } @override - Parser> compile() { + Parser> compile(bool isLast) { return _compile().map((r) => {name: Uri.decodeComponent(r.span.text)}); } @override - Parser> compileNext(Parser> p) { + Parser> compileNext( + Parser> p, bool isLast) { return p.then(_compile()).map((r) { return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])}); }); diff --git a/lib/src/route.dart b/lib/src/route.dart index 5d32a63b..5ce9ec7e 100644 --- a/lib/src/route.dart +++ b/lib/src/route.dart @@ -5,13 +5,13 @@ class Route { final String method; final String path; final List handlers; - final Map> _cache = {}; - final _RouteDefinition _routeDefinition; + final Map> _cache = {}; + final RouteDefinition _routeDefinition; String name; - Parser> _parser; + Parser> _parser; Route(this.path, {@required this.method, @required this.handlers}) - : _routeDefinition = _RouteGrammar.routeDefinition + : _routeDefinition = RouteGrammar.routeDefinition .parse(new SpanScanner(path.replaceAll(_straySlashes, ''))) .value { if (_routeDefinition.segments.isEmpty) _parser = match('').value((r) => {}); @@ -24,7 +24,7 @@ class Route { method: b.method, handlers: b.handlers); } - Parser> get parser => + Parser> get parser => _parser ??= _routeDefinition.compile(); @@ -50,9 +50,9 @@ class Route { for (var seg in _routeDefinition.segments) { if (i++ > 0) b.write('/'); - if (seg is _ConstantSegment) + if (seg is ConstantSegment) b.write(seg.text); - else if (seg is _ParameterSegment) { + else if (seg is ParameterSegment) { if (!params.containsKey(seg.name)) throw new ArgumentError('Missing parameter "${seg.name}".'); b.write(params[seg.name]); diff --git a/lib/src/router.dart b/lib/src/router.dart index 81978461..fb26c83c 100644 --- a/lib/src/router.dart +++ b/lib/src/router.dart @@ -27,9 +27,6 @@ class Router { final List _routes = []; bool _useCache = false; - /// Set to `true` to print verbose debug output when interacting with this route. - bool debug = false; - List get middleware => new List.unmodifiable(_middleware); Map get mounted => @@ -57,7 +54,7 @@ class Router { /// Provide a `root` to make this Router revolve around a pre-defined route. /// Not recommended. - Router({this.debug: false}); + Router(); /// Enables the use of a cache to eliminate the overhead of consecutive resolutions of the same path. void enableCache() { @@ -97,7 +94,7 @@ class Router { /// Returns a [Router] with a duplicated version of this tree. Router clone() { - final router = new Router(debug: debug); + final router = new Router(); final newMounted = new Map.from(mounted); for (Route route in routes) { @@ -169,7 +166,7 @@ class Router { String name: null, String namespace: null}) { final router = new Router().._middleware.addAll(middleware); - callback(router..debug = debug); + callback(router); return mount(path, router, namespace: namespace)..name = name; } @@ -442,7 +439,7 @@ class _ChainedRouter extends Router { String namespace: null}) { final router = new _ChainedRouter(_root, []..addAll(_handlers)..addAll(middleware)); - callback(router..debug = debug); + callback(router); return mount(path, router, namespace: namespace)..name = name; } @@ -470,7 +467,7 @@ class _ChainedRouter extends Router { /// Optimizes a router by condensing all its routes into one level. Router flatten(Router router) { - var flattened = new Router(debug: router.debug == true) + var flattened = new Router() ..requestMiddleware.addAll(router.requestMiddleware); for (var route in router.routes) { diff --git a/lib/src/routing_result.dart b/lib/src/routing_result.dart index c0cf12cd..cd22be81 100644 --- a/lib/src/routing_result.dart +++ b/lib/src/routing_result.dart @@ -3,7 +3,7 @@ part of angel_route.src.router; /// Represents a complex result of navigating to a path. class RoutingResult { /// The parse result that matched the given sub-path. - final ParseResult> parseResult; + final ParseResult> parseResult; /// A nested instance, if a sub-path was matched. final Iterable nested; diff --git a/pubspec.yaml b/pubspec.yaml index adaf7108..ef317b53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: angel_route description: A powerful, isomorphic routing library for Dart. -version: 2.0.3+2 +version: 2.0.4 author: Tobe O homepage: https://github.com/angel-dart/angel_route environment: diff --git a/test/params_test.dart b/test/params_test.dart index a97609e1..ff90d25b 100644 --- a/test/params_test.dart +++ b/test/params_test.dart @@ -2,7 +2,7 @@ import 'package:angel_route/angel_route.dart'; import 'package:test/test.dart'; main() { - final router = new Router(debug: true) + final router = new Router() ..get('/hello', '') ..get('/user/:id', ''); @@ -11,7 +11,7 @@ main() { router.get('/readers/:readerId', ''); }); - router.mount('/color', new Router(debug: true)..get('/:name/shades', '')); + router.mount('/color', new Router()..get('/:name/shades', '')); setUp(router.dumpTree); diff --git a/test/server_test.dart b/test/server_test.dart index 51fcc461..b7e6823c 100644 --- a/test/server_test.dart +++ b/test/server_test.dart @@ -11,7 +11,7 @@ const List> people = const [ main() { http.Client client; - final Router router = new Router(debug: true); + final Router router = new Router(); HttpServer server; String url; diff --git a/test/wildcard_test.dart b/test/wildcard_test.dart new file mode 100644 index 00000000..cae7dc8c --- /dev/null +++ b/test/wildcard_test.dart @@ -0,0 +1,24 @@ +import 'package:angel_route/angel_route.dart'; +import 'package:test/test.dart'; + +void main() { + var router = new Router(); + router.get('/songs/*/key', 'of life'); + router.get('/isnt/she/*', 'lovely'); + router.all('*', 'stevie'); + + test('match until end if * is last', () { + var result = router.resolveAbsolute('/wonder').first; + expect(result.handlers, ['stevie']); + }); + + test('match if not last', () { + var result = router.resolveAbsolute('/songs/what/key').first; + expect(result.handlers, ['of life']); + }); + + test('match if segments before', () { + var result = router.resolveAbsolute('/isnt/she/fierce%20harmonica%solo').first; + expect(result.handlers, ['lovely']); + }); +} \ No newline at end of file