From ab7240d6dfaa3f8b97a5f1d05c9802505d7beeb0 Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Thu, 7 Dec 2023 16:21:21 -0500 Subject: [PATCH 1/6] upgrade dart sdk --- pubspec.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 262ad86..fc6adac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,11 +4,12 @@ version: 0.2.2 homepage: https://github.com/ergonlabs/liquid_dart environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=3.0.0 <=3.2.0' dependencies: string_scanner: ^1.1.0 + more: 4.1.0 dev_dependencies: - pedantic: ^1.11.0 + lints: ^3.0.0 test: ^1.17.7 From b14d754eb1b9147e2925c442093269f398270a12 Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Thu, 7 Dec 2023 16:21:58 -0500 Subject: [PATCH 2/6] replace pedantic with lints/core --- analysis_options.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index a686c1b..d73ee2a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,7 +1,6 @@ -# Defines a default set of lint rules enforced for -# projects at Google. For details and rationale, -# see https://github.com/dart-lang/pedantic#enabled-lints. -include: package:pedantic/analysis_options.yaml + +# see https://github.com/dart-lang/lints. +include: package:lints/core.yaml # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. # Uncomment to specify additional rules. From 3aef82c9cf81fc012e675eabcb205560f25c1e55 Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Thu, 7 Dec 2023 16:24:17 -0500 Subject: [PATCH 3/6] add peekNext for tokens --- lib/src/parser/tag_parser.dart | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/src/parser/tag_parser.dart b/lib/src/parser/tag_parser.dart index 3f62d1e..45ca48a 100644 --- a/lib/src/parser/tag_parser.dart +++ b/lib/src/parser/tag_parser.dart @@ -7,16 +7,28 @@ import '../model.dart'; class TagParser { final Iterator tokens; - TagParser.from(List tokens) : this.fromIterator(tokens.followedBy([Token.eof]).iterator..moveNext()); + int index = 0; - TagParser.fromIterator(this.tokens); + List _originalTokens = []; + + TagParser.from(List tokens) : this.fromIterator(tokens.followedBy([Token.eof]).iterator..moveNext(), tokens); + + TagParser.fromIterator(this.tokens, [this._originalTokens = const []]); Token get current => tokens.current; + Token? peekNext() { + if (index >= _originalTokens.length) { + return null; + } + return _originalTokens.elementAt(index + 1); + } + bool moveNext() { if (current == Token.eof) { return true; } + index += 1; return tokens.moveNext(); } @@ -98,7 +110,7 @@ class TagParser { Expression parseSingleTokenExpression() { expect(types: [TokenType.identifier, TokenType.single_string, TokenType.double_string, TokenType.number]); final name = tokens.current; - tokens.moveNext(); + moveNext(); if (name.type == TokenType.identifier) { if (name.value == 'true') { @@ -123,10 +135,10 @@ class TagParser { var exp = parseSingleTokenExpression(); while (tokens.current.type == TokenType.dot) { - tokens.moveNext(); + moveNext(); exp = MemberExpression(exp, tokens.current); - tokens.moveNext(); + moveNext(); } return exp; @@ -143,17 +155,17 @@ class TagParser { Expression parseFilters(Expression exp) { while (tokens.current.type == TokenType.pipe) { - tokens.moveNext(); + moveNext(); expect(types: [TokenType.identifier]); final name = tokens.current; final arguments = []; - if (tokens.moveNext() && tokens.current.type == TokenType.colon) { - tokens.moveNext(); + if (moveNext() && tokens.current.type == TokenType.colon) { + moveNext(); do { arguments.add(parseMemberExpression()); - } while (tokens.current.type == TokenType.comma && tokens.moveNext()); + } while (tokens.current.type == TokenType.comma && moveNext()); } exp = FilterExpression(exp, name, arguments); From e4fd9dd3ad91f96cbc6faa5f01c6082dd7b573f2 Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Thu, 7 Dec 2023 16:24:47 -0500 Subject: [PATCH 4/6] render tag --- lib/src/buildin_tags/render.dart | 99 ++++++++++++++++++++++++++++++++ lib/src/builtins.dart | 2 + lib/src/parser/parser.dart | 15 +++-- test/liquid_test.dart | 31 ++++++++-- 4 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 lib/src/buildin_tags/render.dart diff --git a/lib/src/buildin_tags/render.dart b/lib/src/buildin_tags/render.dart new file mode 100644 index 0000000..1ad4d45 --- /dev/null +++ b/lib/src/buildin_tags/render.dart @@ -0,0 +1,99 @@ +import '../block.dart'; +import '../context.dart'; +import '../document.dart'; +import '../expressions.dart'; +import '../model.dart'; +import '../parser/parser.dart'; +import '../parser/tag_parser.dart'; +import '../tag.dart'; + +class Render extends Block { + final List<_Assign> assignments; + final DocumentFuture childBuilder; + + Render._(this.assignments, this.childBuilder) : super([]); + + @override + Stream render(RenderContext context) async* { + var innerContext = context; + innerContext = innerContext.clone(); + innerContext.variables.clear(); + + innerContext = innerContext.push({for (var a in assignments) a.to: await a.from.evaluate(context)}); + + yield* (await childBuilder.resolve(innerContext)).render(innerContext); + } + + static BlockParserFactory factory = () => _RenderBlockParser(); +} + +class _Assign { + final String to; + final Expression from; + + _Assign(this.to, this.from); +} + +class _RenderBlockParser extends BlockParser { + @override + bool get hasEndTag => false; + + @override + Block create(List tokens, List children) { + final parser = TagParser.from(tokens); + final childBuilder = parser.parseDocumentReference(context); + + var assignments = <_Assign>[]; + + if (parser.current.value == ",") { + parser.moveNext(); + + while (parser.current.type == TokenType.identifier) { + final to = parser.current; + + if (parser.peekNext() != null && parser.peekNext()!.value == ",") { + assignments.add(_Assign(to.value, parser.parseSingleTokenExpression())); + parser.moveNext(); + continue; + } + + if (parser.current.value == ",") { + parser.moveNext(); + } + + if (parser.peekNext() != null) { + if (parser.peekNext()!.value == ":") { + parser.moveNext(); + parser.moveNext(); + parser.expect(types: [TokenType.identifier, TokenType.single_string, TokenType.double_string, TokenType.number]); + assignments.add(_Assign(to.value, parser.parseSingleTokenExpression())); + } + } + parser.moveNext(); + } + } + + if (parser.current.value == 'with') { + while (parser.current.type == TokenType.identifier) { + + parser.expect(types: [TokenType.identifier]); + + if (parser.peekNext() != null) { + parser.moveNext(); + final to = parser.parseSingleTokenExpression(); + if (parser.current.value == 'as') { + parser.moveNext(); + parser.expect(types: [TokenType.identifier]); + assignments.add(_Assign(parser.current.value, to )); + } + } + parser.moveNext(); + } + } + + return Render._(assignments, childBuilder); + } + + @override + void unexpectedTag(Parser parser, Token start, List args, List childrenSoFar) {} +} diff --git a/lib/src/builtins.dart b/lib/src/builtins.dart index a863f6e..e8be6cd 100644 --- a/lib/src/builtins.dart +++ b/lib/src/builtins.dart @@ -9,6 +9,7 @@ import 'buildin_tags/for.dart'; import 'buildin_tags/if.dart'; import 'buildin_tags/ifchanged.dart'; import 'buildin_tags/include.dart'; +import 'buildin_tags/render.dart'; import 'buildin_tags/load.dart'; import 'buildin_tags/named_block.dart'; import 'buildin_tags/regroup.dart'; @@ -27,6 +28,7 @@ class BuiltinsModule implements Module { context.tags['if'] = If.factory; context.tags['unless'] = If.unlessFactory; context.tags['include'] = Include.factory; + context.tags['render'] = Render.factory; context.tags['filter'] = BlockParser.simple(FilterBlock.factory); context.tags['load'] = BlockParser.simple(Load.factory, hasEndTag: false); context.tags['block'] = BlockParser.simple(NamedBlock.factory); diff --git a/lib/src/parser/parser.dart b/lib/src/parser/parser.dart index 772c69b..2c8280d 100644 --- a/lib/src/parser/parser.dart +++ b/lib/src/parser/parser.dart @@ -1,9 +1,8 @@ -import '../exception/parse_block_exception.dart'; - import '../block.dart'; import '../context.dart'; import '../document.dart'; import '../errors.dart'; +import '../exception/parse_block_exception.dart'; import '../expressions.dart'; import '../model.dart'; import '../tag.dart'; @@ -65,9 +64,15 @@ class Parser { } Token? asTarget; - if (args.length >= 2 && args[args.length - 1].type == TokenType.identifier && args[args.length - 2].value == 'as') { - asTarget = args.last; - args.length = args.length - 2; + if (args.where((element) => element.value == 'as').isNotEmpty) { + if (args.where((element) => ["with", "from"].contains(element.value)).isEmpty) { + if (args.length > 2 && args[args.length - 1].type == TokenType.identifier) { + if (args[args.length - 2].value == 'as') { + asTarget = args.last; + args.length = args.length - 2; + } + } + } } if (context.tags.containsKey(start.value)) { diff --git a/test/liquid_test.dart b/test/liquid_test.dart index eca2b00..4e569c6 100644 --- a/test/liquid_test.dart +++ b/test/liquid_test.dart @@ -1,9 +1,5 @@ import 'package:liquid_engine/liquid_engine.dart'; -import 'package:liquid_engine/src/context.dart'; -import 'package:liquid_engine/src/errors.dart'; import 'package:liquid_engine/src/exception/parse_block_exception.dart'; -import 'package:liquid_engine/src/model.dart'; -import 'package:liquid_engine/src/template.dart'; import 'package:test/test.dart'; void main() { @@ -217,6 +213,33 @@ void main() { expect(await template.render(context), equals('Hi, friend!')); }); + + test('render', () async { + final context = Context.create(); + final root = TestRoot({ + 'render': '{% render "name.html" %}', + 'render_with_parameters': '{% render "name_age.html", first_name, last_name, age:1 %}', + 'render_with_with': '{% render "name.html" with first_name as name %}', + + 'name_age.html': '{{ first_name }} {{ last_name }}! {{age}}', + 'name.html': '{{ name }}!', + }); + + context.variables['first_name'] = 'John'; + context.variables['last_name'] = 'John'; + context.variables['greeting'] = 'Hello'; + + var template = Template.parse(context, await root.resolve('render')); + expect(await template.render(context), equals('!')); + + template = Template.parse(context, await root.resolve('render_wifth_parameters')); + expect(await template.render(context), equals('John John! 1')); + + template = Template.parse(context, await root.resolve('render_with_with')); + expect(await template.render(context), equals('John!')); + + }); + test('extends', () async { final context = Context.create(); final root = TestRoot({ From 6d0d185831cc522bb023411fda45e5cd63fce9b3 Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Fri, 8 Dec 2023 14:13:54 -0500 Subject: [PATCH 5/6] Render: add for loop support --- lib/src/buildin_tags/render.dart | 32 +++++++++++++++++++++++--------- lib/src/parser/parser.dart | 2 +- test/liquid_test.dart | 10 ++++++---- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/src/buildin_tags/render.dart b/lib/src/buildin_tags/render.dart index 1ad4d45..b3ca843 100644 --- a/lib/src/buildin_tags/render.dart +++ b/lib/src/buildin_tags/render.dart @@ -6,22 +6,34 @@ import '../model.dart'; import '../parser/parser.dart'; import '../parser/tag_parser.dart'; import '../tag.dart'; +import './for.dart'; class Render extends Block { final List<_Assign> assignments; final DocumentFuture childBuilder; + bool isLoop; - Render._(this.assignments, this.childBuilder) : super([]); + Render._(this.assignments, this.childBuilder, [this.isLoop = false]) : super([]); @override Stream render(RenderContext context) async* { - var innerContext = context; - innerContext = innerContext.clone(); + var innerContext = context.cloneAsRoot(); innerContext.variables.clear(); - innerContext = innerContext.push({for (var a in assignments) a.to: await a.from.evaluate(context)}); + if (isLoop) { + var collection = (assignments[0].from as LookupExpression).name.value; + var collectionValue = context.variables[collection]; - yield* (await childBuilder.resolve(innerContext)).render(innerContext); + innerContext.variables.addAll({collection: collectionValue}); + + For forBlock = For(assignments[0].to, assignments[0].from, [ + await childBuilder.resolve(innerContext), + ], []); + yield* forBlock.render(innerContext); + } else { + innerContext = innerContext.push({for (var a in assignments) a.to: await a.from.evaluate(context)}); + yield* (await childBuilder.resolve(innerContext)).render(innerContext); + } } static BlockParserFactory factory = () => _RenderBlockParser(); @@ -40,6 +52,7 @@ class _RenderBlockParser extends BlockParser { @override Block create(List tokens, List children) { + bool hasFor = false; final parser = TagParser.from(tokens); final childBuilder = parser.parseDocumentReference(context); @@ -73,9 +86,10 @@ class _RenderBlockParser extends BlockParser { } } - if (parser.current.value == 'with') { - while (parser.current.type == TokenType.identifier) { + if (['with', 'for'].contains(parser.current.value)) { + hasFor = parser.current.value == "for"; + while (parser.current.type == TokenType.identifier) { parser.expect(types: [TokenType.identifier]); if (parser.peekNext() != null) { @@ -84,14 +98,14 @@ class _RenderBlockParser extends BlockParser { if (parser.current.value == 'as') { parser.moveNext(); parser.expect(types: [TokenType.identifier]); - assignments.add(_Assign(parser.current.value, to )); + assignments.add(_Assign(parser.current.value, to)); } } parser.moveNext(); } } - return Render._(assignments, childBuilder); + return Render._(assignments, childBuilder, hasFor); } @override diff --git a/lib/src/parser/parser.dart b/lib/src/parser/parser.dart index 2c8280d..62f2b58 100644 --- a/lib/src/parser/parser.dart +++ b/lib/src/parser/parser.dart @@ -65,7 +65,7 @@ class Parser { Token? asTarget; if (args.where((element) => element.value == 'as').isNotEmpty) { - if (args.where((element) => ["with", "from"].contains(element.value)).isEmpty) { + if (args.where((element) => ["with", "for"].contains(element.value)).isEmpty) { if (args.length > 2 && args[args.length - 1].type == TokenType.identifier) { if (args[args.length - 2].value == 'as') { asTarget = args.last; diff --git a/test/liquid_test.dart b/test/liquid_test.dart index 4e569c6..098efcd 100644 --- a/test/liquid_test.dart +++ b/test/liquid_test.dart @@ -213,14 +213,13 @@ void main() { expect(await template.render(context), equals('Hi, friend!')); }); - test('render', () async { final context = Context.create(); final root = TestRoot({ 'render': '{% render "name.html" %}', 'render_with_parameters': '{% render "name_age.html", first_name, last_name, age:1 %}', 'render_with_with': '{% render "name.html" with first_name as name %}', - + 'render_with_for': '{% render "name.html" for names as name %}', 'name_age.html': '{{ first_name }} {{ last_name }}! {{age}}', 'name.html': '{{ name }}!', }); @@ -228,16 +227,19 @@ void main() { context.variables['first_name'] = 'John'; context.variables['last_name'] = 'John'; context.variables['greeting'] = 'Hello'; + context.variables['names'] = ['john', 'mark', 'mary']; var template = Template.parse(context, await root.resolve('render')); expect(await template.render(context), equals('!')); - template = Template.parse(context, await root.resolve('render_wifth_parameters')); + template = Template.parse(context, await root.resolve('render_with_parameters')); expect(await template.render(context), equals('John John! 1')); - template = Template.parse(context, await root.resolve('render_with_with')); + template = Template.parse(context, await root.resolve('render_with_with')); expect(await template.render(context), equals('John!')); + template = Template.parse(context, await root.resolve('render_with_for')); + expect(await template.render(context), equals(['john', 'mark', 'mary'].map((e) => '$e!').join(''))); }); test('extends', () async { From 6378f9746b08074423c30278ecd4569ff66ff76e Mon Sep 17 00:00:00 2001 From: kingwill101 Date: Sat, 9 Dec 2023 08:39:52 -0500 Subject: [PATCH 6/6] Drop support --- lib/src/drop.dart | 43 ++++++++++++++++++++++++ lib/src/expressions.dart | 13 ++++++++ pubspec.yaml | 5 +-- test/drop_test.dart | 70 ++++++++++++++++++++++++++++++++++++++++ test/liquid_test.dart | 54 ++++++++++++++++++------------- test/shared.dart | 20 ++++++++++++ 6 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 lib/src/drop.dart create mode 100644 test/drop_test.dart create mode 100644 test/shared.dart diff --git a/lib/src/drop.dart b/lib/src/drop.dart new file mode 100644 index 0000000..382c08e --- /dev/null +++ b/lib/src/drop.dart @@ -0,0 +1,43 @@ +import 'package:gato/gato.dart' as gato; +import 'package:liquid_engine/liquid_engine.dart'; + +extension SymbolExtension on Symbol { + String get name { + var _name = toString(); + return toString().replaceRange(_name.length - 2, _name.length, '').replaceAll("Symbol(\"", ''); + } +} + +abstract class Drop { + List invokable = []; + Map attrs = {}; + + RenderContext? context = null; + + dynamic liquidMethodMissing(Symbol method) => null; + + dynamic call(Symbol attr) { + if (get(attr.name) != null) return get(attr.name); + if (invokable.isNotEmpty && invokable.contains(attr)) { + return invoke(attr); + } + + return liquidMethodMissing(attr); + } + + dynamic operator [](String path) { + return get(path); + } + + dynamic invoke(Symbol symbol) { + return null; + } + + dynamic exec(Symbol method) { + return this(method); + } + + dynamic get(String path) { + return gato.get(attrs, path); + } +} diff --git a/lib/src/expressions.dart b/lib/src/expressions.dart index 6105f23..3d65b25 100644 --- a/lib/src/expressions.dart +++ b/lib/src/expressions.dart @@ -3,11 +3,13 @@ import 'dart:async'; import './errors.dart'; import 'block.dart'; import 'context.dart'; +import 'drop.dart'; import 'model.dart'; import 'tag.dart'; abstract class Expression { Future evaluate(RenderContext context); + @override String toString() { return 'Expression{}'; @@ -54,6 +56,7 @@ class NotExpression extends BooleanCastExpression { @override Future evaluate(RenderContext context) async => !(await super.evaluate(context)); + @override String toString() { return 'NotExpression{}'; @@ -69,6 +72,7 @@ class BinaryOperation implements Expression { @override Future evaluate(RenderContext context) async => operation(await left.evaluate(context), await right.evaluate(context)); + @override String toString() { return 'BinaryOperation{operation: $operation, left: $left, right: $right}'; @@ -92,6 +96,7 @@ class ConstantExpression implements Expression { @override Future evaluate(RenderContext context) async => value; + @override String toString() { return 'ConstantExpression{value: $value}'; @@ -126,6 +131,12 @@ class MemberExpression implements Expression { if (base == null) { return null; } + + if (base is Drop) { + base.context = context; + return base(Symbol(member.value)); + } + if (base is Map) { return base[member.value]; } @@ -161,6 +172,7 @@ class FilterExpression implements Expression { } Future _evaluateToFuture(Expression e, RenderContext context) async => await e.evaluate(context); + @override String toString() { return 'FilterExpression{input: $input, name: $name, arguments: $arguments}'; @@ -176,6 +188,7 @@ class BlockExpression implements Expression { @override Future evaluate(RenderContext context) => block.render(context).join(''); + @override String toString() { return 'BlockExpression{block: $block}'; diff --git a/pubspec.yaml b/pubspec.yaml index 262ad86..5af2e0f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,11 +4,12 @@ version: 0.2.2 homepage: https://github.com/ergonlabs/liquid_dart environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=3.0.0 <=3.2.0' dependencies: string_scanner: ^1.1.0 + gato: ^0.0.5+1 dev_dependencies: - pedantic: ^1.11.0 + lints: ^3.0.0 test: ^1.17.7 diff --git a/test/drop_test.dart b/test/drop_test.dart new file mode 100644 index 0000000..f483dad --- /dev/null +++ b/test/drop_test.dart @@ -0,0 +1,70 @@ +import 'package:liquid_engine/liquid_engine.dart'; +import 'package:liquid_engine/src/drop.dart'; +import 'package:test/test.dart'; + +void main() { + group('drop', () { + test('properties', () async { + var context = Context.create().push({"name": PersonDrop(firstName: "John", lastName: "Jones")}); + expect(await Template.parse(context, Source(null, '{{ name.firstName }}', null)).render(context), equals('John')); + expect(await Template.parse(context, Source(null, '{{ name.lastName }}', null)).render(context), equals('Jones')); + }); + + test('nesting', () async { + var context = Context.create().push({"name": PersonDrop(firstName: "John", lastName: "Jones")}); + var template = Template.parse(context, Source(null, '{{ name.address.country }}', null)); + expect(await template.render(context), equals('U.S.A')); + }); + + test('invokable', () async { + var context = Context.create().push({"name": PersonDrop(firstName: "John", lastName: "Jones")}); + expect(await Template.parse(context, Source(null, '{{ name.first }}', null)).render(context), equals('John')); + expect(await Template.parse(context, Source(null, '{{ name.last }}', null)).render(context), equals('Jones')); + }); + }); +} + +class ProductDrop extends Drop {} + +class AddressDrop extends Drop { + @override + Map get attrs => {"country": "U.S.A"}; +} + +class PersonDrop extends Drop { + String firstName; + String lastName; + + PersonDrop({required this.firstName, required this.lastName}); + + String fullName() { + return '$firstName $lastName'; + } + + @override + Map get attrs => { + "firstName": firstName, + "lastName": lastName, + "fullName": fullName(), + "address": AddressDrop(), + }; + + @override + List get invokable => [ + ...super.invokable, + #first, + #last, + ]; + + @override + invoke(Symbol symbol) { + switch (symbol) { + case #first: + return firstName; + case #last: + return lastName; + default: + return liquidMethodMissing(symbol); + } + } +} diff --git a/test/liquid_test.dart b/test/liquid_test.dart index eca2b00..bff0c41 100644 --- a/test/liquid_test.dart +++ b/test/liquid_test.dart @@ -1,11 +1,9 @@ import 'package:liquid_engine/liquid_engine.dart'; -import 'package:liquid_engine/src/context.dart'; -import 'package:liquid_engine/src/errors.dart'; import 'package:liquid_engine/src/exception/parse_block_exception.dart'; -import 'package:liquid_engine/src/model.dart'; -import 'package:liquid_engine/src/template.dart'; import 'package:test/test.dart'; +import 'shared.dart'; + void main() { group('tests', () { test('First Test', () async { @@ -217,6 +215,35 @@ void main() { expect(await template.render(context), equals('Hi, friend!')); }); + test('render', () async { + final context = Context.create(); + final root = TestRoot({ + 'render': '{% render "name.html" %}', + 'render_with_parameters': '{% render "name_age.html", first_name, last_name, age:1 %}', + 'render_with_with': '{% render "name.html" with first_name as name %}', + 'render_with_for': '{% render "name.html" for names as name %}', + 'name_age.html': '{{ first_name }} {{ last_name }}! {{age}}', + 'name.html': '{{ name }}!', + }); + + context.variables['first_name'] = 'John'; + context.variables['last_name'] = 'John'; + context.variables['greeting'] = 'Hello'; + context.variables['names'] = ['john', 'mark', 'mary']; + + var template = Template.parse(context, await root.resolve('render')); + expect(await template.render(context), equals('!')); + + template = Template.parse(context, await root.resolve('render_with_parameters')); + expect(await template.render(context), equals('John John! 1')); + + template = Template.parse(context, await root.resolve('render_with_with')); + expect(await template.render(context), equals('John!')); + + template = Template.parse(context, await root.resolve('render_with_for')); + expect(await template.render(context), equals(['john', 'mark', 'mary'].map((e) => '$e!').join(''))); + }); + test('extends', () async { final context = Context.create(); final root = TestRoot({ @@ -269,22 +296,3 @@ void main() { '
  • India
    • Mumbai: 19,000,000
    • Calcutta: 15,000,000
  • USA
    • New York: 20,000,000
    • Chicago: 7,000,000
  • Japan
    • Tokyo: 33,000,000
')); }); } - -String reverse(String string) { - final sb = StringBuffer(); - for (int i = string.length - 1; i >= 0; i--) { - sb.writeCharCode(string.codeUnitAt(i)); - } - return sb.toString(); -} - -class TestRoot implements Root { - Map files; - - TestRoot(this.files); - - @override - Future resolve(String relPath) async { - return Source(null, files[relPath]!, this); - } -} diff --git a/test/shared.dart b/test/shared.dart new file mode 100644 index 0000000..1ab45e3 --- /dev/null +++ b/test/shared.dart @@ -0,0 +1,20 @@ +import 'package:liquid_engine/liquid_engine.dart'; + +String reverse(String string) { + final sb = StringBuffer(); + for (int i = string.length - 1; i >= 0; i--) { + sb.writeCharCode(string.codeUnitAt(i)); + } + return sb.toString(); +} + +class TestRoot implements Root { + Map files; + + TestRoot(this.files); + + @override + Future resolve(String relPath) async { + return Source(null, files[relPath]!, this); + } +} \ No newline at end of file