{"id":13636202,"url":"https://github.com/bungle/lua-resty-template","last_synced_at":"2025-05-16T14:05:17.593Z","repository":{"id":14344959,"uuid":"17054544","full_name":"bungle/lua-resty-template","owner":"bungle","description":"Templating Engine (HTML) for Lua and OpenResty.","archived":false,"fork":false,"pushed_at":"2023-07-21T13:39:41.000Z","size":420,"stargazers_count":918,"open_issues_count":13,"forks_count":206,"subscribers_count":58,"default_branch":"master","last_synced_at":"2025-04-12T10:59:51.157Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bungle.png","metadata":{"files":{"readme":"README.md","changelog":"Changes.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-02-21T11:33:58.000Z","updated_at":"2025-04-06T00:23:26.000Z","dependencies_parsed_at":"2024-01-08T20:19:18.407Z","dependency_job_id":null,"html_url":"https://github.com/bungle/lua-resty-template","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bungle%2Flua-resty-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bungle%2Flua-resty-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bungle%2Flua-resty-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bungle%2Flua-resty-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bungle","download_url":"https://codeload.github.com/bungle/lua-resty-template/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T00:00:58.486Z","updated_at":"2025-05-16T14:05:17.574Z","avatar_url":"https://github.com/bungle.png","language":"Lua","funding_links":[],"categories":["Libraries","Third Modules","Rust Modules","Lua","资源","Resources"],"sub_categories":["C Modules","Lua Modules","Templating"],"readme":"# lua-resty-template\n\n**lua-resty-template** is a compiling (1) (HTML) templating engine for Lua and OpenResty.\n\n(1) with compilation we mean that templates are translated to Lua functions that you may call or `string.dump`\nas a binary bytecode blobs to disk that can be later utilized with `lua-resty-template` or basic `load` and\n`loadfile` standard Lua functions (see also [Template Precompilation](#template-precompilation)). Although,\ngenerally you don't need to do that as `lua-resty-template` handles this behind the scenes.\n\n\n## Hello World with lua-resty-template\n\n```lua\nlocal template = require \"resty.template\"      -- OR\nlocal template = require \"resty.template.safe\" -- return nil, err on errors\n\n-- Using template.new\nlocal view = template.new \"view.html\"\nview.message = \"Hello, World!\"\nview:render()\n-- Using template.render\ntemplate.render(\"view.html\", { message = \"Hello, World!\" })\n```\n\n\n##### view.html\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003ch1\u003e{{message}}\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n##### Output\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003ch1\u003eHello, World!\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe same can be done with inline template string:\n\n```lua\n-- Using template string\ntemplate.render([[\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003ch1\u003e{{message}}\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e]], { message = \"Hello, World!\" })\n```\n\n\n## Contents\n\n- [Template Syntax](#template-syntax)\n  - [Example](#example)\n  - [Reserved Context Keys and Remarks](#reserved-context-keys-and-remarks)\n- [Installation](#installation)\n  - [Using OpenResty Package Manager (opm)](#using-openresty-package-manager-opm)\n  - [Using LuaRocks](#using-luarocks)\n- [Nginx / OpenResty Configuration](#nginx--openresty-configuration)\n- [Lua API](#lua-api)\n  - [template.root](#templateroot)\n  - [template.location](#templatelocation)\n  - [table template.new(view, layout)](#table-templatenewview-layout)\n  - [boolean template.caching(boolean or nil)](#boolean-templatecachingboolean-or-nil)\n  - [function, boolean template.compile(view, cache_key, plain)](#function-boolean-templatecompileview-cache_key-plain)\n  - [function, boolean template.compile_string(view, cache_key)](#function-boolean-templatecompile_stringview-cache_key)\n  - [function, boolean template.compile_file(view, cache_key)](#function-boolean-templatecompile_fileview-cache_key)\n  - [template.visit(func)](#templatevisitfunc)\n  - [string template.process(view, context, cache_key, plain)](#string-templateprocessview-context-cache_key-plain)\n  - [string template.process_string(view, context, cache_key)](#string-templateprocess_stringview-context-cache_key)\n  - [string template.process_file(view, context, cache_key)](#string-templateprocess_fileview-context-cache_key)  \n  - [template.render(view, context, cache_key, plain)](#templaterenderview-context-cache_key-plain)\n  - [template.render_string(view, context, cache_key)](#templaterender_stringview-context-cache_key)\n  - [template.render_file(view, context, cache_key)](#templaterender_fileview-context-cache_key)\n  - [string template.parse(view, plain)](#string-templateparseview-plain)\n  - [string template.parse_string(view, plain)](#string-templateparse_stringview-plain)\n  - [string template.parse_file(view, plain)](#string-templateparse_fileview-plain)\n  - [string template.precompile(view, path, strip)](#string-templateprecompileview-path-strip)\n  - [string template.precompile_string(view, path, strip)](#string-templateprecompile_stringview-path-strip)\n  - [string template.precompile_file(view, path, strip)](#string-templateprecompile_fileview-path-strip)  \n  - [string template.load(view, plain)](#string-templateloadview-plain)\n  - [string template.load_string(view)](#string-templateload_stringview)\n  - [string template.load_file(view)](#string-templateload_fileview)\n  - [template.print](#templateprint)\n- [Template Precompilation](#template-precompilation)\n- [Template Helpers](#template-helpers)\n  - [Built-in Helpers](#built-in-helpers)\n    - [echo(...)](#echo)\n    - [include(view, context)](#includeview-context)\n  - [Other Ways to Extend](#other-ways-to-extend)\n- [Usage Examples](#usage-examples)\n   - [Template Including](#template-including)\n   - [Views with Layouts](#views-with-layouts)\n   - [Using Blocks](#using-blocks)\n   - [Grandfather-Father-Son Inheritance](#grandfather-father-son-inheritance)\n   - [Macros](#macros)\n   - [Calling Methods in Templates](#calling-methods-in-templates)\n   - [Embedding Angular or other tags / templating inside the Templates](#embedding-angular-or-other-tags--templating-inside-the-templates)\n   - [Embedding Markdown inside the Templates](#embedding-markdown-inside-the-templates)\n   - [Lua Server Pages (LSP) with OpenResty](#lua-server-pages-lsp-with-openresty)\n- [FAQ](#faq)\n- [Alternatives](#alternatives)\n- [Benchmarks](#benchmarks)\n- [Changes](#changes)\n- [Roadmap](#roadmap)\n- [See Also](#see-also)\n- [License](#license)\n\n\n## Template Syntax\n\nYou may use the following tags in templates:\n\n* `{{expression}}`, writes result of expression - html escaped\n* `{*expression*}`, writes result of expression \n* `{% lua code %}`, executes Lua code\n* `{(template)}`, includes `template` file, you may also supply context for include file `{(file.html, { message = \"Hello, World\" } )}` (NOTE: you cannot use comma (`,`) in `file.html`, in that case use `{[\"file,with,comma\"]}` instead)\n* `{[expression]}`, includes `expression` file (the result of expression), you may also supply context for include file `{[\"file.html\", { message = \"Hello, World\" } ]}`\n* `{-block-}...{-block-}`, wraps inside of a `{-block-}` to a value stored in a `blocks` table with a key `block` (in this case), see [using blocks](https://github.com/bungle/lua-resty-template#using-blocks). Don't use predefined block names `verbatim` and `raw`.\n* `{-verbatim-}...{-verbatim-}` and `{-raw-}...{-raw-}` are predefined blocks whose inside is not processed by the `lua-resty-template` but the content is outputted as is.\n* `{# comments #}` everything between `{#` and `#}` is considered to be commented out (i.e. not outputted or executed)\n\nFrom templates you may access everything in `context` table, and everything in `template` table.\nIn templates you can also access `context` and `template` by prefixing keys.\n\n```html\n\u003ch1\u003e{{message}}\u003c/h1\u003e == \u003ch1\u003e{{context.message}}\u003c/h1\u003e\n```\n\n\n##### Short Escaping Syntax\n\nIf you don't want a particular template tag to be processed you may escape the starting tag with backslash `\\`:\n\n```html\n\u003ch1\u003e\\{{message}}\u003c/h1\u003e\n```\n\nThis will output (instead of evaluating the message):\n\n```html\n\u003ch1\u003e{{message}}\u003c/h1\u003e\n```\n\nIf you want to add backslash char just before template tag, you need to escape that as well:\n\n```html\n\u003ch1\u003e\\\\{{message}}\u003c/h1\u003e\n```\n\nThis will output:\n\n```html\n\u003ch1\u003e\\[message-variables-content-here]\u003c/h1\u003e\n```\n\n\n##### A Word About Complex Keys in Context Table\n\nSay you have this kind of a context table:\n\n```lua\nlocal ctx = {[\"foo:bar\"] = \"foobar\"}\n```\n\nAnd you want to render the `ctx[\"foo:bar\"]`'s value `foobar` in your template.  You have to specify it explicitly\nby referencing the `context` in your template:\n\n```html\n{# {*[\"foo:bar\"]*} won't work, you need to use: #}\n{*context[\"foo:bar\"]*}\n```\n\nOr altogether:\n\n```lua\ntemplate.render([[\n{*context[\"foo:bar\"]*}\n]], {[\"foo:bar\"] = \"foobar\"})\n```\n\n\n##### A Word About HTML Escaping\n\nOnly strings are escaped, functions are called without arguments (recursively) and results are returned as is,\nother types are `tostring`ified. `nil`s and `ngx.null`s are converted to empty strings `\"\"`.\n\nEscaped HTML characters:\n\n* `\u0026` becomes `\u0026amp;`\n* `\u003c` becomes `\u0026lt;`\n* `\u003e` becomes `\u0026gt;`\n* `\"` becomes `\u0026quot;`\n* `'` becomes `\u0026#39;`\n* `/` becomes `\u0026#47;`\n\n\n#### Example\n\n##### Lua\n```lua\nlocal template = require \"resty.template\"\ntemplate.render(\"view.html\", {\n  title   = \"Testing lua-resty-template\",\n  message = \"Hello, World!\",\n  names   = { \"James\", \"Jack\", \"Anne\" },\n  jquery  = '\u003cscript src=\"js/jquery.min.js\"\u003e\u003c/script\u003e' \n})\n```\n\n\n##### view.html\n```html\n{(header.html)}\n\u003ch1\u003e{{message}}\u003c/h1\u003e\n\u003cul\u003e\n{% for _, name in ipairs(names) do %}\n    \u003cli\u003e{{name}}\u003c/li\u003e\n{% end %}\n\u003c/ul\u003e\n{(footer.html)}\n```\n\n\n##### header.html\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003ctitle\u003e{{title}}\u003c/title\u003e\n  {*jquery*}\n\u003c/head\u003e\n\u003cbody\u003e\n```\n\n\n##### footer.html\n```html\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n#### Reserved Context Keys and Remarks\n\nIt is advised that you do not use these keys in your context tables:\n\n* `___`, holds the compiled template, if set you need to use `{{context.___}}`\n* `context`, holds the current context, if set you need to use `{{context.context}}`\n* `echo`, holds the echo helper function, if set you need to use `{{context.echo}}`\n* `include`, holds the include helper function, if set you need to use `{{context.include}}`\n* `layout`, holds the layout by which the view will be decorated, if set you need to use `{{context.layout}}`\n* `blocks`, holds the blocks, if set you need to use `{{context.blocks}}` (see: [using blocks](#using-blocks))\n* `template`, holds the template table, if set you need to use `{{context.template}}`\n\nIn addition to that with `template.new` you should not overwrite:\n\n* `render`, the function that renders a view, obviously ;-)\n\nYou should also not `{(view.html)}` recursively:\n\n\n##### Lua\n```lua\ntemplate.render \"view.html\"\n```\n\n\n##### view.html\n```html\n{(view.html)}\n```\n\nYou can  load templates from \"sub-directories\" as well with `{(syntax)}`:\n\n\n##### view.html\n```html\n{(users/list.html)}\n```\n\n**Also note that you can provide template either as a file path or as a string. If the file exists,\nit will be used, otherwise the string is used. See also [`template.load`](#templateload).**\n\n\n## Installation\n\nJust place [`template.lua`](https://github.com/bungle/lua-resty-template/blob/master/lib/resty/template.lua) and\n[`template`](https://github.com/bungle/lua-resty-template/tree/master/lib/resty/template) directory somewhere in\nyour `package.path`, under `resty` directory. If you are using OpenResty, the default location would\nbe `/usr/local/openresty/lualib/resty`.\n\n\n### Using OpenResty Package Manager (opm)\n\n```Shell\n$ opm get bungle/lua-resty-template\n```\n\n\n### Using LuaRocks\n\n```Shell\n$ luarocks install lua-resty-template\n```\n\nLuaRocks repository for `lua-resty-template` is located at https://luarocks.org/modules/bungle/lua-resty-template.\n\n\n## Nginx / OpenResty Configuration\n\nWhen `lua-resty-template` is used in context of Nginx / OpenResty there are a few configuration directives\nthat you need to be aware:\n\n* `template_root` (`set $template_root /var/www/site/templates`)\n* `template_location` (`set $template_location /templates`)\n\nIf none of these are set in Nginx configuration, `ngx.var.document_root` (aka root-directive) value is used.\nIf `template_location` is set, it will be used first, and if the location returns anything but `200` as\na status code, we do fallback to either `template_root` (if defined) or `document_root`.\n\nWith `lua-resty-template` `2.0` it is possible to override `$template_root` and `$template_location` with\n`Lua` code:\n\n```lua\nlocal template = require \"resty.template\".new({\n  root     = \"/templates\",\n  location = \"/templates\" \n})\n```\n\n\n##### Using `document_root`\n\nThis one tries to load file content with Lua code from `html` directory (relative to Nginx prefix).\n\n```nginx\nhttp {\n  server {\n    location / {\n      root html;\n      content_by_lua '\n        local template = require \"resty.template\"\n        template.render(\"view.html\", { message = \"Hello, World!\" })\n      ';      \n    }\n  }\n}\n```\n\n\n##### Using `template_root`\n\nThis one tries to load file content with Lua code from `/usr/local/openresty/nginx/html/templates` directory.\n\n```nginx\nhttp {\n  server {\n    set $template_root /usr/local/openresty/nginx/html/templates;\n    location / {\n      root html;\n      content_by_lua '\n        local template = require \"resty.template\"\n        template.render(\"view.html\", { message = \"Hello, World!\" })\n      ';      \n    }\n  }\n}\n```\n\n\n##### Using `template_location`\n\nThis one tries to load content with `ngx.location.capture` from `/templates` location (in this case this is\nserved with `ngx_static` module).\n\n```nginx\nhttp {\n  server {\n    set $template_location /templates;\n    location / {\n      root html;\n      content_by_lua '\n        local template = require \"resty.template\"\n        template.render(\"view.html\", { message = \"Hello, World!\" })\n      ';      \n    }\n    location /templates {\n      internal;\n      alias html/templates/;\n    }    \n  }\n}\n```\n\n**See also [`template.load`](#templateload).**\n\n\n## Lua API\n\n#### template.root\n\nYou can setup template root by setting this variable which will be looked for template files:\n\n```lua\nlocal template = require \"resty.template\".new({\n  root = \"/templates\"\n})\ntemplate.render_file(\"test.html\")\n```\n\nThis property overrides the one set in Nginx configuration (`set $template_root /my-templates;`)\n\n\n#### template.location\n\nThis is what you can use with OpenResty as that will use `ngx.location.capture` to fetch templates\nfiles in non-blocking fashion.\n\n```lua\nlocal template = require \"resty.template\".new({\n  location = \"/templates\"\n})\ntemplate.render_file(\"test.html\")\n```\n\nThis property overrides the one set in Nginx configuration (`set $template_location /my-templates;`)\n\n\n#### table template.new(view, layout)\n\nCreates a new template instance that is used as a (default) context when `render`ed. A table that gets created has\nonly one method `render`, but the table also has metatable with `__tostring` defined. See the example below. Both\n`view` and `layout` arguments can either be strings or file paths, but layout can also be a table created previously\nwith `template.new`.\n\nWith 2.0 the new can also be used without arguments, which creates a new template instance:\n\n```lua\nlocal template = require \"resty.template\".new()\n```\n\nYou can also pass a table that is then modified to be a template:\n\n```lua\nlocal config = {\n  root = \"/templates\"\n}\n\nlocal template = require \"resty.template\".new(config)\n```\n\nThis is handy as the `template` created by `new` does not share the cache with the global template returned\nby `require \"resty.template\"` (this was reported with issue [#25](https://github.com/bungle/lua-resty-template/issues/25)).\n\nYou can also pass a boolean `true` or `false` as a `view` parameter which means that either `safe` or `un-safe`\nversion of template is returned:\n\n```lua\nlocal unsafe = require \"resty.template\"\nlocal safe   = unsafe.new(true)\n```\n\nThere is also a default `safe` implementation available:\n\n```lua\nlocal safe = require \"resty.template.safe\"\n-- you can create instance of safe too:\nlocal safe_instance = safe.new()\n```\n\n`safe` version uses `return nil, err` Lua error handling pattern and `unsafe` just throws the errors, which you\ncan catch with `pcall`, `xpcall` or `coroutine.wrap`.\n\n\nHere are examples of using `new` with arguments:\n\n```lua\nlocal view = template.new\"template.html\"              -- or\nlocal view = template.new(\"view.html\", \"layout.html\") -- or\nlocal view = template.new[[\u003ch1\u003e{{message}}\u003c/h1\u003e]]     -- or\nlocal view = template.new([[\u003ch1\u003e{{message}}\u003c/h1\u003e]], [[\n\u003chtml\u003e\n\u003cbody\u003e\n  {*view*}\n\u003c/body\u003e\n\u003c/html\u003e\n]])\n```\n\n\n##### Example\n```lua\nlocal template = require \"resty.template\"\nlocal view = template.new\"view.html\"\nview.message  = \"Hello, World!\"\nview:render()\n-- You may also replace context on render\nview:render{ title = \"Testing lua-resty-template\" }\n-- If you want to include view context in  replacement context\nview:render(setmetatable({ title = \"Testing lua-resty-template\" }, { __index = view }))\n-- To get rendered template as a string, you can use tostring\nlocal result = tostring(view)\n```\n\n\n#### boolean template.caching(boolean or nil)\n\nThis function enables or disables template caching, or if no parameters are passed, returns current state of\ntemplate caching. By default template caching is enabled, but you may want to disable it on development or\nlow-memory situations.\n\n```lua\nlocal template = require \"resty.template\"   \n-- Get current state of template caching\nlocal enabled = template.caching()\n-- Disable template caching\ntemplate.caching(false)\n-- Enable template caching\ntemplate.caching(true)\n```\n\nPlease note that if the template was already cached when compiling a template, the cached version will be returned.\nYou may want to flush cache with `template.cache = {}` to ensure that your template really gets recompiled.\n\n\n#### function, boolean template.compile(view, cache_key, plain)\n\nParses, compiles and caches (if caching is enabled) a template and returns the compiled template as a function\nthat takes context as a parameter and returns rendered template as a string. Optionally you may pass `cache_key` that\nis used as a cache key. If cache key is not provided `view` wil be used as a cache key. If cache key is `no-cache`\nthe template cache will not be checked and the resulting function will not be cached. You may also optionally\npass `plain` with a value of `true` if the `view` is plain text string (this will skip `template.load` and binary\nchunk detection in `template.parse` phase). If `plain` is `false` the template is considered to be a file,\nand all the issues with file reading are considered as errors. If the `plain` is set to `nil` (the default)\nthe template does not consider file reading errors as fatal, and returns back the `view` (usually the path of\nthe template).\n\n```lua\nlocal func = template.compile(\"template.html\")          -- or\nlocal func = template.compile([[\u003ch1\u003e{{message}}\u003c/h1\u003e]])\n```\n\n\n##### Example\n\n```lua\nlocal template = require \"resty.template\"\nlocal func     = template.compile(\"view.html\")\nlocal world    = func{ message = \"Hello, World!\" }\nlocal universe = func{ message = \"Hello, Universe!\" }\nprint(world, universe)\n```\n\nAlso note the second return value which is a boolean. You may discard it, or use it to determine if\nthe returned function was cached.\n\n\n#### function, boolean template.compile_string(view, cache_key)\n\nThis just calls `template.compile(view, cache_key, true)`\n\n\n#### function, boolean template.compile_file(view, cache_key)\n\nThis just calls `template.compile(view, cache_key, false)`\n\n\n#### template.visit(func)\n\nAllows you to register template parser visitor functions. Visitors are called in the order they\nare registered. And once registered, cannot be removed from parser. Perhaps it is easier to show\nhow it works:\n\n```lua\nlocal template = require \"resty.template.safe\".new()\n\nlocal i = 0\n\ntemplate.visit(function(content, type, name)\n  local trimmed = content:gsub(\"^%s+\", \"\"):gsub(\"%s+$\", \"\")\n  if trimmed == \"\" then return content end\n  i = i + 1\n  print(\"  visit: \", i)\n  if type then print(\"   type: \", type) end\n  if name then print(\"   name: \", name) end\n  print(\"content: \", trimmed)\n  print()\n  return content\nend)\n\nlocal func = template.compile([[\nHow are you, {{user.name}}?\n\nHere is a new cooking recipe for you!\n\n{% for i, ingredient in ipairs(ingredients) do %}\n  {*i*}. {{ingredient}}\n{% end %}\n{-ad-}`lua-resty-template` the templating engine for OpenResty!{-ad-}\n]])\n\nlocal content = func{\n  user = {\n    name = \"bungle\"\n  },\n  ingredients = {\n    \"potatoes\",\n    \"sausages\"\n  }\n}\n\nprint(content)\n```\n\nThis will output the following:\n\n```\n  visit: 1\ncontent: How are you,\n\n  visit: 2\n   type: {\ncontent: user.name\n\n  visit: 3\ncontent: ?\n\nHere is a new cooking recipe for you!\n\n  visit: 4\n   type: %\ncontent: for i, ingredient in ipairs(ingredients) do\n\n  visit: 5\n   type: *\ncontent: i\n\n  visit: 6\ncontent: .\n\n  visit: 7\n   type: {\ncontent: ingredient\n\n  visit: 8\n   type: %\ncontent: end\n\n  visit: 9\n   type: -\n   name: ad\ncontent: `lua-resty-template` the templating engine for OpenResty!\n\n  visit: 10\ncontent: `lua-resty-template` the templating engine for OpenResty!\n\nHow are you, bungle?\n\nHere is a new cooking recipe for you!\n\n  1. potatoes\n  2. sausages\n```\n\nThe visitor functions should have this signature:\n```\nstring function(content, type, name)\n```\n\nIf the function doesn't modify the `content` it should return the `content` back, like the visitor\nabove does.\n\nHere is a bit more advanced visitor example that handles run-time errors on expressions:\n\n```lua\nlocal template = require \"resty.template\".new()\n\ntemplate.render \"Calculation: {{i*10}}\"\n```\n\nThis will runtime error with:\n```\nERROR: [string \"context=... or {}...\"]:7: attempt to perform arithmetic on global 'i' (a nil value)\nstack traceback:\n\tresty/template.lua:652: in function 'render'\n\ta.lua:52: in function 'file_gen'\n\tinit_worker_by_lua:45: in function \u003cinit_worker_by_lua:43\u003e\n\t[C]: in function 'xpcall'\n\tinit_worker_by_lua:52: in function \u003cinit_worker_by_lua:50\u003e\n```\n\nNow let's add a visitor that handles this error:\n\n```lua\nlocal template = require \"resty.template\".new()\n\ntemplate.visit(function(content, type)\n  if type == \"*\" or type == \"{\" then\n    return \"select(3, pcall(function() return nil, \" .. content .. \" end)) or ''\"\n  end\n\n  return content\nend)\n\ntemplate.render \"Calculation: {{i*10}}\\n\"\ntemplate.render(\"Calculation: {{i*10}}\\n\", { i = 1 })\n```\n\nThis will output:\n\n```\nCalculation: \nCalculation: 10\n```\n\n\n#### string template.process(view, context, cache_key, plain)\n\nParses, compiles, caches (if caching is enabled) and returns output as string. You may optionally also\npass `cache_key` that is used as a cache key. If `plain` evaluates to `true`, the `view` is considered\nto be plain string template (`template.load` and binary chunk detection is skipped on `template.parse`).\nIf `plain` is `false\"` the template is considered to be a file, and all the issues with file reading are\nconsidered as errors. If the `plain` is set to `nil` (the default) the template does not consider file\nreading errors as fatal, and returns back the `view`.\n\n```lua\nlocal output = template.process(\"template.html\", { message = \"Hello, World!\" })          -- or\nlocal output = template.process([[\u003ch1\u003e{{message}}\u003c/h1\u003e]], { message = \"Hello, World!\" })\n```\n\n#### string template.process_string(view, context, cache_key)\n\nThis just calls `template.process(view, context, cache_key, true)`\n\n\n#### string template.process_file(view, context, cache_key)\n\nThis just calls `template.process(view, context, cache_key, false)`\n\n\n#### template.render(view, context, cache_key, plain)\n\nParses, compiles, caches (if caching is enabled) and outputs template either with `ngx.print` if available,\nor `print`. You may optionally also pass `cache_key` that is used as a cache key. If `plain` evaluates to\n`true`, the `view` is considered to be plain string template (`template.load` and binary chunk detection\nis skipped on `template.parse`). If `plain` is `false\"` the template is considered to be a file, and\nall the issues with file reading are considered as errors. If the `plain` is set to `nil` (the default)\nthe template does not consider file reading errors as fatal, and returns back the `view`.\n\n```lua\ntemplate.render(\"template.html\", { message = \"Hello, World!\" })          -- or\ntemplate.render([[\u003ch1\u003e{{message}}\u003c/h1\u003e]], { message = \"Hello, World!\" })\n```\n\n\n#### template.render_string(view, context, cache_key)\n\nThis just calls `template.render(view, context, cache_key, true)`\n\n\n#### template.render_file(view, context, cache_key)\n\nThis just calls `template.render(view, context, cache_key, false)`\n\n\n#### string template.parse(view, plain)\n\nParses template file or string, and generates a parsed template string. This may come useful when debugging\ntemplates. You should note that if you are trying to parse a binary chunk (e.g. one returned with\n`template.compile`), `template.parse` will return that binary chunk as is. If `plain` evaluates to\n`true`, the `view` is considered to be plain string template (`template.load` and binary chunk detection\nis skipped on `template.parse`). If `plain` is `false\"` the template is considered to be a file, and\nall the issues with file reading are considered as errors. If the `plain` is set to `nil` (the default)\nthe template does not consider file reading errors as fatal, and returns back the `view`.\n\n```lua\nlocal t1 = template.parse(\"template.html\")\nlocal t2 = template.parse([[\u003ch1\u003e{{message}}\u003c/h1\u003e]])\n```\n\n\n#### string template.parse_string(view, plain)\n\nThis just calls `template.parse(view, plain, true)`\n\n\n#### string template.parse_file(view, plain)\n\nThis just calls `template.parse(view, plain, false)`\n\n\n#### string template.precompile(view, path, strip, plain)\n\nPrecompiles template as a binary chunk. This binary chunk can be written out as a file (and you may use it\ndirectly with Lua's `load` and `loadfile`). For convenience you may optionally specify `path` argument to\noutput binary chunk to file. You may also supply `strip` parameter with value of `false` to make precompiled\ntemplates to have debug information as well (defaults to `true`). The last parameter `plain` means that\nshould complilation treat the `view` as `string` (`plain = true`) or as `file path` (`plain = false`) or\ntry first as a file, and fallback to `string` (`plain = nil`). In case the `plain=false` (a file) and there\nis error with `file io` the function will also error with an assertion failure. \n\n```lua\nlocal view = [[\n\u003ch1\u003e{{title}}\u003c/h1\u003e\n\u003cul\u003e\n{% for _, v in ipairs(context) do %}\n    \u003cli\u003e{{v}}\u003c/li\u003e\n{% end %}\n\u003c/ul\u003e]]\n\nlocal compiled = template.precompile(view)\n\nlocal file = io.open(\"precompiled-bin.html\", \"wb\")\nfile:write(compiled)\nfile:close()\n\n-- Alternatively you could just write (which does the same thing as above)\ntemplate.precompile(view, \"precompiled-bin.html\")\n\ntemplate.render(\"precompiled-bin.html\", {\n    title = \"Names\",\n    \"Emma\", \"James\", \"Nicholas\", \"Mary\"\n})\n```\n\n\n#### string template.precompile_string(view, path, strip)\n\nThis just calls `template.precompile(view, path, strip, true)`.\n\n\n#### string template.precompile_file(view, path, strip)\n\nThis just calls `template.precompile(view, path, strip, false)`.\n\n\n#### string template.load(view, plain)\n\nThis field is used to load templates. `template.parse` calls this function before it starts parsing the template\n(assuming that optional `plain` argument in `template.parse` evaluates to `false` or `nil` (the default).\nBy default there are two loaders in `lua-resty-template`: one for Lua and the other for Nginx / OpenResty.\nUsers can overwrite this field with their own function. For example you may want to write a template loader\nfunction that loads templates from a database.\n\nThe default `template.load` for Lua (attached as template.load when used directly with Lua):\n\n```lua\nfunction(view, plain)\n    if plain == true then return view end\n    local path, root = view, template.root\n    if root and root ~= EMPTY then\n        if byte(root, -1) == SOL then root = sub(root, 1, -2) end\n        if byte(view,  1) == SOL then path = sub(view, 2) end\n        path = root .. \"/\" .. path\n    end\n    return plain == false and assert(read_file(path)) or read_file(path) or view\nend\n```\n\nThe default `template.load` for Nginx / OpenResty (attached as template.load when used in context\nof Nginx / OpenResty):\n\n```lua\nfunction(view, plain)\n    if plain == true then return view end\n    local vars = VAR_PHASES[phase()]\n    local path = view\n    local root = template.location\n    if (not root or root == EMPTY) and vars then\n        root = var.template_location\n    end\n    if root and root ~= EMPTY then\n        if byte(root, -1) == SOL then root = sub(root, 1, -2) end\n        if byte(path,  1) == SOL then path = sub(path, 2) end\n        path = root .. \"/\" .. path\n        local res = capture(path)\n        if res.status == 200 then return res.body end\n    end\n    path = view\n    root = template.root\n    if (not root or root == EMPTY) and vars then\n        root = var.template_root\n        if not root or root == EMPTY then root = var.document_root or prefix end\n    end\n    if root and root ~= EMPTY then\n        if byte(root, -1) == SOL then root = sub(root, 1, -2) end\n        if byte(path,  1) == SOL then path = sub(path, 2) end\n        path = root .. \"/\" .. path\n    end\n    return plain == false and assert(read_file(path)) or read_file(path) or view\nend\n```\n\nAs you can see, `lua-resty-template` always tries (by default) to load a template from a file\n(or with `ngx.location.capture`) even if you provided template as a string. `lua-resty-template`.\nBut if you know that your templates are always strings, and not file paths, you may use `plain`\nargument in `template.compile`, `template.render`, and `template.parse` OR replace `template.load`\nwith the simplest possible template loader there is (but be aware that if your templates use\n`{(file.html)}` includes, those are considered as strings too, in this case `file.html` will\nbe the template string that is parsed) - you could also setup a loader that finds templates in\nsome database system, e.g. Redis:\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.load = function(view, plain) return view end\n```\n\nIf the `plain` parameter is `false` (`nil` is not treated as `false`), all the issues with file\nio are considered assertion errors.\n\n\n#### string template.load_string(view)\n\nThis just calls `template.load(view, true)`\n\n\n#### string template.load_file(view)\n\nThis just calls `template.load(view, false)`\n\n\n#### template.print\n\nThis field contains a function that is used on `template.render()` or\n`template.new(\"example.html\"):render()` to output the results. By default this holds either\n`ngx.print` (if available) or `print`. You may want to (and are allowed to) overwrite this\nfield, if you want to use your own output function instead. This is also useful if you are\nusing some other framework, e.g. Turbo.lua (http://turbolua.org/).\n\n```lua\nlocal template = require \"resty.template\"\n\ntemplate.print = function(s)\n  print(s)\n  print(\"\u003c!-- Output by My Function --\u003e\")\nend\n```\n\n\n## Template Precompilation\n\n`lua-resty-template` supports template precompilation. This can be useful when you want to\nskip template parsing (and Lua interpretation) in production or if you do not want your\ntemplates distributed as plain text files on production servers. Also by precompiling,\nyou can ensure that your templates do not contain something, that cannot be compiled\n(they are syntactically valid Lua). Although templates are cached (even without precompilation),\nthere are some performance (and memory) gains. You could integrate template precompilation in\nyour build (or deployment) scripts (maybe as Gulp, Grunt or Ant tasks).\n\n\n##### Precompiling template, and output it as a binary file\n\n```lua\nlocal template = require \"resty.template\"\nlocal compiled = template.precompile(\"example.html\", \"example-bin.html\")\n```\n\n\n##### Load precompiled template file, and run it with context parameters\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.render(\"example-bin.html\", { \"Jack\", \"Mary\" })\n```\n\n\n## Template Helpers\n\n### Built-in Helpers\n\n#### echo(...)\n\nEchoes output. This is useful with `{% .. %}`:\n\n```lua\nrequire \"resty.template\".render[[\nbegin\n{%\nfor i=1, 10 do\n  echo(\"\\tline: \", i, \"\\n\")\nend\n%}\nend\n]]\n```\n\nThis will output:\n\n```\nbegin\n\tline: 1\n\tline: 2\n\tline: 3\n\tline: 4\n\tline: 5\n\tline: 6\n\tline: 7\n\tline: 8\n\tline: 9\n\tline: 10\nend\n```\n\nThis can also be written as but `echo` might come handy in some cases:\n\n```lua\nrequire \"resty.template\".render[[\nbegin\n{% for i=1, 10 do %}\n  line: {* i *}\n{% end %}\nend\n]]\n```\n\n\n#### include(view, context)\n\nThis is mainly used with internally with `{(view.hmtl)}`, `{[\"view.hmtl\"]}` and\nwith blocks `{-block-name-}..{-block-name-}`. If `context` is not given the context\nused to compile parent view is used. This function will compile the `view` and call\nthe resulting function with `context` (or the `context` of parent view if not given).\n\n\n### Other Ways to Extend\n\nWhile `lua-resty-template` does not have much infrastucture or ways to extend it,\nyou still have a few possibilities that you may try.\n\n* Adding methods to global `string`, and `table` types (not encouraged, though)\n* Wrap your values with something before adding them in context (e.g. proxy-table)\n* Create global functions\n* Add local functions either to `template` table or `context` table\n* Use metamethods in your tables\n\nWhile modifying global types seems convenient, it can have nasty side effects.\nThat's why I suggest you to look at these libraries, and articles first:\n\n* Method Chaining Wrapper (http://lua-users.org/wiki/MethodChainingWrapper)\n* Moses (https://github.com/Yonaba/Moses)\n* underscore-lua (https://github.com/jtarchie/underscore-lua)\n\nYou could for example add Moses' or Underscore's `_` to template table or context table.\n\n\n##### Example\n\n```lua\nlocal _ = require \"moses\"\nlocal template = require \"resty.template\"\ntemplate._ = _\n```\n\nThen you can use `_` inside your templates. I created one example template helper\nthat can be found from here:\nhttps://github.com/bungle/lua-resty-template/blob/master/lib/resty/template/html.lua\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\nlocal html = require \"resty.template.html\"\n\ntemplate.render([[\n\u003cul\u003e\n{% for _, person in ipairs(context) do %}\n    {*html.li(person.name)*}\n{% end %}\n\u003c/ul\u003e\n\u003ctable\u003e\n{% for _, person in ipairs(context) do %}\n    \u003ctr data-sort=\"{{(person.name or \"\"):lower()}}\"\u003e\n        {*html.td{ id = person.id }(person.name)*}\n    \u003c/tr\u003e\n{% end %}\n\u003c/table\u003e]], {\n    { id = 1, name = \"Emma\"},\n    { id = 2, name = \"James\" },\n    { id = 3, name = \"Nicholas\" },\n    { id = 4 }\n})\n```\n\n\n##### Output\n\n```html\n\u003cul\u003e\n    \u003cli\u003eEmma\u003c/li\u003e\n    \u003cli\u003eJames\u003c/li\u003e\n    \u003cli\u003eNicholas\u003c/li\u003e\n    \u003cli /\u003e\n\u003c/ul\u003e\n\u003ctable\u003e\n    \u003ctr data-sort=\"emma\"\u003e\n        \u003ctd id=\"1\"\u003eEmma\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr data-sort=\"james\"\u003e\n        \u003ctd id=\"2\"\u003eJames\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr data-sort=\"nicholas\"\u003e\n        \u003ctd id=\"3\"\u003eNicholas\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr data-sort=\"\"\u003e\n        \u003ctd id=\"4\" /\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n```\n\n\n## Usage Examples\n\n### Template Including\n\nYou may include templates inside templates with `{(template)}` and `{(template, context)}` syntax.\nThe first one uses the current context as a context for included template, and the second one replaces\nit with a new context. Here is example of using includes and passing a different context to include file:\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.render(\"include.html\", { users = {\n    { name = \"Jane\", age = 29 },\n    { name = \"John\", age = 25 }\n}})\n```\n\n##### include.html\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003cul\u003e\n{% for _, user in ipairs(users) do %}\n    {(user.html, user)}\n{% end %}\n\u003c/ul\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n##### user.html\n\n```html\n\u003cli\u003eUser {{name}} is of age {{age}}\u003c/li\u003e\n```\n\n##### Output\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003cul\u003e\n    \u003cli\u003eUser Jane is of age 29\u003c/li\u003e\n    \u003cli\u003eUser John is of age 25\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n### Views with Layouts\n\nLayouts (or Master Pages) can be used to wrap a view inside another view (aka layout).\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\nlocal layout   = template.new \"layout.html\"\nlayout.title   = \"Testing lua-resty-template\"\nlayout.view    = template.compile \"view.html\" { message = \"Hello, World!\" }\nlayout:render()\n-- Or like this\ntemplate.render(\"layout.html\", {\n  title = \"Testing lua-resty-template\",\n  view  = template.compile \"view.html\" { message = \"Hello, World!\" }\n})\n-- Or maybe you like this style more\n-- (but please remember that context.view is overwritten on rendering the layout.html)\nlocal view     = template.new(\"view.html\", \"layout.html\")\nview.title     = \"Testing lua-resty-template\"\nview.message   = \"Hello, World!\"\nview:render()\n-- Well, maybe like this then?\nlocal layout   = template.new \"layout.html\"\nlayout.title   = \"Testing lua-resty-template\"\nlocal view     = template.new(\"view.html\", layout)\nview.message   = \"Hello, World!\"\nview:render()\n```\n\n\n##### view.html\n\n```html\n\u003ch1\u003e{{message}}\u003c/h1\u003e\n```\n\n##### layout.html\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003e{{title}}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    {*view*}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n##### Alternatively you can define the layout in a view as well:\n\n##### Lua\n\n```lua\nlocal view     = template.new(\"view.html\", \"layout.html\")\nview.title     = \"Testing lua-resty-template\"\nview.message   = \"Hello, World!\"\nview:render()\n```\n\n\n##### view.html\n\n```html\n{% layout=\"section.html\" %}\n\u003ch1\u003e{{message}}\u003c/h1\u003e\n```\n\n\n##### section.html\n\n```html\n\u003cdiv id=\"section\"\u003e\n    {*view*}\n\u003c/div\u003e\n```\n\n\n##### layout.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003e{{title}}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    {*view*}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n##### Output\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eTesting lua-resty-template\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv id=\"section\"\u003e\n    \u003ch1\u003eHello, World!\u003c/h1\u003e\n\u003c/div\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n### Using Blocks\n\nBlocks can be used to move different parts of the views to specific places in layouts. Layouts have placeholders\nfor blocks.\n\n\n##### Lua\n\n```lua\nlocal view     = template.new(\"view.html\", \"layout.html\")\nview.title     = \"Testing lua-resty-template blocks\"\nview.message   = \"Hello, World!\"\nview.keywords  = { \"test\", \"lua\", \"template\", \"blocks\" }\nview:render()\n```\n\n\n##### view.html\n\n```html\n\u003ch1\u003e{{message}}\u003c/h1\u003e\n{-aside-}\n\u003cul\u003e\n    {% for _, keyword in ipairs(keywords) do %}\n    \u003cli\u003e{{keyword}}\u003c/li\u003e\n    {% end %}\n\u003c/ul\u003e\n{-aside-}\n```\n\n\n##### layout.html\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\u003ctitle\u003e{*title*}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003carticle\u003e\n    {*view*}\n\u003c/article\u003e\n{% if blocks.aside then %}\n\u003caside\u003e\n    {*blocks.aside*}\n\u003c/aside\u003e\n{% end %}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n##### Output\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\u003ctitle\u003eTesting lua-resty-template blocks\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003carticle\u003e\n    \u003ch1\u003eHello, World!\u003c/h1\u003e\n\u003c/article\u003e\n\u003caside\u003e\n    \u003cul\u003e\n        \u003cli\u003etest\u003c/li\u003e\n        \u003cli\u003elua\u003c/li\u003e\n        \u003cli\u003etemplate\u003c/li\u003e\n        \u003cli\u003eblocks\u003c/li\u003e\n    \u003c/ul\u003e\n\u003c/aside\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n### Grandfather-Father-Son Inheritance\n\nSay you have `base.html`, `layout1.html`, `layout2.html` and `page.html`. You want an inheritance like this:\n`base.html ➡ layout1.html ➡ page.html` or `base.html ➡ layout2.html ➡ page.html` (actually this nesting is\nnot limited to three levels).\n\n\n##### Lua\n\n```lua\nlocal res = require\"resty.template\".compile(\"page.html\"){} \n```\n\n\n##### base.html\n\n```html\n\u003chtml lang='zh'\u003e\n   \u003chead\u003e\n   \u003clink href=\"css/bootstrap.min.css\" rel=\"stylesheet\"\u003e\n   {* blocks.page_css *}\n   \u003c/head\u003e\n   \u003cbody\u003e\n   {* blocks.main *}\n   \u003cscript src=\"js/jquery.js\"\u003e\u003c/script\u003e\n   \u003cscript src=\"js/bootstrap.min.js\"\u003e\u003c/script\u003e\n   {* blocks.page_js *}\n   \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n##### layout1.html\n\n```html\n{% layout = \"base.html\" %}\n{-main-}\n    \u003cdiv class=\"sidebar-1\"\u003e\n      {* blocks.sidebar *}\n    \u003c/div\u003e\n    \u003cdiv class=\"content-1\"\u003e\n      {* blocks.content *}\n    \u003c/div\u003e\n{-main-}\n```\n    \n\n##### layout2.html\n\n```html\n{% layout = \"base.html\" %}\n{-main-}\n    \u003cdiv class=\"sidebar-2\"\u003e\n      {* blocks.sidebar *}\n    \u003c/div\u003e\n    \u003cdiv class=\"content-2\"\u003e\n      {* blocks.content *}\n    \u003c/div\u003e\n    \u003cdiv\u003eI am different from layout1 \u003c/div\u003e\n{-main-}\n```\n\n\n##### page.html \n\n```html\n{% layout = \"layout1.html\" %}\n{-sidebar-}\n  this is sidebar\n{-sidebar-}\n\n{-content-}\n  this is content\n{-content-}\n\n{-page_css-}\n  \u003clink href=\"css/page.css\" rel=\"stylesheet\"\u003e\n{-page_css-}\n\n{-page_js-}\n  \u003cscript src=\"js/page.js\"\u003e\u003c/script\u003e\n{-page_js-}\n```\n\nOr:\n\n\n##### page.html\n\n```html\n{% layout = \"layout2.html\" %}\n{-sidebar-}\n  this is sidebar\n{-sidebar-}\n\n{-content-}\n  this is content\n{-content-}\n\n{-page_css-}\n  \u003clink href=\"css/page.css\" rel=\"stylesheet\"\u003e\n{-page_css-}\n\n{-page_js-}\n  \u003cscript src=\"js/page.js\"\u003e\u003c/script\u003e\n{-page_js-}\n```\n    \n\n### Macros\n\n[@DDarko](https://github.com/DDarko) mentioned in an [issue #5](https://github.com/bungle/lua-resty-template/issues/5)\nthat he has a use case where he needs to have macros or parameterized views. That is a nice feature that you can\nuse with `lua-resty-template`.\n\nTo use macros, let's first define some Lua code:\n\n```lua\ntemplate.render(\"macro.html\", {\n    item = \"original\",\n    items = { a = \"original-a\", b = \"original-b\" } \n})\n```\n\nAnd the `macro-example.html`:\n\n```lua\n{% local string_macro = [[\n\u003cdiv\u003e{{item}}\u003c/div\u003e\n]] %}\n{* template.compile(string_macro)(context) *}\n{* template.compile(string_macro){ item = \"string-macro-context\" } *}\n```\n\nThis will output:\n\n```html\n\u003cdiv\u003eoriginal\u003c/div\u003e\n\u003cdiv\u003estring-macro-context\u003c/div\u003e\n```\n\nNow let's add function macro, in `macro-example.html` (you can omit `local` if you want):\n\n```lua\n{% local function_macro = function(var, el)\n    el = el or \"div\"\n    return \"\u003c\" .. el .. \"\u003e{{\" .. var .. \"}}\u003c/\" .. el .. \"\u003e\\n\"\nend %}\n\n{* template.compile(function_macro(\"item\"))(context) *}\n{* template.compile(function_macro(\"a\", \"span\"))(items) *}\n```\n\nThis will output:\n\n```html\n\u003cdiv\u003eoriginal\u003c/div\u003e\n\u003cspan\u003eoriginal-a\u003c/span\u003e\n```\n\nBut this is even more flexible, let's try another function macro:\n\n```lua\n{% local function function_macro2(var)\n    return template.compile(\"\u003cdiv\u003e{{\" .. var .. \"}}\u003c/div\u003e\\n\")\nend %}\n{* function_macro2 \"item\" (context) *}\n{* function_macro2 \"b\" (items) *}\n```\n\nThis will output:\n\n```html\n\u003cdiv\u003eoriginal\u003c/div\u003e\n\u003cdiv\u003eoriginal-b\u003c/div\u003e\n```\n\nAnd here is another one:\n\n```lua\n{% function function_macro3(var, ctx)\n    return template.compile(\"\u003cdiv\u003e{{\" .. var .. \"}}\u003c/div\u003e\\n\")(ctx or context)\nend %}\n{* function_macro3(\"item\") *}\n{* function_macro3(\"a\", items) *}\n{* function_macro3(\"b\", items) *}\n{* function_macro3(\"b\", { b = \"b-from-new-context\" }) *}\n```\n\nThis will output:\n\n```html\n\u003cdiv\u003eoriginal\u003c/div\u003e\n\u003cdiv\u003eoriginal-a\u003c/div\u003e\n\u003cdiv\u003eoriginal-b\u003c/div\u003e\n\u003cdiv\u003eb-from-new-context\u003c/div\u003e\n```\n\nMacros are really flexible. You may have form-renderers and other helper-macros to have a reusable and\nparameterized template output. One thing you should know is that inside code blocks (between `{%` and `%}`)\nyou cannot have `%}`, but you can work around this using string concatenation `\"%\" .. \"}\"`.\n\n\n### Calling Methods in Templates\n\nYou can call string methods (or other table functions) in templates too.\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.render([[\n\u003ch1\u003e{{header:upper()}}\u003c/h1\u003e\n]], { header = \"hello, world!\" })\n```\n\n\n##### Output\n\n```html\n\u003ch1\u003eHELLO, WORLD!\u003c/h1\u003e\n```\n\n\n### Embedding Angular or other tags / templating inside the Templates\n \nSometimes you need to mix and match other templates (say client side Javascript templates like Angular) with\nserver side lua-resty-templates. Say you have this kind of Angular template:\n\n```html\n\u003chtml ng-app\u003e\n \u003cbody ng-controller=\"MyController\"\u003e\n   \u003cinput ng-model=\"foo\" value=\"bar\"\u003e\n   \u003cbutton ng-click=\"changeFoo()\"\u003e{{buttonText}}\u003c/button\u003e\n   \u003cscript src=\"angular.js\"\u003e\n \u003c/body\u003e\n\u003c/html\u003e\n```\n\nNow you can see that there is `{{buttonText}}` that is really for Angular templating, and not for lua-resty-template.\nYou can fix this by wrapping either the whole code with `{-verbatim-}` or `{-raw-}` or only the parts that you want:\n\n```html\n{-raw-}\n\u003chtml ng-app\u003e\n \u003cbody ng-controller=\"MyController\"\u003e\n   \u003cinput ng-model=\"foo\" value=\"bar\"\u003e\n   \u003cbutton ng-click=\"changeFoo()\"\u003e{{buttonText}}\u003c/button\u003e\n   \u003cscript src=\"angular.js\"\u003e\n \u003c/body\u003e\n\u003c/html\u003e\n{-raw-}\n```\n\nor (see the `{(head.html)}` is processed by lua-resty-template):\n\n```html\n\u003chtml ng-app\u003e\n {(head.html)}\n \u003cbody ng-controller=\"MyController\"\u003e\n   \u003cinput ng-model=\"foo\" value=\"bar\"\u003e\n   \u003cbutton ng-click=\"changeFoo()\"\u003e{-raw-}{{buttonText}}{-raw-}\u003c/button\u003e\n   \u003cscript src=\"angular.js\"\u003e\n \u003c/body\u003e\n\u003c/html\u003e\n```\n\nYou may also use short escaping syntax:\n\n```html\n...\n\u003cbutton ng-click=\"changeFoo()\"\u003e\\{{buttonText}}\u003c/button\u003e\n...\n```\n\n\n### Embedding Markdown inside the Templates\n\nIf you want to embed Markdown (and SmartyPants) syntax inside your templates you can do it by using for example\n[`lua-resty-hoedown`](https://github.com/bungle/lua-resty-hoedown) (it depends on LuaJIT). Here is an example of\nusing that:\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.markdown = require \"resty.hoedown\"\n\ntemplate.render[=[\n\u003chtml\u003e\n\u003cbody\u003e\n{*markdown[[\n#Hello, World\n\nTesting Markdown.\n]]*}\n\u003c/body\u003e\n\u003c/html\u003e\n]=]\n```\n\n\n##### Output\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003ch1\u003eHello, World\u003c/h1\u003e\n\n\u003cp\u003eTesting Markdown.\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nYou may also add config parameters that are documented in `lua-resty-hoedown` project.\nSay you want also to use SmartyPants:\n\n\n##### Lua\n\n```lua\nlocal template = require \"resty.template\"\ntemplate.markdown = require \"resty.hoedown\"\n\ntemplate.render[=[\n\u003chtml\u003e\n\u003cbody\u003e\n{*markdown([[\n#Hello, World\n\nTesting Markdown with \"SmartyPants\"...\n]], { smartypants = true })*}\n\u003c/body\u003e\n\u003c/html\u003e\n]=]\n```\n\n\n##### Output\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003ch1\u003eHello, World\u003c/h1\u003e\n\n\u003cp\u003eTesting Markdown with \u0026ldquo;SmartyPants\u0026rdquo;\u0026hellip;\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nYou may also want to add caching layer for your Markdowns, or a helper functions instead of placing\nHoedown library directly  as a template helper function in `template`.   \n\n\n### Lua Server Pages (LSP) with OpenResty\n\nLua Server Pages or LSPs is similar to traditional PHP or Microsoft Active Server Pages (ASP)\nwhere you can just place source code files in your document root (of your web server) and have\nthem processed by compilers of the respective languages (PHP, VBScript, JScript, etc.).\nYou can emulate quite closely this, sometimes called spaghetti-style of develoment, easily with\n`lua-resty-template`. Those that have been doing ASP.NET Web Forms development, know a concept\nof Code Behind files. There is something similar, but this time we call it Layout in Front here\n(you may include Lua modules with normal `require` calls if you wish in LSPs). To help you\nunderstand the concepts, let's have a small example:\n\n\n##### nginx.conf:\n\n```nginx\nhttp {\n  init_by_lua '\n    require \"resty.core\"\n    template = require \"resty.template\"\n    template.caching(false); -- you may remove this on production\n  ';\n  server {\n    location ~ \\.lsp$ {\n      default_type text/html;\n      content_by_lua 'template.render(ngx.var.uri)';\n    }\n  }\n}\n```\n\nThe above configuration creates a global `template` variable in Lua environment (you may not want that).\nWe also created location to match all `.lsp` files (or locations), and then we just render the template.\n\nLet's imagine that the request is for `index.lsp`.\n\n\n##### index.lsp\n\n```html\n{%\nlayout = \"layouts/default.lsp\"\nlocal title = \"Hello, World!\"\n%}\n\u003ch1\u003e{{title}}\u003c/h1\u003e\n```\n\nHere you can see that this file includes a little bit of a view (`\u003ch1\u003e{{title}}\u003c/h1\u003e`) in addition to some\nLua code that we want to run. If you want to have a pure code file with Layout in Front, then just don't\nwrite any view code in this file. The `layout` variable is already defined in views as documented else\nwhere in this documentation. Now let's see the other files too.\n\n\n##### layouts/default.lsp\n\n```html\n\u003chtml\u003e\n{(include/header.lsp)}\n\u003cbody\u003e\n{*view*}\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nHere we have a layout to decorate the `index.lsp`, but we also have include here, so let's look at it.\n\n\n##### include/header.lsp\n\n```html\n\u003chead\u003e\n  \u003ctitle\u003eTesting Lua Server Pages\u003c/title\u003e\n\u003c/head\u003e\n```\n\nStatic stuff here only.\n\n\n##### Output\n\nThe final output will look like this:\n\n```html\n\u003chtml\u003e\n\u003chead\u003e\n  \u003ctitle\u003eTesting Lua Server Pages\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003ch1\u003eHello, World!\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nAs you can see, `lua-resty-template` can be quite flexibile and easy to start with. Just place\nfiles under your document root and use the normal save-and-refresh style of development. The\nserver will automatically pick the new files and reload the templates (if the caching is turned\nof) on save.\n\nIf you want to pass variables to layouts or includes you can add stuff to context table (in the\nexample below see `context.title`):\n\n```html\n{%\nlayout = \"layouts/default.lsp\"\nlocal title = \"Hello, World!\"\ncontext.title = 'My Application - ' .. title\n%}\n\u003ch1\u003e{{title}}\u003c/h1\u003e\n```\n\n\n## FAQ\n\n### How Do I Clear the Template Cache\n\n`lua-resty-template` automatically caches (if caching is enabled) the resulting template functions\nin `template.cache` table. You can clear the cache by issuing `template.cache = {}`.\n\n\n### Where is `lua-resty-template` Used\n\n* [jd.com](http://www.jd.com/) – Jingdong Mall (Chinese: 京东商城; pinyin: Jīngdōng Shāngchéng),\nformerly 360Buy, is a Chinese electronic commerce company\n\nPlease let me know if there are errors or old information in this list. \n\n\n## Alternatives\n\nYou may also look at these (as alternatives, or to mix them with `lua-resty-template`):\n\n* lemplate (https://github.com/openresty/lemplate)\n* lua-resty-tags (https://github.com/bungle/lua-resty-tags)\n* lua-resty-hoedown (https://github.com/bungle/lua-resty-hoedown)\n* etlua (https://github.com/leafo/etlua)\n* lua-template (https://github.com/dannote/lua-template)\n* lua-resty-tmpl (https://github.com/lloydzhou/lua-resty-tmpl) (a fork of the [lua-template](https://github.com/dannote/lua-template))\n* htmlua (https://github.com/benglard/htmlua)\n* cgilua (http://keplerproject.github.io/cgilua/manual.html#templates)\n* orbit (http://keplerproject.github.io/orbit/pages.html)\n* lustache (https://github.com/Olivine-Labs/lustache)\n* luvstache (https://github.com/james2doyle/luvstache)\n* luaghetti (https://github.com/AterCattus/luaghetti)\n* lust (https://github.com/weshoke/Lust)\n* templet (http://colberg.org/lua-templet/)\n* luahtml (https://github.com/TheLinx/LuaHTML)\n* mixlua (https://github.com/LuaDist/mixlua)\n* lutem (https://github.com/daly88/lutem)\n* tirtemplate (https://github.com/torhve/LuaWeb/blob/master/tirtemplate.lua)\n* groucho (https://github.com/hanjos/groucho)\n* simple lua preprocessor (http://lua-users.org/wiki/SimpleLuaPreprocessor)\n* slightly less simple lua preprocessor (http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor)\n* ltp (http://www.savarese.com/software/ltp/)\n* slt (https://code.google.com/p/slt/)\n* slt2 (https://github.com/henix/slt2)\n* view0 (https://bitbucket.org/jimstudt/view0)\n* leslie (https://code.google.com/p/leslie/)\n* fraudster (https://bitbucket.org/sphen_lee/fraudster)\n* lua-haml (https://github.com/norman/lua-haml)\n* lua-template (https://github.com/tgn14/Lua-template)\n* hige (https://github.com/nrk/hige)\n* mod_pLua (https://sourceforge.net/p/modplua/wiki/Home/)\n* lapis html generation (http://leafo.net/lapis/reference.html#html-generation)\n\n`lua-resty-template` *was originally forked from Tor Hveem's* `tirtemplate.lua` *that he had extracted\nfrom Zed Shaw's Tir web framework (http://tir.mongrel2.org/). Thank you Tor, and Zed for your earlier\ncontributions.*\n\n\n## Benchmarks\n\nThere is a small microbenchmark located here:\nhttps://github.com/bungle/lua-resty-template/blob/master/lib/resty/template/microbenchmark.lua\n\nThere is also a regression in LuaJIT that affects the results. If you want your LuaJIT patched against this,\nyou need to merge this pull request: https://github.com/LuaJIT/LuaJIT/pull/174.\n\nOthers have [reported](https://github.com/bungle/lua-resty-template/issues/21#issuecomment-226786051)\nthat in simple benchmarks running this template engine actually beats Nginx serving static files by\na factor of three. So I guess this engine is quite fast. \n\n\n##### Lua\n\n```lua\nlocal benchmark = require \"resty.template.microbenchmark\"\nbenchmark.run()\n-- You may also pass iteration count (by default it is 1,000)\nbenchmark.run(100)\n```\n\nHere are some results from my laptop (2021 MacBook Pro, M1 Max)):\n```\n\u003clua|luajit|resty\u003e -e 'require \"resty.template.microbenchmark\".run()'\n```\n\n\n##### Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.007365\nCompilation Time: 0.022014 (template)\nCompilation Time: 0.000103 (template, cached)\n  Execution Time: 0.020429 (same template)\n  Execution Time: 0.002542 (same template, cached)\n  Execution Time: 0.022314 (different template)\n  Execution Time: 0.003347 (different template, cached)\n  Execution Time: 0.021259 (different template, different context)\n  Execution Time: 0.003264 (different template, different context, cached)\n      Total Time: 0.102637\n```\n\n\n##### Lua 5.2.4  Copyright (C) 1994-2015 Lua.org, PUC-Rio\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.008281\nCompilation Time: 0.024388 (template)\nCompilation Time: 0.000112 (template, cached)\n  Execution Time: 0.021825 (same template)\n  Execution Time: 0.002534 (same template, cached)\n  Execution Time: 0.018918 (different template)\n  Execution Time: 0.002217 (different template, cached)\n  Execution Time: 0.017005 (different template, different context)\n  Execution Time: 0.002164 (different template, different context, cached)\n      Total Time: 0.097444\n```\n\n\n##### Lua 5.3.6  Copyright (C) 1994-2020 Lua.org, PUC-Rio\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.007645\nCompilation Time: 0.020548 (template)\nCompilation Time: 0.000104 (template, cached)\n  Execution Time: 0.019389 (same template)\n  Execution Time: 0.002644 (same template, cached)\n  Execution Time: 0.016816 (different template)\n  Execution Time: 0.002188 (different template, cached)\n  Execution Time: 0.015269 (different template, different context)\n  Execution Time: 0.002153 (different template, different context, cached)\n      Total Time: 0.086756\n```\n\n\n### Lua 5.4.3  Copyright (C) 1994-2021 Lua.org, PUC-Rio\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.003963\nCompilation Time: 0.015456 (template)\nCompilation Time: 0.000057 (template, cached)\n  Execution Time: 0.015738 (same template)\n  Execution Time: 0.001903 (same template, cached)\n  Execution Time: 0.015082 (different template)\n  Execution Time: 0.001752 (different template, cached)\n  Execution Time: 0.014412 (different template, different context)\n  Execution Time: 0.001758 (different template, different context, cached)\n      Total Time: 0.070121\n```\n\n\n##### LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.002669\nCompilation Time: 0.011761 (template)\nCompilation Time: 0.000066 (template, cached)\n  Execution Time: 0.012112 (same template)\n  Execution Time: 0.001427 (same template, cached)\n  Execution Time: 0.022134 (different template)\n  Execution Time: 0.002520 (different template, cached)\n  Execution Time: 0.021685 (different template, different context)\n  Execution Time: 0.002381 (different template, different context, cached)\n      Total Time: 0.076755\n```\n\n\n##### resty (resty 0.28, nginx version: openresty/1.19.9.1)\n\n```\nRunning 1000 iterations in each test\n    Parsing Time: 0.001615\nCompilation Time: 0.010031 (template)\nCompilation Time: 0.000029 (template, cached)\n  Execution Time: 0.011654 (same template)\n  Execution Time: 0.001203 (same template, cached)\n  Execution Time: 0.011206 (different template)\n  Execution Time: 0.001933 (different template, cached)\n  Execution Time: 0.010281 (different template, different context)\n  Execution Time: 0.001925 (different template, different context, cached)\n      Total Time: 0.049877\n```\n\nI have not yet compared the results against the alternatives.\n\n\n## Changes\n\nThe changes of every release of this module is recorded in\n[Changes.md](https://github.com/bungle/lua-resty-template/blob/master/Changes.md) file.\n\n\n## See Also\n\n* [lua-resty-route](https://github.com/bungle/lua-resty-route) — Routing library\n* [lua-resty-reqargs](https://github.com/bungle/lua-resty-reqargs) — Request arguments parser\n* [lua-resty-session](https://github.com/bungle/lua-resty-session) — Session library\n* [lua-resty-validation](https://github.com/bungle/lua-resty-validation) — Validation and filtering library\n\n\n## Roadmap\n\nSome things I and the community wishes to be added:\n\n- Better debugging capabilities and better error messages\n- Proper sandboxing\n\n\n## License\n\n`lua-resty-template` uses three clause BSD license (because it was originally forked from one that uses it).\n\n```\nCopyright (c) 2014 - 2022, Aapo Talvensaari\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this\n  list of conditions and the following disclaimer in the documentation and/or\n  other materials provided with the distribution.\n\n* Neither the name of the {organization} nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbungle%2Flua-resty-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbungle%2Flua-resty-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbungle%2Flua-resty-template/lists"}