WebMacro Template Script Documentation
Who Should Read This
This is an overview of the script language used in WebMacro templates. It is aimed at web page designers who are building the front end look and feel of an application based on WebMacro.Programmers interested in the servlet back end should read the API documentation; and possibly my article on the Fundamentals of Servlet Design which explains the idea behind WebMacro.
You might want to glance over the Script Intro page as well, for an overview.
Script Language Design Goals
The main ideas behind WebMacro's script language are:
- Page designer doesn't need to know about back end programming
- Conceptually simple script language
- Compact, elegant syntax
- Orthagonal to HTML/XML to avoid conflict
the main goal of the script language is to free the page designer from having to know how the back end programming was done. The page designer and back end programmer need only agree on what information is to be provided, not how it works.
The intent was to create a script language that was ideal for page designers, while providing flexible access to all of the underlying data that may be needed--and enough power to lay it out in the desired way.
A secondary goal was to create a script language which could be freely used with XML and HTML: Many similar products exist which try and use HTML or XML as the script language--resulting in an obscure collection of tags that can't be properly parsed except with a modified editor; and where conflicts between the content of the document and the structure of the script language are a constant headache.
WebMacro avoids this by carefully choosing a script language syntax in which none of the tokens are meaningful in HTML or XML.
The end result is a language which is easy to use, frees the page designer from worrying about programming issues, and can generally be manipulated from within a standard web editor (most of the time).
Templates and Contexts
A template is an HTML (or other) document containing some WebMacro script codes. The template should look so much like an HTML document that it can (mostly) be edited as if it were one. Template creation is the job of a page designer, and does not involve application programming.Templates are loaded by the WebMacro processing engine and parsed into an intermediate form for faster execution. When it's executed, the WebMacro engine resolves all the script codes in the document to produce the output, which is returned to the user.
Normally, a template will be parsed once and executed many times.
Each time the template is executed, it will be supplied with a new context. A context is a bundle of information provided by the back-end system which the template can access and display.
The general idea behind WebMacro is that an application programmer writes some back end software which evaluates the users request, selects a template to display, and then populates the context with information that should be returned to the user.
The page designer writes the template which displays the information found in this context. With each request the context will contain different information.
What follows is a description of the kinds of things you can do in a WebMacro template--how to access data in the context, how to iterate through lists, set variables, include different information by testing for conditions, and so on.
Variable
Variables are the basic way you access information in the context that's been supplied to you. Most are quite simple, though some can be fairly complex. Here are some examples:$Title $Customer.Name $Inventory.Products.findProduct("hinge").PartNumberEach of these variables refers to an object somewhere in the context. It's WebMacro's job to figure out how to find the object referred to; it's the application programmers job to make sure that it's there.In the second example, the Customer object has a Name property--the dot operator is used to navigate from an object to one of its property. You can navigate as deeply as you want into an object.
The third example includes a method call. Generally you should prefer regular property evaluation, since it's simpler and faster. However, from time to time you will simply have to call a method on an object to get what you want--and WebMacro allows you to do so. You can continue navigating from the object returned by the method into its properties.
Note that all text in WebMacro resolves to a String. Thus in a method call such as foo.bar(baz) you are passing the String arguement "baz" to the method bar on object foo. If you want to use an object of another type, you must get it as the return value of a Variable reference.
The back end programmer should provide you with a list of the objects you may find in your context, along with the properties they contain. If you need to call any methods, the back end programmer will explain to you what they are and how to use them.
Technical note: For the curious, what happens here is that WebMacro uses Java property introspection to examine an object and determine how to extract the requested information from it. It goes far beyond the simple introspection described in the Bean standard and is more or less capable of getting anything you want out of an object. Programmers should refer to the PropertyOperator documentation for more details on how WebMacro extracts values from objects.
In addition to accessing variables and properties, you can also set them using the #set directive. See below for information on that.
The WebMacro servlet environment provides you with a number of standard variables and objects pre-defined, in addition to those that the back end programmer created. Check the WebContext documentation for more information on what's available.
Term
Many WebMacro expressions require some kind of value--for example, you may want to set a variable called $hi to the value "Hello". The value "Hello" is called a term.WebMacro allows you to use six different kinds of things as terms: words, numbers, booleans, variable references, quoted text, and lists.
Here are some examples of terms:
#set $thing = "this is a term" #foreach $item in [ "these are all terms", $term, term ] $variable.method( "method arguments are terms", $term, term ); #if (true) { true is a Term equal to Java's Boolean.TRUE; similarly false means Boolean.FALSE } #set $myInteger = 594 #set $myLong = 42LAnd here are more detailed explanations of each type:
- Word
- A word is a series of letters and numbers
examples: Hello 1
If your word has something other than letters and numbers in it you should quote it--see the description of Quoted Text below.
There are two special constants: true, and false. You can use these anywhere a bare word can be used as a Term; they are equivalent to Boolean.TRUE, and Boolean.FALSE. One useful thing this allows you to do is set a variable to false with the #set directive: "#set $v = false".
- Number
- An Integer object will be constructed from a Term when it is fully numeric. For example, if you write:
#set $a = 10that is equivalent to context.put("a", new Integer(10)). You may prefix a value with a minus sign to create negative values. Also, you may construct a Long instead of an Integer by adding the suffix l or L, as in Java.An Integer will automatically be converted to an int primitive, so you can use these constants to call methods which require an int argument.
No support is provided for Double, Float, or other values. If you wish to write a numeric value as a string, enclose it in quotation marks. For example, "101L" is a four character string, whereas 101L when used as a Term, is a three digit Integer.
Note that this only applies to Terms: list members, arguments to functions, parameters of directives, etc.; numeric values in running text are unaffected.
- Boolean
- There are only two boolean values: true and false. They are equivalent to the Java objects Boolean.TRUE and Boolean.FALSE. You can use them to call functions or set properties which expect a boolean. Or you could save them in a variable, and conditionally include text using #if directives throughout your document.
For example:
#set $Testing = true ... #if ($Testing) { logged in user is $Customer.Name }Introspection automatically convets Booleans to primitive Java boolean types when necessary.
- Variable Reference
- Just as you can have variables floating freely anywhere in your template, you can use a variable reference as a term.
example: $Customer.Name
Variables are more fully explained above.
- Quoted Text
- You can quote a string of words to group them together as a single term. You also need to use quotation marks if your term includes special characters. Without the quotes, WebMacro will view each word or special characer as a separate term (or possibly a parse error).
A string begins with either a double or single quotation mark, and ends with the same closing quote. It may contain variables, and you can use the \ character to escape characters within the string (if you need a literal $, for example).
example: "This is a string. Here is a variable: $var"
Notice that the string contains a variable reference, $var.
- List
- A list is an ordered set of terms. You might create a list so that you can iterate through it in a #foreach statement. Here is an example of creating a list:
#set $myList = [ "first thing", $second, third ]This example creates a list with three things. You can create lists that contain lists too, if you want to do that:#set $complex = [ [ first ], [second, two], [third, third, "number 3" ] ]This complicated example creates a list of three elements, each of which is a list. The first list has one element, the second two, and the third three elements.Programmer note: These lists are of type Object[], and may be returned to you via the Template API (parameters) or added to your context by your template script.
Directives
Directives are WebMacro statements that perform some operation; conditionally include text; or repeat a block within your template.Directives all begin with a '#' character, and must be the first thing appearing on a line (they may have optional whitespace in front of them).
Many directives operate on a block. A block begins with a { and continues up until a matching } closes it. The closing } character will only be recognized if it appears at the start of a line.
Here's an overview of the available directives.
## comments
Two (or more) #'s at the start of a line form a comment. The rest of the line is ignored. The comment may be preceeded by some whitespace, but otherwise is only recognized at the start of a line.Here's an example:
## this is a comment
#foreach $thing in list
The #foreach directive is the way you create tables and lists in WebMacro.#foreach iterates through a list, including its output block once for each element in the list.
Here's an example:
<ul> #foreach $customer in $list { <li>$customer.name lives at $customer.address } </ul>Each time through the list the variable $customer takes on the value of a term from the supplied $list.You may create the list yourself using the #set statement, or put the list here directly with [] syntax. More likely the back end programmer has provided the list for you by placing it in your context.
For example the list could be the result of a database query.
You can replace "$customer" with any variable you like. The block of text within the braces will be output once for each item in the list, performing any substitution required. The block can contain arbitrary WebMacro script code, including further #foreach directives, #if statements, etc.
One warning: when the #foreach directive sets $customer to an element of the list, it will overwrite any value which may have previously existed in the context under the name "customer".
#if (condition) { ... } #else { ... }
Use the #if directive to conditionally include text. The text is included if the condition is true, and not include if it is false.In WebMacro, a condition is true if it is defined and has a non-null value other than Boolean.FALSE. Since WebMacro automatically converts to and from primitive boolean types, you are encouraged to use boolean values in conditions whenever possible.
#if directive conditions can make use of the boolean operators: && (and), || (or), and the unary operator ! (not). These boolean ooerators mean the same thing they do in Java. They are left-associative, and short circuiting (meaning the right operand is evaluated only if it has to be).
You can also use the boolean ooerators == (equals) and != to compare objects. These do not behave like Java operators do: they rely on Object.equals() rather than object identity to determine equivalence.
A conditional can use any WebMacro Term as its operands. A Term is considered false if it is null, undefined, or equal to the constant Boolean.FALSE; and true otherwise. You are encouraged to use regular boolean primitives in your code as these will be translated to Boolean.TRUE and Boolean.FALSE during introspection.
Finally, you can group expressions based on Terms and these five operators using parentheses, as you would expect. Thus the following is a valid condition: (($User && $User.isOK()) || ($Magic == "foo"))
Here's an example:
#if ( $Customer.owesMoney() && ($Customer.Name != "Fred") ) { Pay up, or else! } #else { Thank you for your patronage. }The #else block is optional--you can leave it off.#include file
Use the #include directive to read in a file. The contents of the file will not be parsed, so this is a good way to include JavaScript and other things that might conflict with WebMacro.You can also use it to read in standard block of HTML which you would like to include in every page.
Here is an example:
#include /web/script/banner.html#param name = value
The #param directive can be used to specify parameters of the template which can be examined by the handler. These are static values that do not change over the life of the template.The purpose of the #param directive is to allow the template author to specify some parameters to the back end programmer that may be used to determine what kind of information to put into the context.
Here are some examples:
#param author = "Joe" #param require = [ "user", "document", "session" ]The first parameter sets the "author" keyword to "joe". The second parameter sets the "require" keyword to a list of three things: user, document, and session.These parameters are arbitrary and only meaningful if the back end programmer makes use of them.
#parse file
The #parse directive includes a target file and parses it as though it were part of the current template.You can use this to include common template code that may be shared among several different templates.
Here are some examples:
#parse "C:/webmacro/library/banner.wm" #set $library = "C:/webmacro/library" #parse "$library/banner.wm"Note that you must normally specify an absolute path to the target. Future versions of WebMacro may alleviate this restriction.#set $property = value
Just as you can access values in the context, you can set them.Here is a simple use of the #set keyword:
#set name = "Fred Flinstone" Hi my name is $nameThe #set directive is much more poweful than this simple use, however. It can also be used to set properties on complicated objects--if there is a way to do it:#set $Customer.name = "Sidney" #set $Database.findItem($partNum).Description = $newDescriptionWhether or not it is possible for you to set a value depends on the public methods available on the target object. WebMacro will use normal Variable reference evaluation to evaluate all but the last term specified in the left hand side of the #set. Then it will introspect the final object to determine if it's possible to set the value in some way.Refer to the PropertyOperator documentation for a complete list of the mechanisms by which WebMacro can succeed in setting a value.
#use 'parser' until end-marker
The #use directive switches to a parser other than the one normally used by WebMacro. It's a hook into other languages. Currently, the only other parser that WebMacro can call is a "text" parser that does nothing.You can use the "text" parser to mark off a portion of your template as being non-Webmacro script. For example, you could surround some JavaScript this way so that the {'s don't confuse Webmacro.
Here is how it works:
#use 'text' until '-end-' this appears verbatim in the output, the #number signs and { characters do not } confuse WebMacro but will be output as is. It is safe to put JavaScript code here. -end-The specified parser (in this case 'text') eats all the input until a line matching the until clause is found (in this case '-end').Retrospective Overview
WebMacro's script language is fairly powerful--but it is completely focussed on formatting and laying out a page.As a designer you will have to learn some simple scripting. However, it should never be very complicated, and it is always related to what you are trying to do: lay out information on a page.
All programming issues, the creation and manipulatio of information, are expected to be handled by the programmer in Java.
As a programmer you may be surprised that WebMacro is lacking more sophisticated programming structures. This is intentional. You have an excellent, full fledged language--Java--in which to write your back end code.
Because Webmacro separates all the programming out of the template, both the program and the HTML templates are much more elegant and understandable than when those two worlds collide in one document.
In addition, because they are separate, programmers and page designers can work on a project simultaneously.
To learn more about WebMacro, its design, and to share and exchange ideas on its use, please join the mailing list: just send an email with the word "subscribe" in the subject or body to webmacro-request@webmacro.org.
The mailing list is also the appropriate place to direct support questions, and we do encourage you to post if you run into trouble. We'd also like to hear from you if you figure out a clever way to do something interesting, or feel that an important feature is missing from WebMacro.
api | design | faq | goals | links | license | othertech | quickstart | script | status