Stubble Documentation
repository·master·Indexed 19 days ago
https://github.com/stubbleorg/stubbleA 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.
What's inside Stubble
- 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.
Customize Truthiness Evaluation
masterStubble 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
DataTablewhere truthiness depends onRows.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-nullbooldetermines the truthiness.Handle asynchronous and synchronous template lookups
masterThe
IStubbleLoaderinterface supports both synchronous (Load) and asynchronous (LoadAsync) template retrieval.To optimize performance, Stubble uses
ValueTaskfor 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.
Load templates using custom loaders
masterStubble 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.Compile templates for performance
masterTo improve performance, Stubble can compile templates into functions that accept strongly typed arguments. To use this feature, instantiate a
StubbleCompilationRendererand use theCompileorCompileAsyncmethods after configuring the renderer.// Conceptual usage of template compilation var renderer = new StubbleCompilationRenderer(); // ... configure renderer ... var compiledFunc = await renderer.CompileAsync("template_name", context);Choose between Stubble and Nustache
masterDeciding 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.
Extend Stubble with custom logic
masterStubble allows for loosely coupled extensions to its parsing and rendering processes. Extensions can be added to theIRendererSettingsBuilderusing extension methods to provide a simplified user experience. For detailed implementation details on specific extension types, refer to the extensibility documentation.Use Section Lambdas in templates
masterSection 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 namedrender), 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.Corerenderer; 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"Use Tag Lambdas in templates
masterTag 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.Corerenderer; 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"Implement Tag Rendering with MustacheTokenRenderer
masterIf you add a new tag parser, you likely need to implement its rendering logic. This is done by adding a
MustacheTokenRendererfor your specific renderer type.The Rendering Hierarchy:
RendererBase(e.g.,StringRender): Defines the high-level rendering target (e.g., string or compiled function).MustacheTokenRenderer(e.g.,StringObjectRenderer<T>): Implements how a specific tag is rendered for a specificRendererBase.
To register your new renderer, use the
OrderedListofTokenRenderersavailable inIRenderSettingsBuilder.Recursive partials in Stubble vs Nustache
masterUnlike 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.Extend the Rendering System with Renderer Extensions
masterStubble 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 fromStubbleBuilder) 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)