@@ -161,6 +161,253 @@ types:
161
161
162
162
## Generated renderer for a specific type which interprets templates at runtime
163
163
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< ; 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
+
164
411
TODO(srawlins): Write.
165
412
166
413
## Generated renderer for a specific type and a static template which pre-compiles the templates
0 commit comments