Skip to content

x.templating.dtm #

dtm - Dynamic Template Manager

A simple template manager integrated into the V project, designed to combine the power of V templates with Vweb, without the need to recompile the application with every change.

Quick Start

Using the dynamic template manager ( named 'dtm' in this readme) is relatively straightforward. You just need to initialize an instance. Then, call the 'expand' function in the code that manages your template.

Before starting, You can specify a folder, but it is not mandatory to store the generated cache. If nothing is specified or if there is a problem with the targeted folder (for example, a permission issue), the DTM will attempt to create a cache folder in the temporary file area of your OS. Finally, if all this does not prove successful, then the DTM will disable the cache system and notify the user if the cache system was previously required.

However, at the root directory of your project, you need to create a 'templates' folder. The absence of these directories will prevent you from using the dtm. You must add your template files into the folder you previously created. Be aware that if the templates are not placed in the correct directory, the DTM will return an error message indicating that it cannot find the template!

The DTM currently only works with 2 types of files:

  • html
  • raw text

Below you will find 2 usage contexts:

1. Minimal static generator example :

import x.templating.dtm

fn main() {
    mut dtmi := dtm.initialize()

    // No need to add this 'defer' if you have chosen to disable the cache system in the options.
    defer {
        dtmi.stop_cache_handler()
    }
    mut tmp_var := map[string]dtm.DtmMultiTypeMap{}
    tmp_var['title'] = 'V dtm best title'
    tmp_var['non_string_type'] = 7
    tmp_var['html_section_#includehtml'] = '<span>will <br> be <br> escaped <br> in <br> text mode</span>'

    render := dtmi.expand('test.txt', placeholders: &tmp_var)

    println(render)
}

and its template text format :

Title of text : @title
Value in the text : @non_string_type
HTML tags are always escaped in text file : @html_section

2. Minimal Vweb example:

import x.vweb
import x.templating.dtm
import os

pub struct App {
pub mut:
    dtmi &dtm.DynamicTemplateManager = unsafe { nil }
}

pub struct Context {
    vweb.Context
}

fn main() {
    cache_folder_path := os.join_path(os.dir(os.executable()), 'vcache_dtm')
    mut app := &App{
        dtmi: dtm.initialize(def_cache_path: cache_folder_path)
    }
    // No need to add this 'defer' if you have chosen to disable the cache system in the options.
    defer {
        app.dtmi.stop_cache_handler()
    }

    //
    Here is an example of init configuration :

    dtm.initialize(
      def_cache_path: cache_folder_path
      compress_html: false
      active_cache_server: false
      max_size_data_in_mem: 100
    )
    

    vweb.run[App, Context](mut app, 18081)
}

@['/']
pub fn (mut app App) index(mut ctx Context) vweb.Result {
    mut tmpl_var := map[string]dtm.DtmMultiTypeMap{}
    tmpl_var['title'] = 'The true title'
    html_content := app.dtmi.expand('index.html', placeholders: &tmpl_var)
    return ctx.html(html_content)
}

and its template html format :

<!doctype html>
<html>
    <head>
        <title>@title</title>
    </head>
    <body>
        <div id='container'>
            <h1>@title</h1>
        </div>
    </body>
</html>

You have a ready-to-view demonstration available here.

Available Options

There are two types of option possibilities:

  • Specific to the initialization
  • Defined for each call of expand function

Specific to the initialization

Three parameters are available:

  • def_cache_path : ( String value ) User can define the path of cache folder.
  • max_size_data_in_mem : ( Int value ) Maximum size of data allowed in memory for each cachedtemplate. The value must be specified in kilobytes. ( Default is: 500KB / Limit max is : 500KB )- compress_html : ( Bool value ) Light 'minifier' of the HTML output, to remove allunnecessary spacing. ( Default is true, parameter taken into account only for HTML files )- active_cache_server : ( Bool value ) Activate or not the template cache system. ( Defaultis true, Highly recommended to keep it enabled for optimal performance )

Regarding the compress_html option, it is recommended for performance reasons to disable it when working directly with raw template generation (i.e., with the cache system disabled).

Use it like this :

initialize(
    def_cache_path: 'your/directorie/cache/path'
    max_size_data_in_mem: 500
    compress_html: true
    active_cache_server: true
)

Defined for each call of expand function

  • placeholders ( &map[string]DtmMultiTypeMap value ) Used to map placeholders within thetemplate to their corresponding content, facilitating dynamic content insertion, by specifying values in the placeholders map. Templates can dynamically display content.

  • cache_delay_expiration ( i64 value ) Specifies the cache expiration time for the concernedpage in seconds. ( Default value is 86400 seconds or one day ). You can add any value you want in seconds as long as it remains within the indicated range ( see below ).

Possibility to use already defined cache delay constants like:

  • cache_delay_expiration_at_min : five minutes
  • cache_delay_expiration_at_max : one year
  • cache_delay_expiration_by_default : one day

For specific cases, you can cancel the generation and use of cache file, even if the cache system is active :

  • cache_delay_expiration : -1

Or set a cache that will never expire:

  • cache_delay_expiration : 0

Example :

expand('path/of/template.html',
        placeholders: &the_map_var
        cache_delay_expiration: -1
)

The PlaceHolders System

On The User's Side Code :

The placeholder system allows for the insertion of dynamic content into your template. As of the current state of the module, it accepts the following types like:

- string
- i8, i16, int, i64
- u8, u16, u32, u64
- f32, f64

Example:

mut plhs := map[string]dtm.DtmMultiTypeMap{}
plhs['placeholder_name_1'] = 'title content'
plhs['placeholder_name_2'] = 123456
plhs['placeholder_name_3_#includehtml'] = '<p>allow to include</p><span>certain HTML tags</span>'

expand('path/of/template.html',
        placeholders: &plhs
)

Pay attention to this particular tag: "_#includehtml", it enables you to include HTML in the dynamic content. Without this tag, all characters will be escaped for obvious security reasons. By using this tag, only certain HTML tags are allowed. Here is the list:

'<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>', '<menu>', '</menu>', '<table>', '</table>', '<caption>', '</caption>', '<th>', '</th>',
'<tr>', '</tr>', '<td>', '</td>', '<thread>', '</thread>', '<tbody>', '</tbody>', '<tfoot>',
'</tfoot>', '<col>', '</col>', '<colgroup>', '</colgroup>', '<header>', '</header>', '<footer>',
'</footer>', '<main>', '</main>', '<section>', '</section>', '<article>', '</article>', '<aside>',
'</aside>', '<details>', '</details>', '<dialog>', '</dialog>', '<data>', '</data>', '<summary>',
'</summary>'

Note that with a raw text template, all HTML tag inclusions are escaped.

On The Template Side :

An example of a template, corresponding to the previous subsection:

<!doctype html>
<html>
    <head>
        <title>@placeholder_name_1</title>
    </head>
    <body>
        <div id='container'>
            <h1>@placeholder_name_1</h1>
            <p>@placeholder_name_2</p>
            @placeholder_name_3
        </div>
    </body>
</html>

You will note that the '_#includehtml' directive is not found in the template with '@placeholder_name_3', and this is entirely normal. Directives are specially handled by the DTM, and including them in the name of your placeholders within the template will result in the placeholder not being found because it does not match the key name defined in the map containing the dynamic content.

Like the traditional template system in V, inclusions or placeholders start with the '@' character. The traditional inclusion system is still perfectly usable, such as:

- @include 'my/html/path.html'
- @css 'my/css/path.css'
- @js 'my/js/path.js'

In The Future

As you've understood, the DTM is still under development and optimization. There are functionalities to be added, such as data compression, managing loops or conditions within the template itself. Able to be used in contexts other than HTML and raw text.

This will come in time.

Feel free to report any issues or contribute to the project!

Constants #

const cache_delay_expiration_at_min = 300

cache_delay_expiration_at_min is the minimum setting for cache expiration delay, fixed at 5 minutes (measured in seconds).

const cache_delay_expiration_at_max = 31536000

cache_delay_expiration_at_max maximal is the maximal setting for cache expiration delay, fixed at 1 year (measured in seconds).

const cache_delay_expiration_by_default = 86400

cache_delay_expiration_by_default is the default setting for cache expiration delay, fixed at 1 day (measured in seconds).

fn initialize #

fn initialize(dtm_init_params DynamicTemplateManagerInitialisationParams) &DynamicTemplateManager

initialize create and init the 'DynamicTemplateManager' with the storage mode, cache/templates path folders. A cache directory can be created by the user for storage. If it is not defined or encounters issues such as permission problems, the DTM will attempt to create it in the OS's temporary area. If this proves impossible, the cache system will be deactivated and the user will be informed if cache system was required. Initialisation params are :

  • def_cache_path 'type string' User can define the path of cache folder.
  • max_size_data_in_mem 'type int' Maximum size of data allowed in memory for caching. The value must be specified in kilobytes. ( Default is: 500KB / Limit max is : 500KB)
  • compress_html: 'type bool' Light compress of the HTML output. ( default is true )
  • active_cache_server: 'type bool' Activate or not the template cache system. ( default is true )
  • test_cache_dir: 'type string' Used only for DTM internal test file, parameter is ignored otherwise.
  • test_template_dir: 'type string' Used only for DTM internal test file, parameter is ignored otherwise.

enum CacheStorageMode #

enum CacheStorageMode {
	memory
	disk
}

CacheStorageMode

struct DynamicTemplateManager #

@[heap]
struct DynamicTemplateManager {
mut:
	// Determines if the DTM initialization was performed successfully and if DTM is usable
	dtm_init_is_ok bool
	// Store the path to the cache directory.
	template_cache_folder string
	// Store the path to the HTML templates directory.
	template_folder string
	// cache database
	template_caches shared []TemplateCache = []TemplateCache{}
	// counter for each individual TemplateCache created/updated
	id_counter       int = 1
	ch_cache_handler chan TemplateCache = chan TemplateCache{cap: dtm.cache_handler_channel_cap}
	// 'id_to_handlered' field is used exclusively by the cache handler to update or delete specific 'TemplateCache' in the cache database.
	id_to_handlered     int
	close_cache_handler bool
	// Initialisation params options for these two (Compress_html and active_cache_server)
	compress_html       bool = true
	active_cache_server bool = true
	// Initialisation of max data size in memory storage
	max_size_data_in_memory int = dtm.max_size_data_in_memory
	// This array is designed to store a control process that checks whether cached data is currently in use while simultaneously handling expiration.
	// This allows for the harmonious management of both aspects and facilitates the necessary actions.
	nbr_of_remaining_template_request shared []RemainingTemplateRequest = []RemainingTemplateRequest{}
	//	Dtm clock
	c_time            i64
	ch_stop_dtm_clock chan bool = chan bool{cap: 5}
	// Store small information about already cached pages to improve the verification speed of the check_tmpl_and_placeholders_size function.
	html_file_info shared map[string]HtmlFileInfo = map[string]HtmlFileInfo{}
	// Indicates whether the cache file storage directory is located in a temporary OS area
	cache_folder_is_temporary_storage bool
	// Handler for all threads used in the DTM
	threads_handler []thread = []thread{}
	// This channel used only for CI. Allows to check during CI tests in case of slowness in the creation/management of the cache to allow enough time for it to be done
	is_ready chan bool = chan bool{cap: 5}
	// If despite the synchronization attempt during the cache handler tests nothing happens, cancel the tests targeting the cached data
	abort_test bool
}

DynamicTemplateManager

fn (DynamicTemplateManager) expand #

fn (mut tm DynamicTemplateManager) expand(tmpl_path string, tmpl_var TemplateCacheParams) string

expand manages the cache and returns generated HTML. Requires an initialization via 'initialize' to running. To use this function, HTML templates must be located in the 'templates' directory at the project's root. However, it allows the use of subfolder paths within the 'templates' directory, enabling users to structure their templates in a way that best suits their project's organization.

fn (DynamicTemplateManager) stop_cache_handler #

fn (mut tm DynamicTemplateManager) stop_cache_handler()

stop_cache_handler signals the termination of the cache handler by setting 'close_cache_handler' to true and sending a signal through the channel which will trigger a cascading effect to close the cache handler thread as well as the DTM clock thread.

struct DynamicTemplateManagerInitialisationParams #

@[params]
struct DynamicTemplateManagerInitialisationParams {
pub:
	def_cache_path       string
	compress_html        bool = true
	active_cache_server  bool = true
	max_size_data_in_mem int  = dtm.max_size_data_in_memory
	test_cache_dir       string
	test_template_dir    string
}

DynamicTemplateManagerInitialisationParams is used with 'initialize' function. (See below at initialize section)

struct TemplateCacheParams #

@[params]
struct TemplateCacheParams {
pub:
	placeholders           &map[string]DtmMultiTypeMap = &map[string]DtmMultiTypeMap{}
	cache_delay_expiration i64 = dtm.cache_delay_expiration_by_default
}

TemplateCacheParams are used to specify cache expiration delay and provide placeholder data for substitution in templates.