### Liquid View Start File Source: https://github.com/sebastienros/fluid/blob/main/MinimalApis.LiquidViews/README.md The `_viewstart.liquid` file is executed for each view in its folder. This example specifies the layout file to be used for rendering. ```liquid {% layout '_Layout' %} ``` -------------------------------- ### Example: Synchronous String Rendering with Context Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md A concise example of synchronously rendering a template to a string using a TemplateContext. ```csharp var result = template.Render(context); ``` -------------------------------- ### Usage Examples Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Illustrates how to configure TemplateOptions for various customization scenarios. ```APIDOC ## Usage Examples ### Basic Configuration ```csharp var options = new TemplateOptions { StrictVariables = true, StrictFilters = true, CultureInfo = new CultureInfo("en-US"), }; options.Filters.AddFilter("customfilter", MyCustomFilter); ``` ### Custom Member Access ```csharp var options = new TemplateOptions(); // Register specific properties as accessible options.MemberAccessStrategy.Register( (person, name) => name switch { "Name" => person.Name, "Email" => person.Email, _ => null } ); ``` ### Setting Global Variables ```csharp options.Scope.SetValue("AppName", new StringValue("MyApp")); options.Scope.SetValue("Version", NumberValue.Create(1)); ``` ### Custom Value Converters ```csharp options.ValueConverters.Add(obj => { if (obj is MyCustomType custom) { return new StringValue(custom.ToString()); } return null; // Let other converters handle it }); ``` ### Undefined Variable Handling ```csharp options.Undefined = async (name, type) => { Console.WriteLine($"Template accessed undefined variable: {name}"); return new StringValue($"[undefined: {name}]"); }; ``` ``` -------------------------------- ### Usage Example with TemplateContext and Scopes Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Scope.md Provides a comprehensive usage example of Scope and TemplateContext, showing how to set initial values, simulate block scopes using EnterChildScope/ReleaseScope, and manage variable accessibility. ```csharp var root = new Scope(); root.SetValue("AppName", StringValue.Create("MyApp")); root.SetValue("Version", NumberValue.Create(1)); var options = new TemplateOptions(); options.Scope = root; var context = new TemplateContext(options: options); // Simulate a block (if statement) context.EnterChildScope(); context.SetValue("blockVar", NumberValue.Create(42)); // blockVar is accessible here context.ReleaseScope(); // blockVar is no longer accessible; AppName and Version still are ``` -------------------------------- ### Miscellaneous Filters Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Provides examples for general-purpose filters including default, date, and slugify. ```csharp // {{ undefined_value | default: "fallback" }} => "fallback" // {{ date_value | date: "yyyy-MM-dd" }} => "2024-01-01" // {{ "hello world" | slugify }} => "hello-world" ``` -------------------------------- ### Custom Filter Usage Examples Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Demonstrates how to use the custom filters defined previously within Fluid templates. These examples show the input, filter name, and expected output. ```liquid {{ "hello" | reverse }} => "olleh" {{ "x" | repeat: 3 }} => "xxx" {{ "https://api.example.com" | fetch }} => API response ``` -------------------------------- ### Number Filters Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Shows examples of number formatting and arithmetic filters like ceil, floor, round, and divided_by. ```csharp var n = 3.14; // {{ n | ceil }} => 4 // {{ n | floor }} => 3 // {{ n | round }} => 3 // {{ 10 | divided_by: 3 }} => 3 (integer division) ``` -------------------------------- ### Example: TextWriter Rendering with Context Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Demonstrates rendering a template to the console output using a custom TemplateContext. ```csharp var context = new TemplateContext(model); await template.RenderAsync(Console.Out, context); ``` -------------------------------- ### Filter Chaining Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Shows how multiple filters can be chained together, with execution proceeding from left to right. ```liquid {{ " hello world " | strip | upcase | append: "!" }} ``` -------------------------------- ### Hello World with FluidParser Source: https://github.com/sebastienros/fluid/blob/main/README.md A simple 'Hello World' example demonstrating how to parse a template string and render it with a model using FluidParser and TemplateContext. ```C# var parser = new FluidParser(); var model = new { Firstname = "Bill", Lastname = "Gates" }; var source = "Hello {{ Firstname }} {{ Lastname }}"; if (parser.TryParse(source, out var template, out var error)) { var context = new TemplateContext(model); Console.WriteLine(template.Render(context)); } else { Console.WriteLine($"Error: {error}"); } ``` -------------------------------- ### Example: Simple Synchronous String Rendering Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Demonstrates the simplest way to synchronously render a template to a string without specifying a context. ```csharp var result = template.Render(); ``` -------------------------------- ### Example: Simple TextWriter Rendering Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Shows how to use the simple RenderAsync extension to render a template directly to the console output. ```csharp var template = parser.Parse("Hello World"); await template.RenderAsync(Console.Out); ``` -------------------------------- ### Render Custom Identifier Tag Source: https://github.com/sebastienros/fluid/blob/main/README.md Example of using the custom 'hello' tag with an identifier. ```liquid {% hello you %} ``` -------------------------------- ### Array Filters Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Illustrates array manipulation filters such as sort, join, and first. ```csharp var arr = new[] { 3, 1, 2 }; // {{ arr | sort }} => [1, 2, 3] // {{ arr | join: "-" }} => "3-1-2" // {{ arr | first }} => 3 ``` -------------------------------- ### Basic Liquid Template Example Source: https://github.com/sebastienros/fluid/blob/main/README.md Demonstrates a basic Liquid template with a for loop, accessing product properties, and applying filters. Note the whitespace control in the output. ```Liquid ``` -------------------------------- ### TemplateContext Usage Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateContext.md Demonstrates setting up a TemplateContext with a model, additional variables, locale, and custom current time before rendering a template. ```csharp var parser = new FluidParser(); var model = new { FirstName = "John", LastName = "Doe" }; var context = new TemplateContext(model); // Set additional variables context.SetValue("title", "Mr."); context.SetValue("year", 2024); // Configure locale context.CultureInfo = new CultureInfo("fr-FR"); // Set custom current time context.Now = () => new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero); // Parse and render template var template = parser.Parse("{{ title }} {{ FirstName }} {{ LastName }}"); await template.RenderAsync(Console.Out, context); ``` -------------------------------- ### String Filters Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Demonstrates common string manipulation filters like upcase, capitalize, replace, and slice. ```csharp var s = "hello world"; // {{ s | upcase }} => "HELLO WORLD" // {{ s | capitalize }} => "Hello world" // {{ s | replace: "world", "you" }} => "hello you" // {{ s | slice: 0, 5 }} => "hello" ``` -------------------------------- ### Typical Template Options Setup Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/configuration.md Create and configure TemplateOptions once at application startup, including settings for strict variables, culture, file provider, custom filters, and global scope. ```csharp // Create options at application startup var options = new TemplateOptions { StrictVariables = Environment.IsProduction == false, CultureInfo = new CultureInfo("en-US"), FileProvider = new PhysicalFileProvider("./templates"), }; // Register custom filters options.Filters.AddFilter("shout", ShoutFilter); // Register global variables options.Scope.SetValue("SiteName", new StringValue("Example")); // Create parser var parserOptions = new FluidParserOptions { AllowFunctions = true }; var parser = new FluidParser(parserOptions); // Reuse in all rendering operations public async Task RenderAsync(string templateSource, object model) { var template = parser.Parse(templateSource); var context = new TemplateContext(model, options); await template.RenderAsync(Console.Out, context); } ``` -------------------------------- ### Set Global Variable Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Demonstrates how to set global variables within the template scope. ```csharp options.Scope.SetValue("appName", new StringValue("MyApp")); ``` -------------------------------- ### Complete Template Rendering Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Demonstrates the typical rendering flow: parsing a template, creating a context with a model, and rendering the template to a string writer. The result is the rendered string. ```csharp // Complete example var parser = new FluidParser(); var template = parser.Parse("{{ greeting }} {{ name }}!"); var model = new { greeting = "Hello", name = "World" }; var context = new TemplateContext(model); using var writer = new StringWriter(); await template.RenderAsync(writer, context); var result = writer.ToString(); // "Hello World!" ``` -------------------------------- ### Example: Rendering IFluidTemplate to String Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Demonstrates how to parse a Liquid template, set up a TemplateContext with variables, and render the template to a StringWriter asynchronously. ```csharp var template = parser.Parse("Hello {{ name }}!"); var context = new TemplateContext(); context.SetValue("name", "World"); using var output = new StringWriter(); await template.RenderAsync(output, NullEncoder.Default, context); var result = output.ToString(); ``` -------------------------------- ### Setting Global Variables Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Sets global variables 'AppName' and 'Version' with string and number values respectively. ```csharp options.Scope.SetValue("AppName", new StringValue("MyApp")); options.Scope.SetValue("Version", NumberValue.Create(1)); ``` -------------------------------- ### Render Template Asynchronously Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Shows how to render a compiled template asynchronously to a writer. This is suitable for scenarios where direct string output is not required or for large outputs. ```csharp await template.RenderAsync(writer, context) ``` -------------------------------- ### TemplateOptions.FileProvider Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the IFileProvider used to load files for include and render statements. Defaults to NullFileProvider. ```APIDOC ## TemplateOptions.FileProvider ### Description Gets or sets the `IFileProvider` used to load files for include and render statements. ### Property Signature ```csharp public IFileProvider FileProvider { get; set; } ``` ### Type `IFileProvider` ### Default `NullFileProvider` (no files available) ### Example ```csharp options.FileProvider = new PhysicalFileProvider("/templates"); ``` ``` -------------------------------- ### Scope Chain Behavior Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Scope.md Illustrates how variables are resolved through the scope chain, including shadowing and inheritance. ```APIDOC ## Scope Chain Behavior Variables are resolved through the scope chain: ```csharp var root = new Scope(); root.SetValue("a", NumberValue.Create(1)); root.SetValue("b", NumberValue.Create(2)); var child = new Scope(root); child.SetValue("b", NumberValue.Create(20)); child.SetValue("c", NumberValue.Create(30)); child.GetValue("a"); // Finds in root: 1 child.GetValue("b"); // Finds in child (shadows root): 20 child.GetValue("c"); // Finds in child: 30 root.GetValue("c"); // Undefined (not in root or parents) ``` ``` -------------------------------- ### URL Encoding Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Shows how to render a template with URL encoding using UrlEncoder.Default. This escapes special URL characters. ```csharp // URL encoding (escapes special URL characters) await template.RenderAsync(writer, context, UrlEncoder.Default); ``` -------------------------------- ### Parse Template String Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Demonstrates the basic usage of FluidParser to parse a template string. This is a fundamental step before rendering. ```csharp parser.Parse("{{ name }}") ``` -------------------------------- ### Example: Advanced TextWriter Rendering with Culture Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Illustrates rendering a template with specific culture formatting and context isolation using HtmlEncoder. ```csharp var template = parser.Parse("{{ 10 | divide: 3 }}"); var context = new TemplateContext(); context.CultureInfo = new CultureInfo("fr-FR"); using var writer = new StringWriter(); await template.RenderAsync(writer, context, HtmlEncoder.Default, isolateContext: true); Console.WriteLine(writer.ToString()); // Output formatted according to French culture ``` -------------------------------- ### No Encoding Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Illustrates rendering a template with no encoding applied by using a NullEncoder. This is useful when output does not require escaping. ```csharp // No encoding using var output = new NullEncoder(); await template.RenderAsync(writer, context, output); ``` -------------------------------- ### Custom Value Converter Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Adds a custom value converter that transforms 'MyCustomType' objects into string values. ```csharp options.ValueConverters.Add(obj => { if (obj is MyCustomType custom) { return new StringValue(custom.ToString()); } return null; // Let other converters handle it }); ``` -------------------------------- ### Example: Using Default Member Access Strategy Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/MemberAccessStrategy.md Demonstrates accessing public properties of a C# object within a Fluid template using the default strategy. ```csharp public class Person { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } var options = new TemplateOptions(); var strategy = options.MemberAccessStrategy; // DefaultMemberAccessStrategy // Template can access all public properties: // {{ person.FirstName }} // {{ person.LastName }} // {{ person.Age }} ``` -------------------------------- ### Custom Filter: Async Fetch Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Example of creating an asynchronous custom filter to fetch content from a URL. ```APIDOC ### Async Filter ```csharp static ValueTask FetchFilter(FluidValue input, FilterArguments args, TemplateContext context) { return FetchAsync(input.ToStringValue(context)); } static async ValueTask FetchAsync(string url) { using var client = new HttpClient(); var response = await client.GetStringAsync(url); return StringValue.Create(response); } options.Filters.AddFilter("fetch", FetchFilter); // Usage: {{ url | fetch }} (awaits HTTP request) ``` ``` -------------------------------- ### List All Unit Tests Source: https://github.com/sebastienros/fluid/blob/main/AGENTS.md Use the dotnet CLI to list all available unit tests within the project. This command is useful for getting an overview of all testable components. ```shell dotnet test --list-tests ``` -------------------------------- ### Custom Member Access Strategy Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Demonstrates registering a custom member accessor to provide computed properties like 'FullName' and 'Age' for a 'Person' type. ```csharp var strategy = new DefaultMemberAccessStrategy(); // Register a custom accessor for a specific property strategy.Register( (person, name) => name switch { "FullName" => person.FirstName + " " + person.LastName, "Age" => DateTime.Now.Year - person.BirthYear, _ => null } ); options.MemberAccessStrategy = strategy; ``` -------------------------------- ### Asynchronous Template Rendering Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Provides an example of asynchronous template rendering to prevent blocking the main thread, suitable for web applications. ```csharp public async Task RenderTemplateAsync(string source, object model) { var template = _parser.Parse(source); var context = new TemplateContext(model); using var writer = new StringWriter(); await template.RenderAsync(writer, context); return writer.ToString(); } // In ASP.NET Core controller: public async Task GetPage(string id) { var template = await _templateService.LoadTemplate(id); var html = await RenderTemplateAsync(template, pageData); return Content(html, "text/html"); } ``` -------------------------------- ### Undefined Variable Handling Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Configures a handler for undefined variables, logging the access and returning a default string value. ```csharp options.Undefined = async (name, type) => { Console.WriteLine($"Template accessed undefined variable: {name}"); return new StringValue($"[undefined: {name}]"); }; ``` -------------------------------- ### Example: Find and Run Golden Test Source: https://github.com/sebastienros/fluid/blob/main/AGENTS.md Demonstrates finding the test ID for 'identifiers_ascii_lowercase' and then running it using the obtained ID. This illustrates the two-step process for individual test execution. ```shell # Find the test ID for identifiers_ascii_lowercase ./Fluid.Tests/bin/Debug/net10.0/Fluid.Tests -preEnumerateTheories -list full 2>&1 | grep -B2 -A5 "identifiers_ascii_lowercase" # Run it (use the ID from the output above) ./Fluid.Tests/bin/Debug/net10.0/Fluid.Tests -preEnumerateTheories -id "71958641a76ed3a8219c73a9e5f956b4ecf2cb1b07ca728d3d8c8365646e7895" ``` -------------------------------- ### Create a Root Scope Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Scope.md Instantiate a new Scope object to create a root scope with no parent. This is the starting point for a scope hierarchy. ```csharp var scope = new Scope(); ``` -------------------------------- ### HTML Encoding Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Demonstrates rendering a template with HTML encoding enabled using HtmlEncoder.Default. This escapes characters like <, >, &, ", and '. ```csharp // HTML encoding (escapes <, >, &, ", ') await template.RenderAsync(writer, context, HtmlEncoder.Default); ``` -------------------------------- ### FunctionArguments Example Usage Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Illustrates how function arguments are accessed within macros when template functions are enabled. Shows a macro definition and its invocation. ```csharp // When AllowFunctions=true, templates can call functions: // {% macro greet(name) %}Hello {{ name }}{% endmacro %} // {{ greet("Alice") }} // Inside the function, arguments are available as FunctionArguments ``` -------------------------------- ### FluidValue Type Checking Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Demonstrates how to create a FluidValue and check its type. Use this to conditionally process values based on their data type. ```csharp var value = FluidValue.Create(42, options); if (value.Type == FluidValues.Number) { var num = value.ToNumberValue(context); } ``` -------------------------------- ### Format String with format_string Filter Source: https://github.com/sebastienros/fluid/blob/main/README.md Example of using the custom 'format_string' filter to format a string with placeholders and a numeric value formatted as currency. ```Input "hello {0} {1:C}" | format_string: "world" 123 ``` -------------------------------- ### Registering a Custom Filter Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Demonstrates how to add a custom filter to the FilterCollection. This example registers an 'upcase' filter that converts input strings to uppercase. ```csharp var filters = new FilterCollection(); filters.AddFilter("upcase", (input, args, context) => new ValueTask(StringValue.Create(input.ToStringValue(context).ToUpper())) ); ``` -------------------------------- ### TemplateOptions with Trimming Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Configures template options to enable whitespace trimming. Set the Trimming property to control left, right, or both, and Greedy to trim all successive whitespace. ```csharp var options = new TemplateOptions { Trimming = TrimmingFlags.Left | TrimmingFlags.Right, Greedy = true, // Trim all successive whitespace }; ``` -------------------------------- ### Implementing a Divide Filter Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Provides an example implementation of a custom filter named 'divide'. This filter takes a divisor as an argument and returns the result of dividing the input by the divisor, throwing an exception for division by zero. ```csharp static ValueTask DivideFilter(FluidValue input, FilterArguments args, TemplateContext context) { var divisor = args.At(0).ToNumberValue(context); if (divisor == 0) throw new DivideByZeroException(); var result = input.ToNumberValue(context) / divisor; return new ValueTask(NumberValue.Create(result)); } options.Filters.AddFilter("divide", DivideFilter); ``` -------------------------------- ### Registering Custom Filters in Fluid Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Extend Fluid's capabilities by registering custom filters. This example shows how to add simple, argument-based, and asynchronous filters. ```csharp var options = new TemplateOptions(); // Simple filter options.Filters.AddFilter("reverse", (input, args, context) => new ValueTask(StringValue.Create( new string(input.ToStringValue(context).Reverse().ToArray()) )) ); // Filter with arguments options.Filters.AddFilter("repeat", (input, args, context) => { var count = (int)args.At(0).ToNumberValue(context); var text = input.ToStringValue(context); return new ValueTask(StringValue.Create( string.Concat(Enumerable.Repeat(text, count)) )); }); // Async filter options.Filters.AddFilter("fetch", async (input, args, context) => { using var client = new HttpClient(); var url = input.ToStringValue(context); var content = await client.GetStringAsync(url); return StringValue.Create(content); }); var context = new TemplateContext(model, options); ``` -------------------------------- ### Liquid Template Example Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Scope.md Shows a Liquid template demonstrating variable scope behavior within if statements. Variables assigned inside an if block are accessible within the block but not outside after it concludes. ```liquid {% assign x = 1 %} {% if true %} {% assign y = 2 %} {{ x }} {{ y }} {# Both accessible #} {% endif %} {{ x }} {{ y }} {# Only x accessible; y undefined #} ``` -------------------------------- ### Custom AstVisitor for Template Analysis Source: https://github.com/sebastienros/fluid/blob/main/README.md Create a custom visitor by inheriting from AstVisitor to analyze and potentially alter a template's structure. This example checks for identifier access. ```csharp public class IdentifierIsAccessedVisitor : AstVisitor { private readonly string _identifier; public IdentifierIsAccessedVisitor(string identifier) { _identifier = identifier; } public bool IsAccessed { get; private set; } public override IFluidTemplate VisitTemplate(IFluidTemplate template) { // Initialize the result each time a template is visited with the same visitor instance IsAccessed = false; return base.VisitTemplate(template); } protected override Expression VisitMemberExpression(MemberExpression memberExpression) { var firstSegment = memberExpression.Segments.FirstOrDefault() as IdentifierSegment; if (firstSegment != null) { IsAccessed |= firstSegment.Identifier == _identifier; } return base.VisitMemberExpression(memberExpression); } } ``` -------------------------------- ### TemplateParsed Delegate Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the delegate executed after a template is parsed. ```csharp public TemplateParsedDelegate TemplateParsed { get; set; } ``` -------------------------------- ### Custom Filter: Validation Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Example of a custom filter demonstrating input and argument validation. ```APIDOC ### Filter Validation ```csharp static ValueTask DivideFilter(FluidValue input, FilterArguments args, TemplateContext context) { if (args.Count != 1) { throw new LiquidException($"'divide' filter requires exactly 1 argument, got {args.Count}"); } var divisor = args.At(0).ToNumberValue(context); if (divisor == 0) { throw new InvalidOperationException("Division by zero"); } var result = input.ToNumberValue(context) / divisor; return new ValueTask(NumberValue.Create(result)); } ``` ``` -------------------------------- ### FilterArguments Example Usage Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/types.md Demonstrates how to access filter arguments within a custom filter implementation. Shows retrieving positional arguments by index for string manipulation. ```csharp static ValueTask ReplaceFilter(FluidValue input, FilterArguments args, TemplateContext context) { var search = args.At(0).ToStringValue(context); var replace = args.At(1).ToStringValue(context); var result = input.ToStringValue(context).Replace(search, replace); return new ValueTask(StringValue.Create(result)); } // Usage: "hello world" | replace: "world", "universe" // - At(0) = "world" // - At(1) = "universe" ``` -------------------------------- ### Define Layout and Assign Variable in _ViewStart Source: https://github.com/sebastienros/fluid/blob/main/README.md Shows how to set a layout template and assign a variable within a _ViewStart.liquid file, which is executed before other views in its directory. ```Liquid {% layout '_layout.liquid' %} {% assign background = 'ffffff' } ``` -------------------------------- ### Undefined Delegate Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the delegate executed when an undefined variable is accessed. ```csharp public UndefinedDelegate Undefined { get; set; } ``` -------------------------------- ### Captured Delegate Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the delegate executed when a capture tag is evaluated. ```csharp public CapturedDelegate Captured { get; set; } ``` -------------------------------- ### Usage of Custom AST Rewriter Source: https://github.com/sebastienros/fluid/blob/main/README.md Demonstrates how to use a custom AST rewriter to modify a Fluid template. The example shows parsing a template, applying the visitor, and rendering the modified template. ```csharp var template = new FluidParser().Parse("{{ 1 | plus: 2 }}"); var visitor = new ReplacePlusFiltersVisitor(); var changed = visitor.VisitTemplate(template); var result = changed.Render(); Console.WriteLine(result); // writes -1 ``` -------------------------------- ### Assigned Delegate Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the delegate executed when an assign tag is evaluated. ```csharp public AssignedDelegate Assigned { get; set; } ``` -------------------------------- ### Custom Filter: Quote Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Example of creating a simple custom filter that wraps input in quotes. ```APIDOC ## Custom Filters ### Simple Filter ```csharp static ValueTask QuoteFilter(FluidValue input, FilterArguments args, TemplateContext context) { var text = input.ToStringValue(context); return new ValueTask(StringValue.Create($"\"{text}\"")); } options.Filters.AddFilter("quote", QuoteFilter); // Usage: {{ "hello" | quote }} => "hello" ``` ``` -------------------------------- ### Basic TemplateOptions Configuration Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/configuration.md Sets up fundamental template parsing and execution options. Use this for a standard configuration. ```csharp var options = new TemplateOptions { StrictVariables = true, StrictFilters = true, OutputBufferSize = 16384, MaxSteps = 10000, MaxRecursion = 100, }; ``` -------------------------------- ### ModelNamesComparer Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateContext.md Gets the comparer used for model property names. This property is read-only after initialization. ```csharp public StringComparer ModelNamesComparer { get; private set; } ``` -------------------------------- ### Configure Template and Parser Options Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Illustrates how to instantiate `TemplateOptions` and `FluidParserOptions` to customize template rendering and parsing behavior, respectively. This allows fine-grained control over the template engine's features. ```csharp var options = new TemplateOptions { /* settings */ }; var parserOptions = new FluidParserOptions { /* features */ }; ``` -------------------------------- ### Using the Custom XOR Operator in Liquid Source: https://github.com/sebastienros/fluid/blob/main/README.md Demonstrates the usage of the custom 'xor' operator within a Liquid template. This example shows a conditional statement that evaluates to true if exactly one operand is true. ```liquid {% if true xor false %}Hello{% endif %} ``` -------------------------------- ### Custom Filter: Pad with Arguments Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Example of creating a custom filter that accepts arguments for padding and character. ```APIDOC ### Filter with Arguments ```csharp static ValueTask PadFilter(FluidValue input, FilterArguments args, TemplateContext context) { var text = input.ToStringValue(context); var width = (int)args.At(0).ToNumberValue(context); var char c = args.Count > 1 ? args.At(1).ToStringValue(context)[0] : ' '; return new ValueTask(StringValue.Create(text.PadRight(width, c))); } options.Filters.AddFilter("pad", PadFilter); // Usage: {{ "hi" | pad: 5 }} => "hi " // {{ "hi" | pad: 5, "*" }} => "hi***" ``` ``` -------------------------------- ### Configure Minimal API with Liquid Views Source: https://github.com/sebastienros/fluid/blob/main/MinimalApis.LiquidViews/README.md Sets up the ASP.NET Minimal API to use Liquid views by adding Fluid services. This snippet demonstrates the basic configuration required in the Program.cs file. ```csharp var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddFluid(); var app = builder.Build(); // Configure the HTTP request pipeline. app.MapGet("", () => Results.Extensions.View("Index", new Todo(1, "Go back to work!", false))); await app.RunAsync(); record Todo(int Id, string Name, bool IsComplete); ``` -------------------------------- ### Catching FluidException During Parsing Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/errors.md Example of how to catch a FluidException when template parsing fails, such as due to invalid syntax. ```csharp try { var parser = new FluidParser(); var template = parser.Parse("{% if invalid syntax %}"); // Invalid if tag } catch (FluidException ex) { Console.WriteLine($"Template error: {ex.Message}"); } ``` -------------------------------- ### Create Default FluidParser Instance Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/FluidParser.md Instantiate FluidParser with default settings. This is the most common way to create a parser. ```csharp var parser = new FluidParser(); ``` -------------------------------- ### Parse Templates with Options and Error Handling Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Shows how to create a `FluidParser` with specific `TemplateOptions` and how to use `TryParse` for parsing with error reporting. This is useful for handling potential parsing issues gracefully. ```csharp var parser = new FluidParser(options); var template = parser.Parse(source); var success = parser.TryParse(source, out template, out error); ``` -------------------------------- ### Example of Parentheses Usage in Template Source: https://github.com/sebastienros/fluid/blob/main/README.md With AllowParentheses enabled, expressions can be grouped using parentheses for controlled evaluation order. ```liquid {{ 1 | plus : (2 | times: 3) }} ``` -------------------------------- ### FluidParser() Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/FluidParser.md Creates a new FluidParser instance with default options. This is the most straightforward way to initialize the parser for standard Liquid syntax. ```APIDOC ## FluidParser() ### Description Creates a new `FluidParser` instance with default options. ### Constructor `public FluidParser()` ### Example ```csharp var parser = new FluidParser(); ``` ``` -------------------------------- ### Catching InvalidOperationException for Undefined Variable Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/errors.md Example of catching an InvalidOperationException during rendering when accessing an undefined variable with StrictVariables enabled. ```csharp var options = new TemplateOptions { StrictVariables = true }; var context = new TemplateContext(options: options); var parser = new FluidParser(); var template = parser.Parse("{{ undefined_variable }}"); try { // Will throw because undefined_variable doesn't exist var result = template.Render(context); } catch (InvalidOperationException ex) { Console.WriteLine($"Runtime error: {ex.Message}"); } ``` -------------------------------- ### Parse and Render a Template Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Demonstrates the basic workflow of parsing a template string and rendering it with a model. Ensure you have a `FluidParser` and `TemplateContext` initialized. ```csharp var parser = new FluidParser(); var template = parser.Parse("Hello {{ name }}!"); var model = new { name = "World" }; var context = new TemplateContext(model); var result = template.Render(context); // "Hello World!" ``` -------------------------------- ### Configure Fluid Options for Development vs. Production Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Create adaptable TemplateOptions based on the environment (development or production). This includes settings like strictness, resource limits, caching, and error handling to optimize performance and debugging. ```csharp public static TemplateOptions CreateOptions(bool isDevelopment) { var options = new TemplateOptions { // Strict mode in development to catch mistakes StrictVariables = isDevelopment, StrictFilters = isDevelopment, // Limit template complexity in production MaxSteps = isDevelopment ? 0 : 100000, MaxRecursion = isDevelopment ? 0 : 100, // Disable caching in development for faster iteration TemplateCache = isDevelopment ? new DefaultTemplateCache(capacity: 10) // Small cache : new DefaultTemplateCache(capacity: 1000), // Large cache // Log template errors in production }; if (!isDevelopment) { // Production handlers options.Undefined = async (name, type) => { LogWarning($"Template accessed undefined: {name}"); return StringValue.Create($"[{name}]"); }; } return options; } ``` -------------------------------- ### Configure Template Options and Add Custom Filter Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/README.md Shows how to configure `TemplateOptions`, including setting strict variables and culture, and how to add a custom filter named 'shout' that converts input to uppercase. A `TemplateContext` can then be created with these options. ```csharp var options = new TemplateOptions { StrictVariables = true, CultureInfo = new CultureInfo("en-US"), }; // Add custom filter options.Filters.AddFilter("shout", (input, args, context) => new ValueTask(StringValue.Create( input.ToStringValue(context).ToUpper() )) ); var context = new TemplateContext(model, options); ``` -------------------------------- ### Access Model Object Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateContext.md Gets the model object that is exposed to templates. If no model was provided during initialization, it defaults to NilValue.Instance. ```csharp public FluidValue Model { get; } ``` -------------------------------- ### TemplateOptions.MemberAccessStrategy Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the strategy for accessing object member properties in templates. Allows customizing which properties are accessible. ```APIDOC ## TemplateOptions.MemberAccessStrategy ### Description Gets or sets the strategy for accessing object member properties in templates. ### Property Signature ```csharp public MemberAccessStrategy MemberAccessStrategy { get; set; } ``` ### Type `MemberAccessStrategy` ### Default `DefaultMemberAccessStrategy` ### Remarks Allows customizing which properties are accessible to templates, or implementing custom property access logic. ``` -------------------------------- ### TemplateOptions.Default Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets the shared default template options instance. This is used when no options are explicitly provided to parsers or contexts. ```APIDOC ## TemplateOptions.Default ### Description Gets the shared default template options instance. Used when no options are explicitly provided to parsers or contexts. ### Property Signature ```csharp public static readonly TemplateOptions Default = new(); ``` ### Type `TemplateOptions` ``` -------------------------------- ### Registering a Custom Filter with Arguments Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Shows how to create a custom filter 'pad' that accepts arguments for padding width and character. ```csharp static ValueTask PadFilter(FluidValue input, FilterArguments args, TemplateContext context) { var text = input.ToStringValue(context); var width = (int)args.At(0).ToNumberValue(context); var char c = args.Count > 1 ? args.At(1).ToStringValue(context)[0] : ' '; return new ValueTask(StringValue.Create(text.PadRight(width, c))); } options.Filters.AddFilter("pad", PadFilter); // Usage: {{ "hi" | pad: 5 }} => "hi " // {{ "hi" | pad: 5, "*" }} => "hi***" ``` -------------------------------- ### FluidValue Type Property Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/FluidValue.md Gets the type of the value as a FluidValues enum. This property is abstract and must be implemented by subclasses. ```csharp public abstract FluidValues Type { get; } ``` -------------------------------- ### Downcase Filter Implementation Source: https://github.com/sebastienros/fluid/blob/main/README.md Example implementation of an asynchronous filter named 'Downcase' in Fluid. It converts the input string to lowercase. ```csharp public static ValueTask Downcase(FluidValue input, FilterArguments arguments, TemplateContext context) { return new StringValue(input.ToStringValue().ToLower()); } ``` -------------------------------- ### Set Culture Information Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateContext.md Gets or sets the CultureInfo used for number and date formatting within templates. Defaults to CultureInfo.InvariantCulture. ```csharp public CultureInfo CultureInfo { get; set; } ``` -------------------------------- ### Benchmark.NET Data for Rendering Source: https://github.com/sebastienros/fluid/blob/main/README.md This data illustrates the rendering performance for a simple HTML template with 100 elements. Fluid is substantially faster and more memory-efficient than Scriban, DotLiquid, and Handlebars.Net. ```text | Fluid_Render | 31.527 us | 7.0754 us | 0.3878 us | 1.00 | 0.02 | 5.1880 | 0.0610 | 47.91 KB | 1.00 | | Scriban_Render | 94.043 us | 14.6300 us | 0.8019 us | 2.98 | 0.04 | 15.2588 | 2.5635 | 140.46 KB | 2.93 | | DotLiquid_Render | 245.327 us | 30.0185 us | 1.6454 us | 7.78 | 0.09 | 74.2188 | 13.6719 | 685.53 KB | 14.31 | | Handlebars_Render | 88.330 us | 11.2139 us | 0.6147 us | 2.80 | 0.03 | 16.8457 | 2.8076 | 155.7 KB | 3.25 | ``` -------------------------------- ### Handle Unknown Filters in Templates Source: https://github.com/sebastienros/fluid/blob/main/README.md By default, unknown filters return the input value unchanged. This example shows the default behavior. ```liquid {{ 'hello' | unknown }} => hello ``` -------------------------------- ### Using Profile Method with Any Options Instance Source: https://github.com/sebastienros/fluid/blob/main/README.md Applies generated registrations to a standard TemplateOptions instance using a predefined profile method. This allows for flexible integration of compile-time generated accessors. ```csharp var options = new TemplateOptions(); FluidProfiles.ApplyPublic(options); ``` -------------------------------- ### Liquid Component Partial View Source: https://github.com/sebastienros/fluid/blob/main/MinimalApis.LiquidViews/README.md A Liquid file (`component.liquid`) demonstrating a partial view that performs a simple calculation using input parameters. ```liquid
Using a component {{ x }} + {{ y }} = {{ x | plus: y }}
``` -------------------------------- ### Registering an Async Custom Filter Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/Filters.md Illustrates creating an asynchronous custom filter 'fetch' that performs an HTTP request. ```csharp static ValueTask FetchFilter(FluidValue input, FilterArguments args, TemplateContext context) { return FetchAsync(input.ToStringValue(context)); } static async ValueTask FetchAsync(string url) { using var client = new HttpClient(); var response = await client.GetStringAsync(url); return StringValue.Create(response); } options.Filters.AddFilter("fetch", FetchFilter); // Usage: {{ url | fetch }} (awaits HTTP request) ``` -------------------------------- ### Basic TemplateOptions Configuration Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Configures strict variable/filter checking and culture for template rendering. Also shows how to add a custom filter. ```csharp var options = new TemplateOptions { StrictVariables = true, StrictFilters = true, CultureInfo = new CultureInfo("en-US"), }; options.Filters.AddFilter("customfilter", MyCustomFilter); ``` -------------------------------- ### Basic Template Rendering Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Use this pattern for simple, one-off template rendering tasks. It involves parsing a template string and rendering it with a given model. ```csharp var parser = new FluidParser(); var template = parser.Parse("Hello {{ name }}, you are {{ age }} years old"); var model = new { name = "Alice", age = 30 }; var context = new TemplateContext(model); string result = template.Render(context); // "Hello Alice, you are 30 years old" ``` -------------------------------- ### Publishing with NativeAOT Source: https://github.com/sebastienros/fluid/blob/main/README.md Command to publish a .NET application with NativeAOT and trimming enabled. Replace with your target runtime identifier. ```shell dotnet publish -c Release -r -p:PublishAot=true ``` -------------------------------- ### Template Inheritance with Includes Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Illustrates template organization using includes and inheritance, where a main template incorporates content from header and footer partial templates. ```liquid // main.liquid {{ title }} {% include 'header' %} {{ content }} {% include 'footer' %} // header.liquid

{{ site_name }}

// footer.liquid ``` ```csharp var options = new TemplateOptions { FileProvider = new PhysicalFileProvider("./templates"), }; var context = new TemplateContext(new { title = "My Page", content = "Page content here", site_name = "My Site", year = DateTime.Now.Year }, options); var template = _parser.Parse(System.IO.File.ReadAllText("./templates/main.liquid")); string result = template.Render(context); ``` -------------------------------- ### Safe Template Rendering with Error Handling Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md Demonstrates how to safely render templates by catching various exceptions during parsing and rendering, providing fallback messages and logging errors. ```csharp public string SafeRenderTemplate(string source, object model) { var parser = new FluidParser(); // Parse errors try { var template = parser.Parse(source); var context = new TemplateContext(model); return template.Render(context); } catch (ParseException ex) { logger.LogError(ex, "Template parse error"); return "[Template error - see logs]"; } catch (InvalidOperationException ex) when (ex.Message.Contains("recursion")) { logger.LogError("Template too deeply nested"); return "[Template too complex]"; } catch (InvalidOperationException ex) { logger.LogError(ex, "Template rendering error"); return "[Template error - see logs]"; } catch (Exception ex) { logger.LogError(ex, "Unexpected error rendering template"); return "[Unexpected error - see logs]"; } } ``` -------------------------------- ### Benchmark.NET Data for Parsing Source: https://github.com/sebastienros/fluid/blob/main/README.md This data shows the performance metrics for parsing simple HTML templates. Fluid demonstrates superior speed and memory allocation compared to Scriban, DotLiquid, and Handlebars.Net. ```text BenchmarkDotNet=v0.14.0, Windows 11 (10.0.26100.3476) 12th Gen Intel Core i7-1260P, 1 CPU, 16 logical and 12 physical cores .NET SDK 9.0.201 [Host] : .NET 9.0.3 (9.0.325.11113), X64 RyuJIT AVX2 ShortRun : .NET 9.0.3 (9.0.325.11113), X64 RyuJIT AVX2 Job=ShortRun IterationCount=3 LaunchCount=1 WarmupCount=3 | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | |------------------- |-------------:|--------------:|-----------:|---------:|--------:|--------:|--------:|----------:|------------:| | Fluid_Parse | 2.333 us | 0.4108 us | 0.0225 us | 1.00 | 0.01 | 0.3090 | - | 2.84 KB | 1.00 | | Scriban_Parse | 3.231 us | 0.4593 us | 0.0252 us | 1.39 | 0.01 | 0.7744 | 0.0267 | 7.14 KB | 2.51 | | DotLiquid_Parse | 5.420 us | 1.2515 us | 0.0686 us | 2.32 | 0.03 | 1.7548 | 0.0229 | 16.15 KB | 5.68 | | Handlebars_Parse | 2,365.620 us | 1,080.6364 us | 59.2333 us | 1,014.02 | 23.55 | 15.6250 | - | 155.22 KB | 54.58 | ``` -------------------------------- ### Access Root Scope Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateContext.md Gets the root scope that was created when the context was initialized. This property is internal and used for managing the overall scope hierarchy. ```csharp internal Scope RootScope { get; set; } ``` -------------------------------- ### Try-Catch Parsing with FluidParser Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/errors.md Shows how to use `TryParse` to safely parse a template source, catching and handling potential parsing errors. ```csharp var parser = new FluidParser(); if (parser.TryParse(templateSource, out var template, out var error)) { // Rendering code here } else { Console.WriteLine($"Parse failed: {error}"); // Handle error } ``` -------------------------------- ### Reusable Parser and Options for Multiple Templates Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/USAGE-PATTERNS.md For applications rendering many templates, create and reuse single instances of `FluidParser` and `TemplateOptions` to improve performance. Initialize these once at application startup. ```csharp // Create once at startup private static readonly FluidParser _parser = new(); private static readonly TemplateOptions _options = new() { StrictVariables = Environment.IsDevelopment(), StrictFilters = Environment.IsDevelopment(), CultureInfo = new CultureInfo("en-US"), }; // Reuse in all rendering public string RenderTemplate(string source, object model) { var template = _parser.Parse(source); var context = new TemplateContext(model, _options); return template.Render(context); } ``` -------------------------------- ### TemplateOptions.TemplateCache Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/TemplateOptions.md Gets or sets the cache for compiled templates loaded via include/render statements. Must be thread-safe. Set to null to disable caching. ```APIDOC ## TemplateOptions.TemplateCache ### Description Gets or sets the cache for compiled templates loaded via include/render statements. ### Property Signature ```csharp public ITemplateCache TemplateCache { get; set; } ``` ### Type `ITemplateCache` ### Default `DefaultTemplateCache` ### Remarks Must be thread-safe. Set to null to disable caching. ``` -------------------------------- ### Format Number with format_number Filter Source: https://github.com/sebastienros/fluid/blob/main/README.md Example of using the custom 'format_number' filter to format a number using a standard .NET numeric format specifier. ```Input 123 | format_number: "N" ``` -------------------------------- ### Output Buffering Configuration Source: https://github.com/sebastienros/fluid/blob/main/_autodocs/api-reference/IFluidTemplate.md Illustrates how to configure the output buffer size for template rendering. Setting OutputBufferSize to 0 disables buffering, which is not recommended for large templates. ```csharp var options = new TemplateOptions { OutputBufferSize = 8192 }; // 8 KB buffer var context = new TemplateContext(options: options); ``` -------------------------------- ### Set CultureInfo for Template Rendering Source: https://github.com/sebastienros/fluid/blob/main/README.md Set the CultureInfo for a TemplateContext to control how culture-sensitive values like dates and numbers are rendered. This example uses 'en-US'. ```csharp var options = new TemplateOptions(); options.CultureInfo = new CultureInfo("en-US"); var context = new TemplateContext(options); var result = template.Render(context); ``` -------------------------------- ### JSON Encoding Example with Chinese Characters Source: https://github.com/sebastienros/fluid/blob/main/README.md Demonstrates JSON encoding of a string containing Chinese characters. The output shows the default escaped version. ```liquid {{ "你好,这是一条短信" | json }} ``` -------------------------------- ### Pattern 1: Template for Server Time Source: https://github.com/sebastienros/fluid/blob/main/TimeZones.md A simple template to display the 'ServerTime' which, due to the configured value converter, will be shown in the user's time zone. ```liquid Server time: {{ ServerTime }} ```