Experimental hack to unify mustache and mustache_template libraries
diff --git a/lib/mustache.dart b/lib/mustache.dart
index 687459c..f6c6ccc 100644
--- a/lib/mustache.dart
+++ b/lib/mustache.dart
@@ -12,7 +12,8 @@
bool htmlEscapeValues,
String name,
PartialResolver partialResolver,
- String delimiters}) = t.Template.fromSource;
+ String delimiters,
+ ValueResolver valueResolver}) = t.Template.fromSource;
String get name;
String get source;
@@ -30,6 +31,27 @@
typedef PartialResolver = Template Function(String);
+const Object noSuchProperty = Object();
+
+typedef ValueResolver = Object Function(Object, Object);
+
+final RegExp _integerTag = RegExp(r'^[0-9]+$');
+
+//FIXME should name be String??
+// Returns the property of the given object by name. For a map,
+// which contains the key name, this is object[name]. For other
+// objects, this is object.name or object.name(). If no property
+// by the given name exists, this method returns noSuchProperty.
+Object defaultValueResolver(Object object, Object name) {
+ if (object is Map && object.containsKey(name)) return object[name];
+
+ if (object is List && _integerTag.hasMatch(name)) {
+ return object[int.parse(name)];
+ }
+
+ return noSuchProperty;
+}
+
typedef LambdaFunction = Object Function(LambdaContext context);
/// Passed as an argument to a mustache lambda function. The methods on
diff --git a/lib/mustache_with_mirrors.dart b/lib/mustache_with_mirrors.dart
new file mode 100644
index 0000000..29bc8be
--- /dev/null
+++ b/lib/mustache_with_mirrors.dart
@@ -0,0 +1,66 @@
+import 'dart:mirrors';
+
+export 'mustache.dart' hide Template;
+import 'mustache.dart' as m;
+import 'src/template.dart' as t;
+
+class Template extends t.Template {
+ Template(String source,
+ {bool lenient = false,
+ bool htmlEscapeValues = true,
+ String name,
+ m.PartialResolver partialResolver,
+ String delimiters = '{{ }}',
+ m.ValueResolver valueResolver})
+ : super.fromSource(source,
+ lenient: lenient,
+ htmlEscapeValues: htmlEscapeValues,
+ name: name,
+ partialResolver: partialResolver,
+ delimiters: delimiters,
+ valueResolver:
+ valueResolver ??
+ (lenient ? lenientMirrorValueResolver : mirrorValueResolver));
+}
+
+final RegExp _validTag = RegExp(r'^[0-9a-zA-Z\_\-\.]+$');
+final RegExp _integerTag = RegExp(r'^[0-9]+$');
+
+Object mirrorValueResolver(Object object, Object name) =>
+ _mirrorValueResolver(object, name, lenient: false);
+
+Object lenientMirrorValueResolver(Object object, Object name) =>
+ _mirrorValueResolver(object, name, lenient: true);
+
+//FIXME name should be string right?
+// Returns the property of the given object by name. For a map,
+// which contains the key name, this is object[name]. For other
+// objects, this is object.name or object.name(). If no property
+// by the given name exists, this method returns noSuchProperty.
+Object _mirrorValueResolver(Object object, Object name,
+ {bool lenient = false}) {
+ if (object is Map && object.containsKey(name)) return object[name];
+
+ if (object is List && _integerTag.hasMatch(name)) {
+ return object[int.parse(name)];
+ }
+
+ if (lenient && !_validTag.hasMatch(name)) return m.noSuchProperty;
+
+ var instance = reflect(object);
+ var field = instance.type.instanceMembers[Symbol(name)];
+ if (field == null) return m.noSuchProperty;
+
+ var invocation;
+ if ((field is VariableMirror) ||
+ ((field is MethodMirror) && (field.isGetter))) {
+ invocation = instance.getField(field.simpleName);
+ } else if ((field is MethodMirror) &&
+ (field.parameters.where((p) => !p.isOptional).isEmpty)) {
+ invocation = instance.invoke(field.simpleName, []);
+ }
+ if (invocation == null) {
+ return m.noSuchProperty;
+ }
+ return invocation.reflectee;
+}
diff --git a/lib/src/renderer.dart b/lib/src/renderer.dart
index 74ca3cf..56e95f0 100644
--- a/lib/src/renderer.dart
+++ b/lib/src/renderer.dart
@@ -4,12 +4,18 @@
import 'template.dart';
import 'template_exception.dart';
-const Object noSuchProperty = Object();
-final RegExp _integerTag = RegExp(r'^[0-9]+$');
class Renderer extends Visitor {
- Renderer(this.sink, List stack, this.lenient, this.htmlEscapeValues,
- this.partialResolver, this.templateName, this.indent, this.source)
+ Renderer(
+ this.sink,
+ List stack,
+ this.lenient,
+ this.htmlEscapeValues,
+ this.partialResolver,
+ this.valueResolver,
+ this.templateName,
+ this.indent,
+ this.source)
: _stack = List.from(stack);
Renderer.partial(Renderer ctx, Template partial, String indent)
@@ -19,24 +25,42 @@
ctx.lenient,
ctx.htmlEscapeValues,
ctx.partialResolver,
+ ctx.valueResolver,
ctx.templateName,
ctx.indent + indent,
partial.source);
Renderer.subtree(Renderer ctx, StringSink sink)
- : this(sink, ctx._stack, ctx.lenient, ctx.htmlEscapeValues,
- ctx.partialResolver, ctx.templateName, ctx.indent, ctx.source);
+ : this(
+ sink,
+ ctx._stack,
+ ctx.lenient,
+ ctx.htmlEscapeValues,
+ ctx.partialResolver,
+ ctx.valueResolver,
+ ctx.templateName,
+ ctx.indent,
+ ctx.source);
Renderer.lambda(Renderer ctx, String source, String indent, StringSink sink,
String delimiters)
- : this(sink, ctx._stack, ctx.lenient, ctx.htmlEscapeValues,
- ctx.partialResolver, ctx.templateName, ctx.indent + indent, source);
+ : this(
+ sink,
+ ctx._stack,
+ ctx.lenient,
+ ctx.htmlEscapeValues,
+ ctx.partialResolver,
+ ctx.valueResolver,
+ ctx.templateName,
+ ctx.indent + indent,
+ source);
final StringSink sink;
final List _stack;
final bool lenient;
final bool htmlEscapeValues;
final m.PartialResolver partialResolver;
+ final m.ValueResolver valueResolver;
final String templateName;
final String indent;
final String source;
@@ -92,7 +116,7 @@
context.close();
}
- if (value == noSuchProperty) {
+ if (value == m.noSuchProperty) {
if (!lenient) {
throw error('Value was missing for variable tag: ${node.name}.', node);
}
@@ -130,7 +154,7 @@
} else if (value == false) {
// Do nothing.
- } else if (value == noSuchProperty) {
+ } else if (value == m.noSuchProperty) {
if (!lenient) {
throw error('Value was missing for section tag: ${node.name}.', node);
}
@@ -155,7 +179,7 @@
} else if (value == true || value is Map || value is Iterable) {
// Do nothing.
- } else if (value == noSuchProperty) {
+ } else if (value == m.noSuchProperty) {
if (lenient) {
_renderWithValue(node, null);
} else {
@@ -208,36 +232,22 @@
return _stack.last;
}
var parts = name.split('.');
- var object = noSuchProperty;
+ var object = m.noSuchProperty;
for (var o in _stack.reversed) {
- object = _getNamedProperty(o, parts[0]);
- if (object != noSuchProperty) {
+ object = valueResolver(o, parts[0]);
+ if (object != m.noSuchProperty) {
break;
}
}
for (var i = 1; i < parts.length; i++) {
- if (object == null || object == noSuchProperty) {
- return noSuchProperty;
+ if (object == null || object == m.noSuchProperty) {
+ return m.noSuchProperty;
}
- object = _getNamedProperty(object, parts[i]);
+ object = valueResolver(object, parts[i]);
}
return object;
}
- // Returns the property of the given object by name. For a map,
- // which contains the key name, this is object[name]. For other
- // objects, this is object.name or object.name(). If no property
- // by the given name exists, this method returns noSuchProperty.
- Object _getNamedProperty(dynamic object, dynamic name) {
- if (object is Map && object.containsKey(name)) return object[name];
-
- if (object is List && _integerTag.hasMatch(name)) {
- return object[int.parse(name)];
- }
-
- return noSuchProperty;
- }
-
m.TemplateException error(String message, Node node) =>
TemplateException(message, templateName, source, node.start);
diff --git a/lib/src/template.dart b/lib/src/template.dart
index a38cc97..9f7349b 100644
--- a/lib/src/template.dart
+++ b/lib/src/template.dart
@@ -9,13 +9,15 @@
bool htmlEscapeValues = true,
String name,
m.PartialResolver partialResolver,
- String delimiters = '{{ }}'})
+ String delimiters = '{{ }}',
+ m.ValueResolver valueResolver = m.defaultValueResolver})
: source = source,
_nodes = parser.parse(source, lenient, name, delimiters),
_lenient = lenient,
_htmlEscapeValues = htmlEscapeValues,
_name = name,
- _partialResolver = partialResolver;
+ _partialResolver = partialResolver,
+ _valueResolver = valueResolver;
@override
final String source;
@@ -24,6 +26,7 @@
final bool _htmlEscapeValues;
final String _name;
final m.PartialResolver _partialResolver;
+ final m.ValueResolver _valueResolver;
@override
String get name => _name;
@@ -38,7 +41,7 @@
@override
void render(values, StringSink sink) {
var renderer = Renderer(sink, [values], _lenient, _htmlEscapeValues,
- _partialResolver, _name, '', source);
+ _partialResolver, _valueResolver, _name, '', source);
renderer.render(_nodes);
}
}