pages

Aspect › API Documentation

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.

Test > Table Of Content

Basic API Usage

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()
})

Render result

local output, err = aspect:display(template, vars)

Rendering Templates

Note. render functions and display functions returns aspect.output object with template result (if result not displayed) and more useful information

Options

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:

Cache

Aspect template has 3 level of cache:

  1. lua code cache — normal (Aspect’s functions luacode_load and luacode_save).
  2. byte code cache — fast (Aspect’s functions bytecode_load and bytecode_save).
  3. aspect.view object cache (internal) — fastest (Aspect’s property cache)

Cache stages:

  1. Require the template
  2. Get aspect.view from internal (table in the aspect.template) cache.
  3. Get bytcode using bytecode_load function.
  4. Get lua code using luacode_load function.
  5. Get template source code using loader function. Compile it.
  6. Save lua code using luacode_save function.
  7. Save bytcode using bytecode_save function.
  8. Save aspect.view into internal (table in the aspect.template) cache.
  9. Render the template
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
})

Loaders

Loader should be callback or callable object.

File system loader

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.

Resty loader

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).

Array loader

local tpls = require("aspect.loader.array").new()

tpls["dashboard.tpl"] = [[ ... template ... ]]
tpls["theme.tpl"] = [[<html> ... template ... </html>]]

aspect.loader = tpls

Extending

Add tags

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.

Add filters

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 functions

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

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.

Add operators

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.

Behaviors

Condition behaviour

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.

Empty string behaviour

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

Number behaviour

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

Custom escaper

Add custom escaper via config, using escaper name:

require("aspect.config").escapers.csv = function(value) 
    -- ... returns escaped value
end
{{ data.raw|e("csv") }}

Date processing

strtotime

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")

Date localization

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.

Date parser

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:

See date.parsers for more information.

Iterator and countable objects

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.