This chapter describes the API to Aspect and not the template language. It will be most useful as reference to those implementing the template interface to the application and not those who are creating Aspect templates.
Get Aspect from aspect.template
package
local aspect = require("aspect.template").new(options)
options
variable store Aspect configuration.
Aspect uses a loader aspect.loader
to locate templates
aspect.loader = function (name)
local templates = {
index = 'Hello {{ name }}!',
}
return templates[name]
end
The display()
method loads the template passed as a first argument and renders it with the variables passed as a second argument.
aspect:display("dashboard.tpl", {
title = "hello world",
data = app:get_data()
})
local output, err = aspect:display(template, vars)
output
is aspect.output
object and contains rendering information, even if the rendering failed.
If you use aspect:render
method output
contains rendered string:
local output, err = aspect:render(template, vars)
if not err then
io.write(tostring(output))
end
err
is aspect.error
object and contains error information. nil
if no errors.render()
method:
print(aspect:render('index.html', {the = 'variables', go = 'here'}))
render_block()
:
print(aspect:render_block('index.html', 'block_name', {the = 'variables', go = 'here'}))
render_macro()
:
print(aspect:render_macro('index.html', 'macro_name', {the = 'variables', go = 'here'}))
The display()
, display_block()
, display_macro()
methods are shortcuts to output the rendered template.
aspect:display('index.html', {the = 'variables', go = 'here'})
aspect:display_block('index.html', 'block_name', {the = 'variables', go = 'here'})
aspect:display_macro('index.html', 'macro_name', {the = 'variables', go = 'here'})
Also method has some options for output:
aspect:display('index.html', vars, {chunk_size = 8198, print = ngx.print})
Possible options are:
chunk_size
(number) - buffer size before sending data to print
. By default - nil
, buffer disabled.print
(callable) - callback used to send data. By default - ngx.print or print
.Note. render
functions and display
functions returns aspect.output
object with template result
(if result not displayed) and more useful information
When creating a new aspect.template
instance, you can pass an table of options as the constructor argument:
local aspect = require("aspect.template").new({
cache = true,
debug = true
})
The following options are available:
debug
boolean.
When set to true, templates generates notices in some obscure situations. Also enables dump
function.cache
table or false
or true
Enables or disables in-memory cache. If this parameter is a table, then it will be used to store the cache.
If true
- own table will be used.loader
function fun(name: string, tpl: aspect.template):string,string
.
Template source code loader with etag (optionally)luacode_load
function fun(name: string, tpl: aspect.template):string
Function used for loading compiled lua code of the template.luacode_save
function fun(name: string, luacode: string, tpl: aspect.template)
Function used for saving compiled lua code of the template.bytecode_load
function fun(name: string, tpl: aspect.template):string
Function used for loading byte-code of the template.bytecode_save
function fun(name: string, bytecode: string, tpl: aspect.template)
Function used for saving byte-code of the template.autoescape
boolean
Enables or disables auto-escaping with ‘html’ strategy.Aspect template has 3 level of cache:
luacode_load
and luacode_save
).bytecode_load
and bytecode_save
).aspect.view
object cache (internal) — fastest (Aspect’s property cache
)Cache stages:
aspect.view
from internal (table in the aspect.template
) cache.bytecode_load
function.luacode_load
function.loader
function. Compile it.luacode_save
function.bytecode_save
function.aspect.view
into internal (table in the aspect.template
) cache.local aspect = require("aspect.template")
local template = aspect.new({
cache = true -- enable internal lua in-memory cache
})
template.bytecode_load = function (name, aspect)
-- load bytecode from nginx shared dictionary
return ngx.shared["cache"]:get(name)
end
template.luacode_load = function (name, aspect)
-- load lua code from redis
return redis:get(name)
end
template.luacode_save = function (name, luacode, aspect)
-- save lua code into redis
redis:set(name, luacode)
end
template.bytecode_save = function (name, bytecode, aspect)
-- save bytecode into nginx shared dictionary
ngx.shared["cache"]:set(name, bytecode)
end
Use custom table for internal cache:
local aspect = require("aspect.template")
local template = aspect.new({
cache = app.views -- use own app.views table for storing aspect.view objects
})
Loader should be callback or callable object.
aspect.loader = require("aspect.loader.filesystem").new("/var/project/templates")
The code bellow
aspect:display("pages/about.html", vars)
loads /var/project/templates/pages/about.html
template.
aspect.loader = require("aspect.loader.resty").new("/.templates/")
The code bellow
aspect:display("pages/about.html", vars)
loads /.templates/pages/about.html
template (via ngx.location.capture).
local tpls = require("aspect.loader.array").new()
tpls["dashboard.tpl"] = [[ ... template ... ]]
tpls["theme.tpl"] = [[<html> ... template ... </html>]]
aspect.loader = tpls
Add inline tag {% foo %}
:
local tags = require("aspect.tags")
--- @param compiler aspect.compiler
--- @param tok aspect.tokenizer
--- @return string|table of lua code
function tags.foo(compiler, tok)
-- ...
end
Add block tag {% bar %} ... {% endbar %}
:
local tags = require("aspect.tags")
--- @param compiler aspect.compiler
--- @param tok aspect.tokenizer
--- @return string|table of lua code
function tags.bar(compiler, tok)
-- ...
local tag = compiler:push_tag('bar')
--- tag.buz = ...
-- ...
end
--- @param compiler aspect.compiler
--- @param tok aspect.tokenizer
--- @return string|table of lua code
function tags.endbar(compiler, tok)
-- ...
local tag = compiler:pop_tag('bar')
-- ...
end
See aspect.tags for more examples.
local filters = require("aspect.filters")
filters.add("foo", {
input = "any", -- input value type
output = "string", -- output value type
-- define foo's arguments
args = {
[1] = {name = "arg1", type = "string"},
[2] = {name = "arg2", type = "number"}
}
}, function (v, arg1, arg2)
end)
See aspect.filters for more examples.
Add function {{ foo(arg1=x, arg2=y) }}
:
local funcs = require("aspect.funcs")
--- add {{ foo(arg1, arg2) }}
funcs.add("foo", {
-- Define foo's arguments
args = {
[1] = {name = "arg1", type = "string"},
[2] = {name = "arg2", type = "number"},
},
--- Optional. The function will be called when the template is compiled.
--- @param compiler aspect.compiler
--- @param args table
parser = function (compiler, args)
-- ...
end
}, function (__, args)
-- ...
end)
See aspect.funcs for more examples.
Add tests foo
, bar
and baz
local tests = require("aspect.tests")
tests.add('foo', function (__, v)
-- return boolean
end)
tests.add('bar', function (__, v, arg)
-- return boolean
end, true)
tests.add('baz', function (__, v, arg)
-- return boolean
end, "quux")
Result:
{{ a is foo }}
{{ a is bar(c) }}
{{ a is baz quux(c) }}
See aspect.tests for more examples.
For example add bitwise operator &
(using bitop package):
local ops = require("aspect.ast.ops")
-- Push new operator to the operators array
--- Define 'and' bitwise operator.
--- @see aspect.ast.op
table.insert(ops, {
token = "&", -- one-symbol-token for parser
order = 7, -- operator precedence (1 - high priority, 14 - low priority)
l = "number", -- left operand should be number
r = "number", -- right operand should be number
out = "number", -- result of the operator is number
type = "binary", -- operator with two operands
pack = function (left, right) -- build lua code
return "bit.band(" .. left .. ", " .. right .. ")"
end
})
See aspect.ast.ops for more examples.
Cast specific tables and userdata to false.
For persistent tables and userdata:
local cjson = require("cjson.safe")
local is_false = require("aspect.config").is_false
--- cjson.null the same thing every time
is_false[cjson.null] = true -- use userdata as key
For runtime tables and userdata use their metatable:
local cbson = require("cbson")
local is_false = require("aspect.config").is_false
--- cbson.null() creates every time new 'null' userdata object
--- metatable will be used for comparison
is_false[getmetatable(cbson.null())] = true
Add zero-string behaviour.
{% set zero = "0" %} {# zero as string #}
{% if zero %}
Unaceptable condition!
{% end %}
Example do nothing because zero
will be casted to true
("0"
not ‘empty’).
Add behaviour for 0
local is_false = require("aspect.config").is_false
is_false['0'] = true
Now example output Unaceptable condition!
because zero
will be casted to false.
Configure aspect.config.is_empty_string
table. Indicate which values are empty string or values with specific metatable.
local is_empty_string = require("aspect.config").is_empty_string
is_empty_string[ngx.null] = true
is_empty_string[getmetatable(cbson.null())] = true
Configure aspect.config.is_n
table. Indicate which objects can behave like numbers.
local is_n = require("aspect.config").is_n
is_n[getmetatable(cbson.number(0))] = 0
Add custom escaper via config, using escaper name:
require("aspect.config").escapers.csv = function(value)
-- ... returns escaped value
end
{{ data.raw|e("csv") }}
Parse about any textual datetime description into a Unix timestamp:
local strtotime = require("aspect.utils.date").strtotime
local ts, info = strtotime("2009-02-13 23:31:30")
Add or change month localizations. For example add localized months for russian and spain languages.
local months = require("aspect.config").date.months
months["дек"] = 12 -- add short name of december on russian
months["декабрь"] = 12 -- add long name of december on russian
months["dic"] = 12 -- add short name of december on spain
months["diciembre"] = 12 -- add long name of december on spain
-- ...
There 1 - january, 12 - december.
Add or change date parsers. For example add parser for date like 2009Y02M13D23h31m30s+0230z
(it is 2009-02-13 23:31:30 UTC+02:30
)
local date = require("aspect.utils.date")
table.insert(date.parsers.date, { -- parse date segment
pattern = "(%d%d%d%d)Y(%d%d)M(%d%d)D",
match = function(y, m, d)
return {year = tonumber(y), month = tonumber(m), day = tonumber(d)}
end
})
table.insert(date.parsers.time, { -- parse time segment
pattern = "(%d%d)h(%d%d)m(%d%d)s",
match = function(h, m, s)
return {hour = tonumber(h), min = tonumber(m), sec = tonumber(s)}
end
})
table.insert(date.parsers.zone, { -- parse offset/timezone offset
pattern = "([+-]%d%d)(%d?%d?)z",
match = function(h, m)
return {offset = tonumber(h) * 60 * 60 + (tonumber(m) or 0) * 60} -- seconds
end
})
How parsers work:
date
parser.
match
function will be called.Match
function returns table like os.date("*t")
if success, nil if failed (if nil resume 1.1)time
parser. search continues with the next character after matched date
match
function will be called.Match
function returns table like os.date("*t")
if success, nil if failed (if nil resume 2.1)zone
parser. search continues with the next character after matched time
match
function will be called.Match
function returns table with key offset
if success, nil if failed (if nil resume 3.1)See date.parsers
for more information.
The Aspect
implements custom iterators as in Lua 5.2+ - through the metatable and __pairs()
function.
Works for all lua/luajit versions.
For example see range iterator.
As in Lua 5.2+, the Aspect allows to determine the length of objects through the __len()
function.
Works for all lua/luajit versions.