Stubble Documentation

repository·master·Indexed 19 days ago

https://github.com/stubbleorg/stubble

A high-performance, lightweight, and spec-compliant implementation of the Mustache templating system (v.1.1.2) for .NET. Stubble focuses on raw rendering speed and strict adherence to the Mustache specification, offering features such as template compilation via StubbleCompilationRenderer for high-performance scenarios, custom template loaders, and an extensible architecture for adding custom ValueGetters, truthiness evaluations, and parser pipelines.

Tokens
4.3K
Snippets
10
Records
23
Agent score
66%

What's inside Stubble

  1. What is Stubble?

    master
    Stubble is a lightweight, spec-compliant implementation of the Mustache template system for .NET. It is designed to be a fast and simple parser and renderer by focusing only on the bare essentials of the Mustache specification (v.1.1.2, including lambdas). It avoids non-spec tags, complex object-to-value logic, or built-in template discovery methods to maintain high performance.
  2. Customize Truthiness Evaluation

    master

    Stubble uses truthy/falsey checks to determine if sections (or inverted sections) should render. While defaults exist for common types, you can provide custom check functions for types that don't follow standard rules (e.g., a DataTable where truthiness depends on Rows.Count > 0).

    Custom check functions are executed sequentially. To indicate that a specific check function is not applicable to the current value, return null. The first function to return a non-null bool determines the truthiness.

  3. Handle asynchronous and synchronous template lookups

    master

    The IStubbleLoader interface supports both synchronous (Load) and asynchronous (LoadAsync) template retrieval.

    To optimize performance, Stubble uses ValueTask for asynchronous operations. This is designed for scenarios where most lookups are actually synchronous, allowing the system to avoid memory allocations in those cases.

    Best Practice: If your template lookups require asynchronous work (e.g., database or network calls), it is recommended to perform the work once and cache the results. This ensures that subsequent lookups remain primarily synchronous and performant.

  4. Load templates using custom loaders

    master
    Stubble provides a minimal set of built-in template loaders but is designed to be extensible. You can implement your own synchronous or asynchronous methods to retrieve templates by name via the provided loader interfaces. This allows you to fetch templates from various sources (files, databases, memory, etc.) or use loaders from separate packages.
  5. Compile templates for performance

    master

    To improve performance, Stubble can compile templates into functions that accept strongly typed arguments. To use this feature, instantiate a StubbleCompilationRenderer and use the Compile or CompileAsync methods after configuring the renderer.

    // Conceptual usage of template compilation
    var renderer = new StubbleCompilationRenderer();
    // ... configure renderer ...
    var compiledFunc = await renderer.CompileAsync("template_name", context);
  6. Choose between Stubble and Nustache

    master

    Deciding which library to use depends on your requirements for complexity vs. performance:

    • Use Stubble if: You need a high-performance, lightweight, and strictly spec-compliant Mustache renderer with minimal overhead.
    • Use Nustache if: You require extra features that extend beyond the Mustache spec, such as specialized input types, built-in helpers, or more complex compilation features.
  7. Extend Stubble with custom logic

    master
    Stubble allows for loosely coupled extensions to its parsing and rendering processes. Extensions can be added to the IRendererSettingsBuilder using extension methods to provide a simplified user experience. For detailed implementation details on specific extension types, refer to the extensibility documentation.
  8. Use Section Lambdas in templates

    master

    Section Lambdas wrap sections of a template (e.g., {{#Foo}}...{{/Foo}}). The contents of the section are passed into the lambda as an argument.

    Supported Types:

    • Func<dynamic, string, object>
    • Func<string, object>
    • Func<string, Func<string, string>, object>
    • Func<dynamic, string, Func<string, string>, object>

    Advanced Usage: If the signature includes a Func<string, string> (often named render), this function provides a renderer that can render the passed template string within the current context. This is useful for highly dynamic templates but is less performant than performing logic outside the template.

    Note: Lambdas are only supported by the Stubble.Core renderer; they are not supported by the compilation renderer.

    // Example 1: Modifying the section content string
    var obj = new {
       Bar = "Bar",
       Foo = new Func<dynamic, string, object>((dyn, str) => { return str.Replace("World", dyn.Bar); })
    };
    stubble.Render("{{#Foo}} Hello World {{/Foo}}", obj); // Outputs: " Hello Bar "
    
    // Example 2: Returning a new template string to be rendered
    var obj2 = new {
       Bar = "Bar",
       Foo = new Func<string, object>((str) => { return "Foo {{Bar}}"; })
    };
    stubble.Render("{{#Foo}} Hello World {{/Foo}}", obj2); // Outputs: "Foo Bar"
    
    // Example 3: Using the provided renderer to render nested tags
    var obj3 = new {
       Bar = "Bar",
       Foo = new Func<string, Func<string, string>, object>((str, render) => { return "Foo " + render("{{Bar}}"); })
    };
    stubble.Render("{{#Foo}} Hello World {{/Foo}}", obj3); // Outputs: "Foo Bar"
  9. Use Tag Lambdas in templates

    master

    Tag Lambdas are anonymous functions used to replace a tag in a template. They are rendered in place of the tag.

    Supported Types:

    • Func<dynamic, object>
    • Func<object>

    Note: If you return tags (strings containing {{tags}}) from a lambda, they will be expanded before being rendered. This allows you to build new templates dynamically from within a lambda.

    Note: Lambdas are only supported by the Stubble.Core renderer; they are not supported by the compilation renderer.

    // Example: Using a Tag Lambda to access a property via dynamic context
    var obj = new {
       Bar = "Bar",
       Foo = new Func<dynamic, object>((dyn) => { return dyn.Bar; })
    };
    
    stubble.Render("{{Foo}}", obj); // Outputs: "Bar"
  10. Implement Tag Rendering with MustacheTokenRenderer

    master

    If you add a new tag parser, you likely need to implement its rendering logic. This is done by adding a MustacheTokenRenderer for your specific renderer type.

    The Rendering Hierarchy:

    1. RendererBase (e.g., StringRender): Defines the high-level rendering target (e.g., string or compiled function).
    2. MustacheTokenRenderer (e.g., StringObjectRenderer<T>): Implements how a specific tag is rendered for a specific RendererBase.

    To register your new renderer, use the OrderedList of TokenRenderers available in IRenderSettingsBuilder.

  11. Recursive partials in Stubble vs Nustache

    master
    Unlike Nustache, Stubble does not have limitations regarding recursive partials or using the same partial multiple times within a single template. You can freely use recursive partials and multiple instances of the same partial in your templates.
  12. Extend the Rendering System with Renderer Extensions

    master

    Stubble uses a visitor pattern for rendering. This separates parsing from rendering, allowing you to add new tag renderers without modifying the parser.

    To add a new renderer, the recommended approach is to create a new IStubbleBuilder (ideally by inheriting from StubbleBuilder) that returns your custom renderer.

    Key classes to explore for implementation:

    • StubbleVisitorRenderer (the starting point)
    • StringRender (a specific renderer type)
    • RendererBase (the base for all renderers)
    • MustacheTokenRenderer (handles specific token rendering)