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">
|
||||
<option name="filePath" value="$PROJECT_DIR$/test" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</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.
|
||||
* 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();
|
||||
|
||||
/// Identical to [all].
|
||||
Route on(Pattern path, handler, {List middleware});
|
||||
Route on(String path, handler, {List middleware});
|
||||
}
|
||||
|
||||
abstract class _BrowserRouterImpl extends Router implements BrowserRouter {
|
||||
|
@ -64,7 +64,7 @@ abstract class _BrowserRouterImpl extends Router implements BrowserRouter {
|
|||
@override
|
||||
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);
|
||||
|
||||
void prepareAnchors() {
|
||||
|
@ -176,7 +176,7 @@ class _PushStateRouter extends _BrowserRouterImpl {
|
|||
|
||||
void handleState(state) {
|
||||
if (state is Map && state.containsKey('path')) {
|
||||
var path = state['path'];
|
||||
var path = state['path'].toString();
|
||||
final resolved = resolveAbsolute(path).first;
|
||||
|
||||
if (resolved != null && resolved.route != _current) {
|
||||
|
|
|
@ -2,13 +2,13 @@ part of angel_route.src.router;
|
|||
|
||||
class RouteGrammar {
|
||||
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'\((.+)\)'))
|
||||
.value((r) => new RegExp(r.scanner.lastMatch[1]));
|
||||
|
||||
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));
|
||||
|
||||
static final Parser<ParameterSegment> parameterSegment = chain([
|
||||
|
@ -16,7 +16,7 @@ class RouteGrammar {
|
|||
match('?').value((r) => true).opt(),
|
||||
regExp.opt(),
|
||||
]).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;
|
||||
});
|
||||
|
||||
|
@ -26,16 +26,17 @@ class RouteGrammar {
|
|||
.map((r) => r.span.text),
|
||||
parameterSegment,
|
||||
]).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 =
|
||||
match('*').value((r) => new WildcardSegment());
|
||||
match<WildcardSegment>('*').value((r) => new WildcardSegment());
|
||||
|
||||
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,
|
||||
parameterSegment,
|
||||
wildcardSegment,
|
||||
|
@ -62,7 +63,8 @@ class RouteDefinition {
|
|||
if (out == null)
|
||||
out = s.compile(isLast);
|
||||
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;
|
||||
|
@ -88,13 +90,13 @@ class ConstantSegment extends RouteSegment {
|
|||
|
||||
@override
|
||||
Parser<Map<String, dynamic>> compile(bool isLast) {
|
||||
return match(text).value((r) => {});
|
||||
return match<Map<String, dynamic>>(text).value((r) => <String, dynamic>{});
|
||||
}
|
||||
|
||||
@override
|
||||
Parser<Map<String, dynamic>> compileNext(
|
||||
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
|
||||
Parser<Map<String, dynamic>> compileNext(
|
||||
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>> p, bool isLast) {
|
||||
return p.then(_compile().opt()).map((r) {
|
||||
if (r.value[1] == null) return r.value[0];
|
||||
return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])});
|
||||
if (r.value[1] == null) return r.value[0] as Map<String, dynamic>;
|
||||
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';
|
||||
}
|
||||
|
||||
Parser<Map<String, dynamic>> _compile() {
|
||||
Parser<String> _compile() {
|
||||
return regExp != null
|
||||
? match(regExp).value((r) => r.span.text)
|
||||
? match<String>(regExp).value((r) => r.span.text)
|
||||
: RouteGrammar.notSlash;
|
||||
}
|
||||
|
||||
@override
|
||||
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
|
||||
Parser<Map<String, dynamic>> compileNext(
|
||||
Parser<Map<String, dynamic>> p, bool isLast) {
|
||||
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>> p, bool isLast) {
|
||||
return p.then(parameter._compile()).map((r) {
|
||||
return r.value[0]
|
||||
..addAll({parameter.name: getValue(Uri.decodeComponent(r.value[1]))});
|
||||
return (r.value[0] as Map<String, dynamic>)
|
||||
..addAll({
|
||||
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import 'router.dart';
|
||||
|
||||
/// A chain of arbitrary handlers obtained by routing a path.
|
||||
class MiddlewarePipeline {
|
||||
class MiddlewarePipeline<T> {
|
||||
/// All the possible routes that matched the given path.
|
||||
final List<RoutingResult> routingResults;
|
||||
List _handlers;
|
||||
final Iterable<RoutingResult<T>> routingResults;
|
||||
List<T> _handlers;
|
||||
|
||||
/// An ordered list of every handler delegated to handle this request.
|
||||
List get handlers {
|
||||
List<T> get 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
part of angel_route.src.router;
|
||||
|
||||
/// Represents a virtual location within an application.
|
||||
class Route {
|
||||
class Route<T> {
|
||||
final String method;
|
||||
final String path;
|
||||
final List handlers;
|
||||
final List<T> handlers;
|
||||
final Map<String, Map<String, dynamic>> _cache = {};
|
||||
final RouteDefinition _routeDefinition;
|
||||
String name;
|
||||
|
@ -14,10 +14,12 @@ class Route {
|
|||
: _routeDefinition = RouteGrammar.routeDefinition
|
||||
.parse(new SpanScanner(path.replaceAll(_straySlashes, '')))
|
||||
.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 end = b.path.replaceAll(_straySlashes, '');
|
||||
return new Route('$start/$end'.replaceAll(_straySlashes, ''),
|
||||
|
@ -27,23 +29,16 @@ class Route {
|
|||
Parser<Map<String, dynamic>> get parser =>
|
||||
_parser ??= _routeDefinition.compile();
|
||||
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$method $path => $handlers';
|
||||
}
|
||||
|
||||
Route clone() {
|
||||
return new Route(path, method: method, handlers: handlers)
|
||||
Route<T> clone() {
|
||||
return new Route<T>(path, method: method, handlers: handlers)
|
||||
.._cache.addAll(_cache);
|
||||
}
|
||||
|
||||
/// Use the setter instead.
|
||||
@deprecated
|
||||
void as(String n) {
|
||||
name = n;
|
||||
}
|
||||
|
||||
String makeUri(Map<String, dynamic> params) {
|
||||
var b = new StringBuffer();
|
||||
int i = 0;
|
||||
|
|
|
@ -3,42 +3,46 @@ library angel_route.src.router;
|
|||
import 'package:combinator/combinator.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:string_scanner/string_scanner.dart';
|
||||
import 'routing_exception.dart';
|
||||
|
||||
import '../string_util.dart';
|
||||
import 'routing_exception.dart';
|
||||
|
||||
part 'grammar.dart';
|
||||
part 'symlink_route.dart';
|
||||
|
||||
part 'route.dart';
|
||||
|
||||
part 'routing_result.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'/+\$');
|
||||
part 'symlink_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'(^/+)|(/+$)');
|
||||
|
||||
/// 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 List<_ChainedRouter> _chained = [];
|
||||
final List _middleware = [];
|
||||
final Map<Pattern, Router> _mounted = {};
|
||||
final List<Route> _routes = [];
|
||||
final List<T> _middleware = [];
|
||||
final Map<Pattern, Router<T>> _mounted = {};
|
||||
final List<Route<T>> _routes = [];
|
||||
bool _useCache = false;
|
||||
|
||||
List get middleware => new List.unmodifiable(_middleware);
|
||||
List<T> get middleware => new List<T>.unmodifiable(_middleware);
|
||||
|
||||
Map<Pattern, Router> get mounted =>
|
||||
new Map<Pattern, Router>.unmodifiable(_mounted);
|
||||
|
||||
/// Additional filters to be run on designated requests.
|
||||
Map<String, dynamic> requestMiddleware = {};
|
||||
|
||||
List<Route> get routes {
|
||||
return _routes.fold<List<Route>>([], (out, route) {
|
||||
if (route is SymlinkRoute) {
|
||||
var childRoutes = route.router.routes.fold<List<Route>>([], (out, r) {
|
||||
List<Route<T>> get routes {
|
||||
return _routes.fold<List<Route<T>>>([], (out, route) {
|
||||
if (route is SymlinkRoute<T>) {
|
||||
var childRoutes =
|
||||
route.router.routes.fold<List<Route<T>>>([], (out, r) {
|
||||
return out
|
||||
..add(
|
||||
route.path.isEmpty ? r : new Route.join(route, r),
|
||||
|
@ -64,46 +68,44 @@ class Router {
|
|||
/// Adds a route that responds to the given path
|
||||
/// for requests with the given method (case-insensitive).
|
||||
/// Provide '*' as the method to respond to all methods.
|
||||
Route addRoute(String method, String path, Object handler,
|
||||
{List middleware: const []}) {
|
||||
Route<T> addRoute(String method, String path, T handler,
|
||||
{Iterable<T> middleware: const []}) {
|
||||
if (_useCache == true)
|
||||
throw new StateError('Cannot add routes after caching is enabled.');
|
||||
|
||||
// Check if any mounted routers can match this
|
||||
final handlers = [handler];
|
||||
final handlers = <T>[handler];
|
||||
|
||||
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);
|
||||
return route;
|
||||
}
|
||||
|
||||
/// Prepends the given middleware to any routes created
|
||||
/// Prepends the given [middleware] to any routes created
|
||||
/// by the resulting router.
|
||||
///
|
||||
/// [middleware] can be either an `Iterable`, or a single object.
|
||||
///
|
||||
/// The resulting router can be chained, too.
|
||||
_ChainedRouter chain(middleware) {
|
||||
var piped = new _ChainedRouter(this, middleware);
|
||||
var route = new SymlinkRoute('/', piped);
|
||||
_ChainedRouter<T> chain(Iterable<T> middleware) {
|
||||
var piped = new _ChainedRouter<T>(this, middleware);
|
||||
var route = new SymlinkRoute<T>('/', piped);
|
||||
_routes.add(route);
|
||||
return piped;
|
||||
}
|
||||
|
||||
/// Returns a [Router] with a duplicated version of this tree.
|
||||
Router clone() {
|
||||
final router = new Router();
|
||||
final newMounted = new Map<Pattern, Router>.from(mounted);
|
||||
Router<T> clone() {
|
||||
final router = new Router<T>();
|
||||
final newMounted = new Map<Pattern, Router<T>>.from(mounted);
|
||||
|
||||
for (Route route in routes) {
|
||||
if (route is! SymlinkRoute) {
|
||||
for (var route in routes) {
|
||||
if (route is! SymlinkRoute<T>) {
|
||||
router._routes.add(route.clone());
|
||||
} else if (route is SymlinkRoute) {
|
||||
} else if (route is SymlinkRoute<T>) {
|
||||
final newRouter = route.router.clone();
|
||||
newMounted[route.path] = newRouter;
|
||||
final symlink = new SymlinkRoute(route.path, newRouter);
|
||||
final symlink = new SymlinkRoute<T>(route.path, newRouter);
|
||||
router._routes.add(symlink);
|
||||
}
|
||||
}
|
||||
|
@ -161,13 +163,11 @@ class Router {
|
|||
///
|
||||
/// Returns the created route.
|
||||
/// You can also register middleware within the router.
|
||||
SymlinkRoute group(String path, void callback(Router router),
|
||||
{Iterable middleware: const [],
|
||||
String name: null,
|
||||
String namespace: null}) {
|
||||
final router = new Router().._middleware.addAll(middleware);
|
||||
SymlinkRoute<T> group(String path, void callback(Router router),
|
||||
{Iterable<T> middleware: const [], String name: null}) {
|
||||
final router = new Router<T>().._middleware.addAll(middleware);
|
||||
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.
|
||||
|
@ -265,12 +265,6 @@ class Router {
|
|||
: segments.join('/');
|
||||
}
|
||||
|
||||
/// Manually assign via [requestMiddleware] instead.
|
||||
@deprecated
|
||||
registerMiddleware(String name, middleware) {
|
||||
requestMiddleware[name] = middleware;
|
||||
}
|
||||
|
||||
/// Finds the first [Route] that matches the given path,
|
||||
/// with the given method.
|
||||
bool resolve(String absolute, String relative, List<RoutingResult> out,
|
||||
|
@ -344,122 +338,97 @@ class Router {
|
|||
}
|
||||
|
||||
/// Incorporates another [Router]'s routes into this one's.
|
||||
///
|
||||
/// If `hooked` is set to `true` and a [Service] is provided,
|
||||
/// 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);
|
||||
SymlinkRoute<T> mount(String path, Router<T> router) {
|
||||
final route = new SymlinkRoute<T>(path, router);
|
||||
_mounted[route.path] = router;
|
||||
_routes.add(route);
|
||||
//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.
|
||||
Route all(String path, Object handler, {List middleware}) {
|
||||
Route<T> all(String path, T handler, {Iterable<T> middleware}) {
|
||||
return addRoute('*', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChainedRouter extends Router {
|
||||
final List _handlers = [];
|
||||
class _ChainedRouter<T> extends Router<T> {
|
||||
final List<T> _handlers = <T>[];
|
||||
Router _root;
|
||||
|
||||
_ChainedRouter.empty();
|
||||
|
||||
_ChainedRouter(Router root, middleware) {
|
||||
_ChainedRouter(Router root, Iterable<T> middleware) {
|
||||
this._root = root;
|
||||
_handlers.addAll(middleware is Iterable ? middleware : [middleware]);
|
||||
_handlers.addAll(middleware);
|
||||
}
|
||||
|
||||
@override
|
||||
Route addRoute(String method, String path, handler,
|
||||
{List middleware: const []}) {
|
||||
Route<T> addRoute(String method, String path, handler,
|
||||
{Iterable<T> middleware: const []}) {
|
||||
var route = super.addRoute(method, path, handler,
|
||||
middleware: []..addAll(_handlers)..addAll(middleware ?? []));
|
||||
//_root._routes.add(route);
|
||||
return route;
|
||||
}
|
||||
|
||||
SymlinkRoute group(String path, void callback(Router router),
|
||||
{Iterable middleware: const [],
|
||||
String name: null,
|
||||
String namespace: null}) {
|
||||
SymlinkRoute<T> group(String path, void callback(Router<T> router),
|
||||
{Iterable<T> middleware: const [], String name: null}) {
|
||||
final router =
|
||||
new _ChainedRouter(_root, []..addAll(_handlers)..addAll(middleware));
|
||||
new _ChainedRouter<T>(_root, []..addAll(_handlers)..addAll(middleware));
|
||||
callback(router);
|
||||
return mount(path, router, namespace: namespace)..name = name;
|
||||
return mount(path, router)..name = name;
|
||||
}
|
||||
|
||||
@override
|
||||
SymlinkRoute mount(String path, Router router,
|
||||
{bool hooked: true, String namespace: null}) {
|
||||
final route =
|
||||
super.mount(path, router, hooked: hooked, namespace: namespace);
|
||||
SymlinkRoute<T> mount(String path, Router<T> router) {
|
||||
final route = super.mount(path, router);
|
||||
route.router._middleware.insertAll(0, _handlers);
|
||||
//_root._routes.add(route);
|
||||
return route;
|
||||
}
|
||||
|
||||
@override
|
||||
_ChainedRouter chain(middleware) {
|
||||
final piped = new _ChainedRouter.empty().._root = _root;
|
||||
piped._handlers.addAll([]
|
||||
..addAll(_handlers)
|
||||
..addAll(middleware is Iterable ? middleware : [middleware]));
|
||||
var route = new SymlinkRoute('/', piped);
|
||||
_ChainedRouter<T> chain(Iterable<T> middleware) {
|
||||
final piped = new _ChainedRouter<T>.empty().._root = _root;
|
||||
piped._handlers.addAll([]..addAll(_handlers)..addAll(middleware));
|
||||
var route = new SymlinkRoute<T>('/', piped);
|
||||
_routes.add(route);
|
||||
return piped;
|
||||
}
|
||||
|
@ -467,14 +436,12 @@ class _ChainedRouter extends Router {
|
|||
|
||||
/// Optimizes a router by condensing all its routes into one level.
|
||||
Router flatten(Router router) {
|
||||
var flattened = new Router()
|
||||
..requestMiddleware.addAll(router.requestMiddleware);
|
||||
var flattened = new Router();
|
||||
|
||||
for (var route in router.routes) {
|
||||
if (route is SymlinkRoute) {
|
||||
var base = route.path.replaceAll(_straySlashes, '');
|
||||
var child = flatten(route.router);
|
||||
flattened.requestMiddleware.addAll(child.requestMiddleware);
|
||||
|
||||
for (var route in child.routes) {
|
||||
var path = route.path.replaceAll(_straySlashes, '');
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
part of angel_route.src.router;
|
||||
|
||||
/// Represents a complex result of navigating to a path.
|
||||
class RoutingResult {
|
||||
class RoutingResult<T> {
|
||||
/// The parse result that matched the given sub-path.
|
||||
final ParseResult<Map<String, dynamic>> parseResult;
|
||||
|
||||
/// 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.
|
||||
final Map<String, dynamic> params = {};
|
||||
|
@ -14,18 +14,18 @@ class RoutingResult {
|
|||
/// The [Route] that answered this sub-path.
|
||||
///
|
||||
/// This is mostly for internal use, and useless in production.
|
||||
final Route shallowRoute;
|
||||
final Route<T> shallowRoute;
|
||||
|
||||
/// The [Router] that answered this sub-path.
|
||||
///
|
||||
/// 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.
|
||||
final String tail;
|
||||
|
||||
/// The [RoutingResult] that matched the most specific sub-path.
|
||||
RoutingResult get deepest {
|
||||
RoutingResult<T> get deepest {
|
||||
var search = this;
|
||||
|
||||
while (search?.nested?.isNotEmpty == true) search = search.nested.first;
|
||||
|
@ -34,21 +34,21 @@ class RoutingResult {
|
|||
}
|
||||
|
||||
/// The most specific route.
|
||||
Route get route => deepest.shallowRoute;
|
||||
Route<T> get route => deepest.shallowRoute;
|
||||
|
||||
/// The most specific router.
|
||||
Router get router => deepest.shallowRouter;
|
||||
Router<T> get router => deepest.shallowRouter;
|
||||
|
||||
/// The handlers at this sub-path.
|
||||
List get handlers {
|
||||
return []..addAll(shallowRouter.middleware)..addAll(shallowRoute.handlers);
|
||||
List<T> get handlers {
|
||||
return <T>[]..addAll(shallowRouter.middleware)..addAll(shallowRoute.handlers);
|
||||
}
|
||||
|
||||
/// All handlers on this sub-path and its children.
|
||||
List get allHandlers {
|
||||
final handlers = [];
|
||||
List<T> get allHandlers {
|
||||
final handlers = <T>[];
|
||||
|
||||
void crawl(RoutingResult result) {
|
||||
void crawl(RoutingResult<T> result) {
|
||||
handlers.addAll(result.handlers);
|
||||
|
||||
if (result.nested?.isNotEmpty == true) {
|
||||
|
|
|
@ -2,7 +2,7 @@ part of angel_route.src.router;
|
|||
|
||||
/// Placeholder [Route] to serve as a symbolic link
|
||||
/// to a mounted [Router].
|
||||
class SymlinkRoute extends Route {
|
||||
final Router router;
|
||||
class SymlinkRoute<T> extends Route<T> {
|
||||
final Router<T> router;
|
||||
SymlinkRoute(String path, this.router) : super(path, method: null, handlers: null);
|
||||
}
|
||||
|
|
|
@ -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.0-alpha
|
||||
version: 3.0.0
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_route
|
||||
environment:
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'package:angel_route/angel_route.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
var router = new Router()
|
||||
..chain('a').group('/b', (router) {
|
||||
router.chain('c').chain('d').group('/e', (router) {
|
||||
var router = new Router<String>()
|
||||
..chain(['a']).group('/b', (router) {
|
||||
router.chain(['c']).chain(['d']).group('/e', (router) {
|
||||
router.get('f', 'g');
|
||||
});
|
||||
})
|
||||
|
|
|
@ -9,7 +9,7 @@ main() {
|
|||
router.get('/first/:first/last/:last', 'GET').name = 'full_name';
|
||||
|
||||
navigate(params) {
|
||||
final uri = router.navigate(params);
|
||||
final uri = router.navigate(params as Iterable);
|
||||
print('Uri: $uri');
|
||||
return uri;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ void main() {
|
|||
|
||||
num getId(String path) {
|
||||
var result = router.resolveAbsolute(path).first;
|
||||
return result.allParams['id'];
|
||||
return result.allParams['id'] as num;
|
||||
}
|
||||
|
||||
test('parse', () {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:angel_route/angel_route.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:test/test.dart';
|
||||
|
@ -76,7 +77,7 @@ main() {
|
|||
client = new http.Client();
|
||||
|
||||
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}';
|
||||
|
||||
server.listen((req) async {
|
||||
|
@ -89,11 +90,11 @@ main() {
|
|||
|
||||
if (pipeline.handlers.isEmpty) {
|
||||
res
|
||||
..statusCode = HttpStatus.NOT_FOUND
|
||||
..statusCode = 404
|
||||
..writeln('404 Not Found');
|
||||
} else {
|
||||
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