Cleaned up partials support a little
diff --git a/lib/mustache_context.dart b/lib/mustache_context.dart
index 9a722d3..36379a7 100644
--- a/lib/mustache_context.dart
+++ b/lib/mustache_context.dart
@@ -13,15 +13,19 @@
final bool _htmlEscapeValues;
String renderString(String templateName, values) {
- var template = _partialResolver(templateName);
+ _Template template = _partialResolver(templateName);
return template.renderString(values,
- lenient: _lenient, htmlEscapeValues: _htmlEscapeValues);
+ lenient: _lenient,
+ htmlEscapeValues: _htmlEscapeValues,
+ partialResolver: _partialResolver);
}
void render(String templateName, values, StringSink sink) {
- var template = _partialResolver(templateName);
+ _Template template = _partialResolver(templateName);
template.render(values, sink,
- lenient: _lenient, htmlEscapeValues: _htmlEscapeValues);
+ lenient: _lenient,
+ htmlEscapeValues: _htmlEscapeValues,
+ partialResolver: _partialResolver);
}
}
diff --git a/lib/template.dart b/lib/template.dart
index 29a71ff..6691c8b 100644
--- a/lib/template.dart
+++ b/lib/template.dart
@@ -5,7 +5,7 @@
final RegExp _validTag = new RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
final RegExp _integerTag = new RegExp(r'^[0-9]+$');
-Template _parse(String source, {bool lenient : false}) {
+Template _parse(String source, {bool lenient : false, PartialResolver partialResolver}) {
var tokens = _scan(source, lenient);
var ast = _parseTokens(tokens, lenient);
return new _Template(ast, lenient);
@@ -57,60 +57,65 @@
}
}
+
class _Template implements Template {
- _Template(this._root, this._lenient)
- {
- _htmlEscapeMap[_AMP] = '&';
- _htmlEscapeMap[_LT] = '<';
- _htmlEscapeMap[_GT] = '>';
- _htmlEscapeMap[_QUOTE] = '"';
- _htmlEscapeMap[_APOS] = ''';
- _htmlEscapeMap[_FORWARD_SLASH] = '/';
- }
+ _Template(this._root, this._lenient);
+
+ final _Node _root;
+ final bool _lenient;
+
+ String renderString(values,
+ {bool lenient : false,
+ bool htmlEscapeValues : true,
+ PartialResolver partialResolver}) {
+ var buf = new StringBuffer();
+ render(values, buf, lenient: lenient, htmlEscapeValues: htmlEscapeValues,
+ partialResolver: partialResolver);
+ return buf.toString();
+ }
+
+ void render(values, StringSink sink,
+ {bool lenient : false,
+ bool htmlEscapeValues : true,
+ PartialResolver partialResolver}) {
+ var renderer = new _TemplateRenderer(_root, sink, values, [values],
+ lenient, htmlEscapeValues, partialResolver);
+ renderer.render();
+ }
+}
+
+
+class _TemplateRenderer {
+
+ _TemplateRenderer(this._root,
+ this._sink,
+ this._values,
+ this._stack,
+ this._lenient,
+ this._htmlEscapeValues,
+ this._partialResolver);
+
+ _TemplateRenderer.partial(_TemplateRenderer renderer, _Template partial)
+ : this(partial._root,
+ renderer._sink,
+ renderer._values,
+ renderer._stack,
+ renderer._lenient,
+ renderer._htmlEscapeValues,
+ renderer._partialResolver);
final _Node _root;
-
- //FIXME careful there is a potential concurrency bug as _stack is mutated
- // during rendering, and if async lazy loading of partials is added this
- // could be mutated by multiple async calls to render running concurrently.
- // Perhaps pass this around as an argument, or create a render context object.
- // Same is true with sink.
- final List _stack = new List();
- final Map _htmlEscapeMap = new Map<int, String>();
+ final StringSink _sink;
+ final _values;
+ final List _stack;
final bool _lenient;
-
- //FIXME quick ugly hack.
- PartialResolver partialResolver;
+ final bool _htmlEscapeValues;
+ final PartialResolver _partialResolver;
- bool _htmlEscapeValues;
- StringSink _sink;
-
- String renderString(values, {bool lenient : false, bool htmlEscapeValues : true}) {
- var buf = new StringBuffer();
- render(values, buf, lenient: lenient, htmlEscapeValues: htmlEscapeValues);
- return buf.toString();
- }
-
- void render(values, StringSink sink, {bool lenient : false, bool htmlEscapeValues : true}) {
- _sink = sink;
- _htmlEscapeValues = htmlEscapeValues;
- _stack.clear();
- _stack.add(values);
+ void render() {
_root.children.forEach(_renderNode);
- _sink = null;
}
-
- //TODO Share code with render.
- void _renderWithStack(List stack, StringSink sink,
- {bool lenient : false, bool htmlEscapeValues : true}) {
- _sink = sink;
- _htmlEscapeValues = htmlEscapeValues;
- _stack.clear();
- _stack.addAll(stack);
- _root.children.forEach(_renderNode);
- _sink = null;
- }
_write(String output) => _sink.write(output);
@@ -278,11 +283,22 @@
_renderPartial(_Node node) {
var partialName = node.value;
- _Template template = partialResolver(partialName);
- template._renderWithStack(_stack, _sink);
+ _Template template = _partialResolver(partialName);
+ var renderer = new _TemplateRenderer.partial(this, template);
+ renderer.render();
}
+ static const Map<String,String> _htmlEscapeMap = const {
+ _AMP: '&',
+ _LT: '<',
+ _GT: '>',
+ _QUOTE: '"',
+ _APOS: ''',
+ _FORWARD_SLASH: '/'
+ };
+
String _htmlEscape(String s) {
+
var buffer = new StringBuffer();
int startIndex = 0;
int i = 0;
diff --git a/test/mustache_test.dart b/test/mustache_test.dart
index 30fd6b7..e865466 100644
--- a/test/mustache_test.dart
+++ b/test/mustache_test.dart
@@ -234,18 +234,13 @@
});
});
- solo_group('Partial tag', () {
+ group('Partial tag', () {
test('MustacheContext', () {
var template = parse('{{>partial}}');
var includedTemplate = parse('{{foo}}');
var resolver = (name) =>
- {'root': template, 'partial': includedTemplate}[name];
-
- //FIXME Need a sensible way to initialise the resolver.
- template.partialResolver = resolver;
- includedTemplate.partialResolver = resolver;
-
+ {'root': template, 'partial': includedTemplate}[name];
var ctx = new MustacheContext(resolver);
var output = ctx.renderString('root', {'foo': 'bar'});
expect(output, 'bar');