diff --git a/.idea/angel_route.iml b/.idea/angel_route.iml index eae13016..954fa6c5 100644 --- a/.idea/angel_route.iml +++ b/.idea/angel_route.iml @@ -2,6 +2,7 @@ + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..932dd204 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# 3.0.0-alpha +* Make `Router` and `Route` single-parameter generic. +* Remove `package:browser` dependency. \ No newline at end of file diff --git a/README.md b/README.md index cc511f49..259aeb99 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,13 @@ main() { return { 'result': pow(int.parse(n), 0.5) }; }); + // You can also have parameters auto-parsed. + // + // Supports int, double, and num. + router.get('/square_root/int:id([0-9]+)', (int n) { + return { 'result': pow(n, 0.5) }; + }); + router.group('/show/:id', (router) { router.get('/reviews', (id) { return someQuery(id).reviews; diff --git a/analysis_options.yaml b/analysis_options.yaml index 518eb901..eae1e42a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,2 +1,3 @@ analyzer: - strong-mode: true \ No newline at end of file + strong-mode: + implicit-casts: false \ No newline at end of file diff --git a/lib/src/grammar.dart b/lib/src/grammar.dart index 6c061e00..870264a4 100644 --- a/lib/src/grammar.dart +++ b/lib/src/grammar.dart @@ -3,7 +3,10 @@ part of angel_route.src.router; class RouteGrammar { static final Parser notSlash = match(new RegExp(r'[^/]+')).value((r) => r.span.text); - static final Parser regExp = new _RegExpParser(); + + static final Parser regExp = match(new RegExp(r'\((.+)\)')) + .value((r) => new RegExp(r.scanner.lastMatch[1])); + static final Parser parameterName = match(new RegExp(r':([A-Za-z0-9_]+)')) .value((r) => r.span.text.substring(1)); @@ -17,14 +20,27 @@ class RouteGrammar { return r.value[1] == true ? new OptionalSegment(s) : s; }); + static final Parser parsedParameterSegment = chain([ + match(new RegExp(r'(int|num|double)'), + errorMessage: 'Expected "int","double", or "num".') + .map((r) => r.span.text), + parameterSegment, + ]).map((r) { + return new ParsedParameterSegment(r.value[0], r.value[1]); + }); + static final Parser wildcardSegment = match('*').value((r) => new WildcardSegment()); static final Parser constantSegment = notSlash.map((r) => new ConstantSegment(r.value)); - static final Parser routeSegment = - any([parameterSegment, wildcardSegment, constantSegment]); + static final Parser routeSegment = any([ + parsedParameterSegment, + parameterSegment, + wildcardSegment, + constantSegment + ]); static final Parser routeDefinition = routeSegment .separatedBy(match('/')) @@ -32,17 +48,6 @@ class RouteGrammar { .surroundedBy(match('/').star().opt()); } -class _RegExpParser extends Parser { - static final RegExp rgx = new RegExp(r'\((.+)\)'); - - @override - ParseResult parse(SpanScanner scanner, [int depth = 1]) { - if (!scanner.matches(rgx)) return new ParseResult(this, false, []); - return new ParseResult(this, true, [], - span: scanner.lastSpan, value: new RegExp(scanner.lastMatch[1])); - } -} - class RouteDefinition { final List segments; @@ -66,6 +71,7 @@ class RouteDefinition { abstract class RouteSegment { Parser> compile(bool isLast); + Parser> compileNext( Parser> p, bool isLast); } @@ -171,3 +177,36 @@ class ParameterSegment extends RouteSegment { }); } } + +class ParsedParameterSegment extends RouteSegment { + final String type; + final ParameterSegment parameter; + + ParsedParameterSegment(this.type, this.parameter); + + num getValue(String s) { + switch (type) { + case 'int': + return int.parse(s); + case 'double': + return double.parse(s); + default: + return num.parse(s); + } + } + + @override + Parser> compile(bool isLast) { + return parameter._compile().map( + (r) => {parameter.name: getValue(Uri.decodeComponent(r.span.text))}); + } + + @override + Parser> compileNext( + Parser> p, bool isLast) { + return p.then(parameter._compile()).map((r) { + return r.value[0] + ..addAll({parameter.name: getValue(Uri.decodeComponent(r.value[1]))}); + }); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ef317b53..0a2e9fb0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,13 +1,14 @@ name: angel_route -description: A powerful, isomorphic routing library for Dart. -version: 2.0.4 +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 author: Tobe O homepage: https://github.com/angel-dart/angel_route environment: - sdk: ">=1.19.0" + sdk: ">=2.0.0-dev <3.0.0" dependencies: - combinator: ^1.0.0-beta + combinator: ^1.0.0 dev_dependencies: - browser: ">=0.10.0 < 0.11.0" + build_runner: ^0.10.0 + build_web_compilers: ^0.4.0 http: ">=0.11.3 <0.12.0" - test: ">=0.12.15 <0.13.0" \ No newline at end of file + test: ^1.0.0 \ No newline at end of file diff --git a/test/parse_test.dart b/test/parse_test.dart new file mode 100644 index 00000000..ab90526b --- /dev/null +++ b/test/parse_test.dart @@ -0,0 +1,20 @@ +import 'package:angel_route/angel_route.dart'; +import 'package:test/test.dart'; + +void main() { + var router = new Router() + ..get('/int/int:id', '') + ..get('/double/double:id', '') + ..get('/num/num:id', ''); + + num getId(String path) { + var result = router.resolveAbsolute(path).first; + return result.allParams['id']; + } + + test('parse', () { + expect(getId('/int/2'), 2); + expect(getId('/double/2.0'), 2.0); + expect(getId('/num/-2.4'), -2.4); + }); +} diff --git a/test/server_test.dart b/test/server_test.dart index b7e6823c..d926ffee 100644 --- a/test/server_test.dart +++ b/test/server_test.dart @@ -27,7 +27,7 @@ main() { router.group('/people', (router) { router.get('/', (req, res) { - res.write(JSON.encode(people)); + res.write(json.encode(people)); return false; }); @@ -35,14 +35,14 @@ main() { router.get('/', (req, res) { // In a real application, we would take the param, // but not here... - res.write(JSON.encode(people.first)); + res.write(json.encode(people.first)); return false; }); router.get('/name', (req, res) { // In a real application, we would take the param, // but not here... - res.write(JSON.encode(people.first['name'])); + res.write(json.encode(people.first['name'])); return false; }); }); @@ -129,20 +129,20 @@ main() { test('root', () async { final res = await client.get('$url/people'); print('Response: ${res.body}'); - expect(JSON.decode(res.body), equals(people)); + expect(json.decode(res.body), equals(people)); }); group('param', () { test('root', () async { final res = await client.get('$url/people/0'); print('Response: ${res.body}'); - expect(JSON.decode(res.body), equals(people.first)); + expect(json.decode(res.body), equals(people.first)); }); test('path', () async { final res = await client.get('$url/people/0/name'); print('Response: ${res.body}'); - expect(JSON.decode(res.body), equals(people.first['name'])); + expect(json.decode(res.body), equals(people.first['name'])); }); }); });