Finally fixed router!
This commit is contained in:
parent
5241e66eff
commit
34a234cb4d
24 changed files with 385 additions and 101 deletions
|
@ -1,6 +0,0 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
|
||||||
<option name="filePath" value="$PROJECT_DIR$/test/all_tests.dart" />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
6
.idea/runConfigurations/Method_Tests.xml
Normal file
6
.idea/runConfigurations/Method_Tests.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Method Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" folderName="Router Tests" singleton="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/test/method/all_tests.dart" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -1,6 +1,6 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Use" type="DartTestRunConfigurationType" factoryName="Dart Test" folderName="Router Tests" singleton="true">
|
<configuration default="false" name="Use" type="DartTestRunConfigurationType" factoryName="Dart Test" folderName="Router Tests" singleton="true">
|
||||||
<option name="filePath" value="$PROJECT_DIR$/test/router/use.dart" />
|
<option name="filePath" value="$PROJECT_DIR$/test/use.dart" />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
1
.travis.yml
Normal file
1
.travis.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
language: dart
|
|
@ -1,6 +1,5 @@
|
||||||
/// A powerful, isomorphic routing library for Dart.
|
/// A powerful, isomorphic routing library for Dart.
|
||||||
library angel_route;
|
library angel_route;
|
||||||
|
|
||||||
export 'src/route.dart';
|
|
||||||
export 'src/router.dart';
|
export 'src/router.dart';
|
||||||
export 'src/routing_exception.dart';
|
export 'src/routing_exception.dart';
|
|
@ -52,6 +52,11 @@ class _BrowserRouterImpl extends Router implements BrowserRouter {
|
||||||
throw new RoutingException.noSuchRoute(path);
|
throw new RoutingException.noSuchRoute(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void listen() {
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
|
||||||
void prepareAnchors() {
|
void prepareAnchors() {
|
||||||
final anchors = window.document.querySelectorAll('a:not([dynamic])');
|
final anchors = window.document.querySelectorAll('a:not([dynamic])');
|
||||||
|
|
||||||
|
@ -85,9 +90,10 @@ class _HashRouter extends _BrowserRouterImpl {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void listen() {
|
void listen() {
|
||||||
|
super.listen();
|
||||||
window.onHashChange.listen((_) {
|
window.onHashChange.listen((_) {
|
||||||
final path = window.location.hash.replaceAll(_hash, '');
|
final path = window.location.hash.replaceAll(_hash, '');
|
||||||
final resolved = resolve(path);
|
final resolved = resolveOnRoot(path);
|
||||||
|
|
||||||
if (resolved == null || (path.isEmpty && resolved == root)) {
|
if (resolved == null || (path.isEmpty && resolved == root)) {
|
||||||
_onRoute.add(_current = null);
|
_onRoute.add(_current = null);
|
||||||
|
@ -115,6 +121,7 @@ class _PushStateRouter extends _BrowserRouterImpl {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void listen() {
|
void listen() {
|
||||||
|
super.listen();
|
||||||
window.onPopState.listen((e) {
|
window.onPopState.listen((e) {
|
||||||
if (e.state is Map && e.state.containsKey('path')) {
|
if (e.state is Map && e.state.containsKey('path')) {
|
||||||
final resolved = resolve(e.state['path']);
|
final resolved = resolve(e.state['path']);
|
||||||
|
|
|
@ -1,11 +1,4 @@
|
||||||
import 'extensible.dart';
|
part of angel_route.src.router;
|
||||||
import 'routing_exception.dart';
|
|
||||||
|
|
||||||
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
|
||||||
final RegExp _rgxEnd = new RegExp(r'\$+$');
|
|
||||||
final RegExp _rgxStart = new RegExp(r'^\^+');
|
|
||||||
final RegExp _rgxStraySlashes = new RegExp(r'(^((\\/)|(/))+)|(((\\/)|(/))+$)');
|
|
||||||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
|
||||||
|
|
||||||
String _matcherify(String path, {bool expand: true}) {
|
String _matcherify(String path, {bool expand: true}) {
|
||||||
var p = path.replaceAll(new RegExp(r'/\*$'), "*").replaceAll('/', r'\/');
|
var p = path.replaceAll(new RegExp(r'/\*$'), "*").replaceAll('/', r'\/');
|
||||||
|
@ -48,6 +41,7 @@ String _pathify(String path) {
|
||||||
class Route {
|
class Route {
|
||||||
final List<Route> _children = [];
|
final List<Route> _children = [];
|
||||||
final List _handlers = [];
|
final List _handlers = [];
|
||||||
|
RegExp _head;
|
||||||
RegExp _matcher;
|
RegExp _matcher;
|
||||||
String _method;
|
String _method;
|
||||||
String _name;
|
String _name;
|
||||||
|
@ -94,6 +88,14 @@ class Route {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [Route] instances that will respond to requests
|
||||||
|
/// to the index of this instance's path.
|
||||||
|
///
|
||||||
|
/// May return `this`.
|
||||||
|
Iterable<Route> get allIndices {
|
||||||
|
return children.where((r) => r.path.replaceAll(path, '').isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
/// Backtracks up the hierarchy, and builds
|
/// Backtracks up the hierarchy, and builds
|
||||||
/// a sequential list of all handlers from both
|
/// a sequential list of all handlers from both
|
||||||
/// this route, and every found parent route.
|
/// this route, and every found parent route.
|
||||||
|
@ -128,6 +130,8 @@ class Route {
|
||||||
if (debug) print(msg);
|
if (debug) print(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Route._base();
|
||||||
|
|
||||||
Route(Pattern path,
|
Route(Pattern path,
|
||||||
{Iterable<Route> children: const [],
|
{Iterable<Route> children: const [],
|
||||||
this.debug: false,
|
this.debug: false,
|
||||||
|
@ -190,8 +194,11 @@ class Route {
|
||||||
name: name);
|
name: name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var head = '';
|
||||||
|
|
||||||
for (int i = 0; i < segments.length; i++) {
|
for (int i = 0; i < segments.length; i++) {
|
||||||
final segment = segments[i];
|
final segment = segments[i];
|
||||||
|
head = (head + '/$segment').replaceAll(_straySlashes, '');
|
||||||
|
|
||||||
if (i == segments.length - 1) {
|
if (i == segments.length - 1) {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
@ -206,6 +213,8 @@ class Route {
|
||||||
result = result.child(segment, debug: debug, method: "*");
|
result = result.child(segment, debug: debug, method: "*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result._head = new RegExp(_matcherify(head).replaceAll(_rgxEnd, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,33 +253,16 @@ class Route {
|
||||||
|
|
||||||
parent._children.add(route
|
parent._children.add(route
|
||||||
.._matcher = new RegExp('$pattern1$separator$pattern2')
|
.._matcher = new RegExp('$pattern1$separator$pattern2')
|
||||||
|
.._head = new RegExp(_matcherify('$path1/$path2'.replaceAll(_straySlashes, '')).replaceAll(_rgxEnd, ''))
|
||||||
.._parent = parent
|
.._parent = parent
|
||||||
.._stub = child.matcher);
|
.._stub = child.matcher);
|
||||||
|
|
||||||
parent._printDebug(
|
parent._printDebug(
|
||||||
"Joined '/$path1' and '/$path2', created stub: ${route._stub.pattern}");
|
"Joined '/$path1' and '/$path2', created head: ${route._head.pattern} and stub: ${route._stub.pattern}");
|
||||||
|
|
||||||
return route..debug = parent.debug || child.debug || debug;
|
return route..debug = parent.debug || child.debug || debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
Route _inherit(Route route) {
|
|
||||||
/*
|
|
||||||
final List<Route> _children = [];
|
|
||||||
final List _handlers = [];
|
|
||||||
RegExp _matcher;
|
|
||||||
String _method;
|
|
||||||
String _name;
|
|
||||||
Route _parent;
|
|
||||||
RegExp _parentResolver;
|
|
||||||
String _path;
|
|
||||||
String _pathified;
|
|
||||||
RegExp _resolver;
|
|
||||||
RegExp _stub;
|
|
||||||
|
|
||||||
*/
|
|
||||||
return route.._parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls [addChild] on all given routes.
|
/// Calls [addChild] on all given routes.
|
||||||
List<Route> addAll(Iterable<Route> routes, {bool join: true}) {
|
List<Route> addAll(Iterable<Route> routes, {bool join: true}) {
|
||||||
return routes.map((route) => addChild(route, join: join)).toList();
|
return routes.map((route) => addChild(route, join: join)).toList();
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
|
library angel_route.src.router;
|
||||||
|
|
||||||
import 'extensible.dart';
|
import 'extensible.dart';
|
||||||
import 'route.dart';
|
|
||||||
import 'routing_exception.dart';
|
import 'routing_exception.dart';
|
||||||
|
|
||||||
|
part 'route.dart';
|
||||||
|
|
||||||
|
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
||||||
|
final RegExp _rgxEnd = new RegExp(r'\$+$');
|
||||||
|
final RegExp _rgxStart = new RegExp(r'^\^+');
|
||||||
|
final RegExp _rgxStraySlashes = new RegExp(r'(^((\\/)|(/))+)|(((\\/)|(/))+$)');
|
||||||
|
final RegExp _slashDollar = new RegExp(r'/+\$');
|
||||||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
||||||
|
|
||||||
/// An abstraction over complex [Route] trees. Use this instead of the raw API. :)
|
/// An abstraction over complex [Route] trees. Use this instead of the raw API. :)
|
||||||
|
@ -20,7 +28,8 @@ class Router extends Extensible {
|
||||||
/// Provide a `root` to make this Router revolve around a pre-defined route.
|
/// Provide a `root` to make this Router revolve around a pre-defined route.
|
||||||
/// Not recommended.
|
/// Not recommended.
|
||||||
Router({this.debug: false, Route root}) {
|
Router({this.debug: false, Route root}) {
|
||||||
_root = (_root = root ?? new _RootRoute())..debug = debug;
|
_root = (_root = root ?? new _RootRoute())
|
||||||
|
..debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _printDebug(msg) {
|
void _printDebug(msg) {
|
||||||
|
@ -40,7 +49,10 @@ class Router extends Extensible {
|
||||||
|
|
||||||
if (path is RegExp) {
|
if (path is RegExp) {
|
||||||
return root.child(path, debug: debug, handlers: handlers, method: method);
|
return root.child(path, debug: debug, handlers: handlers, method: method);
|
||||||
} else if (path.toString().replaceAll(_straySlashes, '').isEmpty) {
|
} else if (path
|
||||||
|
.toString()
|
||||||
|
.replaceAll(_straySlashes, '')
|
||||||
|
.isEmpty) {
|
||||||
return root.child(path.toString(),
|
return root.child(path.toString(),
|
||||||
debug: debug, handlers: handlers, method: method);
|
debug: debug, handlers: handlers, method: method);
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,8 +67,8 @@ class Router extends Extensible {
|
||||||
return new Route('/', debug: debug, handlers: handlers, method: method)
|
return new Route('/', debug: debug, handlers: handlers, method: method)
|
||||||
..debug = debug;
|
..debug = debug;
|
||||||
} else {
|
} else {
|
||||||
result = resolve(segments[0],
|
result = resolveOnRoot(segments[0],
|
||||||
(route) => route.method == method || route.method == '*');
|
filter: (route) => route.method == method || route.method == '*');
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (segments.length > 1) {
|
if (segments.length > 1) {
|
||||||
|
@ -110,15 +122,17 @@ class Router extends Extensible {
|
||||||
final buf = new StringBuffer();
|
final buf = new StringBuffer();
|
||||||
|
|
||||||
void dumpRoute(Route route, {Pattern replace: null}) {
|
void dumpRoute(Route route, {Pattern replace: null}) {
|
||||||
for (var i = 0; i < tabs; i++) buf.write(tab);
|
for (var i = 0; i < tabs; i++)
|
||||||
|
buf.write(tab);
|
||||||
|
|
||||||
if (route == root)
|
if (route == root)
|
||||||
buf.write('(root)');
|
buf.writeln('(root)');
|
||||||
else {
|
else {
|
||||||
buf.write('- ${route.method} ');
|
buf.write('- ${route.method} ');
|
||||||
|
|
||||||
final p =
|
var p =
|
||||||
replace != null ? route.path.replaceAll(replace, '') : route.path;
|
replace != null ? route.path.replaceAll(replace, '') : route.path;
|
||||||
|
p = p.replaceAll(_straySlashes, '');
|
||||||
|
|
||||||
if (p.isEmpty)
|
if (p.isEmpty)
|
||||||
buf.write("'/'");
|
buf.write("'/'");
|
||||||
|
@ -179,9 +193,130 @@ class Router extends Extensible {
|
||||||
///
|
///
|
||||||
/// You can pass an additional filter to determine which
|
/// You can pass an additional filter to determine which
|
||||||
/// routes count as matches.
|
/// routes count as matches.
|
||||||
Route resolve(String path, [bool filter(Route route)]) =>
|
Route resolveOnRoot(String path, {bool filter(Route route)}) =>
|
||||||
root.resolve(path, filter: filter);
|
root.resolve(path, filter: filter);
|
||||||
|
|
||||||
|
/// Finds the first [Route] that matches the given path,
|
||||||
|
/// with the given method.
|
||||||
|
Route resolve(String path, {String method: 'GET'}) {
|
||||||
|
final String _path = path.replaceAll(_straySlashes, '');
|
||||||
|
final segments = _path.split('/').where((str) => str.isNotEmpty);
|
||||||
|
_printDebug('Segments: $segments');
|
||||||
|
return _resolve(root, _path, method, segments.first, segments.skip(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
_validHead(RegExp rgx) {
|
||||||
|
return !rgx.hasMatch('');
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolve(Route ref, String fullPath, String method, String head,
|
||||||
|
Iterable<String> tail) {
|
||||||
|
_printDebug(
|
||||||
|
'$method on $ref: path: $fullPath, head: $head, tail: ${tail.join(
|
||||||
|
'/')}');
|
||||||
|
|
||||||
|
// Does the index route match?
|
||||||
|
if (ref.matcher.hasMatch(fullPath)) {
|
||||||
|
final index = ref.indexRoute;
|
||||||
|
|
||||||
|
for (Route child in ref.allIndices) {
|
||||||
|
_printDebug('Possible index: $child');
|
||||||
|
|
||||||
|
if (child == child.indexRoute && ['*', method].contains(child.method)) {
|
||||||
|
_printDebug('Possible index was exact match: $child');
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
final resolved = _resolve(child, fullPath, method, head, tail);
|
||||||
|
|
||||||
|
if (resolved != null) {
|
||||||
|
_printDebug('Resolved from possible index: $resolved');
|
||||||
|
return resolved;
|
||||||
|
} else
|
||||||
|
_printDebug('Possible index returned null: $child');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['*', method].contains(index.method)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Try to match children by full path
|
||||||
|
for (Route child in ref.children) {
|
||||||
|
if (child.matcher.hasMatch(fullPath)) {
|
||||||
|
final resolved = _resolve(child, fullPath, method, head, tail);
|
||||||
|
|
||||||
|
if (resolved != null) {
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, let's check if any route's head matches the
|
||||||
|
// given head. If so, we try to resolve with that
|
||||||
|
// route, using a head corresponding to the one we
|
||||||
|
// matched.
|
||||||
|
for (Route child in ref.children) {
|
||||||
|
if (child._head != null &&
|
||||||
|
child._head.hasMatch(fullPath) &&
|
||||||
|
_validHead(child._head)) {
|
||||||
|
final newHead = child._head
|
||||||
|
.firstMatch(fullPath)
|
||||||
|
.group(0)
|
||||||
|
.replaceAll(_straySlashes, '');
|
||||||
|
final newTail = fullPath
|
||||||
|
.replaceAll(child._head, '')
|
||||||
|
.replaceAll(_straySlashes, '')
|
||||||
|
.split('/')
|
||||||
|
.where((str) => str.isNotEmpty);
|
||||||
|
final resolved = _resolve(child, fullPath, method, newHead, newTail);
|
||||||
|
|
||||||
|
if (resolved != null) {
|
||||||
|
_printDebug(
|
||||||
|
'Head match: $resolved from head: ${child._head.pattern}');
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
} else if (child._head != null) {
|
||||||
|
_printDebug(
|
||||||
|
'Head ${child._head
|
||||||
|
.pattern} on $child failed to match $fullPath');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tail.isEmpty)
|
||||||
|
return null;
|
||||||
|
else {
|
||||||
|
return _resolve(
|
||||||
|
ref, fullPath, method, head + '/' + tail.first, tail.skip(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new Router in which the route tree has been
|
||||||
|
/// flattened into a linear list.
|
||||||
|
Router flatten() {
|
||||||
|
final router = new Router();
|
||||||
|
|
||||||
|
_flatten(Route route) {
|
||||||
|
// if (route.children.isNotEmpty && route.method == '*') return;
|
||||||
|
|
||||||
|
final r = new Route._base();
|
||||||
|
|
||||||
|
r
|
||||||
|
.._handlers.addAll(route.handlerSequence)
|
||||||
|
.._head = route._head
|
||||||
|
.._matcher = route.matcher
|
||||||
|
.._method = route.method
|
||||||
|
.._parent = router.root
|
||||||
|
.._path = route.path;
|
||||||
|
|
||||||
|
router.root._children.add(r);
|
||||||
|
route.children.forEach(_flatten);
|
||||||
|
}
|
||||||
|
|
||||||
|
root._children.forEach(_flatten);
|
||||||
|
return router..debug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
/// Incorporates another [Router]'s routes into this one's.
|
/// Incorporates another [Router]'s routes into this one's.
|
||||||
///
|
///
|
||||||
/// If `hooked` is set to `true` and a [Service] is provided,
|
/// If `hooked` is set to `true` and a [Service] is provided,
|
||||||
|
@ -203,7 +338,64 @@ class Router extends Extensible {
|
||||||
copiedMiddleware[middlewareName];
|
copiedMiddleware[middlewareName];
|
||||||
}
|
}
|
||||||
|
|
||||||
root.child(path, debug: debug).addChild(router.root);
|
// final route = root.addChild(router.root, join: false);
|
||||||
|
final route = root.child(path, debug: debug).addChild(router.root);
|
||||||
|
route.debug = debug;
|
||||||
|
|
||||||
|
if (path is! RegExp) {
|
||||||
|
final _path = path.toString().replaceAll(_straySlashes, '');
|
||||||
|
|
||||||
|
_migrateRoute(Route r) {
|
||||||
|
r._path = '$_path/${r.path}'.replaceAll(_straySlashes, '');
|
||||||
|
var stripped = r.matcher.pattern
|
||||||
|
.replaceAll(_rgxStart, '')
|
||||||
|
.replaceAll(_rgxEnd, '')
|
||||||
|
.replaceAll(_rgxStraySlashes, '')
|
||||||
|
.replaceAll(_straySlashes, '');
|
||||||
|
stripped = '$_path/$stripped'.replaceAll(_straySlashes, '');
|
||||||
|
r._matcher = new RegExp('^$stripped\$');
|
||||||
|
|
||||||
|
if (r._head != null) {
|
||||||
|
final head = r._head.pattern
|
||||||
|
.replaceAll(_rgxStart, '')
|
||||||
|
.replaceAll(_rgxEnd, '')
|
||||||
|
.replaceAll(_rgxStraySlashes, '')
|
||||||
|
.replaceAll('\\/', '/')
|
||||||
|
.replaceAll(_straySlashes, '');
|
||||||
|
r._head = new RegExp(_matcherify('$_path/$head').replaceAll(_rgxEnd, ''));
|
||||||
|
_printDebug('Head of migrated route: ${r._head.pattern}');
|
||||||
|
}
|
||||||
|
|
||||||
|
r.children.forEach(_migrateRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
route.children.forEach(_migrateRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes empty routes that could complicate route resolution.
|
||||||
|
void normalize() {
|
||||||
|
_printDebug('Normalizing route tree...');
|
||||||
|
|
||||||
|
_normalize(Route route) {
|
||||||
|
route.children.forEach(_normalize);
|
||||||
|
|
||||||
|
if (route.path
|
||||||
|
.replaceAll(_straySlashes, '')
|
||||||
|
.isEmpty &&
|
||||||
|
route.children.isNotEmpty) {
|
||||||
|
_printDebug('Erasing this route: $route');
|
||||||
|
route.parent._handlers.addAll(route.handlers);
|
||||||
|
|
||||||
|
for (Route child in route.children) {
|
||||||
|
route.parent._children.add(child.._parent = route.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
route.parent._children.remove(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.children.forEach(_normalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to any request matching the given path.
|
/// Adds a route that responds to any request matching the given path.
|
||||||
|
@ -248,7 +440,7 @@ class Router extends Extensible {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RootRoute extends Route {
|
class _RootRoute extends Route {
|
||||||
_RootRoute() : super("/", name: "<root>");
|
_RootRoute() : super("/", method: '*', name: "<root>");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "ROOT";
|
String toString() => "ROOT";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel_route
|
name: angel_route
|
||||||
description: A powerful, isomorphic routing library for Dart.
|
description: A powerful, isomorphic routing library for Dart.
|
||||||
version: 1.0.0-dev+5
|
version: 1.0.0-dev+6
|
||||||
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
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import 'package:test/test.dart';
|
|
||||||
import 'route/all_tests.dart' as route;
|
|
||||||
import 'router/all_tests.dart' as router;
|
|
||||||
import 'server/all_tests.dart' as server;
|
|
||||||
|
|
||||||
main() {
|
|
||||||
group('route', route.main);
|
|
||||||
group('router', router.main);
|
|
||||||
group('server', server.main);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Tests</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="all_tests.browser.dart" type="application/dart"></script>
|
|
||||||
<script src="packages/browser/dart.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
123
test/method/all_tests.dart
Normal file
123
test/method/all_tests.dart
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import 'package:angel_route/angel_route.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
var router = new Router(debug: true);
|
||||||
|
final getFoo = router.get('/foo', 'GET');
|
||||||
|
final postFoo = router.post('/foo', 'POST');
|
||||||
|
|
||||||
|
Route getFooBar, postFooBar, patchFooBarId;
|
||||||
|
|
||||||
|
router.group('/foo/bar', (router) {
|
||||||
|
getFooBar = router.get('/', 'GET');
|
||||||
|
postFooBar = router.post('/', 'POST');
|
||||||
|
patchFooBarId = router.patch('/:id([0-9]+)', 'PATCH');
|
||||||
|
});
|
||||||
|
|
||||||
|
final Router books = new Router();
|
||||||
|
|
||||||
|
final getBooks = books.get('/', 'GET');
|
||||||
|
final postBooks = books.post('/', 'POST');
|
||||||
|
final getBooksFoo = books.get('/foo', 'GET');
|
||||||
|
final postBooksFoo = books.post('/foo', 'POST');
|
||||||
|
|
||||||
|
Route getBooksChapters,
|
||||||
|
postBooksChapters,
|
||||||
|
getBooksChaptersReviews,
|
||||||
|
postBooksChaptersReviews;
|
||||||
|
|
||||||
|
books.group('/:id/chapters', (router) {
|
||||||
|
getBooksChapters = router.get('/', 'GET');
|
||||||
|
postBooksChapters = router.post('/', 'POST');
|
||||||
|
|
||||||
|
router.group('/:id([A-Za-z]+)/reviews', (router) {
|
||||||
|
getBooksChaptersReviews = router.get('/', 'GET');
|
||||||
|
postBooksChaptersReviews = router.post('/', 'POST');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.mount('/books', books);
|
||||||
|
router.normalize();
|
||||||
|
|
||||||
|
group('top level', () {
|
||||||
|
test('get', () => expect(router.resolve('/foo'), equals(getFoo)));
|
||||||
|
|
||||||
|
test('post', () {
|
||||||
|
router.dumpTree();
|
||||||
|
expect(router.resolve('/foo', method: 'POST'), equals(postFoo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('group', () {
|
||||||
|
test('get', () {
|
||||||
|
expect(router.resolve('/foo/bar'), equals(getFooBar));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('post', () {
|
||||||
|
expect(router.resolve('/foo/bar', method: 'POST'), equals(postFooBar));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('patch+id', () {
|
||||||
|
router.dumpTree();
|
||||||
|
expect(
|
||||||
|
router.resolve('/foo/bar/2', method: 'PATCH'), equals(patchFooBarId));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('404', () {
|
||||||
|
expect(router.resolve('/foo/bar/A', method: 'PATCH'), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('mount', () {
|
||||||
|
group('no params', () {
|
||||||
|
test('get', () {
|
||||||
|
expect(router.resolve('/books'), equals(getBooks));
|
||||||
|
expect(router.resolve('/books/foo'), equals(getBooksFoo));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('post', () {
|
||||||
|
expect(router.resolve('/books', method: 'POST'), equals(postBooks));
|
||||||
|
expect(
|
||||||
|
router.resolve('/books/foo', method: 'POST'), equals(postBooksFoo));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('with params', () {
|
||||||
|
test('1 param', () {
|
||||||
|
expect(router.resolve('/books/abc/chapters'), equals(getBooksChapters));
|
||||||
|
expect(router.resolve('/books/abc/chapters', method: 'POST'),
|
||||||
|
equals(postBooksChapters));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('2 params', () {
|
||||||
|
setUp(router.dumpTree);
|
||||||
|
|
||||||
|
test('get', () {
|
||||||
|
expect(router.resolve('/books/abc/chapters/ABC/reviews'),
|
||||||
|
equals(getBooksChaptersReviews));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('post', () {
|
||||||
|
expect(
|
||||||
|
router.resolve('/books/abc/chapters/ABC/reviews', method: 'POST'),
|
||||||
|
equals(postBooksChaptersReviews));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('404', () {
|
||||||
|
expect(router.resolve('/books/abc/chapters/1'), isNull);
|
||||||
|
expect(router.resolve('/books/abc/chapters/12'), isNull);
|
||||||
|
expect(router.resolve('/books/abc/chapters/13.!'), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('flatten', () {
|
||||||
|
router.dumpTree(header: 'BEFORE FLATTENING:');
|
||||||
|
final flat = router.flatten();
|
||||||
|
|
||||||
|
for (Route route in flat.root.children) {
|
||||||
|
print('${route.method} ${route.path} => ${route.matcher.pattern}');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
../packages
|
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:angel_route/angel_route.dart';
|
import 'package:angel_route/angel_route.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'fallback.dart' as fallback;
|
import 'fallback.dart' as fallback;
|
||||||
import 'use.dart' as use;
|
|
||||||
|
|
||||||
final ABC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
final ABC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||||
|
|
||||||
|
@ -13,7 +12,8 @@ main() {
|
||||||
router.delete('/user/:id/detail', (id) => num.parse(id));
|
router.delete('/user/:id/detail', (id) => num.parse(id));
|
||||||
|
|
||||||
Route lower;
|
Route lower;
|
||||||
final letters = router.group('/letter///', (router) {
|
|
||||||
|
router.group('/letter///', (router) {
|
||||||
lower = router
|
lower = router
|
||||||
.get('/:id([A-Za-z])', (id) => ABC.indexOf(id[0]))
|
.get('/:id([A-Za-z])', (id) => ABC.indexOf(id[0]))
|
||||||
.child('////lower', handlers: [(String id) => id.toLowerCase()[0]]);
|
.child('////lower', handlers: [(String id) => id.toLowerCase()[0]]);
|
||||||
|
@ -30,7 +30,6 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group('fallback', fallback.main);
|
group('fallback', fallback.main);
|
||||||
test('group & use', use.main);
|
|
||||||
|
|
||||||
test('hierarchy', () {
|
test('hierarchy', () {
|
||||||
expect(lower.absoluteParent, equals(router.root));
|
expect(lower.absoluteParent, equals(router.root));
|
||||||
|
@ -41,10 +40,10 @@ main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolve', () {
|
test('resolve', () {
|
||||||
expect(router.resolve('/'), equals(indexRoute));
|
expect(router.resolveOnRoot('/'), equals(indexRoute));
|
||||||
expect(router.resolve('user/1337/detail'), equals(deleteUserById));
|
expect(router.resolveOnRoot('user/1337/detail'), equals(deleteUserById));
|
||||||
expect(router.resolve('/user/1337/detail'), equals(deleteUserById));
|
expect(router.resolveOnRoot('/user/1337/detail'), equals(deleteUserById));
|
||||||
expect(router.resolve('letter/a/lower'), equals(lower));
|
expect(router.resolveOnRoot('letter/a/lower'), equals(lower));
|
||||||
expect(router.resolve('letter/2/lower'), isNull);
|
expect(router.resolveOnRoot('letter/2/lower'), isNull);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ main() {
|
||||||
final fallback = router.get('*', () => 'fallback');
|
final fallback = router.get('*', () => 'fallback');
|
||||||
|
|
||||||
test('resolve', () {
|
test('resolve', () {
|
||||||
expect(router.resolve('/foo'), equals(fallback));
|
expect(router.resolveOnRoot('/foo'), equals(fallback));
|
||||||
expect(router.resolve('/user/:id'), equals(userById));
|
expect(router.resolveOnRoot('/user/:id'), equals(userById));
|
||||||
expect(router.resolve('/user/:id', checkPost), isNull);
|
expect(router.resolveOnRoot('/user/:id', filter: checkPost), isNull);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
|
@ -22,7 +22,7 @@ main() {
|
||||||
url = 'http://${server.address.address}:${server.port}';
|
url = 'http://${server.address.address}:${server.port}';
|
||||||
|
|
||||||
server.listen((request) async {
|
server.listen((request) async {
|
||||||
final resolved = router.resolve(request.uri.toString(), (route) {
|
final resolved = router.resolveOnRoot(request.uri.toString(), filter: (route) {
|
||||||
print(
|
print(
|
||||||
'$route matches ${request.method} ${request.uri}? ${route.method == request.method || route.method == '*'}');
|
'$route matches ${request.method} ${request.uri}? ${route.method == request.method || route.method == '*'}');
|
||||||
return route.method == request.method || route.method == '*';
|
return route.method == request.method || route.method == '*';
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
|
@ -22,14 +22,14 @@ main() {
|
||||||
|
|
||||||
group('no params', () {
|
group('no params', () {
|
||||||
test('resolve', () {
|
test('resolve', () {
|
||||||
expect(child.resolve('a'), equals(a));
|
expect(child.resolveOnRoot('a'), equals(a));
|
||||||
expect(child.resolve('b'), equals(b));
|
expect(child.resolveOnRoot('b'), equals(b));
|
||||||
expect(child.resolve('b/c'), equals(c));
|
expect(child.resolveOnRoot('b/c'), equals(c));
|
||||||
|
|
||||||
expect(parent.resolve('child/a'), equals(a));
|
expect(parent.resolveOnRoot('child/a'), equals(a));
|
||||||
expect(parent.resolve('a'), isNull);
|
expect(parent.resolveOnRoot('a'), isNull);
|
||||||
expect(parent.resolve('child/b'), equals(b));
|
expect(parent.resolveOnRoot('child/b'), equals(b));
|
||||||
expect(parent.resolve('child/b/c'), equals(c));
|
expect(parent.resolveOnRoot('child/b/c'), equals(c));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
|
@ -1 +0,0 @@
|
||||||
../packages
|
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
|
@ -1 +0,0 @@
|
||||||
../../packages
|
|
Loading…
Reference in a new issue