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
* Support trailing text after parameters with custom Regexes.

View file

@ -95,8 +95,8 @@ class RouteDefinition {
RouteDefinition(this.segments);
Parser<Map<String, dynamic>> compile() {
Parser<Map<String, dynamic>> out;
Parser<RouteResult> compile() {
Parser<RouteResult> out;
for (int i = 0; i < segments.length; i++) {
var s = segments[i];
@ -105,7 +105,7 @@ class RouteDefinition {
out = s.compile(isLast);
else
out = s.compileNext(
out.then(match('/')).index(0).cast<Map<String, dynamic>>(), isLast);
out.then(match('/')).index(0).cast<RouteResult>(), isLast);
}
return out;
@ -113,10 +113,9 @@ class RouteDefinition {
}
abstract class RouteSegment {
Parser<Map<String, dynamic>> compile(bool isLast);
Parser<RouteResult> compile(bool isLast);
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast);
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast);
}
class SlashSegment implements RouteSegment {
@ -125,14 +124,13 @@ class SlashSegment implements RouteSegment {
const SlashSegment();
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return match(rgx).map((_) => {});
Parser<RouteResult> compile(bool isLast) {
return match(rgx).map((_) => RouteResult({}));
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<RouteResult>();
}
@override
@ -150,14 +148,13 @@ class ConstantSegment extends RouteSegment {
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return match<Map<String, dynamic>>(text).value((r) => <String, dynamic>{});
Parser<RouteResult> compile(bool isLast) {
return match(text).map((r) => RouteResult({}));
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<RouteResult>();
}
}
@ -176,22 +173,27 @@ class WildcardSegment extends RouteSegment {
return r'[^/]*';
}
Parser<Map<String, dynamic>> _compile(bool isLast) {
var rgx = RegExp('$pre${_symbol(isLast)}$post');
return match(rgx);
RegExp _compile(bool isLast) {
return RegExp('$pre(${_symbol(isLast)})$post');
// if (isLast) return match(new RegExp(r'.*'));
// return match(new RegExp(r'[^/]*'));
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return _compile(isLast).map((r) => {});
Parser<RouteResult> compile(bool isLast) {
return match(_compile(isLast))
.map((r) => RouteResult({}, tail: r.scanner.lastMatch[1]));
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile(isLast)).index(0).cast<Map<String, dynamic>>();
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).map((r) {
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
Parser<Map<String, dynamic>> compile(bool isLast) {
Parser<RouteResult> compile(bool isLast) {
return super.compile(isLast).opt();
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(_compile().opt()).map((r) {
if (r.value[1] == null) return r.value[0] as Map<String, dynamic>;
return (r.value[0] as Map<String, dynamic>)
if (r.value[1] == null) return r.value[0] as RouteResult;
return (r.value[0] as RouteResult)
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
});
}
@ -240,16 +241,15 @@ class ParameterSegment extends RouteSegment {
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
Parser<RouteResult> compile(bool isLast) {
return _compile()
.map<Map<String, dynamic>>((r) => {name: Uri.decodeComponent(r.value)});
.map((r) => RouteResult({name: Uri.decodeComponent(r.value)}));
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
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)});
});
}
@ -273,16 +273,15 @@ class ParsedParameterSegment extends RouteSegment {
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return parameter._compile().map(
(r) => {parameter.name: getValue(Uri.decodeComponent(r.span.text))});
Parser<RouteResult> compile(bool isLast) {
return parameter._compile().map((r) => RouteResult(
{parameter.name: getValue(Uri.decodeComponent(r.span.text))}));
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(parameter._compile()).map((r) {
return (r.value[0] as Map<String, dynamic>)
return (r.value[0] as RouteResult)
..addAll({
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 RouteDefinition _routeDefinition;
String name;
Parser<Map<String, dynamic>> _parser;
Parser<RouteResult> _parser;
Route(this.path, {@required this.method, @required this.handlers})
: _routeDefinition = RouteGrammar.routeDefinition
.parse(new SpanScanner(path.replaceAll(_straySlashes, '')))
.value {
if (_routeDefinition?.segments?.isNotEmpty != true)
_parser =
match<Map<String, dynamic>>('').value((r) => <String, dynamic>{});
_parser = match('').map((r) => RouteResult({}));
}
factory Route.join(Route<T> a, Route<T> b) {
@ -26,8 +25,7 @@ class Route<T> {
method: b.method, handlers: b.handlers);
}
Parser<Map<String, dynamic>> get parser =>
_parser ??= _routeDefinition.compile();
Parser<RouteResult> get parser => _parser ??= _routeDefinition.compile();
@override
String toString() {
@ -57,3 +55,23 @@ class Route<T> {
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) {
var result = new RoutingResult<T>(
parseResult: parseResult,
params: parseResult.value,
params: parseResult.value.params,
shallowRoute: route,
shallowRouter: this);
shallowRouter: this,
tail: (parseResult.value.tail ?? '') + scanner.rest);
out.add(result);
success = true;
}

View file

@ -3,7 +3,7 @@ part of angel_route.src.router;
/// Represents a complex result of navigating to a path.
class RoutingResult<T> {
/// 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.
final Iterable<RoutingResult<T>> nested;
@ -85,7 +85,7 @@ class RoutingResult<T> {
this.nested,
this.shallowRoute,
this.shallowRouter,
this.tail}) {
@required this.tail}) {
this.params.addAll(params ?? {});
}
}

View file

@ -1,6 +1,6 @@
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.
version: 3.0.3
version: 3.0.4
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_route
environment:

View file

@ -22,4 +22,25 @@ void main() {
router.resolveAbsolute('/isnt/she/fierce%20harmonica%solo').first;
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');
});
}