Moya code is executable code stored in XML documents within your project, and is responsible for all aspects of building a dynamic website; from querying the database to processing forms. An entire web site may be developed using nothing more than Moya code.
Moya code typically executes in the context of a project, but it can also be used stand-alone. This is useful for testing. If you would like to follow along with the code in this chapter you can use the following document template to run code:
<moya xmlns="http://moyaproject.com" xmlns:let="http://moyaproject.com/let"><macro docname="main"><echo>Hello, World!</echo></macro></moya>
To run this code save it as example.xml
, then enter the following from the command line:
$ moya run example.xml
The above code should display the text Hello, World!
to the console.
The first tag, <moya>, is required for all Moya code files. This tag introduces two XML namespaces; http://moyaproject.com
is the namespaces containing builtin tags (required for virtually all logic files). The http://moyaproject.com/let
namespaces enables the let
extension (explained later), which isn't strictly required, but very commonly used.
The second tag, <macro>, creates a macro (a starting point for executable code), with the docname of 'main'
. In Moya a docname
is a unique name that can be applied to identify tags in a document. The name, "main"
, is the default macro executed with the run command. You can also execute other macros within a document with the -e
switch.
The third tag, <echo>, writes enclosed text to the console (terminal). It's this tag which prints out the text Hello, World!
.
To run snippets of code from this document, replace the <echo>
tag with the code you have cut and pasted.
You can set parameters to the main macro from the command line with the --let
switch. For example, --let foo=bar
would set a parameter called foo
to the value "bar"
. Let's add a few lines to the above code to use a command line parameter:
<moya xmlns="http://moyaproject.com" xmlns:let="http://moyaproject.com/let"><macro docname="main"><echo>Hello, ${name}!</echo></macro></moya>
Run the above code with the following command line:
$ moya run example.xml --let name="Moya Fan"
You should see a more personalized greeting on the command line.
Moya contains a built-in debugger for Moya code, which you can use to step through Moya code line by line and inspect data. You can drop in to the debugger with the run
sub-command, by using the --breakpoint
switch. For example:
$ moya run example.xml --breakpoint
This will open the file "example.xml"
and drop straight in to the debugger. You should see a snippet of code to indicate where Moya is currently executing and a command prompt. If you enter an expression at this point, Moya will evaluate it and print the results. You can also enter one of a number of commands to step through the code and view information about the execution process. Type help
in the debugger for a list of these commands.
An alternative way of entering the debugger is to use the <breakpoint> tag. If you run the code (without the --breakpoint
switch), then Moya will drop in to the debugger when it reaches a <breakpoint> tag.
Try it with the following code:
<moya xmlns="http://moyaproject.com" xmlns:let="http://moyaproject.com/let"><macro docname="main"><input dst="name">What is your name?</input><breakpoint/><if test="name=='Rygel'"><echo>Sorry, we don't serve Hynerians here</echo></if></macro></moya>
You should find that Moya drops in to the debugger immediately after you have entered text. You can see the contents of the variable name
simply by entering it on the command line.
The Moya command line app also includes access to a dynamically generate help system, which can display information on any tag. The same information is available online, but this may save you a little time. Here's how to look up the help for the <if> tag:
$ moya help if
You can also look up a tag defined in a different XML namespaces, by specifiying the namespace URL within curly braces. For example:
$ moya help {http://moyaproject.com/db}query
If you don't include the full namespaces URL, Moya assumes it to start with http://moyaproject.com/
, so the following is equivalent to the above command line:
$ moya help {db}query
You can also access the dynamic help from the debugger via the help
command, which works just like the command line (but without typing moya
).
A statement in Moya code consists of a single tag, which is in essence a function call. When Moya encounters a tag, it executes the associated functionality, using the tag attributes as parameters to the function call.
Lets look at one of the simplest such tags in Moya, the <echo> tag, which writes any text contained within the opening and closing tag to the console:
<echo>Hello, World!</echo>
Moya can perform calculations on numbers, text, and other data with an expression syntax that is somewhat similar to other programming languages, such as Python, C and Javascript. All the familiar operators, such as + - * / will work as you might expect. For example 1 + 1
evaluates to 2
. There are also extensions to simple expression evaluation for data types and operations commonly used in web development. This often makes Moya expressions more concise and readable than expressions in other languages.
Here are some expressions taken from builting Moya libraries:
not (value ^= 'http://' or value ^='https://')score not in [-1,0,1]slug:form.data.titlepermission:.app.settings.new_topic_permissionbasename:path fnmatches wildcardpath:dirname:path / thumbnail_directory / basename:path
Expressions will be covered in detail in the next chapter, but lets introduce some of the basic concepts.
Moya can work with numbers, and the usual arithmetic operators:
5 + (3 * 2)radius * 2 * 3.1426
Strings (text) are defined between either single ('
) or double ("
) quotes. When given in a tag attribute, you will probably have to stick to single quotes, since double quotes have a special meaning in XML. Here are some expressions using strings:
"where there is a will"'Hello, ' + name + '!'
An operator combines two values. For example, the ==
operator checks for equality. Here is an example of some Moya code that uses the equality operator:
<if test="species=='Hynerian'"><echo>Sorry, we don't serve your kind here.</echo></if>
A modifier takes a value and transforms it in some way. The following is an example of modifiers in expressions:
"Hello " + title:name + '!'
In the above code, the title: modifier modifies the string in name
so that it begins with a capital letter.
Expressions may appear within text defined inside a tag, or an attribute that expects text, by wrapping it in ${
and }
. For example, the following code asks the user for their name then outputs their name and a greeting to the console:
<input dst="name">What is your name?</input><echo>Hello, ${title:name}!</echo>
In the above snippet, Moya evaluates the expression title:name
and replaces the text with the result of that expression. So if you enter the name john
, the echo statement will write Hello, John!
to the console.
Moya has a variety of ways to create data, such as numbers and strings, as well as data structures, which are collections of data. Values created in this way may be used in expressions and anywhere Moya expects a value.
Moya has a number of data setter tags which create data. Such tags have two optional attributes; dst
which contains the destination name for the value, and value
which should an expression. If the value
attribute is missing, then the text of the tag is used to create the value.
For example, the following two lines have the same effect; they both assign the value of "Rygel"
to a variable called crew
:
<str dst="crew">Rygel</str>
<str dst="crew" value="'Rygel'" />
You are free to use whichever form is more convenient, but generally value
is best used when you want to set the value to the result of some calculation, and the tag text is best used for constants. An exception would be if you want to use expression substitution in the text, which can be convenient for constructing messages for the user. For example, here is a string definition that would be more verbose as an expression:
<str dst="manifest">There are ${len:count} members in the crew. Their names are ${commaspacelist:crew}.</str>
If you don't include either value
, or any tag text, then the value will be set to the default. In the case of strings, this will be an empty strings, for numbers it will be 0
. The following creates an empty string:
<str dst="empty"/>
A simple data type is one that does not contain other values. The following lists Moya's simple data types:
A string is a sequence of characters, commonly used to store text.
Defining strings is done with the <str> tag. Here's some examples of creating strings:
<str dst="example1">in order to create an apple pie from scratch, you must first create the universe.</str><str dst="example2" value="'Hello ' + title:title + '!'"/><str dst="example3">Mult-linestring</str>
An integer is a whole number. It may be positive or negative, but may not contain a decimal point. Here are some examples of creating integers:
<int dst="zero"/> <!-- sets a value of 0 --><int dst="one_million">1000000</int><int dst="seconds_in_a_minute" value="60 * 60" />
A floating point number is a number with a decimal point. Here are some examples of creating floats:
<float dst="zero"> <!-- sets a value of 0.0 --><float dst="pi_approximation">3.1427</float><float dst="one_third" value="1/3"/>
You can create a boolean (a value which indicates True or False) with the <bool> tag which assigned a value of True if the tag text is True
or yes
, otherwise False. Here's an example:
<bool dst="alive">yes</bool>
There are also shortcuts for boolean values, the <true> creates a boolean True, whereas the <false> tag creates a boolean False. Here's an example of these shortcuts in action:
<true dst="alive"/><false dst="false"/>
The <var> tags assigns the result of an expression to a variable. If tag text is given, it is evaluated as an expression. If it is not given, then the value
attribute should contain an expression. Here's an example:
<float dst="pi">3.1427</float><floar dst="radius">12</float><var dst="circumpherence">2 * pi * radius</var>
A compound data type (also referred to as a collection) contains other data types – which could be strings and floats, but also other collections. Such collections may be deeply nested, i.e. may contain collections that contain collections etc.
Collections tags are typically assembled from other enclosed data setters, i.e. any data defined inside the collection tag is added to the collection. The following code creates a list (explained below) of three strings:
<list dst="crew"><str>Rygel</str><str>John Crichton</str><str>Aeryn Sun</str></list>
You can refer to an element (single value) of a collection with a data index. For example:
<echo>${crew.1} is human</echo>
Collections may also be iterated over with the <for> tag (or anything else that expects a sequence). Here is an example of using <for> to iterate over crew
:
<for src="crew" dst="character"><echo>${crew} is on board!</echo></for>
A list is a sequence of other values. To create a list use the <list> tag. Here is an example of list creation:
<list dst="complex"><str>apples</str><list><int>1</int><int>100</int><list></list>
You can create an empty list just by closing the tag:
<list dst="empty"/> <!-- empty list -->
You can also create a list in an expression. For example:
<let crew="['Rygel', 'John Crichton', 'Aeryn Sun']"/>
You can iterate over a list with the <for> which extracts a value at a time. Here's an example:
<for src="crew" dst="character"><echo>${character} is on board!</echo></for>
You can add an item to the end of a list with the <append> tag. Here's an example:
<let prime="[1,3,5]"/><append src="prime" value="7"/>
You can remove an item from a list with the <remove> tag.
<let crew="['Rygel', 'John Crichton', 'Aeryn Sun']"/><remove src="crew" value="'Rygel'" />
A dictionary stores values with an associated key. Here's how you can create a dictionary:
<dict dst="crew"><str dst="rygel">Hynerian</str><str dst="john">Human</str><str dst="aeryn">Peacekeeper</str></dict>
The dst
attribute of the data setters are used as the keys in the dictionary. You can retrieve a value in a dictionary by using its key in a data index. Here's an example of how you might look up a species, given a name (rygel
in this case):
<echo>Rygel is a ${crew.rygel}.</echo>
Iterating over a dictionary, returns its keys. For example:
<for src="crew" dst="character"><echo>${title:crew} is on board!</echo></for>
You can iterate over the values or items (pairs of key plus value) by using the values:
or items:
modifiers (see next chapter for the details). Here's an example:
<for src="items:crew" dst="character,species"><echo>${title:crew} is a ${title:species}.</echo></for>
You may also create a dictionary in an expression. Here's an example:
<let crew="rygel='Hynerian', john='Human', aeryn='Peacekeeper'" />
JSON is Javascript Object Notation, which is a subset of the Javascript language, used for storing data in text form. It is versatile enough that many languages include support for it.
In Moya, the <json> tag can parse data in JSON format. Here's an example of using JSON in Moya code:
<json dst="ships">{"moya": {"crew": ["Rygel", "Aeryn", "john"]}}</json>
JSON is a good way of storing structure data, and is generally more efficient that the equivalent nested tags.
The <let> tag is a versatile way of assigning the result of expressions to a variable. Simply set an attribute to an expression.
Lets look at an example of assigning a single value:
<str dst="name">John</str><let greeting="'Hello, ' + name"/>
Here, the variable greeting
is assigned the value of expression 'Hello, ' + name
.
Multiple variables may be set with <let>. The only restriction (a requirement of XML) is that a variable appear only once in a tag. Here's an example of assigning multiple variables:
<let x="10" y="x*x" z="x+y" />
Variables are assigned in the order that they appear in the tag. You'll notice that in the above example, the expressions for y
and z
depend on the expression preceding it.
Moya provides an additional way to set data on some compound data statements, and a variety of other tags, via the Let extension. To enable this extension, all you need to do is add the http://moyaproject.com/let
namespace to your xml. The root level tag, <moya> is the best place for this.
<moya xmlns="http://moyaproject.com"xmlns:let="http://moyaproject.com/let"><!-- Let extension now available --></moya>
Once the namespace has been added, you can now assign the result of an expression to a value in a tag. Here's an example of using the let extension to create the values in a dictionary:
<dict dst="crew" let:rygel="'Hynerian'" let:john="'human'" let:aeryn="'peacekeeper'" />
The above code is equivalent to the following:
<dict dst="crew"><str dst="rygel">Hynerian</str><str dst="john">Human</str><str dst="aeryn">Peacekeeper</str></dict>
The let extension can be used in a number of other (non data) tags, where additional data is required. Notably the <call> which uses let attributes to create parameters. Here's an example:
<moya xmlns="http://moyaproject.com" xmlns:let="http://moyaproject.com/let"><macro docname="greet"><echo>Hello, ${name}!</echo></macro><macro docname="main"><call macro="greet" let:name="'World'"/></macro></moya>
Every statement in Moya may be run conditionally via the optional if
attribute, which takes an expression. If the expression is false then the statement will not be executed.
Here's a few examples of the if
attribute in action:
<forbidden if="not permission:'admin'" />
<exit if="not user">Not found</exit>
<fail if="len:value lt 3">Username should be at least 3 characters</fail>
Some statements (known as compound statements) may contain blocks in the form of further statements enclosed within the opening and closing tag. Such blocks may be executed conditionally, or repeatedly. This is the main mechanism which Moya uses to implement constructs found in most programming languages, such as conditions and loops.
Let's take a look at one such construct, the <repeat> tag, which executes the enclosed block a specific number of times. For example:
<repeat times="10"><echo>Hello, World!</echo></repeat>
If you run the above code, it will print the hello world text a total of 10 times.
The <if> tag runs a block if a condition is true. It works much like the if
attribute, but may apply to more than one line. <if> takes a single parameter, test
, which should be the expression to evaluate. Here's an example:
<if test="coffee"><echo>Drink coffee</echo><echo>Mmm, coffee</echo></if>
The <else> tag compliments <if> by running it's code block if a previous if
evaluated its condition to false. Here's an example:
<if test="coffee"><echo>Drink coffee</echo></if><else><echo>Buy more coffee</echo></else>
The <elif> tag is much like <else>, but accepts another condition. You may use any number of chained <elif> tags, which may finish with a final <else>. Here's an example:
<if test="coffee"><echo>Drink coffee</echo></if><elif test="tea"><echo>Drink tea</echo></elif><else><echo>Buy more coffee</echo></else>
The <repeat> tag executes a block a given number of times, specified by the times
attribute (an expression). Here's an example of <repeat>:
<repeat times="3"><echo>Knock</echo></repeat>
This will display "Knock"
3 times.
If you omit the times
attribute then the enclosed block will execute indefinitely. It is generally not useful to do this, as your code will never exit, so is best used in conjunction with <break>.
The <while> statement repeatedly executes a block while a condition is true. Once the condition because false, the enclosed block is skipped and Moya executes the code after the <while>. Here's an example:
<int dst="coffee" value="10"/><while test="coffee"><echo>Drink coffee</echo><dec dst="coffee"/> <!-- subtract 1 from variable coffee --></while>
The above code will display "Drink coffee"
10 times. On each pass through the loop, the variable coffee
is reduced by 1. After 10 times, it reaches the value of 0, which evaluates to false, and Moya exits the <while> loop.
The <for> tag executes its block for each item in a sequence. The <if> tag takes two parameters; src
(an expression) is the sequence you want to iterate over, and dst
. which is the name of the value that will hold the item each time the block is execute.
Here's an example that iterates through a list of strings:
<list dst="crew"><str>Ka D'Argo</str><str>Aeryn Sun<str><str>Rygel</str><str>John Chrichton</str></list><for src="crew" dst="character"><echo>${character} is on board!</echo></for>
An optional parameter, filter
, may contain an expression that will skip an iteration if it evaluates to false. Here's an example of <if> with the filter
attribute:
<for src="crew" dst="character" filter="character != 'Rygel'"><echo>${character} is on board!</echo></for>
The above code is equivalent to the following:
<for src="crew" dst="character"><if test="character != 'Rygel"><echo>${character} is on board!</echo></if></for>
The <break> tag exits a loop (<while>, <for>, <repeat> etc.) prematurely. Here's an example of using <break> inside an <if> tag:
<for src="crew" dst="character"><echo>${character} is on board!</echo><if test="character=='Rygel'"><break/></if></for>
The above code will execute the block inside <for> until a condition becomes true. When the <break> tag executes, it ignores any code still inside the loop and jumps to the point immediately after the loop.
The <continue> tag compliments <break> in that it works with loops, but rather than leave the loop entirely, it move directly to the next iteration (i.e. skips the rest of the block).
Here's an example of <continue> in action:
<for src="crew" dst="character"><if test="character=='Rygel'"><continue/></if><echo>${character} is on board!</echo></for>
A macro is a block of code that may be called from elsewhere in your code. When the code in the macro has been executed, Moya will return to the original point it was called from. We've seen macros in use when calling Moya code from the command line, but you can also call other macros in the process.
Let's define a simple <macro>:
<macro docname="greet"><echo>Hello, ${name}!<echo></macro>
We can call this macro with the <call> tag, which takes a macro
attribute containing the name of the macro. Here's how we might call the above macro:
<call macro="greet"><str dst="name">John Crichton</str></call>
This call could also use the let
extension to be a little more concise. The following will have the same effect:
<call macro="greet" let:name="John Crichton" />
Whatever method you use, Moya jumps from the <call> tag to the body of the macro
, passing in an argument called name
. Arguments used in a call, and any data defined inside the macro
are local variables – they won't exist when the call returns.
You can use macros to return data, with the <return> tag. Let's modify the greet
macro to return a greeting, rather than echo it to the console:
<macro docname="greet"><return><str>Hello, ${name}!<str></return></macro>
If we call the code above, nothing will be printed to the console. Instead, the function returns a string, which may be stored by the caller if it sets the dst
attribute on the <call>. Let's see that in action:
<macro docname="main"><call macro="greet" dst="greeting"><str dst="name">Will</str></call><echo>Macro returned: ${greeting}</echo></macro>
In the above code, the call has dst
set to "greeting"
, which is where the result of the macro call will be stored. It is <echo> in the main macro, prints that to the console.
An alternative way of returning a value is to use the value
attribute of <return>. Here's how we might use this to return the text in the greet
macro:
<macro docname="greet"><str dst="greeting">Hello, ${name}!</str><return value="greeting"/></macro>
By default, you may call a macro with any arguments. It is also possible to specify exactly what arguments the macro requires with a <signature> tag. The advantage of specifying the arguments for a macro is that Moya is able to detect any mistakes in the type or number of parameters.
The <signature> should contain a list of <argument> tags. Here's how we would modify the greet
example to specify the single argument it requires:
<macro docname="greet"><signature><argument name="name" required="yes"/></signature><return><str>Hello, ${name}!<str></return></macro>
The addition of the <signature> tells Moya that the macro accepts one required argument, called name. If you don't call it with a value for name, Moya will raise an exception.
<argument> tags have an optional check
parameter, which should be an expression. If given, and the expression is false, Moya will raise an exception. This is used for detecting errors that may otherwise go unnoticed. The following version of greet
checks that the value of name
has at least one character:
<macro docname="greet"><signature><argument name="name" required="yes" check="len:name gt 1"/></signature><return><str>Hello, ${name}!<str></return></macro>
Moya can call macros lazily, which means that the call returns immediately, but the code is only executed when the return value is used. This is occasionally a very useful thing to do. For instance, the Moya authentication library uses this feature to get the current user. If your code never references the 'user', then Moya can avoid doing a database query.
Let's start with the following code to demonstrate lazy calls:
<moya xmlns="http://moyaproject.com"><macro docname="slow"><echo>Entered in to slow macro</echo><sleep for="5s"/> <!-- do nothing for 5 seconds --><echo>Leaving slow macro</echo><return><str>done</str></return></macro><macro docname="main"><call macro="slow" dst="result"/></macro></moya>
Save the above code as lazy.xml
and call it with the following:
$ moya run lazy.xml --breakpoint
If you then step through the code (hit s
), you should see Moya jump from the <call> to the other macro and execute it. Nothing out of the ordinary. But lets look at what happens if the call is done lazily. Replace the call with the following:
<call macro="slow" dst="result" lazy="yes"/>
Now when you run lazy.xml
, nothing appears to happen. This is because we haven't attempted to use the return value. Insert the following line after the call
:
<echo>${result}</echo>
Now if you run lazy.xml
, you should find that as soon as you try to use the value of result
, the macro is actually called. The macro will be called if you do anything with result
, such as using it in an expression or looking up an attribute.
The preceding section on Macros introduced the docname
attribute. All elements (instances of a tag) in Moya have an associated docname set with the docname
attribute. An element's docname should be unique to the XML file it is defined in (it's an error to use the same docname for more than one tag). The purpose of a docname is to reference another element; we've seen it used when calling a macro. Moya knows which <macro> to call by matching the value of macro
to an element with that docname.
There is a second type of name common to all elements, called a libname. A libname is similar to a docname, but must be unique to the library and is used to reference an element that may be in an different XML file within the library. When you use a libname to reference an element, it should be preceded with a #
, otherwise Moya would interpret it as a docname. Here's an example of using a libname to call a macro:
<macro libname="greet"><echo>Hello, ${name}!</echo></macro><macro docname="main"><call macro="#greet" name="World" /></macro>
In the above example, <call> has a macro
attribute of #greet
which references the macro with libname greet
. Because we're using a libname rather than a docname, the greet
macro could be in any XML file contained within the same library.
Occasionally it is also necessary to reference an element in another library entirely. In these cases, you specify the application name before the #
symbol, so that the element reference would be <application name>#<libname>
. Let's look at an example of using an element reference with an application name. Consider a fictitious macro in a library called moyaproject.sushifinder
, and installed as an application called sushi
:
<macro libname="order"><echo>Ordering product ${product}</echo></macro>
If we wanted to call the above macro from another application, we could do so with the following code:
<call macro="sushi#order" let:product="nigiri" />
It is also possible to use the library name in place of the application name. This works as long as the library is installed exactly once. Here's how we would use the library name to call the order
macro:
<call macro="moya.sushifinder#order" product="nigiri" />
Generally speaking it is preferable to use libnames over docnames, so that you aren't tied to a single XML file.
In some situations, Moya is unable to do what is asked of it, or a condition occurs that makes it unable to continue normally. In these cases Moya uses exceptions to report the problem, which allows you to write code that can recover from such conditions.
Let's see what happens when an exception occurs. The following code attempts to divide by zero, which Moya (and most languages) can't do:
<moya xmlns="http://moyaproject.com"><macro docname="main"><let infinity="1/0"/></macro></moya>
if you run the above code from the command line, you will see a message in the console, that looks something like this:
math.division-error: Can't divide by zero in '1/0'
In this message, math.division-error
is the exception name, the text that follows is a description of the problem. The reason that the message appears in the console is that the exception was unhandled, meaning there was nothing in the code to detect and respond to that exception. Moya stopped running code at the point the error occurred and generated the error message.
If this occurred when running a web application, the user will see a 500 (error) page, or an error report if in debug mode. If we don't want this to happen, we must first catch the exception.
We can catch an exception from the previous statement with the <catch> tag. Here's how we might catch the "math.divisionerror"
exception:
<let infinity="1/0"/><catch><echo>Can't divide by zero!</echo></catch>
Here the code doesn't exit when we divide by zero. The code inside the <catch> statement is executed, and we get a message telling us we can't divide by zero.
A <catch> without any attributes will catch all exceptions that could occur. It is rarely a good idea to do this, because it might mask unrelated errors. We can catch a specific error with the exception
attribute. Let's modify the previous sample to only catch "math.division-error"
:
<let infinity="1/0"/><catch exception="math.division-error"> <!-- catch the one exception --><echo>Can't divide by zero!</echo></catch>
It is possible to catch a range of errors with a single <catch> by using an exception wildcard. For example, math.*
will match all mathematical exceptions. Other exceptions are organized in to hierarchies so you can catch specific classes of exception.
Exceptions follow a naming convention. There are a number of exceptions that Moya itself may throw; these contain a single period (math.division-error
is one example). Code in a library may also throw exceptions (See user defined exceptions below); these consist of the library long name, further broken down with more periods.
For example, the Moya User library can throw an exception called moya.auth.password-fail
if the password is not correct. This naming convention allows us to catch any exceptions from the moya.auth library. For example:
<catch exception="moya.auth.*"><echo>Not logged in!</echo></catch>
When you write your own libraries, you should follow this same convention. See below for an explanation of working with your own exceptions.
The <catch> tag, catches exceptions from the previous statement. Sometimes it is necessary to catch exceptions that may occur in a block of statements. We can do this with <try> which simply executes a block. Here's how we could catch all math.division-error
exceptions in a block of code:
<try><echo>Attempting to divide by zero...</echo><let infinity="1/0"/><echo>We will never get here</echo></try><catch exception="math.division-error"> <!-- catch the one exception --><echo>Can't divide by zero!</echo></catch>
The code in a <try> block is rarely as simple as this. The actual exception may occur in a macro you have called in your <try> block, or even more deeply nested. As long as the execution started inside the <try>, you will be able to catch any exceptions.
Up to this point we have been catching exceptions thrown by Moya itself. It is also possible to throw your own exceptions to handle conditions that may otherwise be difficult to report. Throwing an exception is done with the <throw> tag, which takes a single attribute, exception
, containing the name of the exception you want to throw.
Here's an example of how to throw an exception:
<try><throw exception="bobs.widgets.error"/><echo>Will never get here</echo></try><catch exception="bobs.widgets.*"><echo>Sorry, Can't do that with Bob's widgets</echo></catch>
In the above code, Moya executes the <throw> statement, which creates the exception. It is immediately caught by the <catch> statement underneath the <try>. Of course, in real code, exceptions would be thrown if some real-world condition is detected. The name of the exception follows the naming guidelines of beginning with the name of your library, here the library would be the fictitious bobs.widgets
library.
When you throw an exception, you can also attach a message with the msg
attribute. This text is displayed if the exception is unhandled. Here's how to attach a message to the exception above:
<throw exception="bobs.widgets.error" msg="Out of Stock"/>
It is good practice to add a msg
; it can help diagnose problems.
Exceptions may have additional data associated with them, presumably containing information that may help the code that catches the exception decide how to respond. You can attach such data to exceptions with the let
extension. Here's an example:
<throw exception="bobs.widgets.error" msg="Out of Stock" let:item="deluxe"/>
The above code associates a string named item
with the exception being thrown. The <catch> statement may retrieve this information via its dst
attribute. Inside the <catch>, the value referenced by dst
, will contain an exception object. Exception objects have 3 attributes; msg
, type
and info
. The type
attribute contains the exception type (e.g. bobs.widgets.error
); msg
contains a short description of the exception; and info
is a dictionary with the attached information.
Here's how you might respond to data in an exception:
<throw exception="bobs.widgets.error" msg="Out of Stock" let:item="deluxe"/><catch exception="bobs.widgets.*" dst="error"><echo>Message is: ${error.msg}</echo><echo>Exception type is: ${error.type}</echo><echo>Item is: ${error.info.item}</echo></catch>