0 branches
Tree Top files
Code
Clone with HTTPS:
56 years ago
..
README.md all: fix more tests last Apr 30 9.65 KB

dtm2 - Dynamic Template Manager 2

dtm2 is the modern runtime renderer for Dynamic Template Manager. It keeps the original DTM idea: templates are normal files on disk, so they can be edited without recompiling the application.

The main change from x.templating.dtm is architectural. dtm2 caches parsed template trees, not rendered HTML responses. This keeps rendering fast while removing the old async rendered-cache server from the hot path.

Quick Start

Create a templates/ folder in your application and put your templates inside it. DTM2 supports .html, .htm, .xml, .txt, and .text by default.

import x.templating.dtm2

fn main() {
    mut manager := dtm2.initialize(
        template_dir: 'templates'
    )
    placeholders := {
        'title': 'DTM2'
        'body':  '<strong>escaped by default</strong>'
    }
    rendered := manager.expand('page.html', placeholders: &placeholders)
    println(rendered)
}

Example template:

<!doctype html>
<html>
    <head>
        
    </head>
    <body>
        
    </body>
</html>

The rendered @body value is escaped by default.

Veb Example

import veb
import x.templating.dtm2

pub struct App {
pub mut:
    templates &dtm2.Manager = unsafe { nil }
}

pub struct Context {
    veb.Context
}

fn main() {
    mut app := &App{
        templates: dtm2.initialize(
            template_dir: 'templates'
        )
    }
    veb.run[App, Context](mut app, 18081)
}

@['/']
pub fn (mut app App) index(mut ctx Context) veb.Result {
    placeholders := {
        'title': 'Home'
        'body':  'Hello from DTM2'
    }
    html := app.templates.expand('index.html', placeholders: &placeholders)
    return ctx.html(html)
}

Available Options

Manager options

dtm2.initialize() accepts:

Template extensions

DTM2 has two rendering modes:

Render options

manager.expand() accepts:

The Placeholder System

Template placeholders use the @name form.

<h1>@title</h1>
<p>@body</p>

In DTM2, placeholder values are strings:

placeholders := {
    'title': 'Hello'
    'count': '42'
}

Values are escaped by default in both HTML and text templates.

Security note for custom non-HTML formats such as SQL: DTM2 is a template renderer, not a domain-specific sanitizer. By default, it escapes HTML-special characters in placeholder values. It does not make SQL queries safe, does not replace prepared statements, and does not validate business-specific formats. If you add .sql or another sensitive extension through configuration, the security of that generated content remains the responsibility of the application.

Explicit HTML Inclusion

The historical _#includehtml suffix is still supported for compatibility. It allows a placeholder to include a restricted set of HTML tags in .html templates.

placeholders := {
    'body_#includehtml': '<p>allowed</p>'
}
html := manager.expand('page.html', placeholders: &placeholders)

The template still uses the normal placeholder name:


DTM2 escapes the complete value first, then restores only allowed tags. This preserves the old opt-in behavior without allowing arbitrary raw HTML through. In .txt templates, HTML is always escaped.

Allowed tags:

<div>, </div>, <h1>, </h1>, <h2>, </h2>, <h3>, </h3>, <h4>, </h4>,
<h5>, </h5>, <h6>, </h6>, <p>, </p>, <br>, <hr>, <span>, </span>,
<ul>, </ul>, <ol>, </ol>, <li>, </li>, <dl>, </dl>, <dt>, </dt>,
<dd>, </dd>, , <table>, </table>, ,
<th>, </th>, <tr>, </tr>, <td>, </td>, <thead>, </thead>,
, <tbody>, </tbody>, , ,
, , ,
, , ,
, <details>, </details>, ,
, <summary>, </summary>

Includes

Templates can include other templates with a simple line-level directive:



Include paths are resolved relative to the current template. If no file extension is provided, .html is added. The final resolved include path must stay inside the manager template_dir; attempts to include files through ../ or absolute paths outside that root fail.

The same boundary applies to templates passed to expand(). Absolute template paths are accepted only when they resolve inside template_dir.

Included files are tracked as dependencies of the parsed template. When reload_modified_templates is enabled, changing an included file invalidates the cached parsed tree.

Backward Compatibility With DTM v1

Existing code that imports x.templating.dtm is kept source-compatible for the migration period. The v1 facade is deprecated, but it now delegates rendering to DTM2 internally.

That means old code can continue to compile:

import x.templating.dtm

mut manager := dtm.initialize()
mut placeholders := map[string]dtm.DtmMultiTypeMap{}
placeholders['title'] = 'Legacy DTM'
placeholders['count'] = 7
html := manager.expand('page.html', placeholders: &placeholders)

For new code, prefer importing x.templating.dtm2 directly:

import x.templating.dtm2

mut manager := dtm2.initialize(template_dir: 'templates')
placeholders := {
    'title': 'Modern DTM'
    'count': '7'
}
html := manager.expand('page.html', placeholders: &placeholders)

Migration notes:

Design Notes

DTM2 intentionally keeps rendering and rendered-output caching separate.

The manager caches:

Benchmarks

The local benchmark harness lives in:

vlib/x/templating/dtm2/benchmarks/

Run it from the repository root:

vlib/x/templating/dtm2/benchmarks/run_dtm2_benchmark.sh

Useful options: