lua-resty-template

repository·master·Indexed 21 days ago

https://github.com/bungle/lua-resty-template

A compiling HTML templating engine for Lua and OpenResty that translates templates into Lua functions for high performance. It supports external files, inline strings, HTML escaping, and precompilation into binary chunks. The engine integrates with Nginx via $template_root and $template_location variables for template resolution.

Tokens
6.4K
Snippets
21
Records
30
Agent score
26%

What's inside lua-resty-template

  1. Use blocks to inject content into specific layout locations

    master

    Blocks allow you to move specific parts of a view to designated placeholders in a layout.

    1. In the View: Wrap the content you want to move in {-block_name-} tags.
    2. In the Layout: Access the content via the blocks table using {*blocks.block_name*}.

    view.html:

    <h1>{{message}}</h1>
    {-aside-}
    <ul>
        {% for _, keyword in ipairs(keywords) do %}
        <li>{{keyword}}</li>
        {% end %}
    </ul>
    {-aside-}

    layout.html:

    <body>
        {*view*}
        {% if blocks.aside then %}
        <aside>
            {*blocks.aside*}
        </aside>
        {% end %}
        </body>
    </html>
    <h1>{{message}}</h1>
    {-aside-}
    <ul
        {% for _, keyword in ipairs(keywords) do %}
        <li>{{keyword}}</li>
        {% end %}
    </ul>
    {-aside-}
  2. How HTML Escaping works in templates

    master

    The {{expression}} tag performs HTML escaping on strings. Other types are converted via tostring.

    Special Conversions:

    • nil and ngx.null are converted to empty strings "".
    • Functions are called without arguments (recursively).

    Escaped Characters:

    • & $\rightarrow$ &amp;
    • < $\rightarrow$ &lt;
    • > $\rightarrow$ &gt;
    • " $\rightarrow$ &quot;
    • ' $\rightarrow$ &#39;
    • / $\rightarrow$ &#47;
  3. Accessing Context and Template variables

    master

    In templates, you can access everything in the context table and the template table directly by their keys. You can also access them by prefixing keys with context. or template..

    Example: <h1>{{message}}</h1> is equivalent to <h1>{{context.message}}</h1>.

    Handling Complex Keys: If a key in your context table contains special characters (like a colon), you must access it explicitly via the context table: {*context["foo:bar"]*}

  4. Create macros using Lua functions

    master

    You can implement reusable, parameterized template logic by defining Lua functions within your template and calling them using template.compile.

    String-based macros

    Define a string containing template code and compile it:

    {% local string_macro = [[<div>{{item}}</div>]] %}
    {* template.compile(string_macro)(context) *}

    Function-based macros

    Define a Lua function that returns a compiled template. This is highly flexible for creating form renderers or component helpers.

    {% local function_macro = function(var, el)
        el = el or "div"
        return "<" .. el .. ">{{" .. var .. "}}</" .. el .. ">\n"
    end %}
    
    {* template.compile(function_macro("item"))(context) *}

    Note: Inside Lua code blocks ({% ... %}), you cannot use %}. To include a literal %} in a string, use concatenation: "%" .. "}".

  5. Quickstart: Hello World with lua-resty-template

    master

    You can use lua-resty-template to render HTML templates using either a view object or a direct render call. The engine supports both external files and inline template strings.

    To use the safe version which returns nil, err on errors, require resty.template.safe instead of resty.template.

    local template = require "resty.template"
    
    -- Option 1: Using template.new (View Object)
    local view = template.new "view.html"
    view.message = "Hello, World!"
    view:render()
    
    -- Option 2: Using template.render (Direct)
    template.render("view.html", { message = "Hello, World!" })
    
    -- Option 3: Inline template string
    template.render([[<h1>{{message}}</h1>]], { message = "Hello, World!" })
  6. Include templates inside templates

    master

    You can include one template within another using two different syntaxes depending on whether you want to share or replace the current context:

    1. {(template)}: Includes the template using the current context.
    2. {(template, context)}: Includes the template and replaces the current context with the provided one.

    Example of passing a new context to an include:

    local template = require "resty.template"
    template.render("include.html", { users = {
        { name = "Jane", age = 29 },
        { name = "John", age = 25 }
    }})

    In include.html:

    <ul>
    {% for _, user in ipairs(users) do %}
        {(user.html, user)}
    {% end %}
    </ul>
  7. Handle client-side tags (like Angular) in templates

    master

    When using client-side templating engines (like Angular) that use {{ }} syntax, you must prevent lua-resty-template from trying to parse them.

    Use one of the following methods:

    1. Verbatim/Raw blocks: Wrap the entire section in {-verbatim-} or {-raw-}.

      {-raw-}
      <button ng-click="changeFoo()">{{buttonText}}</button>
      {-raw-}
    2. Short escaping: Escape the opening braces with a backslash.

      <button ng-click="changeFoo()">\{{buttonText}}</button>
  8. Configure Lua Server Pages (LSP) in OpenResty

    master

    You can emulate a PHP-like environment where .lsp files are processed by the template engine.

    Nginx Configuration

    Configure a location block to intercept .lsp requests and render the URI using the template engine:

    http {
      init_by_lua ' 
        require "resty.core"
        template = require "resty.template"
        template.caching(false); -- Disable caching for development
      ';
    
      server {
        location ~ \.lsp$ {
          default_type text/html;
          content_by_lua "template.render(ngx.var.uri)";
        }
      }
    }

    LSP File Structure

    An .lsp file can contain both Lua logic and HTML view code. Variables defined in the Lua block are available in the view. To pass variables to layouts or includes, assign them to the context table.

    index.lsp:

    {% 
      layout = "layouts/default.lsp"
      local title = "Hello World"
      context.title = "App - " .. title
    %}
    <h1>{{title}}</h1>
  9. Use layouts (Master Pages) to wrap views

    master

    Layouts allow you to wrap a view inside a master template. The layout uses the {*view*} placeholder to indicate where the content should be injected. There are several ways to implement this:

    1. Using template.new and layout.render()

    Create a layout object, assign properties (like title), and assign the compiled view to the view property.

    local layout = template.new "layout.html"
    layout.title = "My Title"
    layout.view = template.compile "view.html" { message = "Hello" }
    layout:render()

    2. Passing layout and view via template.render

    template.render("layout.html", {
      title = "My Title",
      view = template.compile "view.html" { message = "Hello" }
    })

    3. Defining layout within the view

    You can specify the layout directly inside the view file using the {% layout="filename" %} syntax.

    view.html:

    {% layout="section.html" %}
    <h1>{{message}}</h1>

    section.html:

    <div id="section">
        {*view*}
    </div>
    local template = require "resty.template"
    local layout   = template.new "layout.html"
    layout.title   = "Testing lua-resty-template"
    layout.view    = template.compile "view.html" { message = "Hello, World!" }
    layout:render()
  10. Embed Markdown in templates

    master

    You can embed Markdown using the lua-resty-hoedown library. Assign the hoedown module to template.markdown and use the {*markdown ... *} syntax.

    local template = require "resty.template"
    template.markdown = require "resty.hoedown"
    
    template.render[=[
    <html
    <body>
    {*markdown[[# Hello World]]*}
    </body>
    </html>
    ]=]

    You can also pass configuration options (like smartypants = true) to the markdown parser:

    {*markdown([[**Bold Text**]], { smartypants = true })*}
  11. Install lua-resty-template

    master

    You can install the template engine using OpenResty Package Manager (opm), LuaRocks, or by manually placing the files in your package.path.

    Manual Installation: Place template.lua and the template directory inside a resty directory in your package.path. For OpenResty, this is typically /usr/local/openresty/lualib/resty.

    # Using OpenResty Package Manager (opm)
    $ opm get bungle/lua-resty-template
    
    # Using LuaRocks
    $ luarocks install lua-resty-template
  12. Precompile templates for production performance

    master

    You can precompile templates into binary files to skip template parsing and Lua interpretation at runtime. This improves performance and memory usage, and ensures templates are syntactically valid Lua. Precompiled templates can be distributed as binary files instead of plain text.

    To precompile a template:

    1. Use template.precompile(source_file, target_binary_file).
    2. To render the precompiled file, use template.render(target_binary_file, context).
    local template = require "resty.template"
    -- Precompile template to a binary file
    local compiled = template.precompile("example.html", "example-bin.html")
    
    -- Load and run the precompiled template with context
    template.render("example-bin.html", { "Jack", "Mary" })