Expression in Moya are similar in many ways to expressions in other languages, with extensions for web related tasks. They are designed to be powerful, without compromising on readability.
Perhaps the easiest way to test Moya expressions is to enter them in to the debugger. If you launch a Moya code file (any valid file will do) with moya run
and the --breakpoint
switch, Moya will drop straight in to the debugger. If you type an expression in to the command line, Moya will evaluate the expression and show you the result.
You can also create data types (see below) with the let
command in the debugger. This assigns the result of an expression to a variable, which you can then use in further expressions. Here's an example debugger session:
moya moya.run#debug.1 > let test = "moya"moya moya.run#debug.1 > "Hello, " + title:test + "!"
Some data types contain other values, which may be used independently in expressions. We can refer to these values with a data index, which is a sequence of names, separated by a period (.
). For instance, if we have a list of values called crew
, we can refer to the first value with crew.0
(Moya starts counting from 0), and crew.1
to refer to the second value.
Moya stores all variables in the context which a single data structure containing all information for a request. Most variables you work with will be in a local scope (a view of part of the context). For example, when calling a macro, Moya stores the local variables in a new scope.
A data index may also refer to the top (or root) of the context, even when you are working in a local scope. To refer to such root values, prefix the index with a single period.
One such value stored in the root of the context, is request
, which contains information regarding the current request being handled. Because it is in the root, it is always available, and may be referred with the data index .request
(note the leading period).
Here are some examples of data indices:
color.r.now.epoch.request.POST.back_url
If a data index refers to a value that doesn't exist Moya returns a value of a special type called Missing
. Let's use the following data index as an example:
ships.moya.crew.peacekeepers
If the object refered to by the data index ships.moya.crew
doesn't contain a key called peacekeepers
, then a value of Missing
is returned. This is true if any of the components of the index don't exist. For example, if ships
doesn't exist then the entire index will return Missing
. Similarly if there is no key called moya
in ships
, then Missing
will be returned.
A missing value is always considered False, has no keys, and results in an empty string if you use it in a template. It is also considered an empty sequence if you try to iterate over it. These properties keep code simple. For example, the following code is valid, even with a missing data index:
<if test="not ships.moya.crew.peacekeepers"><echo>No Peacekeepers on board!</echo></if>
There may be some situations where it is not enough to treat a missing index as false. In these situation you may use the missing:
modifier (modifiers explained below).
<if test="missing:ships.moya.crew"><echo>No crew on Moya!</echo></if>
Moya can work with various kinds of data such as numbers and text. These may be created by Moya, but you can also create them within an expression. When values are directly entered in to an expression, they are known as literals. For instances 5
is a number literal.
Moya supports the following data types:
Literal numbers may be positive or negative, and may contain digits after the decimal point. Defining numbers is very simple, here are some examples:
-2303.141000000000
Strings contain text, and are defined by enclosing the text in a quote marks (either single ('
) or double ("
)).
If you want to write a string which contains a quote character, you must escape the quote, so that Moya knows not to treat it as the end of the string. You can do this by preceding the quote with a backslash. An alternative to escaping is tripple quoting; you can enclose your string in three quotes (of the same type), and Moya will know not to end the string at the single quote.
The following are some examples of string definitions:
"Hello, World!"'Where there is a Will, there is a way'""'Ka D\'Argo''''Ka D'Argo'''
There are a few values that always available and have a specific meaning.
A boolean is a value which indicates true or false. Boolean values are used when making decisions, in an <if> tag for example.
The constants, True
and False
, are used for this. Moya also allows the use of yes
and no
, which are equivalent. For example, these values indicate true:
Trueyes
And these values indicate false:
Falseno
Other values can also be interpreted as true or false by Moya. The number 0
is considered false, as is an empty string, or an empty collection (e.g. a list with nothing in it). All other values are considered true.
If you use one of these values in an expression where Moya expects a boolean, then the result of the expression will be interpreted according to the above rules. You can also used the bool:
modifier to convert a value in to a boolean.
Here's are some examples of converting a value in to its boolean interpretation:
<echo>${bool:0}</echo> <!-- False --><echo>${bool:''}</echo> <!-- False --><echo>${bool:3}</echo> <!-- True --><list dst="my_list"/><echo>${bool:my_list}</echo> <!-- False --><list dst="my_list_2"><int>5</int></list><echo>${bool:my_list_2}</echo> <!-- True -->
A list is a sequence of other Moya values. A list consists of a values separated by a colon and enclosed in square brackets ([]
). Here are some examples of lists:
['A','B','C']["apples","oranges","pears"][2,3,5,7,11,13,19][]
Lists are indexed by their position in the list. Where the first item is at index 0, and the second is index 1 etc. The following is an example of using an index with a list:
<var dst="crew">["John", "Ka D'Argo", "Aeryn"]</var><echo>First crew member is ${crew.0}</echo><echo>Second is ${crew.1}</echo>
A dictionary contains named values. Basically, a dictionary associates a values with a keys (usually a string) so that you can look up the value from the original key. You can define a dictionary with the =
operator. For example foo="bar"
creates a dictionary with a single value foo
. To create a dictionary with multiple keys, separate them with commas. Here is an example of an expression that creates a dictionary:
rygel="hynerian", john="human", aeryn="peace keeper"
The keys in the dictionary are used as the index when looking up a value. The following example shows how to look up species for john
:
<var dst="crew">rygel="hynerian", john="human", aeryn="peace keeper"</var><echo>John is a ${crew.john}</echo>
Moya also supports an alternative syntax for dictionaries that may be more familiar if you have experience with other languages. This syntax consists of a number of key / value pairs separated by a colon and enclosed in curly brackets. Here's the same dictionary in this alternate syntax:
{"rygel":"hyerian", "john":"human", "aeytn":"peace keeper"}
You can create empty dictionary with the following:
{}
Note that the alternate syntax can define dictionaries where the keys aren't strings.
A range represents a sequence of values between a start and end point. Ranges are defined with either the ..
operator (2 periods) which defines an inclusive range, or the ...
operator (three periods) which defines an exclusive range. For examples, the expression 1..3
would create an inclusive range between 1 and 3, containing the values 1, 2, and 3. Whereas 1...3
would crate an exclusive range from 1 to 3, but not including the end point, and would contain the values 1, and 2.
Loops may go forward or backwards; 3..1
is the reverse of 1..3
. Ranges also work with letters, and will produce a sequence from one character to another. For example, 'A'..'Z'
would contain all 26 letters of the alphabet.
Probably the main use for range objects is in loops. Here's an example of using range object to count down from 10 to 1:
<for src="10..1" dst="count"><echo>${count} seconds remaining</echo><sleep for="1s"/></for>
A regular expression is a sequence of characters used to match patterns in strings. Regular expressions can be a little cryptic, but they are very useful when working with string data. See the article on Wikipedia for an introduction.
Regular expressions are defined in Moya by entering them between two slashes. Here is an example of a regular expression that matches an email address:
/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/
You can use the matches
operator with regular expressions to do the matching. The left hand side should be the string you want to match. Here's how you would use it with the regular expression above:
'john@example.org' matches /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/
The result of this expression would be True, because the string looks like an email address.
'not an email' matches /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/
The result of this expression would be False, because the strings does not look like an email address.
Timespans represent a period of time. They are used in a variety of places in Moya development, and can represent a period of milliseconds to a period of centuries. A timespan consists of a number followed by a symbol that indicates the unit. The unit may either be ms
for milliseconds, s
for seconds, m
for minutes, h
for hours and d
for days (exactly 24 hours).
Here are some examples of timespans:
250ms1s60m4h7d365d
Timespans work with the arithmetic operator, and may be mixed (i.e. timespans of different units). Here are some examples of arithmetic operators on timestamps:
3m + 10s7d / 24h60s - 1h
Timestamps have a number of useful attributes. The attributes milliseconds
, seconds
, minutes
, hours
, and days
all return a number. The attribute, text
, returns a human readable description of the timespan period. For example, if t
contains the result of 24h - 1s
(24 hours minus one second), then t.text
would produce the following:
23 hours, 59 minutes, 59 seconds
An operator is something that acts on data in an expression. Most operators take two values and combine them in some way. For instance, +
and -
are operators which combine values to produce a new value. The two values used by an operator are often referred to as the left hand side and right hand side of the operator, depending on their relative position. For example, in the expression a + b
, a
would be the left hand side, and b
the right hand side.
Moya supports the following operators:
The standard arithmetic operators work with numbers (and many other data types).
The add operator (+
) adds two values together.
When used with numbers, this operator produced a number result, but it can also be used with strings. Adding two strings together produces a new string that is a combination of both. The following is an example of adding strings together:
'Hello, ' + "World!"
Adding strings together is known as string concatenation.
The subtract operator (-
) subtracts one values from another.
The multiple operator (*
) multiplies one value by another.
The divide operator (/
) divides one value by another.
The modulus operator (%
) calculates the remainder in a divide operation. For example 5 % 2
is 1
, because 2
goes in to 5
, 2
times, with a remainder of 1
.
The integer division operator (//
) is like divide, but rounds down the answer. For example 5 // 2
is 2
.
The unary minus operator (-
) makes the right hand side negative (as this is a unary operator, there is no left hand side). For example if a variable count
has the value 3
, then the result of -count
is the value -3
.
Logic operators combine boolean expressions. They are most often used when making decisions in if
tags.
Check if the left hand side of the operator and
the right hand side are both true. The following truth table shows how and
combines boolean values:
False and False (False)False and True (False)True and False (False)True and True (True)
Here's an example of and
used in an expression:
<echo if="crew and fuel">Take off!</echo>
Check if either the left hand side or the right hand side values are true. Here's the corresponding truth table:
False or False (False)True or False (True)False or True (True)True or True (True)
The not operator operates on a single value, and logically reverses it (so a true value becomes false, and vice versa).
not True (False)not False (True)
Here's an example in an expression:
<echo if="not crew">Can't take off without crew</echo>
A comparision operator compares two values.
The equality operator (==
) checks that two values are the same. If the values are the same, it will return True, otherwise False. Here's an example:
<str dst="name">Rygel</str><echo>${name == 'Rygel'}</echo> <!-- True -->
The inequality operator, !=
, is the inverse of the equality operator. It return True if the two values are not the same.
<str dst="name">Rygel</str><echo>${name != 'John'}</echo> <!-- True -->
The greater than operator (>
or gt
) checks if the left hand side is greater than the right hand side. The reason there are two aliases for this operator, is that the >
symbol has special meaning in XML, so you may use gt
for expressions in XML attributes.
The >=
or gte
operator checks if the left hand side is greater than or equal to the right hand side.
The <
or lt
operator checks if the left hand side is less than the right hand side.
The <=
or lte
operator checks if the left hand side is less than or equal to the right hand side.
The in checks if a value is contained within a sequence, or strings. For example 'e' in 'abcdef'
would return True, but 'z' in 'abcdef'
would result in False.
Here are some examples of in
that result in True:
'World' in 'Hello, World!'5 in [1,2,3,4,5,6,7]5 in 1..10'foo' in foo='bar'
Here are some examples of in
that result in False:
'Mars' in 'Hello, World!'88 in [1,2,3,4,5,6,7]20 in 1..10'baz' in foo='bar'
The not in
operator is the logical reverse of the in
operator. It returns True if the left hand side does not appear in the right hand side.
The index operator looks up a key in a collection, such as a dict or list.
Here's an example of the index operator:
<let species="rygel='hynerian', john='human', aeryn='peace keeper'/><ask dst="crew">Enter a crew member</ask><echo>${crew} is a ${species[crew]}</echo>
The format operator (::
) converts the left hand side to a string, and formats it according the format specification in the right hand side. The format specification is the same as the format specification used in Python.
Here's an example of the format operator which formats a floating point number so that it has commas to separate the thousands, and two places after the decimal point:
<let n="12300232.2325"/><echo>${count::',.2f'}</echo>
A modifier in an expression consists of a name followed by a colon, for example title:
is an modifier. Modifiers processes the value on the right hand side of the expression and returns a result..
Note, that modifiers have a low precedence in expressions, which means they will operate on the value immediately to the right. For instance upper:'hello ' + 'world'
, returns "HELLO world"
and not HELLO WORLD
. If you would like the modifier to apply to the result of an expression, you may enclose the expression in parenthesis, i.e. upper:('hello ' + 'world')
.
See Modifier Reference for a list of available modifiers.
Closely related to modifiers are filters, which take a value and transform it in some way. Filters use the pipe symbol (|
) and work from left to right, so data|my_filter
would process data
with a filter called myfilter
. Filters may be chained so the result of the previous filter is filtered by the next. For example data|my_filter|my_other_filter
would filter data first with my_filter
then by moya_other_filter
.
The difference between modifiers and filters is that filters are defined in library code and not built in. You may define a filter with the <filter> tag. Here's an example of a filter that repeats text three times.
<filter name="repeat3"><return-str>${str:value * 3}</return-str></filter>
A filter is essentially a macro which will be called with a parameter called value
which is the left hand side of the filter expression. The return value is used as the result of the filter.
Filters are stored in the application object, so .app.filters
will contain all the filters for the current app. Here's how you could use the repeat3
filter:
<echo>${"beetlejuice "|.app.filters.repeat3}</echo>
Although this works perfectly well, referencing filters in this way is a little verbose. Moya offers an alternative way of referencing filters. If you replace the filter object (right hand side) with a string, Moya will look up that filter in the current application. Here's an example:
<echo>${"beetlejuice"|"repeat3"}</echo>
It is also possible to look up a filter in another application, by appending from APP
, where APP
is the name of the application containing the filter. For example, lets say we want to use the repeat3
filter in a template not in the same application as the filter. We could use something like the following:
<h1>${"beetlejuice"|"repeat3 from my_app"}</h1>
It is also possible to use a library (long) name in place of the application name. This works as long as the library has been installed just once, otherwise Moya will not be able to detect which application to use. If a library has been desgined to be installed just once – and many are – then the long name can be used, otherwise stick to the app name.
Filters may be associated with additional parameters. For instance, let's make a more flexible version of our example filter that can repeat text by an arbitrary number. Here's the new filter:
<filter name="repeat"><return-str>${str:value * count}</return-str></filter>
This filter references a value called count
, which we can supply in the filter expression as follows:
<echo>${"beetlejuice"|"repeat"(count=5)}</echo>
Here, an additional parameter list is added to the right hand side (i.e. the filter object or filter string reference), that sets count
to 5
. This value is passed to the filter
tag along with value
. This will result in the repeat
filter, repeating our text 5
times.
Filters may be as complex as necessary – you could, for example, lookup object from the database and do some processing. Although if a filter is simple enough to fit in to a single expression, you can supply the expression
attribute to <filter> which will be used in preference to the inner code. For instance, our example filter, could be written more concisely as the following:
<filter name="repeat" expression="str:value * count" />
The following is a list of the modifiers supported by Moya:
Returns the absolute value of a number (makes it positive). For example, abs:-5
is 5
.
Returns True if all the values in a sequence would be considered True if used in a condition.
Return True if any of the values in a sequence would be considered True.
Returns the basename of a path. The basename is the very last component, e.g. basename:'foo/bar/baz'
returns 'baz'
Converts a value to a boolean. Various values are considered False, for example the number 0, empty strings, and empty collections. Most other values will return True. For example bool:[]
would return False, bool:1
would return True.
Capitalizes the first letter of a string. For example capitalize:'hello'
returns 'Hello'.
Returns the ceiling of a number (first integer that is greater than or equal to the number). For example, ceil:2.7
returns 3.0
.
Extracts an indexed item from a sequence, and returns a list of values. This collection should be a applied to a sequence of 2 values, the first contains the sequence to collect, and the second contains the key to look up.
<json dst="crew_ids">[{"id": 1, name:"Aeryn"},{"id": 7, name:"John"},{"id": 99, name:"Scorpius"}]</json><let crew_ids="collect:[crew, 'id']"/> <!-- creates list [1, 7, 99] -->
Returns a string containing values in a sequence separated by commas. For example commalist:[1,5,7]
returns '1,5,7'
.
Returns a string containing values in a sequence separated by a comma and space. For example commaspacelist:[1,5,7]
returns '1, 5, 7'
.
Splits a string by commas, for example commasplit:"1,2,3"
returns ['1', '2', '3']
.
Create a date object from a date in iso8601 format. For example, date:'1974-07-05'
.
Create a datetime object from a date & time in iso8601 format. For example, datetime:'1974-07-05T15:45:00'
.
Returns a debug description of the right hand side. This may contain helpful information. For example, debug:datetime:'19740705T154500'
.
Returns the directory name of a path. Can be considered there inverse of basename:
. For example dirname:'foo/bar/baz'
returns foo/bar
.
Retrieve an enumeration object from its libid. For example, enum:'moya.sociallinks#enum.jsonrpc.errors'
.
Creates an enumerator object that when iterated over returns the 0-based index and the value. For example:
<for src="enumerate:['John', 'Rygel', 'Scorpius']" dst="index, name"><echo>${index + 1}. ${name}</echo></for>
Like enumerate:
but starts counting from 1, which is more natural for your average human. For example:
<for src="enumerate1:['John', 'Rygel', 'Scorpius']" dst="index, name"><echo>${index}. ${name}</echo></for>
Return True if a value is not missing. For example, exists:.requests
would be True in a project, but exists:.santa.clause.and.the.tooth.fairy
would likely return False.
Gets the extension from a path. For example, ext:'foo/bar.html'
would return "html"
.
Converts a file size (in bytes), rounded to the most appropriate unit. For example filesize:1024
returns 1.0kB
, and filesize:32859558
returns 31.3MB.
Returns the first item in a sequence, or None
if the sequence is empty.
Attempts to flatten a sequence containing other sequences. For example, flat:[1,2,3,[4,5,6]]
returns [1,2,3,4,5,6]
.
Converts a string containing a number in to a number, for example float:'3.1427'
returns a floating point number of 3.1427
.
Returns the floor
of a number (the greatest integer that is equal to or less than a number). For example, float:3.1427
returns 3.0
.
Used mainly in templates, this marks text as being valid HTML, so Moya will know not to escape any tags. For example, html:'<h1>Header</h1>'
.
This converts a string to an integer. For example, int:'100'
returns 100
.
Returns True if the right hand side is a boolean (i.e. True or False). For example, bool:False
returns True (because False is a boolean), but bool:"True"
would return False, because the right hand side is a string.
Check if the right hand side is a valid email address. For example:
<if test="not isemail:email"><echo>You didn't enter a valid email address!</if>
Returns True if the right hand side is a floating point number.
Returns True if the right hand side is an integer.
Returns True if the right hand side is a number (either an integer or a floating point number).
Returns True if the right hand side is a string.
Returns the items of a sequence. Used with a dictionary it will return a sequence of the keys and values. Used with a sequence (e.g. a list), it will return a list of the index and the value. For example items:['John','Rygel','Scoprpius']
returns [[0, 'John'], [1, 'Rygel'], [2, 'Scorpius']]
.
Returns the keys from a dictionary, or if the right hand side is a sequence it will return the indexes.
Returns the last item in a sequence, or None if the sequence is empty. For example, last:(1,5,99)
returns 99
.
Return the count of the number of elements in a collection.
Converts the right hand side in to a list.
Returns a text representation of an object, suitable for the current locale. Mainly used for dates and times, which have different formats depending on your location. For example, in a request, localize:.now
create a string containing the data and time in a localized format.
Returns the base 10 logarithm of a number.
Converts a string to lower case.
Strips whitespace (spaces, tabs etc) from the left of a string.
Converts an object in to a dictionary. The right hand side should be a sequence of keys and values.
Returns the largest value in a sequence of numbers. For example, max:[1,99,-6]
returns 99
.
Returns the md5 hash of an object.
Returns the lowest value in a sequence of numbers. For example, min:[1,99,-6]
returns -6
.
Returns True if the right hand side is a missing object (does not exist in the context). For example, missing:.lord.lucan
would likely return True (assuming you haven't create a value called lord.lucan
).
Converts a string in to a path object. Path objects have some syntactic sugar to construct paths. You can use /
as a path join operator on paths, which makes sure there is only a single forward slash between path components. You can also use ../
to go back one directory. For example, path:uploads_directory / 'images' / filename
.
Checks the current user for a permission. For example, permission:'admin'
checks if the current use has admin permission.
Converts a sequence in to a string representation, where each element in the sequence is enclosed with quotes and separated by a command and a space. For example, prettylist:[1,2,3]
returns "'1', '2', '3'"
.
Converts an absolute path in to a path relative path relative to the current request. For example, relto:'/foo/bar/index.html'
would return '../bar/index.html'
, if the current request is for '/foo/index.html'
.
Returns an object that may be more easily rendered. Although rarely necessary, this modifier is typically used in templates to convert an object to a form that is easier to work with. If you apply renderable:
to an value without an additional renderable interface, then the same value is returned, i.e. the modifier has no effect.
Reverses the order of a sequence. For example, reversed:[1,2,3]
returns (3, 2, 1)
.
Rounds a value to the nearest integer. If you supply a sequence of two values, then the first is the number you want to round and the second is the number of decimal places. For example, round:0.777
returns 1.0
, and round:[0.777, 2]
equals 0.78
.
Removes whitespace (spaces, tabs etc.) from the tail end of a string. For example, rstrip:'Hello '
return 'Hello'
.
Takes a sequence and returns a list containing a flag and each value from the sequence. The flag is set to True if the value is the last in the sequence. For example, seqlast:['John', 'Rygel', 'Scorpius']
returns [[False, 'John'], [False, 'Rygel'], [True, 'Scorpius']]
.
Create a set from a sequence. A set is a collection where each item appears only once.
Creates a slug from a string. A slug is a version of the string which is suitable for use in a URL. Slugifying text will remove any characters that may not be used in a URL, convert runs of whitespace to a single hyphen, and make the result lower case. For example, slug:"This may be used in a URL!"
returns 'this-may-be-used-in-a-url'
.
Returns a sorted version of a sequence. For example, sort:[99, 1, 20]
returns [1, 20, 99]
. Works with sequences of other types, including strings.
Converts the right hand side to a string. For example, str:3.14
returns "3.14"
.
Strips whitespace from the start and end of a string. For example, strip:" hello "
returns "hello"
.
Performs expression substitution on a string. For example, sub:'Hello, ${name}'
would return 'Hello, John'
if the variable name
was "John"
.
Swaps the case of a string. i.e. upper case becomes lower case and vice verse. For example, swapcase:'Hello'
returns 'hELLO'
.
Returns a time object from a string in iso8601 format. For example, time:'23:59:59'
.
Capitalizes the first letter of every word in a string. For example, title:'hello world'
return s "Hello World"
.
Modifies a sequence so that each element appears exactly once. For example, unqiue:['john', 'rygel', 'scorpius', 'rygel', 'john']
would return ['john', 'rygel', 'scorpius']
.
Converts a string to upper case, for example upper:'hello'
returns 'HELLO'
.
Parses a URL in a string and returns a URL object. For example, url:'http://example.org/foo/bar/'
. URL objects are interchangeable with strings, but have a number of useful properties and may use the /
operator to join paths.
This modifier decodes query string data in to a dictionary of lists. Lists are used because in a query string a key may appear more than once. For example, urldecode:'crew=Rygel&crew=John&crew=Scorpius'
returns {'crew':['Rygel', 'John', 'Scorpius']}
.
Encodes a dictionary of lists in to a query string suitable for use in a URL. For example, urlencode:(crew=['Rygel', 'John', 'Scorpius'])
returns urldecode:'crew=Rygel&crew=John&crew=Scorpius'
.
Combines the current query string (from .request.query_string
) with new data and returns the an encoded query string. For example, urlupdate:(ship='Moya')
would return a query string with ship
set to 'Moya'
. You can also remove a value from the current query string by setting it to None
. For example, urlupdate:(crew=None)
would return a new query string with no value for crew
.
Return True if a string could be parsed as a floating point number. For example, validfloat:'3.14'
would return True, but validfloat:'pie'
would return False.
Return True is a string could be parsed a valid integer. For example, validint:'1000000'
is True, but validint:'one million'
would return False.
Returns the values in a collection (e.g. list or dictionary). For example, values:{'foo':'bar', 'baz':'egg'}
would return ['bar', 'egg']
.
This modifier combines two or more sequences in to one. For example, zip:[[1, 2, 3], ['a', 'b', 'c']]
returns [[1, 'a'], [2, 'b'], [3, 'c']]
. This is useful in for loops, where you want to an item from each sequence on each iteration. For example:
<for src="zip:[[1, 2, 3], ['a', 'b', 'c']]" dst="number, letter"><echo>(${number}) ${letter}</echo></for>