Bump to 3.0.0
This commit is contained in:
parent
951c593d58
commit
6142f49724
15 changed files with 154 additions and 170 deletions
|
@ -2,6 +2,7 @@
|
||||||
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||||
<option name="filePath" value="$PROJECT_DIR$/test" />
|
<option name="filePath" value="$PROJECT_DIR$/test" />
|
||||||
<option name="scope" value="FOLDER" />
|
<option name="scope" value="FOLDER" />
|
||||||
|
<option name="testRunnerOptions" value="-j4" />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
8
.idea/runConfigurations/parse_in_parse_test_dart.xml
Normal file
8
.idea/runConfigurations/parse_in_parse_test_dart.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="parse in parse_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/test/parse_test.dart" />
|
||||||
|
<option name="scope" value="GROUP_OR_TEST_BY_NAME" />
|
||||||
|
<option name="testName" value="parse" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -1,3 +1,8 @@
|
||||||
# 3.0.0-alpha
|
# 3.0.0
|
||||||
* Make `Router` and `Route` single-parameter generic.
|
* Make `Router` and `Route` single-parameter generic.
|
||||||
* Remove `package:browser` dependency.
|
* Remove `package:browser` dependency.
|
||||||
|
* `BrowserRouter.on` now only accepts a `String`.
|
||||||
|
* `MiddlewarePipeline.routingResults` now accepts
|
||||||
|
an `Iterable<RoutingResult>`, instead of just a `List`.
|
||||||
|
* Removed deprecated `Route.as`, as well as `Router.registerMiddleware`.
|
||||||
|
* Completely removed `Route.requestMiddleware`.
|
|
@ -39,7 +39,7 @@ abstract class BrowserRouter extends Router {
|
||||||
void listen();
|
void listen();
|
||||||
|
|
||||||
/// Identical to [all].
|
/// Identical to [all].
|
||||||
Route on(Pattern path, handler, {List middleware});
|
Route on(String path, handler, {List middleware});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _BrowserRouterImpl extends Router implements BrowserRouter {
|
abstract class _BrowserRouterImpl extends Router implements BrowserRouter {
|
||||||
|
@ -64,7 +64,7 @@ abstract class _BrowserRouterImpl extends Router implements BrowserRouter {
|
||||||
@override
|
@override
|
||||||
void go(Iterable linkParams) => _goTo(navigate(linkParams));
|
void go(Iterable linkParams) => _goTo(navigate(linkParams));
|
||||||
|
|
||||||
Route on(Pattern path, handler, {List middleware}) =>
|
Route on(String path, handler, {List middleware}) =>
|
||||||
all(path, handler, middleware: middleware);
|
all(path, handler, middleware: middleware);
|
||||||
|
|
||||||
void prepareAnchors() {
|
void prepareAnchors() {
|
||||||
|
@ -176,7 +176,7 @@ class _PushStateRouter extends _BrowserRouterImpl {
|
||||||
|
|
||||||
void handleState(state) {
|
void handleState(state) {
|
||||||
if (state is Map && state.containsKey('path')) {
|
if (state is Map && state.containsKey('path')) {
|
||||||
var path = state['path'];
|
var path = state['path'].toString();
|
||||||
final resolved = resolveAbsolute(path).first;
|
final resolved = resolveAbsolute(path).first;
|
||||||
|
|
||||||
if (resolved != null && resolved.route != _current) {
|
if (resolved != null && resolved.route != _current) {
|
||||||
|
|
|
@ -2,13 +2,13 @@ part of angel_route.src.router;
|
||||||
|
|
||||||
class RouteGrammar {
|
class RouteGrammar {
|
||||||
static final Parser<String> notSlash =
|
static final Parser<String> notSlash =
|
||||||
match(new RegExp(r'[^/]+')).value((r) => r.span.text);
|
match<String>(new RegExp(r'[^/]+')).value((r) => r.span.text);
|
||||||
|
|
||||||
static final Parser<RegExp> regExp = match<RegExp>(new RegExp(r'\((.+)\)'))
|
static final Parser<RegExp> regExp = match<RegExp>(new RegExp(r'\((.+)\)'))
|
||||||
.value((r) => new RegExp(r.scanner.lastMatch[1]));
|
.value((r) => new RegExp(r.scanner.lastMatch[1]));
|
||||||
|
|
||||||
static final Parser<String> parameterName =
|
static final Parser<String> parameterName =
|
||||||
match(new RegExp(r':([A-Za-z0-9_]+)'))
|
match<String>(new RegExp(r':([A-Za-z0-9_]+)'))
|
||||||
.value((r) => r.span.text.substring(1));
|
.value((r) => r.span.text.substring(1));
|
||||||
|
|
||||||
static final Parser<ParameterSegment> parameterSegment = chain([
|
static final Parser<ParameterSegment> parameterSegment = chain([
|
||||||
|
@ -16,7 +16,7 @@ class RouteGrammar {
|
||||||
match('?').value((r) => true).opt(),
|
match('?').value((r) => true).opt(),
|
||||||
regExp.opt(),
|
regExp.opt(),
|
||||||
]).map((r) {
|
]).map((r) {
|
||||||
var s = new ParameterSegment(r.value[0], r.value[2]);
|
var s = new ParameterSegment(r.value[0] as String, r.value[2] as RegExp);
|
||||||
return r.value[1] == true ? new OptionalSegment(s) : s;
|
return r.value[1] == true ? new OptionalSegment(s) : s;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,16 +26,17 @@ class RouteGrammar {
|
||||||
.map((r) => r.span.text),
|
.map((r) => r.span.text),
|
||||||
parameterSegment,
|
parameterSegment,
|
||||||
]).map((r) {
|
]).map((r) {
|
||||||
return new ParsedParameterSegment(r.value[0], r.value[1]);
|
return new ParsedParameterSegment(
|
||||||
|
r.value[0] as String, r.value[1] as ParameterSegment);
|
||||||
});
|
});
|
||||||
|
|
||||||
static final Parser<WildcardSegment> wildcardSegment =
|
static final Parser<WildcardSegment> wildcardSegment =
|
||||||
match('*').value((r) => new WildcardSegment());
|
match<WildcardSegment>('*').value((r) => new WildcardSegment());
|
||||||
|
|
||||||
static final Parser<ConstantSegment> constantSegment =
|
static final Parser<ConstantSegment> constantSegment =
|
||||||
notSlash.map((r) => new ConstantSegment(r.value));
|
notSlash.map<ConstantSegment>((r) => new ConstantSegment(r.value));
|
||||||
|
|
||||||
static final Parser<RouteSegment> routeSegment = any([
|
static final Parser<RouteSegment> routeSegment = any<RouteSegment>([
|
||||||
parsedParameterSegment,
|
parsedParameterSegment,
|
||||||
parameterSegment,
|
parameterSegment,
|
||||||
wildcardSegment,
|
wildcardSegment,
|
||||||
|
@ -62,7 +63,8 @@ class RouteDefinition {
|
||||||
if (out == null)
|
if (out == null)
|
||||||
out = s.compile(isLast);
|
out = s.compile(isLast);
|
||||||
else
|
else
|
||||||
out = s.compileNext(out.then(match('/')).index(0), isLast);
|
out = s.compileNext(
|
||||||
|
out.then(match('/')).index(0).cast<Map<String, dynamic>>(), isLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -88,13 +90,13 @@ class ConstantSegment extends RouteSegment {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Parser<Map<String, dynamic>> compile(bool isLast) {
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
||||||
return match(text).value((r) => {});
|
return match<Map<String, dynamic>>(text).value((r) => <String, dynamic>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Parser<Map<String, dynamic>> compileNext(
|
Parser<Map<String, dynamic>> compileNext(
|
||||||
Parser<Map<String, dynamic>> p, bool isLast) {
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
||||||
return p.then(compile(isLast)).index(0);
|
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ class WildcardSegment extends RouteSegment {
|
||||||
@override
|
@override
|
||||||
Parser<Map<String, dynamic>> compileNext(
|
Parser<Map<String, dynamic>> compileNext(
|
||||||
Parser<Map<String, dynamic>> p, bool isLast) {
|
Parser<Map<String, dynamic>> p, bool isLast) {
|
||||||
return p.then(_compile(isLast)).index(0);
|
return p.then(_compile(isLast)).index(0).cast<Map<String, dynamic>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +142,9 @@ class OptionalSegment extends ParameterSegment {
|
||||||
Parser<Map<String, dynamic>> compileNext(
|
Parser<Map<String, dynamic>> compileNext(
|
||||||
Parser<Map<String, dynamic>> 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];
|
if (r.value[1] == null) return r.value[0] as Map<String, dynamic>;
|
||||||
return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])});
|
return (r.value[0] as Map<String, dynamic>)
|
||||||
|
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,22 +161,24 @@ class ParameterSegment extends RouteSegment {
|
||||||
return 'Param: $name';
|
return 'Param: $name';
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser<Map<String, dynamic>> _compile() {
|
Parser<String> _compile() {
|
||||||
return regExp != null
|
return regExp != null
|
||||||
? match(regExp).value((r) => r.span.text)
|
? match<String>(regExp).value((r) => r.span.text)
|
||||||
: RouteGrammar.notSlash;
|
: RouteGrammar.notSlash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Parser<Map<String, dynamic>> compile(bool isLast) {
|
Parser<Map<String, dynamic>> compile(bool isLast) {
|
||||||
return _compile().map((r) => {name: Uri.decodeComponent(r.span.text)});
|
return _compile().map<Map<String, dynamic>>(
|
||||||
|
(r) => {name: Uri.decodeComponent(r.span.text)});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Parser<Map<String, dynamic>> compileNext(
|
Parser<Map<String, dynamic>> compileNext(
|
||||||
Parser<Map<String, dynamic>> 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]..addAll({name: Uri.decodeComponent(r.value[1])});
|
return (r.value[0] as Map<String, dynamic>)
|
||||||
|
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,8 +210,10 @@ class ParsedParameterSegment extends RouteSegment {
|
||||||
Parser<Map<String, dynamic>> compileNext(
|
Parser<Map<String, dynamic>> compileNext(
|
||||||
Parser<Map<String, dynamic>> 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]
|
return (r.value[0] as Map<String, dynamic>)
|
||||||
..addAll({parameter.name: getValue(Uri.decodeComponent(r.value[1]))});
|
..addAll({
|
||||||
|
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import 'router.dart';
|
import 'router.dart';
|
||||||
|
|
||||||
/// A chain of arbitrary handlers obtained by routing a path.
|
/// A chain of arbitrary handlers obtained by routing a path.
|
||||||
class MiddlewarePipeline {
|
class MiddlewarePipeline<T> {
|
||||||
/// All the possible routes that matched the given path.
|
/// All the possible routes that matched the given path.
|
||||||
final List<RoutingResult> routingResults;
|
final Iterable<RoutingResult<T>> routingResults;
|
||||||
List _handlers;
|
List<T> _handlers;
|
||||||
|
|
||||||
/// An ordered list of every handler delegated to handle this request.
|
/// An ordered list of every handler delegated to handle this request.
|
||||||
List get handlers {
|
List<T> get handlers {
|
||||||
if (_handlers != null) return _handlers;
|
if (_handlers != null) return _handlers;
|
||||||
final handlers = [];
|
final handlers = <T>[];
|
||||||
|
|
||||||
for (RoutingResult result in routingResults) {
|
for (var result in routingResults) {
|
||||||
handlers.addAll(result.allHandlers);
|
handlers.addAll(result.allHandlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
part of angel_route.src.router;
|
part of angel_route.src.router;
|
||||||
|
|
||||||
/// Represents a virtual location within an application.
|
/// Represents a virtual location within an application.
|
||||||
class Route {
|
class Route<T> {
|
||||||
final String method;
|
final String method;
|
||||||
final String path;
|
final String path;
|
||||||
final List handlers;
|
final List<T> handlers;
|
||||||
final Map<String, Map<String, dynamic>> _cache = {};
|
final Map<String, Map<String, dynamic>> _cache = {};
|
||||||
final RouteDefinition _routeDefinition;
|
final RouteDefinition _routeDefinition;
|
||||||
String name;
|
String name;
|
||||||
|
@ -14,10 +14,12 @@ class Route {
|
||||||
: _routeDefinition = RouteGrammar.routeDefinition
|
: _routeDefinition = RouteGrammar.routeDefinition
|
||||||
.parse(new SpanScanner(path.replaceAll(_straySlashes, '')))
|
.parse(new SpanScanner(path.replaceAll(_straySlashes, '')))
|
||||||
.value {
|
.value {
|
||||||
if (_routeDefinition.segments.isEmpty) _parser = match('').value((r) => {});
|
if (_routeDefinition?.segments?.isNotEmpty != true)
|
||||||
|
_parser =
|
||||||
|
match<Map<String, dynamic>>('').value((r) => <String, dynamic>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
factory Route.join(Route a, Route b) {
|
factory Route.join(Route<T> a, Route<T> b) {
|
||||||
var start = a.path.replaceAll(_straySlashes, '');
|
var start = a.path.replaceAll(_straySlashes, '');
|
||||||
var end = b.path.replaceAll(_straySlashes, '');
|
var end = b.path.replaceAll(_straySlashes, '');
|
||||||
return new Route('$start/$end'.replaceAll(_straySlashes, ''),
|
return new Route('$start/$end'.replaceAll(_straySlashes, ''),
|
||||||
|
@ -27,23 +29,16 @@ class Route {
|
||||||
Parser<Map<String, dynamic>> get parser =>
|
Parser<Map<String, dynamic>> get parser =>
|
||||||
_parser ??= _routeDefinition.compile();
|
_parser ??= _routeDefinition.compile();
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '$method $path => $handlers';
|
return '$method $path => $handlers';
|
||||||
}
|
}
|
||||||
|
|
||||||
Route clone() {
|
Route<T> clone() {
|
||||||
return new Route(path, method: method, handlers: handlers)
|
return new Route<T>(path, method: method, handlers: handlers)
|
||||||
.._cache.addAll(_cache);
|
.._cache.addAll(_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use the setter instead.
|
|
||||||
@deprecated
|
|
||||||
void as(String n) {
|
|
||||||
name = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
String makeUri(Map<String, dynamic> params) {
|
String makeUri(Map<String, dynamic> params) {
|
||||||
var b = new StringBuffer();
|
var b = new StringBuffer();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
|
@ -3,42 +3,46 @@ library angel_route.src.router;
|
||||||
import 'package:combinator/combinator.dart';
|
import 'package:combinator/combinator.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:string_scanner/string_scanner.dart';
|
import 'package:string_scanner/string_scanner.dart';
|
||||||
import 'routing_exception.dart';
|
|
||||||
import '../string_util.dart';
|
import '../string_util.dart';
|
||||||
|
import 'routing_exception.dart';
|
||||||
|
|
||||||
part 'grammar.dart';
|
part 'grammar.dart';
|
||||||
part 'symlink_route.dart';
|
|
||||||
part 'route.dart';
|
part 'route.dart';
|
||||||
|
|
||||||
part 'routing_result.dart';
|
part 'routing_result.dart';
|
||||||
|
|
||||||
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
part 'symlink_route.dart';
|
||||||
final RegExp _rgxEnd = new RegExp(r'\$+$');
|
|
||||||
final RegExp _rgxStart = new RegExp(r'^\^+');
|
//final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
||||||
final RegExp _rgxStraySlashes =
|
//final RegExp _rgxEnd = new RegExp(r'\$+$');
|
||||||
new RegExp(r'(^((\\+/)|(/))+)|(((\\+/)|(/))+$)');
|
//final RegExp _rgxStart = new RegExp(r'^\^+');
|
||||||
final RegExp _slashDollar = 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. :)
|
||||||
class Router {
|
class Router<T> {
|
||||||
final Map<String, Iterable<RoutingResult>> _cache = {};
|
final Map<String, Iterable<RoutingResult>> _cache = {};
|
||||||
|
|
||||||
//final List<_ChainedRouter> _chained = [];
|
//final List<_ChainedRouter> _chained = [];
|
||||||
final List _middleware = [];
|
final List<T> _middleware = [];
|
||||||
final Map<Pattern, Router> _mounted = {};
|
final Map<Pattern, Router<T>> _mounted = {};
|
||||||
final List<Route> _routes = [];
|
final List<Route<T>> _routes = [];
|
||||||
bool _useCache = false;
|
bool _useCache = false;
|
||||||
|
|
||||||
List get middleware => new List.unmodifiable(_middleware);
|
List<T> get middleware => new List<T>.unmodifiable(_middleware);
|
||||||
|
|
||||||
Map<Pattern, Router> get mounted =>
|
Map<Pattern, Router> get mounted =>
|
||||||
new Map<Pattern, Router>.unmodifiable(_mounted);
|
new Map<Pattern, Router>.unmodifiable(_mounted);
|
||||||
|
|
||||||
/// Additional filters to be run on designated requests.
|
List<Route<T>> get routes {
|
||||||
Map<String, dynamic> requestMiddleware = {};
|
return _routes.fold<List<Route<T>>>([], (out, route) {
|
||||||
|
if (route is SymlinkRoute<T>) {
|
||||||
List<Route> get routes {
|
var childRoutes =
|
||||||
return _routes.fold<List<Route>>([], (out, route) {
|
route.router.routes.fold<List<Route<T>>>([], (out, r) {
|
||||||
if (route is SymlinkRoute) {
|
|
||||||
var childRoutes = route.router.routes.fold<List<Route>>([], (out, r) {
|
|
||||||
return out
|
return out
|
||||||
..add(
|
..add(
|
||||||
route.path.isEmpty ? r : new Route.join(route, r),
|
route.path.isEmpty ? r : new Route.join(route, r),
|
||||||
|
@ -64,46 +68,44 @@ class Router {
|
||||||
/// Adds a route that responds to the given path
|
/// Adds a route that responds to the given path
|
||||||
/// for requests with the given method (case-insensitive).
|
/// for requests with the given method (case-insensitive).
|
||||||
/// Provide '*' as the method to respond to all methods.
|
/// Provide '*' as the method to respond to all methods.
|
||||||
Route addRoute(String method, String path, Object handler,
|
Route<T> addRoute(String method, String path, T handler,
|
||||||
{List middleware: const []}) {
|
{Iterable<T> middleware: const []}) {
|
||||||
if (_useCache == true)
|
if (_useCache == true)
|
||||||
throw new StateError('Cannot add routes after caching is enabled.');
|
throw new StateError('Cannot add routes after caching is enabled.');
|
||||||
|
|
||||||
// Check if any mounted routers can match this
|
// Check if any mounted routers can match this
|
||||||
final handlers = [handler];
|
final handlers = <T>[handler];
|
||||||
|
|
||||||
if (middleware != null) handlers.insertAll(0, middleware);
|
if (middleware != null) handlers.insertAll(0, middleware);
|
||||||
|
|
||||||
final route = new Route(path, method: method, handlers: handlers);
|
final route = new Route<T>(path, method: method, handlers: handlers);
|
||||||
_routes.add(route);
|
_routes.add(route);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepends the given middleware to any routes created
|
/// Prepends the given [middleware] to any routes created
|
||||||
/// by the resulting router.
|
/// by the resulting router.
|
||||||
///
|
///
|
||||||
/// [middleware] can be either an `Iterable`, or a single object.
|
|
||||||
///
|
|
||||||
/// The resulting router can be chained, too.
|
/// The resulting router can be chained, too.
|
||||||
_ChainedRouter chain(middleware) {
|
_ChainedRouter<T> chain(Iterable<T> middleware) {
|
||||||
var piped = new _ChainedRouter(this, middleware);
|
var piped = new _ChainedRouter<T>(this, middleware);
|
||||||
var route = new SymlinkRoute('/', piped);
|
var route = new SymlinkRoute<T>('/', piped);
|
||||||
_routes.add(route);
|
_routes.add(route);
|
||||||
return piped;
|
return piped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a [Router] with a duplicated version of this tree.
|
/// Returns a [Router] with a duplicated version of this tree.
|
||||||
Router clone() {
|
Router<T> clone() {
|
||||||
final router = new Router();
|
final router = new Router<T>();
|
||||||
final newMounted = new Map<Pattern, Router>.from(mounted);
|
final newMounted = new Map<Pattern, Router<T>>.from(mounted);
|
||||||
|
|
||||||
for (Route route in routes) {
|
for (var route in routes) {
|
||||||
if (route is! SymlinkRoute) {
|
if (route is! SymlinkRoute<T>) {
|
||||||
router._routes.add(route.clone());
|
router._routes.add(route.clone());
|
||||||
} else if (route is SymlinkRoute) {
|
} else if (route is SymlinkRoute<T>) {
|
||||||
final newRouter = route.router.clone();
|
final newRouter = route.router.clone();
|
||||||
newMounted[route.path] = newRouter;
|
newMounted[route.path] = newRouter;
|
||||||
final symlink = new SymlinkRoute(route.path, newRouter);
|
final symlink = new SymlinkRoute<T>(route.path, newRouter);
|
||||||
router._routes.add(symlink);
|
router._routes.add(symlink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,13 +163,11 @@ class Router {
|
||||||
///
|
///
|
||||||
/// Returns the created route.
|
/// Returns the created route.
|
||||||
/// You can also register middleware within the router.
|
/// You can also register middleware within the router.
|
||||||
SymlinkRoute group(String path, void callback(Router router),
|
SymlinkRoute<T> group(String path, void callback(Router router),
|
||||||
{Iterable middleware: const [],
|
{Iterable<T> middleware: const [], String name: null}) {
|
||||||
String name: null,
|
final router = new Router<T>().._middleware.addAll(middleware);
|
||||||
String namespace: null}) {
|
|
||||||
final router = new Router().._middleware.addAll(middleware);
|
|
||||||
callback(router);
|
callback(router);
|
||||||
return mount(path, router, namespace: namespace)..name = name;
|
return mount(path, router)..name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a URI string based on the given input.
|
/// Generates a URI string based on the given input.
|
||||||
|
@ -265,12 +265,6 @@ class Router {
|
||||||
: segments.join('/');
|
: segments.join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manually assign via [requestMiddleware] instead.
|
|
||||||
@deprecated
|
|
||||||
registerMiddleware(String name, middleware) {
|
|
||||||
requestMiddleware[name] = middleware;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the first [Route] that matches the given path,
|
/// Finds the first [Route] that matches the given path,
|
||||||
/// with the given method.
|
/// with the given method.
|
||||||
bool resolve(String absolute, String relative, List<RoutingResult> out,
|
bool resolve(String absolute, String relative, List<RoutingResult> out,
|
||||||
|
@ -344,122 +338,97 @@ class Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Incorporates another [Router]'s routes into this one's.
|
/// Incorporates another [Router]'s routes into this one's.
|
||||||
///
|
SymlinkRoute<T> mount(String path, Router<T> router) {
|
||||||
/// If `hooked` is set to `true` and a [Service] is provided,
|
final route = new SymlinkRoute<T>(path, router);
|
||||||
/// then that service will be wired to a [HookedService] proxy.
|
|
||||||
/// If a `namespace` is provided, then any middleware
|
|
||||||
/// from the provided [Router] will be prefixed by that namespace,
|
|
||||||
/// with a dot.
|
|
||||||
/// For example, if the [Router] has a middleware 'y', and the `namespace`
|
|
||||||
/// is 'x', then that middleware will be available as 'x.y' in the main router.
|
|
||||||
/// These namespaces can be nested.
|
|
||||||
SymlinkRoute mount(String path, Router router,
|
|
||||||
{bool hooked: true, String namespace: null}) {
|
|
||||||
// Let's copy middleware, heeding the optional middleware namespace.
|
|
||||||
String middlewarePrefix = namespace != null ? "$namespace." : "";
|
|
||||||
|
|
||||||
Map copiedMiddleware = new Map.from(router.requestMiddleware);
|
|
||||||
for (String middlewareName in copiedMiddleware.keys) {
|
|
||||||
requestMiddleware["$middlewarePrefix$middlewareName"] =
|
|
||||||
copiedMiddleware[middlewareName];
|
|
||||||
}
|
|
||||||
|
|
||||||
final route = new SymlinkRoute(path, router);
|
|
||||||
_mounted[route.path] = router;
|
_mounted[route.path] = router;
|
||||||
_routes.add(route);
|
_routes.add(route);
|
||||||
//route._head = new RegExp(route.matcher.pattern.replaceAll(_rgxEnd, ''));
|
//route._head = new RegExp(route.matcher.pattern.replaceAll(_rgxEnd, ''));
|
||||||
|
|
||||||
return route..name = namespace;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to any request matching the given path.
|
/// Adds a route that responds to any request matching the given path.
|
||||||
Route all(String path, Object handler, {List middleware}) {
|
Route<T> all(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('*', path, handler, middleware: middleware);
|
return addRoute('*', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a DELETE request.
|
/// Adds a route that responds to a DELETE request.
|
||||||
Route delete(String path, Object handler, {List middleware}) {
|
Route<T> delete(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('DELETE', path, handler, middleware: middleware);
|
return addRoute('DELETE', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a GET request.
|
/// Adds a route that responds to a GET request.
|
||||||
Route get(String path, Object handler, {List middleware}) {
|
Route<T> get(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('GET', path, handler, middleware: middleware);
|
return addRoute('GET', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a HEAD request.
|
/// Adds a route that responds to a HEAD request.
|
||||||
Route head(String path, Object handler, {List middleware}) {
|
Route<T> head(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('HEAD', path, handler, middleware: middleware);
|
return addRoute('HEAD', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a OPTIONS request.
|
/// Adds a route that responds to a OPTIONS request.
|
||||||
Route options(String path, Object handler, {List middleware}) {
|
Route<T> options(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('OPTIONS', path, handler, middleware: middleware);
|
return addRoute('OPTIONS', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a POST request.
|
/// Adds a route that responds to a POST request.
|
||||||
Route post(String path, Object handler, {List middleware}) {
|
Route<T> post(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('POST', path, handler, middleware: middleware);
|
return addRoute('POST', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a PATCH request.
|
/// Adds a route that responds to a PATCH request.
|
||||||
Route patch(String path, Object handler, {List middleware}) {
|
Route<T> patch(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('PATCH', path, handler, middleware: middleware);
|
return addRoute('PATCH', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a route that responds to a PUT request.
|
/// Adds a route that responds to a PUT request.
|
||||||
Route put(String path, Object handler, {List middleware}) {
|
Route put(String path, T handler, {Iterable<T> middleware}) {
|
||||||
return addRoute('PUT', path, handler, middleware: middleware);
|
return addRoute('PUT', path, handler, middleware: middleware);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChainedRouter extends Router {
|
class _ChainedRouter<T> extends Router<T> {
|
||||||
final List _handlers = [];
|
final List<T> _handlers = <T>[];
|
||||||
Router _root;
|
Router _root;
|
||||||
|
|
||||||
_ChainedRouter.empty();
|
_ChainedRouter.empty();
|
||||||
|
|
||||||
_ChainedRouter(Router root, middleware) {
|
_ChainedRouter(Router root, Iterable<T> middleware) {
|
||||||
this._root = root;
|
this._root = root;
|
||||||
_handlers.addAll(middleware is Iterable ? middleware : [middleware]);
|
_handlers.addAll(middleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Route addRoute(String method, String path, handler,
|
Route<T> addRoute(String method, String path, handler,
|
||||||
{List middleware: const []}) {
|
{Iterable<T> middleware: const []}) {
|
||||||
var route = super.addRoute(method, path, handler,
|
var route = super.addRoute(method, path, handler,
|
||||||
middleware: []..addAll(_handlers)..addAll(middleware ?? []));
|
middleware: []..addAll(_handlers)..addAll(middleware ?? []));
|
||||||
//_root._routes.add(route);
|
//_root._routes.add(route);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymlinkRoute group(String path, void callback(Router router),
|
SymlinkRoute<T> group(String path, void callback(Router<T> router),
|
||||||
{Iterable middleware: const [],
|
{Iterable<T> middleware: const [], String name: null}) {
|
||||||
String name: null,
|
|
||||||
String namespace: null}) {
|
|
||||||
final router =
|
final router =
|
||||||
new _ChainedRouter(_root, []..addAll(_handlers)..addAll(middleware));
|
new _ChainedRouter<T>(_root, []..addAll(_handlers)..addAll(middleware));
|
||||||
callback(router);
|
callback(router);
|
||||||
return mount(path, router, namespace: namespace)..name = name;
|
return mount(path, router)..name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SymlinkRoute mount(String path, Router router,
|
SymlinkRoute<T> mount(String path, Router<T> router) {
|
||||||
{bool hooked: true, String namespace: null}) {
|
final route = super.mount(path, router);
|
||||||
final route =
|
|
||||||
super.mount(path, router, hooked: hooked, namespace: namespace);
|
|
||||||
route.router._middleware.insertAll(0, _handlers);
|
route.router._middleware.insertAll(0, _handlers);
|
||||||
//_root._routes.add(route);
|
//_root._routes.add(route);
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ChainedRouter chain(middleware) {
|
_ChainedRouter<T> chain(Iterable<T> middleware) {
|
||||||
final piped = new _ChainedRouter.empty().._root = _root;
|
final piped = new _ChainedRouter<T>.empty().._root = _root;
|
||||||
piped._handlers.addAll([]
|
piped._handlers.addAll([]..addAll(_handlers)..addAll(middleware));
|
||||||
..addAll(_handlers)
|
var route = new SymlinkRoute<T>('/', piped);
|
||||||
..addAll(middleware is Iterable ? middleware : [middleware]));
|
|
||||||
var route = new SymlinkRoute('/', piped);
|
|
||||||
_routes.add(route);
|
_routes.add(route);
|
||||||
return piped;
|
return piped;
|
||||||
}
|
}
|
||||||
|
@ -467,14 +436,12 @@ class _ChainedRouter extends Router {
|
||||||
|
|
||||||
/// Optimizes a router by condensing all its routes into one level.
|
/// Optimizes a router by condensing all its routes into one level.
|
||||||
Router flatten(Router router) {
|
Router flatten(Router router) {
|
||||||
var flattened = new Router()
|
var flattened = new Router();
|
||||||
..requestMiddleware.addAll(router.requestMiddleware);
|
|
||||||
|
|
||||||
for (var route in router.routes) {
|
for (var route in router.routes) {
|
||||||
if (route is SymlinkRoute) {
|
if (route is SymlinkRoute) {
|
||||||
var base = route.path.replaceAll(_straySlashes, '');
|
var base = route.path.replaceAll(_straySlashes, '');
|
||||||
var child = flatten(route.router);
|
var child = flatten(route.router);
|
||||||
flattened.requestMiddleware.addAll(child.requestMiddleware);
|
|
||||||
|
|
||||||
for (var route in child.routes) {
|
for (var route in child.routes) {
|
||||||
var path = route.path.replaceAll(_straySlashes, '');
|
var path = route.path.replaceAll(_straySlashes, '');
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
part of angel_route.src.router;
|
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 {
|
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<Map<String, dynamic>> parseResult;
|
||||||
|
|
||||||
/// A nested instance, if a sub-path was matched.
|
/// A nested instance, if a sub-path was matched.
|
||||||
final Iterable<RoutingResult> nested;
|
final Iterable<RoutingResult<T>> nested;
|
||||||
|
|
||||||
/// All route params matching this route on the current sub-path.
|
/// All route params matching this route on the current sub-path.
|
||||||
final Map<String, dynamic> params = {};
|
final Map<String, dynamic> params = {};
|
||||||
|
@ -14,18 +14,18 @@ class RoutingResult {
|
||||||
/// The [Route] that answered this sub-path.
|
/// The [Route] that answered this sub-path.
|
||||||
///
|
///
|
||||||
/// This is mostly for internal use, and useless in production.
|
/// This is mostly for internal use, and useless in production.
|
||||||
final Route shallowRoute;
|
final Route<T> shallowRoute;
|
||||||
|
|
||||||
/// The [Router] that answered this sub-path.
|
/// The [Router] that answered this sub-path.
|
||||||
///
|
///
|
||||||
/// Only really for internal use.
|
/// Only really for internal use.
|
||||||
final Router shallowRouter;
|
final Router<T> shallowRouter;
|
||||||
|
|
||||||
/// The remainder of the full path that was not matched, and was passed to [nested] routes.
|
/// The remainder of the full path that was not matched, and was passed to [nested] routes.
|
||||||
final String tail;
|
final String tail;
|
||||||
|
|
||||||
/// The [RoutingResult] that matched the most specific sub-path.
|
/// The [RoutingResult] that matched the most specific sub-path.
|
||||||
RoutingResult get deepest {
|
RoutingResult<T> get deepest {
|
||||||
var search = this;
|
var search = this;
|
||||||
|
|
||||||
while (search?.nested?.isNotEmpty == true) search = search.nested.first;
|
while (search?.nested?.isNotEmpty == true) search = search.nested.first;
|
||||||
|
@ -34,21 +34,21 @@ class RoutingResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The most specific route.
|
/// The most specific route.
|
||||||
Route get route => deepest.shallowRoute;
|
Route<T> get route => deepest.shallowRoute;
|
||||||
|
|
||||||
/// The most specific router.
|
/// The most specific router.
|
||||||
Router get router => deepest.shallowRouter;
|
Router<T> get router => deepest.shallowRouter;
|
||||||
|
|
||||||
/// The handlers at this sub-path.
|
/// The handlers at this sub-path.
|
||||||
List get handlers {
|
List<T> get handlers {
|
||||||
return []..addAll(shallowRouter.middleware)..addAll(shallowRoute.handlers);
|
return <T>[]..addAll(shallowRouter.middleware)..addAll(shallowRoute.handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All handlers on this sub-path and its children.
|
/// All handlers on this sub-path and its children.
|
||||||
List get allHandlers {
|
List<T> get allHandlers {
|
||||||
final handlers = [];
|
final handlers = <T>[];
|
||||||
|
|
||||||
void crawl(RoutingResult result) {
|
void crawl(RoutingResult<T> result) {
|
||||||
handlers.addAll(result.handlers);
|
handlers.addAll(result.handlers);
|
||||||
|
|
||||||
if (result.nested?.isNotEmpty == true) {
|
if (result.nested?.isNotEmpty == true) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ part of angel_route.src.router;
|
||||||
|
|
||||||
/// Placeholder [Route] to serve as a symbolic link
|
/// Placeholder [Route] to serve as a symbolic link
|
||||||
/// to a mounted [Router].
|
/// to a mounted [Router].
|
||||||
class SymlinkRoute extends Route {
|
class SymlinkRoute<T> extends Route<T> {
|
||||||
final Router router;
|
final Router<T> router;
|
||||||
SymlinkRoute(String path, this.router) : super(path, method: null, handlers: null);
|
SymlinkRoute(String path, this.router) : super(path, method: null, handlers: null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.0-alpha
|
version: 3.0.0
|
||||||
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:
|
||||||
|
|
|
@ -2,9 +2,9 @@ import 'package:angel_route/angel_route.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
var router = new Router()
|
var router = new Router<String>()
|
||||||
..chain('a').group('/b', (router) {
|
..chain(['a']).group('/b', (router) {
|
||||||
router.chain('c').chain('d').group('/e', (router) {
|
router.chain(['c']).chain(['d']).group('/e', (router) {
|
||||||
router.get('f', 'g');
|
router.get('f', 'g');
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,7 @@ main() {
|
||||||
router.get('/first/:first/last/:last', 'GET').name = 'full_name';
|
router.get('/first/:first/last/:last', 'GET').name = 'full_name';
|
||||||
|
|
||||||
navigate(params) {
|
navigate(params) {
|
||||||
final uri = router.navigate(params);
|
final uri = router.navigate(params as Iterable);
|
||||||
print('Uri: $uri');
|
print('Uri: $uri');
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ void main() {
|
||||||
|
|
||||||
num getId(String path) {
|
num getId(String path) {
|
||||||
var result = router.resolveAbsolute(path).first;
|
var result = router.resolveAbsolute(path).first;
|
||||||
return result.allParams['id'];
|
return result.allParams['id'] as num;
|
||||||
}
|
}
|
||||||
|
|
||||||
test('parse', () {
|
test('parse', () {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:angel_route/angel_route.dart';
|
import 'package:angel_route/angel_route.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
@ -76,7 +77,7 @@ main() {
|
||||||
client = new http.Client();
|
client = new http.Client();
|
||||||
|
|
||||||
router.dumpTree();
|
router.dumpTree();
|
||||||
server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 0);
|
server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
||||||
url = 'http://${server.address.address}:${server.port}';
|
url = 'http://${server.address.address}:${server.port}';
|
||||||
|
|
||||||
server.listen((req) async {
|
server.listen((req) async {
|
||||||
|
@ -89,11 +90,11 @@ main() {
|
||||||
|
|
||||||
if (pipeline.handlers.isEmpty) {
|
if (pipeline.handlers.isEmpty) {
|
||||||
res
|
res
|
||||||
..statusCode = HttpStatus.NOT_FOUND
|
..statusCode = 404
|
||||||
..writeln('404 Not Found');
|
..writeln('404 Not Found');
|
||||||
} else {
|
} else {
|
||||||
for (final handler in pipeline.handlers) {
|
for (final handler in pipeline.handlers) {
|
||||||
if (!await handler(req, res)) break;
|
if (!((await handler(req, res)) as bool)) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue