Skip to content

Commit 3d990ca

Browse files
authored
Much of the documentation for the runtime renderer (#2631)
1 parent bf115eb commit 3d990ca

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed

tool/mustachio/README.md

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,253 @@ types:
161161

162162
## Generated renderer for a specific type which interprets templates at runtime
163163

164+
Mustachio's first set of generated renderers render objects into
165+
runtime-interpreted Mustache template blocks. Each template block may be the
166+
content of a Mustache template file, a Mustache partial file, or a Mustache
167+
section. The design for a tool which can generate such a renderer is included
168+
after the design for the renderer.
169+
170+
The mechanics of the tool which generates these renderers is a separate concern
171+
from the mechanics of the renderers themselves. This section is primarily
172+
concerned with documenting how the renderers work. At the end, a higher level
173+
description of the code generator can be found.
174+
175+
### Example types
176+
177+
Any examples in this section will use the following types:
178+
179+
```dart
180+
abstract class User {
181+
String get name;
182+
UserProfile get profile;
183+
List<Post>? get posts;
184+
}
185+
186+
abstract class UserProfile {
187+
String get avatarUrl;
188+
String get biography;
189+
}
190+
191+
abstract class Post {
192+
String get title;
193+
String get content;
194+
bool? get isPublished;
195+
}
196+
```
197+
198+
A User object can be rendered into the following Mustache template:
199+
200+
```dart
201+
<h1>{{ name }}</h1>
202+
{{ #profile }}
203+
<img src=”{{ avatarUrl }}” />
204+
<p>{{ biography }}</p>
205+
{{ /profile }}
206+
{{ #posts }}
207+
{{ #isPublished }}
208+
<div>
209+
<h2>{{ title }}</h2>
210+
<p>{{ content }}</h2>
211+
</div>
212+
{{ /isPublished }}
213+
{{ /posts }}
214+
```
215+
216+
### Renderer outline
217+
218+
In order to support repeated sections and value sections, a renderer for a type
219+
_T_ requires renderers for other types:
220+
221+
* If _T_ has a getter of type _S_, then a renderer for type _T_ may be called
222+
upon to render a [value section][] for that getter, which requires a renderer
223+
for type _S_.
224+
* If _T_ has a getter of type _Iterable&lt;S>_ for some type _S_, then a
225+
renderer for type _T_ may be called upon to render a repeated section for that
226+
getter, which requires a renderer for type _S_.
227+
228+
An instance of a renderer needs four things in order to render an object using a
229+
Mustache syntax tree (a _template block_):
230+
231+
* the context object,
232+
* the Mustache template block (a list of nodes),
233+
* the path to the directory in which the Mustache template is located, in order
234+
to locate partials,
235+
* optionally, a parent renderer
236+
237+
Additionally, a renderer needs various functions in order to render each getter
238+
for the renderer's context type. It may need to render each getter (1) as a
239+
variable, (2) possibly as a conditional section, (3) possibly as a repeated
240+
section, and (4) possibly as a value section).
241+
242+
Here are all of the elements of such a renderer for the User class:
243+
244+
```dart
245+
class Renderer_User extends RendererBase<User> {
246+
static final Map<String, Property<CT_>> propertyMap<CT_ extends User>() => ...;
247+
248+
Renderer_User(User context, RendererBase<Object> parent, Template template)
249+
: super(context, parent, template);
250+
251+
@override
252+
Property<User> getProperty(String key) {
253+
if (propertyMap<User>().containsKey(key)) {
254+
return propertyMap<User>()[key];
255+
} else {
256+
return null;
257+
}
258+
}
259+
}
260+
```
261+
262+
* The base class, **RendererBase**, provides functionality common to all
263+
renderers, for example a buffer to which output may be written; each of the
264+
methods discussed in [Rendering a block][] below.
265+
* The map of properties forms the bulk of each individual renderer. The Property
266+
class is described just below.
267+
* The renderer instance may be asked to render multiple blocks; while rendering
268+
a list of nodes, the children of certain nodes are rendered without changing
269+
context, and so can use the same renderer. In particular:
270+
* when rendering a conditional section,
271+
* when rendering an inverted repeated section or inverted value section,
272+
* when rendering a partial.
273+
274+
#### Map of properties
275+
276+
The core functionality of accessing getters on an object by name (as a String)
277+
is a static map of properties for each type which may be used as a context
278+
object during the rendering process. Each getter names is mapped to a Property
279+
object which holds functions that allow performing certain rendering actions
280+
using the given property.
281+
282+
The property map is actually a function because it needs to be type
283+
parameterized on `CT_`, a type variable bounded to the type of the context
284+
object. This is an unfortunate complication which arises from the design of
285+
Property being a collection of functions. Since a renderer can be used to
286+
render _subtypes_ of the context type, we cannot type all of the functions in
287+
the Properties with the context type; they must each be typed with the runtime
288+
type of the context object.
289+
290+
Here is the Property interface:
291+
292+
```dart
293+
class Property<T> {
294+
final Object Function(T context) getValue;
295+
final String Function(T, Property<T>, List<String>) renderVariable;
296+
final bool Function(T context) getBool;
297+
final Iterable<String> Function(T, RendererBase<T>, List<MustachioNode>)
298+
renderIterable;
299+
final bool Function(T) isNullValue;
300+
final String Function(T, RendererBase<T>, List<MustachioNode>) renderValue;
301+
}
302+
```
303+
304+
For each valid getter on a type, the renderer will map out a Property object
305+
with non-`null` values for the appropriate functions, and `null` values for
306+
inappropriate functions.
307+
308+
##### The `getValue` function
309+
310+
For every valid getter, the Property object will contain a function named
311+
`getValue` which calls the getter and returns the result. This function is used
312+
to render a property in a [variable node][]. For example, the Property for
313+
`name` on the User renderer has the following `getValue` function:
314+
315+
```dart
316+
(CT_ c) => c.name
317+
```
318+
319+
##### The `renderVariable` function
320+
321+
TODO(srawlins): Write.
322+
323+
##### The `getBool` function
324+
325+
For every valid getter with a `bool?` or `bool` return type, the Property object
326+
contains a function named `getBool` which returns the non-`null` `bool` value of
327+
the getter (`null` is converted to `false`). This function is used to render a
328+
property in a conditional section node (or an inverted one). For example, the
329+
Property for `isPublished` on the Post renderer has the following `getBool`
330+
function:
331+
332+
```dart
333+
(CT_ c) => c.isPublished == true
334+
```
335+
336+
##### The `renderIterable` function
337+
338+
For every valid getter with a return type assignable to `Iterable<Object?>?`,
339+
the Property object contains a function named `renderIterable` which requires
340+
some parameters: the context object, the current renderer, and the AST to
341+
render. This function is used to render a property in a repeated section node
342+
(or an inverted one). For example, the Property for `posts` on the User renderer
343+
has the following `renderIterable` function:
344+
345+
```dart
346+
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
347+
return c.posts.map(
348+
(e) => _render_Post(e, ast, r.template, parent: r));
349+
}
350+
```
351+
352+
This function needs the three arguments so that it can iterate over the `posts`
353+
of the context object, and then create new instances of the Post renderer, which
354+
requires the AST to render, and the parent context.
355+
356+
##### The `isNullValue` and `renderValue` functions
357+
358+
For each valid getter which has neither a `bool` return type nor an `Iterable`
359+
return type, the Property object contains a function named `isNullValue` which
360+
returns whether the value of the getter is `null` or not. It also contains a
361+
function named `renderValue` which requires more parameters: the context object,
362+
the current renderer, and the AST to render. These functions are used to render
363+
a property in a value section node (or an inverted one). For example, the
364+
Property for `profile` on the User renderer has the following `isNullValue`
365+
function:
366+
367+
```dart
368+
(CT_ c) => c.profile == null
369+
```
370+
371+
and `renderValue` function:
372+
373+
```dart
374+
(CT_ c, RendererBase<CT_> r, List<MustachioNode> ast) {
375+
return _render_UserProfile(c.profile, ast, r.template, parent: r);
376+
}
377+
```
378+
379+
The `renderValue` function needs the three arguments so that it can render the
380+
property value using a new `UserProfile` renderer, which requires the AST to
381+
render, and the parent context.
382+
383+
#### Rendering a block
384+
385+
TODO(srawlins): Write.
386+
387+
#### Resolving a variable key
388+
389+
TODO(srawlins): Write.
390+
391+
#### Rendering a section
392+
393+
TODO(srawlins): Write.
394+
395+
##### Conditional section
396+
397+
##### Repeated section
398+
399+
##### Value section
400+
401+
#### Rendering a partial
402+
403+
TODO(srawlins): Write.
404+
405+
[value section]: https://mustache.github.io/mustache.5.html#Sections
406+
[Rendering a block]: #rendering-a-block
407+
[variable node]: https://mustache.github.io/mustache.5.html#Variables
408+
409+
### High level design for generating renderers
410+
164411
TODO(srawlins): Write.
165412

166413
## Generated renderer for a specific type and a static template which pre-compiles the templates

0 commit comments

Comments
 (0)