This commit is contained in:
Tobe O 2019-02-03 11:44:44 -05:00
parent 0e3e7ee9bb
commit 0f529c5625
7 changed files with 94 additions and 50 deletions

View file

@ -1,3 +1,8 @@
# 3.0.4
* Add `RouteResult` class, which allows segments (i.e. wildcard) to
modify the `tail`.
* Add more wildcard tests.
# 3.0.3 # 3.0.3
* Support trailing text after parameters with custom Regexes. * Support trailing text after parameters with custom Regexes.

View file

@ -95,8 +95,8 @@ class RouteDefinition {
RouteDefinition(this.segments); RouteDefinition(this.segments);
Parser<Map<String, dynamic>> compile() { Parser<RouteResult> compile() {
Parser<Map<String, dynamic>> out; Parser<RouteResult> out;
for (int i = 0; i < segments.length; i++) { for (int i = 0; i < segments.length; i++) {
var s = segments[i]; var s = segments[i];
@ -105,7 +105,7 @@ class RouteDefinition {
out = s.compile(isLast); out = s.compile(isLast);
else else
out = s.compileNext( out = s.compileNext(
out.then(match('/')).index(0).cast<Map<String, dynamic>>(), isLast); out.then(match('/')).index(0).cast<RouteResult>(), isLast);
} }
return out; return out;
@ -113,10 +113,9 @@ class RouteDefinition {
} }
abstract class RouteSegment { abstract class RouteSegment {
Parser<Map<String, dynamic>> compile(bool isLast); Parser<RouteResult> compile(bool isLast);
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast);
Parser<Map<String, dynamic>> p, bool isLast);
} }
class SlashSegment implements RouteSegment { class SlashSegment implements RouteSegment {
@ -125,14 +124,13 @@ class SlashSegment implements RouteSegment {
const SlashSegment(); const SlashSegment();
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return match(rgx).map((_) => {}); return match(rgx).map((_) => RouteResult({}));
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) { return p.then(compile(isLast)).index(0).cast<RouteResult>();
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
} }
@override @override
@ -150,14 +148,13 @@ class ConstantSegment extends RouteSegment {
} }
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return match<Map<String, dynamic>>(text).value((r) => <String, dynamic>{}); return match(text).map((r) => RouteResult({}));
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) { return p.then(compile(isLast)).index(0).cast<RouteResult>();
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
} }
} }
@ -176,22 +173,27 @@ class WildcardSegment extends RouteSegment {
return r'[^/]*'; return r'[^/]*';
} }
Parser<Map<String, dynamic>> _compile(bool isLast) { RegExp _compile(bool isLast) {
var rgx = RegExp('$pre${_symbol(isLast)}$post'); return RegExp('$pre(${_symbol(isLast)})$post');
return match(rgx);
// if (isLast) return match(new RegExp(r'.*')); // if (isLast) return match(new RegExp(r'.*'));
// return match(new RegExp(r'[^/]*')); // return match(new RegExp(r'[^/]*'));
} }
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return _compile(isLast).map((r) => {}); return match(_compile(isLast))
.map((r) => RouteResult({}, tail: r.scanner.lastMatch[1]));
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) { return p.then(compile(isLast)).map((r) {
return p.then(_compile(isLast)).index(0).cast<Map<String, dynamic>>(); var items = r.value.cast<RouteResult>();
var a = items[0], b = items[1];
return a
..addAll(b?.params ?? {})
.._setTail(b?.tail);
});
} }
} }
@ -206,16 +208,15 @@ class OptionalSegment extends ParameterSegment {
} }
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return super.compile(isLast).opt(); return super.compile(isLast).opt();
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile().opt()).map((r) { return p.then(_compile().opt()).map((r) {
if (r.value[1] == null) return r.value[0] as Map<String, dynamic>; if (r.value[1] == null) return r.value[0] as RouteResult;
return (r.value[0] as Map<String, dynamic>) return (r.value[0] as RouteResult)
..addAll({name: Uri.decodeComponent(r.value[1] as String)}); ..addAll({name: Uri.decodeComponent(r.value[1] as String)});
}); });
} }
@ -240,16 +241,15 @@ class ParameterSegment extends RouteSegment {
} }
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return _compile() return _compile()
.map<Map<String, dynamic>>((r) => {name: Uri.decodeComponent(r.value)}); .map((r) => RouteResult({name: Uri.decodeComponent(r.value)}));
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile()).map((r) { return p.then(_compile()).map((r) {
return (r.value[0] as Map<String, dynamic>) return (r.value[0] as RouteResult)
..addAll({name: Uri.decodeComponent(r.value[1] as String)}); ..addAll({name: Uri.decodeComponent(r.value[1] as String)});
}); });
} }
@ -273,16 +273,15 @@ class ParsedParameterSegment extends RouteSegment {
} }
@override @override
Parser<Map<String, dynamic>> compile(bool isLast) { Parser<RouteResult> compile(bool isLast) {
return parameter._compile().map( return parameter._compile().map((r) => RouteResult(
(r) => {parameter.name: getValue(Uri.decodeComponent(r.span.text))}); {parameter.name: getValue(Uri.decodeComponent(r.span.text))}));
} }
@override @override
Parser<Map<String, dynamic>> compileNext( Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(parameter._compile()).map((r) { return p.then(parameter._compile()).map((r) {
return (r.value[0] as Map<String, dynamic>) return (r.value[0] as RouteResult)
..addAll({ ..addAll({
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String)) parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
}); });

View file

@ -8,15 +8,14 @@ class Route<T> {
final Map<String, Map<String, dynamic>> _cache = {}; final Map<String, Map<String, dynamic>> _cache = {};
final RouteDefinition _routeDefinition; final RouteDefinition _routeDefinition;
String name; String name;
Parser<Map<String, dynamic>> _parser; Parser<RouteResult> _parser;
Route(this.path, {@required this.method, @required this.handlers}) Route(this.path, {@required this.method, @required this.handlers})
: _routeDefinition = RouteGrammar.routeDefinition : _routeDefinition = RouteGrammar.routeDefinition
.parse(new SpanScanner(path.replaceAll(_straySlashes, ''))) .parse(new SpanScanner(path.replaceAll(_straySlashes, '')))
.value { .value {
if (_routeDefinition?.segments?.isNotEmpty != true) if (_routeDefinition?.segments?.isNotEmpty != true)
_parser = _parser = match('').map((r) => RouteResult({}));
match<Map<String, dynamic>>('').value((r) => <String, dynamic>{});
} }
factory Route.join(Route<T> a, Route<T> b) { factory Route.join(Route<T> a, Route<T> b) {
@ -26,8 +25,7 @@ class Route<T> {
method: b.method, handlers: b.handlers); method: b.method, handlers: b.handlers);
} }
Parser<Map<String, dynamic>> get parser => Parser<RouteResult> get parser => _parser ??= _routeDefinition.compile();
_parser ??= _routeDefinition.compile();
@override @override
String toString() { String toString() {
@ -57,3 +55,23 @@ class Route<T> {
return b.toString(); return b.toString();
} }
} }
/// The result of matching an individual route.
class RouteResult {
/// The parsed route parameters.
final Map<String, dynamic> params;
/// Optional. An explicit "tail" value to set.
String get tail => _tail;
String _tail;
RouteResult(this.params, {String tail}) : _tail = tail;
void _setTail(String v) => _tail ??= v;
/// Adds parameters.
void addAll(Map<String, dynamic> map) {
params.addAll(map);
}
}

View file

@ -292,9 +292,10 @@ class Router<T> {
if (parseResult.successful && scanner.isDone) { if (parseResult.successful && scanner.isDone) {
var result = new RoutingResult<T>( var result = new RoutingResult<T>(
parseResult: parseResult, parseResult: parseResult,
params: parseResult.value, params: parseResult.value.params,
shallowRoute: route, shallowRoute: route,
shallowRouter: this); shallowRouter: this,
tail: (parseResult.value.tail ?? '') + scanner.rest);
out.add(result); out.add(result);
success = true; success = true;
} }

View file

@ -3,7 +3,7 @@ part of angel_route.src.router;
/// Represents a complex result of navigating to a path. /// Represents a complex result of navigating to a path.
class RoutingResult<T> { class RoutingResult<T> {
/// The parse result that matched the given sub-path. /// The parse result that matched the given sub-path.
final ParseResult<Map<String, dynamic>> parseResult; final ParseResult<RouteResult> parseResult;
/// A nested instance, if a sub-path was matched. /// A nested instance, if a sub-path was matched.
final Iterable<RoutingResult<T>> nested; final Iterable<RoutingResult<T>> nested;
@ -85,7 +85,7 @@ class RoutingResult<T> {
this.nested, this.nested,
this.shallowRoute, this.shallowRoute,
this.shallowRouter, this.shallowRouter,
this.tail}) { @required this.tail}) {
this.params.addAll(params ?? {}); this.params.addAll(params ?? {});
} }
} }

View file

@ -1,6 +1,6 @@
name: angel_route name: angel_route
description: A powerful, isomorphic routing library for Dart. It is mainly used in the Angel framework, but can be used in Flutter and on the Web. description: A powerful, isomorphic routing library for Dart. It is mainly used in the Angel framework, but can be used in Flutter and on the Web.
version: 3.0.3 version: 3.0.4
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_route homepage: https://github.com/angel-dart/angel_route
environment: environment:

View file

@ -22,4 +22,25 @@ void main() {
router.resolveAbsolute('/isnt/she/fierce%20harmonica%solo').first; router.resolveAbsolute('/isnt/she/fierce%20harmonica%solo').first;
expect(result.handlers, ['lovely']); expect(result.handlers, ['lovely']);
}); });
test('tail explicitly set intermediate', () {
var results = router.resolveAbsolute('/songs/in_the/key');
var result = results.first;
print(results.map((r) => {r.route.path: r.tail}));
expect(result.tail, 'in_the');
});
test('tail explicitly set at end', () {
var results = router.resolveAbsolute('/isnt/she/epic');
var result = results.first;
print(results.map((r) => {r.route.path: r.tail}));
expect(result.tail, 'epic');
});
test('tail with trailing', () {
var results = router.resolveAbsolute('/isnt/she/epic/fail');
var result = results.first;
print(results.map((r) => {r.route.path: r.tail}));
expect(result.tail, 'epic/fail');
});
} }