LtdTemplate is a Ruby Gem implementing resource-limitable, user-editable, textual templating.
Note that "user-editable" does not particularly refer to simplicity (understanding most templates will be facilitated by having a programming background), but rather that, with proper configuration, it can be used to allow sophisticated users to edit their own templates (e.g. for message localization) with limited risk of infinite execution loops or recursion, excessively long strings, or other uncontrolled resource utilization.
This document describes the template syntax. See the Gem documentation for information about the programming interface.
[Contents]
A template consists of inter-mixed sections of literal text and
template code. The shortest sequences between "<<
"
and ">>
" that don't contain "<<
"
or ">>
" are template code; the rest is literal
text. Given a sequence of the form:
A<<B<<C>>D<<E>>F>>G<<<H>>>I
template code cannot begin at "<<B" because it is not allowed
to contain "<<
" or ">>
", so the
first template code is "<<C>>
", the second is
"<<E>>
", and the last is
"<<H>>
". Everything else ("A<<B
",
"D
", "F>>G<
", and ">I
")
is treated as literal text.
If template code begins with "<<.
" (with a period
immediately after the second "<
"), it strips any trailing
white space from immediately preceding literal text. Likewise, if template
code ends with ".>>
", it strips any leading white space
from immediately following literal text. Template code consisting of
only "<<.>>
" is considered to meet both
conditions and is equivalent to "<<..>>
".
There need not be any literal text before, after, or between template
code sequences. Template code sequences between delimiters may also be empty
(<<>>
).
Unless otherwise stated, the remainder of this document will refer to
template code as the code between the "<<
"
(or "<<.
") and ">>
" (or
".>>
") without reference to the delimiters
themselves.
[Contents]
Comments begin with "/*
" and end at the nearest
"*/
".
/* this is a comment */
/* and so is /* this */
[Contents] [Template Code Syntax]
String literals are used to generate string values. LtdTemplate supports two forms: "short" and "regular".
Short string literals begin with a single quote ('
) and
terminate before a period (.
), comma (,
), opening
or closing brackets, parentheses, or braces ([](){}
), or white
space. However, any normally terminating character may be included by
preceding it with a backslash (\
).
Regular string literals begin with a double quote ("
) and
terminate at the next double quote. A double quote may be included by
preceding it with a backslash.
String literals may also contain other "escape sequences" like those in Ruby double-quoted strings. Here is the full list:
Sequence | Meaning |
---|---|
\M-\C-x | meta-control-x |
\M-x | meta-x |
\C-x, \Cx | control-x |
\udddd | Unicode four-digit, 16-bit hexadecimal code point dddd |
\xdd | two-digit, 8-bit hexadecimal dd |
\ddd | one, two, or three-digit, 8-bit octal ddd |
\a | alert (ASCII BEL) |
\b | backspace (ASCII BS) |
\e | escape (ASCII ESC) |
\f | formfeed (ASCII FF) |
\n | new-line/line-feed (ASCII LF) |
\r | carriage return (ASCII CR) |
\s | space (ASCII SP) |
\t | horizontal tab (ASCII HT) |
\v | vertical tab (ASCII VT) |
\c | any other character c is just itself |
Examples, with some comments added for annotation:
' /* empty */ 'short '<\< '>\>
/* escaped code delimiters */ "\"regular\" string"
[Contents] [Template Code Syntax]
Numeric literals are used to generate typical integer or real (decimal)
numeric values and consist of an optional
minus sign (-
) followed by one or more digits, and optionally
followed by a decimal point (.
) and one or more additional
digits.
0.125 1 -2 3.0 -4.5
LtdTemplate does not currently support numeric literals in scientific notation or bases other than ten.
[Contents] [Template Code Syntax]
Variables hold references to values, and can hold references to (be
bound to) different values at different times. They exist within the stack
of namespaces. Referencing a variable
normally searches for it beginning in the most recent or "nearest"
namespace and works back to the first or "root" namespace until a
variable with that name is found. If the name begins with circumflex
(^
), the search begins in the parent namespace (if there is
one) instead of the current namespace. If the name begins with at-sign
(@
), only the root namespace is searched. If the variable
has not been assigned, the
nil value is used.
Variable names must be in one of the following formats (unless accessed as an element of a specific namespace—see last example) and are case-sensitive:
_
), followed by zero
or more letters, numbers, or underscores$
) by itselfname item1 _2 ^parent @root $
@['<\<weird>\>]
[Contents] [Template Code Syntax]
A method call performs an action on, or returns information about,
a value. A method call consists of a value expression followed by a
period (.
) followed by the desired method name, optionally
followed by an open parenthesis "(
", zero or more method
call parameters separated by commas (,
), and a closing
parenthesis ")
". Parameters may be positional (also called
sequential) or named (also called random-access). Use the "dot dot"
symbol (..
) to terminate the positional parameters and begin
the named parameters.
value_expression.method_name(positional_1,
..., positional_N .. name_1, value_1,
..., name_N, value_N)
Method names are case-sensitive and must be in one of the following formats:
_
) followed by zero or more
letters, numbers, or underscores+ - * / % & | ! < <= =
== != >= >
The parentheses are optional if no parameters are to be supplied to the method call. The period is optional for "operator-style" method calls if not immediately preceded by punctuation.
3.type /* => number */
3.type.length /* "chained" method calls; => 6 */
1+(2,3,4) /* short for */ 1.+(2,3,4) /* => 10 */
[Contents] [Template Code Syntax]
Variables (and array subscripts, covered later) support two assignment
method calls: unconditional (=
) and conditional
(?=
). A conditional assignment does nothing if the variable is
already bound to (holds a reference to) a value other than the nil
value. (This is a change from versions prior to 1.0.0; in those versions
even a nil value prevented a conditional assignment.) Assignments do not
render in the template output.
If called with a single parameter and no "dot dot" symbol, the value of the single parameter is assigned. Otherwise, all the parameters are assigned as an array. See also array values.
num=(5) num?=(6) /* num is still 5 */
empty1= empty2=() /* empty arrays */
single=(10 ..) /* one element */ double=(5, 10) /* two */
matrix=($.*(1, 0), $.*(0, 1) .. 'type, 'identity)
matrix[0, 0] /* => 1 */ matrix['type] /* => identity */
Variables beginning with circumflex (^
) will be created in
the parent namespace (if they do not already exist) and variables beginning
with at-sign (@
) will be created in the root namespace. All
others will be created in the current namespace.
See also the var method for namespaces.
[Contents] [Template Code Syntax]
The following variables are pre-defined by LtdTemplate:
Name | Description |
---|---|
$ |
This is the current namespace. A new namespace is automatically created each time a code block is executed and is destroyed when the code block completes. |
^ |
This variable refers to the previous (parent) namespace if there is one, or nil if there isn't. |
@ |
This variable refers to the first (also known as the "root" or "top-level") namespace. |
_ |
This variable refers to the array of positional and/or named parameters passed into the template or current code block. |
[Contents] [Template Code Syntax]
A code sequence refers to a sequence of zero or more template code expressions. Templates, method call parameters, and code block bodies are all code sequences.
If a code sequence consists of a single value and does not constitute the entire template code, it is retained as-is. Otherwise, the string value of each element in the sequence, if any, is combined to produce the result of the code sequence.
'Hello $.true ", " num=(5) num-(4) " world!"
/* 5 and 4 are single values in their code sequences */
/* $.true and num=(5) render nothing */
/* num-(4) returns the number 1 but it's string value gets used */
/* => Hello, 1 world! */
[Contents] [Template Code Syntax]
If you surround a code sequence
by braces ({
and }
), you create a code
block. A code block is a value which does not render directly, but
can be called as a method call, executing
the code sequence within the code block.
render_method=({ $.method })
render_method.hi " " render_method.there
/* => hi there */
Code blocks (or other values) may be bound to values using the
methods
method and subsequently called like
methods.
The methods
method takes any number of name-and-binding
pairs to be added or updated. If there are an odd number of parameters
(i.e. there is a final name without a binding), the existing binding, if any,
associated with that name is returned. Specifying nil ($.nil) as the
binding will actually delete any existing binding for the given name.
@.methods('greet,{"Hello, "_[0]"."})
@.greet('Dave) /* => Hello, Dave. */
You can also bind code blocks to proxy objects to be used by an entire class of values. For convenience, binding to an unset variable automatically causes the variable to be set to an empty string.
@Array?=(') /* bind the Array proxy to a unique object
(before 1.0.0) */
@Array.methods('list, { $.target.join(", ") }) /* bind the list method */
$.*(1, 2, 3).list /* => 1, 2, 3 */
Methods added this way are only called if there is no standard method with the same name.
[Contents] [Template Code Syntax]
Any expression that evaluates to an array (or tree of nested arrays)
may be subscripted to select a particular value or nested array from the
array. A subscript consists of an open bracket ([
) followed
by one or more selector expressions separated by commas followed by a
closing bracket (]
). Consecutive selector expressions are
used to traverse progressively more deeply nested arrays.
array_var['items, 5] /* fetches item "5" (which
might also be an array) from array "items" in the array referenced by
array_var */
array_var['items][5] /* as above - alternate syntax */
If the requested item does not exist, the special value "nil" is used instead.
Prior to version 1.0.0, subscript syntax could also be used to bind code blocks to non-array values. The syntax is shown here for historical reference but is no longer supported:
x['greeting]=({'hello}) /* where x is not an array
value */
/* this syntax is no longer supported! */
[Contents] [Template Code Syntax]
LtdTemplate does not have array literals, but you can construct arrays using the variable assignment method (see variable syntax) or via the array method on namespaces:
empty=() /* empty array */ single=(10 ..) /* one element */
double=(5, 10) /* two */
matrix=($.*(1, 0), $.*(0, 1) .. 'type, 'identity)
matrix[0, 0] /* => 1 */ matrix['type] /* => identity */
Arrays render to the concatenation of the renderings of their sequential (positional) elements; random-access (named) elements are ignored.
Standard methods:
Method | Description |
---|---|
call | Same as the array itself |
class | Returns the string "Array" |
each( code_block) |
Executes code_block.each_seq( index,
value) for each sequential index then
code_block.each_rnd( key,
value) for each random-access key
and returns an array of results (since 0.2.1) |
each_rnd( code_block) |
Executes code_block.each_rnd( key,
value) for each random-access key
and returns an array of results (since 0.2.1) |
each_seq( code_block) |
Executes code_block.each_seq( index,
value) for each sequential index
and returns an array of results (since 0.2.1) |
join | Joins sequential (positional) elements |
join( separator) | Join with separator between sequential (positional) elements |
join( two, first,
middle, last) |
Joins two sequential elements with two as the separator or more than two sequential elements with first as the first separator, last as the last separator, and middle for all other separators |
pop , -> | Pop the last sequential element |
push( list) ,
+>( list) |
Adds zero or more elements in the list to the end of the array |
rnd_size | The number of random-access (named) elements |
seq_size | The number of sequential (positional) elements |
shift , <- | Shift the first sequential element |
size | The total number of elements in the array |
type | Returns the string "array" |
unshift( list) ,
<+( list) |
Adds zero or more elements in the list to the beginning of the array |
/ | Interpolate the sequential and random-access values of the array into the positional and named parameters of another method call (since 0.2.1) |
% |
Interpolate pairs of sequential values in the array as named parameters of another method call (since 0.2.1) |
Code blocks may be bound as methods for all array values as follows:
@Array.methods('
method_name, {
code sequence})
[Contents] [Values And Methods]
LtdTemplate supports boolean values (true and false). They can be
accessed via the namespace method calls $.true
and
$.false
. There is only one true value and one false value,
although other values can be interpreted in boolean context (the nil
value is treated as false; all others, including the number 0 and
empty strings, are treated as true).
Standard methods:
Method | Description |
---|---|
call | Same as the boolean itself |
class | Returns the string "Boolean" |
string , str |
Returns the string "true" for true and "false" for false |
type | Returns the string "boolean" |
+ , | , or |
Returns the "logical or" of the value and any supplied call parameters (if any are true, the result is true, otherwise the result is false) |
* , & , and |
Returns the "logical and" of the value and any supplied call parameters (if all are true, the result is true, otherwise the result is false) |
! , not |
Returns the "logical not and" of the value and any supplied call parameters (if all are false, the result is true, otherwise the result is false) |
Code blocks may be bound as methods for true, false, or both boolean values as follows:
$.true.methods('
method_name, {
code sequence})
method_name
$.false.methods(', {
code sequence})
method_name
@Boolean.methods(', {
code sequence})
[Contents] [Values And Methods]
Standard methods:
Method | Description |
---|---|
type | Returns the string "code" |
All other methods execute the code block in a new namespace.
[Contents] [Values And Methods]
A namespace is a storage area for variables. A new namespace is created
each time the template is rendered or a code
block is executed. In the case of code blocks, the previously active
namespace becomes the parent (^
) of the new namespace
($
).
Namespaces provide a number of pre-defined variables, as well as method calls for loops and conditionals that are traditionally statements in other programming environments.
Standard methods:
Method | Description |
---|---|
array( elements) ,
*( elements) |
Returns an anonymous array (see array values, assignments, and the var method) |
false | Returns the boolean false value |
if( condition_1,
return_1, ...,
condition_N, return_N,
default) |
Processes each condition in turn. The return value corresponding to the first condition that evaluates to true is returned. If none of the conditions evaluate to true, the optional default value (or the nil value, in the case of an even number of parameters) is returned. Parameters can be supplied as code blocks to avoid execution unless and until needed. |
loop( before,
body, after) |
If the before condition evaluates to true, the body is executed and the optional after condition is evaluated. If the after condition is absent or evaluates to true, the loop starts over with the before condition. The body and non-constant conditions should be supplied as code blocks. Returns an array of body results (one element per loop iteration). |
method | Returns the method name used to call a code block, or "render" for top-level template code |
nil | Returns the nil value |
target | Returns the target object in a code block being called as part of an object binding |
true | Returns the boolean true value |
use( name) |
Calls the template loader to load resource name |
var( name_1, ...,
name_N .. set_1,
value_1, ..., set_N,
value_N) |
Creates and (re-)sets variables named by the supplied strings. The variables in the sequential parameters section, name_1 through name_N, are assigned the nil value. The variables in the random-access section, set_1 through set_N, are assigned the corresponding values from value_1 through value_N. |
Code blocks may be bound as methods for all namespaces as follows:
@Namespace.methods('
method_name, {
code sequence})
[Contents] [Values And Methods]
LtdTemplate supports a "nil" value to represent the lack of another
value. It can be accessed via the namespace method call
$.nil
. There is only one nil value.
Standard methods:
Method | Description |
---|---|
type | Returns the string "nil" |
Code blocks may be bound as methods for the nil value as follows:
$.nil.methods('
method_name, {
code sequence})
[Contents] [Values And Methods]
Standard methods:
Method | Description |
---|---|
abs |
Returns the absolute value of the number |
call | Returns the number itself |
ceil | Returns the closest greater or equal integer |
class | Returns the string "Number" |
floor | Returns the closest lesser or equal integer |
flt, float | Returns the number as a floating-point (decimal) number |
int | Returns the integer portion of the number |
str , string |
Returns the number as a string |
type | Returns the string "number" |
+( list) |
Returns the sum of the number and numeric items in list |
- |
With no parameters, returns the negative of the number |
-( list) |
Returns the difference of the number and the sum of numeric items in the non-empty list |
*( list) |
Returns the product of the number and the numeric items in list |
/( list) |
Returns the quotient of the number and the numeric items in list |
%( list) |
Returns the progressive remainder of the number and the numeric items in list |
&( list) |
Returns the bit-wise "AND" of the number and the numeric items in list |
|( list) |
Returns the bit-wise "OR" of the number and the numeric items in list |
^( list) |
Returns the bit-wise "exclusive OR" of the number and the numeric items in list |
op or op( value) for
op in <= , < , == ,
!= , > , >= |
Returns whether the number is less than or equal to, less than, equal to, not equal to, greater than, or greater than or equal to, value. If value is not supplied or is not numeric, zero is used. |
Code blocks may be bound as methods for all numeric values as follows:
@Number.methods('
method_name, {
code sequence})
[Contents] [Values And Methods]
Standard methods:
<Method | Description |
---|---|
call | Returns the string itself |
class | Returns the string "String" |
capcase |
Returns the string with each space-separated word capitalized (since 0.2.2) |
downcase |
Returns the string entirely in lowercase (since 0.2.2) |
flt , float |
Returns the floating-point value of the string |
html |
Returns the HTML encoding of the string (since 0.1.4) |
idx( target,
offset) ,
index( target,
offset) |
Search for target left-to-right in the string beginning at position offset (default 0) and return the offset from the start of the string at which found (or -1 if not found) |
int |
Returns the integer value of the string |
join( list) |
Equivalent to
$.array(list |
len , length |
Returns the length of the string |
match( regexp,
offset) | Match a string against a regular expression. Returns MatchData if the pattern matches, or nil if it doesn't. |
pcte |
Returns the "percent encoding" of the string (for URI components; since 0.1.4) |
regexp |
Returns a "regular expression" version of the string (only if enabled by the calling program; since 0.2.2) |
rep( pattern,
replacement) ,
replace( pattern,
replacement) |
Returns a copy of the string with all occurrences of pattern replaced with replacement; the pattern may be a regular expression (since 0.2.2) |
rep1, replace1 |
Like rep /replace , but with only the
first occurrence replaced (since 0.2.2) |
split( pattern, limit) |
Returns an array of up to limit (default unlimited) substrings split at pattern (since 0.2.2) |
ridx( string,
offset) ,
rindex( string,
offset) |
Search for target right-to-left in the string beginning at position offset (the end of the string by default) and return the offset from the start of the string at which found (or -1 if not found) |
rng( begin,
end) ,
range( begin,
end) |
Returns characters begin through end of the string, inclusive, counting from zero. Either may be negative, in which case counting is backwards from the end of the string. |
slc( begin,
length) ,
slice( begin,
length) |
Returns length characters beginning at begin, counting from zero. Begin may be negative, in which case counting is backwards from the end of the string. |
str , string |
Returns the string itself |
type | Returns the string "string" |
upcase |
Returns the string entirely in uppercase |
+( list) |
Returns the concatenation of the string and the items in list |
*( value) |
Returns the concatenation of the string repeated value times. If value is negative, the reverse of the string repeated -value times is returned. If value is omitted or not numeric, an empty string is returned. |
op or op( value) for
op in <= , < , == ,
!= , > , >= |
Returns whether the string is less than or equal to, less than, equal to, not equal to, greater than, or greater than or equal to, value. If value is not supplied, the empty string is used. |
Code blocks may be bound as methods for all string values as follows:
@String.methods('
method_name, {
code sequence})
[Contents] [Values And Methods]
"Regular
expressions" ("regex" or "regexp" for short) in LtdTemplate are
generated from string values using the
regexp
method.
Regular expressions must be enabled by the calling program. See the gem documentation for details.
Regexp-specific methods:
Method | Description |
---|---|
ci, ignorecase |
Make pattern case-insensitive |
class |
Returns the string "Regexp" |
ext, extended |
Allow white space and comments in the pattern |
match( string,
offset) |
Match a string against a regular expression. Returns MatchData if the pattern matches, or nil if it doesn't. |
multi, multiline |
Makes . match newlines too |
type | Returns the string "regexp" |
"Example".replace("[aeiou]".regexp.ci,'_)
/* => _x_mpl_ */
Code blocks may be bound as methods for all regular expression values as follows:
@Regexp.methods('
method_name, {
code sequence})
Regular expression match-result methods:
Method | Description |
---|---|
[ index] |
Returns the entire (index 0) or specific sub-pattern match. |
[ name] |
Returns the named sub-pattern match. |
begin( n) |
Returns the offset of the start of the nth match array in the string (numeric index or named sub-match). |
class |
Returns the string "Match". |
end( n) |
Returns the offset of the character immediately following the nth match in the string (numeric index or named sub-match). |
length , size |
Returns the size of the match results array. |
offset( n) |
Returns the begin and end values in a
two-element array. |
type |
Returns the string "match". |
[Contents] [Values And Methods]
This example binds a list method to arrays to include commas and "and" between elements:
@Array.methods('list, {
$.target.join(" and ", ", ", ", ", ", and ") })
$.*('Ruby).list /* => Ruby */
$.*('Perl, 'Ruby).list /* => Perl and Ruby */
$.*('Perl, 'PHP, 'Python, 'Ruby).list /* => Perl, PHP, Python, and Ruby */
This example binds a method called "en_nth" on number values to return the English "nth" for the number (e.g. 1st for 1, 2nd for 2, 3rd for 3, etc.) and then generates them from -11 to 24:
@Number.methods('en_nth, {
$.var(.. 'n, $.target) $.var(.. 'n10, n.abs%(10), 'n100, n.abs%(100))
n $.if({ n100>=(11)&(n100<=(20)) }, 'th, { n10==(1) }, 'st,
{ n10==(2) }, 'nd, { n10==(3) }, 'rd, 'th) })
n=(-11) $.loop({ n<=(24) }, { n.en_nth n=(n+(1)) }).join(", ")
[Contents]