Skip to content

Commit ab96e93

Browse files
authored
Mustachio: add SimpleRenderer and demote many classes from using full renderers (#2458)
1 parent dcfd249 commit ab96e93

File tree

9 files changed

+545
-4097
lines changed

9 files changed

+545
-4097
lines changed

lib/src/generator/templates.renderers.dart

Lines changed: 239 additions & 3512 deletions
Large diffs are not rendered by default.

lib/src/mustachio/annotations.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ class Renderer {
77
/// The type of the context type, specified as the [Context] type argument.
88
final Context context;
99

10-
const Renderer(this.name, this.context);
10+
/// A set of types which are "visible" to Mustache. Mustache rendering has
11+
/// access to all of a type's public getters if it is visible to Mustache.
12+
///
13+
/// Note that all subtypes and supertypes of a "visible" type are also visible
14+
/// to Mustache.
15+
final Set<Type> visibleTypes;
16+
17+
const Renderer(this.name, this.context, {this.visibleTypes = const {}});
1118
}
1219

1320
/// A container for a type, [T], which is the type of a context object,

lib/src/mustachio/renderer_base.dart

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ abstract class RendererBase<T> {
2020

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

23+
String get contextChainString =>
24+
parent == null ? '$T' : '${parent.contextChainString} > $T';
25+
2326
/// Returns the [Property] on this renderer named [name].
2427
///
2528
/// If no property named [name] exists for this renderer, `null` is returned.
@@ -39,19 +42,21 @@ abstract class RendererBase<T> {
3942
if (property != null) {
4043
try {
4144
return property.renderVariable(context, property, [...names.skip(1)]);
42-
} on MustachioResolutionError {
45+
} on PartialMustachioResolutionError catch (e) {
4346
// The error thrown by [Property.renderVariable] does not have all of
4447
// the names required for a decent error. We throw a new error here.
4548
throw MustachioResolutionError(
46-
'Failed to resolve $names as a property chain on any types in the '
47-
'current context');
49+
"Failed to resolve '${e.name}' on ${e.contextType} while resolving "
50+
'${names.skip(1)} as a property chain on any types in the context '
51+
"chain: $contextChainString, after first resolving '${names.first}'"
52+
'to a property on $T');
4853
}
4954
} else if (parent != null) {
5055
return parent.getFields(names);
5156
} else {
5257
throw MustachioResolutionError(
5358
'Failed to resolve ${names.first} as a property on any types in the '
54-
'current context');
59+
'context chain: $contextChainString');
5560
}
5661
}
5762

@@ -117,6 +122,33 @@ abstract class RendererBase<T> {
117122
}
118123
}
119124

125+
String renderSimple(Object context, List<MustachioNode> ast,
126+
{RendererBase parent}) {
127+
var renderer = SimpleRenderer(context, parent);
128+
renderer.renderBlock(ast);
129+
return renderer.buffer.toString();
130+
}
131+
132+
class SimpleRenderer extends RendererBase<Object> {
133+
SimpleRenderer(Object context, RendererBase<Object> parent)
134+
: super(context, parent);
135+
136+
@override
137+
Property<Object> getProperty(String key) => null;
138+
139+
@override
140+
String getFields(List<String> keyParts) {
141+
if (keyParts.length == 1 && keyParts.single == '.') {
142+
return context.toString();
143+
}
144+
if (parent != null) {
145+
return parent.getFields(keyParts);
146+
} else {
147+
return 'null';
148+
}
149+
}
150+
}
151+
120152
/// An individual property of objects of type [T], including functions for
121153
/// rendering various types of Mustache nodes.
122154
class Property<T> {
@@ -156,4 +188,17 @@ class MustachioResolutionError extends Error {
156188
String message;
157189

158190
MustachioResolutionError([this.message]);
191+
192+
@override
193+
String toString() => 'MustachioResolutionError: $message';
194+
}
195+
196+
/// An error indicating that a renderer failed to resolve a follow-on name in a
197+
/// multi-name key.
198+
class PartialMustachioResolutionError extends Error {
199+
String name;
200+
201+
Type contextType;
202+
203+
PartialMustachioResolutionError(this.name, this.contextType);
159204
}

test/mustachio/builder_test.dart

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class Renderer {
1313
1414
final Context context;
1515
16-
const Renderer(this.name, this.context);
16+
final Set<Type> visibleTypes;
17+
18+
const Renderer(this.name, this.context, {this.visibleTypes = const {}});
1719
}
1820
1921
class Context<T> {
@@ -23,7 +25,7 @@ class Context<T> {
2325
};
2426

2527
const _libraryFrontMatter = '''
26-
@Renderer(#renderFoo, Context<Foo>())
28+
@Renderer(#renderFoo, Context<Foo>(), visibleTypes: {Bar})
2729
library foo;
2830
import 'package:mustachio/annotations.dart';
2931
''';
@@ -137,14 +139,11 @@ class Bar {}
137139
getValue: (CT_ c) => c.b1,
138140
renderVariable:
139141
(CT_ c, Property<CT_> self, List<String> remainingNames) {
140-
if (remainingNames.isEmpty) return self.getValue(c).toString();
141-
var name = remainingNames.first;
142-
if (_Renderer_bool.propertyMap().containsKey(name)) {
143-
var nextProperty = _Renderer_bool.propertyMap()[name];
144-
return nextProperty.renderVariable(
145-
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
142+
if (remainingNames.isEmpty) {
143+
return self.getValue(c).toString();
146144
} else {
147-
throw MustachioResolutionError();
145+
throw MustachioResolutionError(
146+
\'Failed to resolve simple renderer use @visibleToMustache\');
148147
}
149148
},
150149
getBool: (CT_ c) => c.b1 == true,
@@ -158,22 +157,19 @@ class Bar {}
158157
getValue: (CT_ c) => c.l1,
159158
renderVariable:
160159
(CT_ c, Property<CT_> self, List<String> remainingNames) {
161-
if (remainingNames.isEmpty) return self.getValue(c).toString();
162-
var name = remainingNames.first;
163-
if (_Renderer_List.propertyMap().containsKey(name)) {
164-
var nextProperty = _Renderer_List.propertyMap()[name];
165-
return nextProperty.renderVariable(
166-
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
160+
if (remainingNames.isEmpty) {
161+
return self.getValue(c).toString();
167162
} else {
168-
throw MustachioResolutionError();
163+
throw MustachioResolutionError(
164+
'Failed to resolve simple renderer use @visibleToMustache');
169165
}
170166
},
171167
isEmptyIterable: (CT_ c) => c.l1?.isEmpty ?? true,
172168
renderIterable:
173169
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
174170
var buffer = StringBuffer();
175171
for (var e in c.l1) {
176-
buffer.write(_render_int(e, ast, parent: r));
172+
buffer.write(renderSimple(e, ast, parent: r));
177173
}
178174
return buffer.toString();
179175
},
@@ -187,19 +183,16 @@ class Bar {}
187183
getValue: (CT_ c) => c.s1,
188184
renderVariable:
189185
(CT_ c, Property<CT_> self, List<String> remainingNames) {
190-
if (remainingNames.isEmpty) return self.getValue(c).toString();
191-
var name = remainingNames.first;
192-
if (_Renderer_String.propertyMap().containsKey(name)) {
193-
var nextProperty = _Renderer_String.propertyMap()[name];
194-
return nextProperty.renderVariable(
195-
self.getValue(c), nextProperty, [...remainingNames.skip(1)]);
186+
if (remainingNames.isEmpty) {
187+
return self.getValue(c).toString();
196188
} else {
197-
throw MustachioResolutionError();
189+
throw MustachioResolutionError(
190+
'Failed to resolve simple renderer use @visibleToMustache');
198191
}
199192
},
200193
isNullValue: (CT_ c) => c.s1 == null,
201194
renderValue: (CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
202-
return _render_String(c.s1, ast, parent: r);
195+
return renderSimple(c.s1, ast, parent: r);
203196
},
204197
),
205198
'''));
@@ -276,6 +269,7 @@ import 'package:mustachio/annotations.dart';
276269
test('builds a renderer for a generic, bounded type', () async {
277270
await testMustachioBuilder('''
278271
class Foo<T extends num> {}
272+
class Bar {}
279273
''');
280274
var renderersLibrary = await resolveGeneratedLibrary(writer);
281275

@@ -288,9 +282,6 @@ class Foo<T extends num> {}
288282
expect(fooRendererClass.typeParameters, hasLength(1));
289283
var cBound = fooRenderFunction.typeParameters.single.bound;
290284
expect(cBound.getDisplayString(withNullability: false), equals('num'));
291-
292-
expect(renderersLibrary.getTopLevelFunction('_render_num'), isNotNull);
293-
expect(renderersLibrary.getType('_Renderer_num'), isNotNull);
294285
});
295286

296287
group('does not generate a renderer', () {
@@ -305,6 +296,7 @@ class Foo {
305296
void set setter1(Setter s);
306297
Method method1(Method m);
307298
}
299+
class Bar {}
308300
class Static {}
309301
class Private {}
310302
class Setter {}
@@ -332,6 +324,11 @@ class Method {}
332324
expect(renderersLibrary.getTopLevelFunction('_render_Method'), isNull);
333325
expect(renderersLibrary.getType('_Renderer_Method'), isNull);
334326
});
327+
328+
test('for types not @visibleToMustache', () {
329+
expect(renderersLibrary.getTopLevelFunction('_render_String'), isNull);
330+
expect(renderersLibrary.getType('_Renderer_String'), isNull);
331+
});
335332
});
336333
}
337334

test/mustachio/foo.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@Renderer(#renderFoo, Context<Foo>())
22
@Renderer(#renderBar, Context<Bar>())
3+
@Renderer(#renderBaz, Context<Baz>())
34
library dartdoc.testing.foo;
45

56
import 'package:dartdoc/src/mustachio/annotations.dart';
@@ -8,9 +9,14 @@ class Foo {
89
String s1;
910
bool b1;
1011
List<int> l1;
12+
Baz baz;
1113
}
1214

1315
class Bar {
1416
Foo foo;
1517
String s2;
1618
}
19+
20+
class Baz {
21+
Bar bar;
22+
}

0 commit comments

Comments
 (0)