Skip to content

Mustachio: add SimpleRenderer and demote many classes from using full renderers #2458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,751 changes: 239 additions & 3,512 deletions lib/src/generator/templates.renderers.dart

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion lib/src/mustachio/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ class Renderer {
/// The type of the context type, specified as the [Context] type argument.
final Context context;

const Renderer(this.name, this.context);
/// A set of types which are "visible" to Mustache. Mustache rendering has
/// access to all of a type's public getters if it is visible to Mustache.
///
/// Note that all subtypes and supertypes of a "visible" type are also visible
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

/// to Mustache.
final Set<Type> visibleTypes;

const Renderer(this.name, this.context, {this.visibleTypes = const {}});
}

/// A container for a type, [T], which is the type of a context object,
Expand Down
53 changes: 49 additions & 4 deletions lib/src/mustachio/renderer_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ abstract class RendererBase<T> {

void write(String text) => buffer.write(text);

String get contextChainString =>
parent == null ? '$T' : '${parent.contextChainString} > $T';

/// Returns the [Property] on this renderer named [name].
///
/// If no property named [name] exists for this renderer, `null` is returned.
Expand All @@ -39,19 +42,21 @@ abstract class RendererBase<T> {
if (property != null) {
try {
return property.renderVariable(context, property, [...names.skip(1)]);
} on MustachioResolutionError {
} on PartialMustachioResolutionError catch (e) {
// The error thrown by [Property.renderVariable] does not have all of
// the names required for a decent error. We throw a new error here.
throw MustachioResolutionError(
'Failed to resolve $names as a property chain on any types in the '
'current context');
"Failed to resolve '${e.name}' on ${e.contextType} while resolving "
'${names.skip(1)} as a property chain on any types in the context '
"chain: $contextChainString, after first resolving '${names.first}'"
'to a property on $T');
}
} else if (parent != null) {
return parent.getFields(names);
} else {
throw MustachioResolutionError(
'Failed to resolve ${names.first} as a property on any types in the '
'current context');
'context chain: $contextChainString');
}
}

Expand Down Expand Up @@ -117,6 +122,33 @@ abstract class RendererBase<T> {
}
}

String renderSimple(Object context, List<MustachioNode> ast,
{RendererBase parent}) {
var renderer = SimpleRenderer(context, parent);
renderer.renderBlock(ast);
return renderer.buffer.toString();
}

class SimpleRenderer extends RendererBase<Object> {
SimpleRenderer(Object context, RendererBase<Object> parent)
: super(context, parent);

@override
Property<Object> getProperty(String key) => null;

@override
String getFields(List<String> keyParts) {
if (keyParts.length == 1 && keyParts.single == '.') {
return context.toString();
}
if (parent != null) {
return parent.getFields(keyParts);
} else {
return 'null';
}
}
}

/// An individual property of objects of type [T], including functions for
/// rendering various types of Mustache nodes.
class Property<T> {
Expand Down Expand Up @@ -156,4 +188,17 @@ class MustachioResolutionError extends Error {
String message;

MustachioResolutionError([this.message]);

@override
String toString() => 'MustachioResolutionError: $message';
}

/// An error indicating that a renderer failed to resolve a follow-on name in a
/// multi-name key.
class PartialMustachioResolutionError extends Error {
String name;

Type contextType;

PartialMustachioResolutionError(this.name, this.contextType);
}
53 changes: 25 additions & 28 deletions test/mustachio/builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class Renderer {

final Context context;

const Renderer(this.name, this.context);
final Set<Type> visibleTypes;

const Renderer(this.name, this.context, {this.visibleTypes = const {}});
}

class Context<T> {
Expand All @@ -23,7 +25,7 @@ class Context<T> {
};

const _libraryFrontMatter = '''
@Renderer(#renderFoo, Context<Foo>())
@Renderer(#renderFoo, Context<Foo>(), visibleTypes: {Bar})
library foo;
import 'package:mustachio/annotations.dart';
''';
Expand Down Expand Up @@ -137,14 +139,11 @@ class Bar {}
getValue: (CT_ c) => c.b1,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) return self.getValue(c).toString();
var name = remainingNames.first;
if (_Renderer_bool.propertyMap().containsKey(name)) {
var nextProperty = _Renderer_bool.propertyMap()[name];
return nextProperty.renderVariable(
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
} else {
throw MustachioResolutionError();
throw MustachioResolutionError(
\'Failed to resolve simple renderer use @visibleToMustache\');
}
},
getBool: (CT_ c) => c.b1 == true,
Expand All @@ -158,22 +157,19 @@ class Bar {}
getValue: (CT_ c) => c.l1,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) return self.getValue(c).toString();
var name = remainingNames.first;
if (_Renderer_List.propertyMap().containsKey(name)) {
var nextProperty = _Renderer_List.propertyMap()[name];
return nextProperty.renderVariable(
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
} else {
throw MustachioResolutionError();
throw MustachioResolutionError(
'Failed to resolve simple renderer use @visibleToMustache');
}
},
isEmptyIterable: (CT_ c) => c.l1?.isEmpty ?? true,
renderIterable:
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
var buffer = StringBuffer();
for (var e in c.l1) {
buffer.write(_render_int(e, ast, parent: r));
buffer.write(renderSimple(e, ast, parent: r));
}
return buffer.toString();
},
Expand All @@ -187,19 +183,16 @@ class Bar {}
getValue: (CT_ c) => c.s1,
renderVariable:
(CT_ c, Property<CT_> self, List<String> remainingNames) {
if (remainingNames.isEmpty) return self.getValue(c).toString();
var name = remainingNames.first;
if (_Renderer_String.propertyMap().containsKey(name)) {
var nextProperty = _Renderer_String.propertyMap()[name];
return nextProperty.renderVariable(
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
if (remainingNames.isEmpty) {
return self.getValue(c).toString();
} else {
throw MustachioResolutionError();
throw MustachioResolutionError(
'Failed to resolve simple renderer use @visibleToMustache');
}
},
isNullValue: (CT_ c) => c.s1 == null,
renderValue: (CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
return _render_String(c.s1, ast, parent: r);
return renderSimple(c.s1, ast, parent: r);
},
),
'''));
Expand Down Expand Up @@ -276,6 +269,7 @@ import 'package:mustachio/annotations.dart';
test('builds a renderer for a generic, bounded type', () async {
await testMustachioBuilder('''
class Foo<T extends num> {}
class Bar {}
''');
var renderersLibrary = await resolveGeneratedLibrary(writer);

Expand All @@ -288,9 +282,6 @@ class Foo<T extends num> {}
expect(fooRendererClass.typeParameters, hasLength(1));
var cBound = fooRenderFunction.typeParameters.single.bound;
expect(cBound.getDisplayString(withNullability: false), equals('num'));

expect(renderersLibrary.getTopLevelFunction('_render_num'), isNotNull);
expect(renderersLibrary.getType('_Renderer_num'), isNotNull);
});

group('does not generate a renderer', () {
Expand All @@ -305,6 +296,7 @@ class Foo {
void set setter1(Setter s);
Method method1(Method m);
}
class Bar {}
class Static {}
class Private {}
class Setter {}
Expand Down Expand Up @@ -332,6 +324,11 @@ class Method {}
expect(renderersLibrary.getTopLevelFunction('_render_Method'), isNull);
expect(renderersLibrary.getType('_Renderer_Method'), isNull);
});

test('for types not @visibleToMustache', () {
expect(renderersLibrary.getTopLevelFunction('_render_String'), isNull);
expect(renderersLibrary.getType('_Renderer_String'), isNull);
});
});
}

Expand Down
6 changes: 6 additions & 0 deletions test/mustachio/foo.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@Renderer(#renderFoo, Context<Foo>())
@Renderer(#renderBar, Context<Bar>())
@Renderer(#renderBaz, Context<Baz>())
library dartdoc.testing.foo;

import 'package:dartdoc/src/mustachio/annotations.dart';
Expand All @@ -8,9 +9,14 @@ class Foo {
String s1;
bool b1;
List<int> l1;
Baz baz;
}

class Bar {
Foo foo;
String s2;
}

class Baz {
Bar bar;
}
Loading