Statement Overview
reviewed: 15 February 2025
The statements of Cms are presented in alphabetical order.
Sync - async - end-sync
Note: advanced use
The sync - async - end-sync construct can be used to evaluate an expression asynchronously and wait for all separate threads to complete.
The following is a highly trivial example that will write the numbers 0 to 9 to the response. You have no control over the sequence in which each asynchronous thread completes.
One know shortcoming of the sync - async - end-sync construct is that you need to know the number of threads upfront. I have however seen clever examples using script.quickRun(). Ii it beyond the scope of this document to explain that in detail.
function asyncTest()
sync
async response.write(0)
async response.write(1)
async response.write(2)
async response.write(3)
async response.write(4)
async response.write(5)
async response.write(6)
async response.write(7)
async response.write(8)
async response.write(9)
end-sync
end-function
Executing the above can result in various outcomes as we have no control over the sequence of execution.
10234678950291385647094185376
Do - until
The do - until construct is used to execute a block of statements until a certain expression evaluates to true. Do - until can be useful when a statement block needs to be executed at least once.
set( 'ok', false() )
do
set( 'ok', script.call( './fileTransferReady' ) )
sleep( 500 )
until [ok]
// File transfer is complete
In the above (highly simplified) example we keep checking whether a file transfer has completed.
The following example shows a slightly more advanced version of the do - until where we also restrict the number of iterations, regardless of whether the file transfer has completed or not.
set( 'ok', false() )
do
set( 'ok', script.call( './fileTransferReady' ) )
sleep( 500 )
until condition: [ok], maxIterations: 100
if not( [ok] )
// File transfer not completed after 100 iterations
else
// File transfer completed
end-if
The exact behaviour is perhaps best explained using the following example:
set( 'i', 0 )
do
set( 'i', add( [i], 1 ) )
until gt( [i], 50 ), maxIterations: 35
response.write( [i] )
| MaxIterations | Outcome |
|---|---|
| 100 | 51 |
| 35 | 35 |
You can force an early-exit inside the do - until using the exit statement.
You can force a next iteration using the next statement.
Exit
The exit statement is relevant inside a loop construct (see do - until, while - loop and iterate - end-iterate).
The following example prints 46:
set( 'i', 0 )
do
if gt( [i], 45 )
exit
end-if
set( 'i', add( [i], 1 ) )
until gt( [i], 50 ), maxIterations: 100
response.write( [i] )
Exit-function
The exit-function statement allows early exit from a function without returning a value.
The following example
function main()
if not( ingroup( 'DEVL' ) )
page.call( 'error/404' )
exit-function
end-if
// More code here
Expression
The bulk of each Cms script is made up of expressions. As explained earlier, the actual functionality is provided by function handlers, the Cms language is merely the glue that binds expressions together.
We have already seen plenty of examples of expression statements:
set( 'i', 0 ) // Expression statement
do
if gt( [i], 45 )
exit
end-if
set( 'i', add( [i], 1 ) ) // Expression statement
until gt( [i], 50 ), maxIterations: 100
response.write( [i] )
If - else - else-if - end-if
The if - else - else-if - end-if construct is used to execute statement blocks depending on conditions.
set( 'i', random( 1, 10 ) )
if eq( [i], 1 )
response.write( '1' )
else-if between( [i], 2, 5 )
response.write( 'Between 2 and 5' )
else
response.write( '> 5' )
end-if
The else-if and else are both optional. You can have any number of else-if's.
Isolate - end-isolate
Note: advanced use
The isolate - end-isolate construct can be used to execute a statement block isolating it from variables that may be context.
set( 'i', ' i-before ' )
set( 'j', ' j-before ' )
isolate
response.write( [i] )
response.write( [j] )
set( 'i', ' i-override ' )
set( 'j', ' j-override ' )
response.write( [i] )
response.write( [j] )
end-isolate
response.write( [i] )
response.write( [j] )
The example above displays the following: i-override j-override i-before j-before as the variables i and j are isolated inside the isolate- end-isolate.
You can list variables that you want to 'include' inside the isolate - end-isolate and variables that you want to retain after the isolate - end-isolate.
set( 'i', ' i-before ' )
set( 'j', ' j-before ' )
isolate 'i'
response.write( [i] ) // Prints i-before as 'i' is copied forward
response.write( [j] ) // Does not print anything as 'j' is not copied forward
set( 'i', ' i-override ' )
set( 'j', ' j-override ' )
response.write( [i] ) // Prints i-override
response.write( [j] ) // Prints j-override
end-isolate 'j'
response.write( [i] ) // Prints i-before as 'i' is not copied back
response.write( [j] ) // Prints j-override as 'j' is copied back
The example above results in the following i-before i-override j-override i-before j-override.
Iterate - end-iterate
The iterate - end-iterate construct is used to repeat a statement block whilst iterating over an iterator. The concept of iterators is explained here.
iterate iterator.ofNumber( 1, 10, 'i' )
set( 'total', add( [total], [i] ) )
end-iterate
response.writeLine( [total] )
response.writeLine( [i] )
The example above generates 55 10 as output.
The next statement can be used to force a next iteration and exit can be used to exit the iterations prematurely.
Next
The next statement can be used to force the next iteration in a loop. See the statements do - until, while - loop and iterate - end-iterate.
iterate iterator.ofNumber( 1, 10, 'i' )
if eq( [i], 5 )
next
end-if
set( 'total', add( [total], [i] ) )
end-iterate
response.writeLine( [total] )
response.writeLine( [i] )
The example above generates 50 10 as output.
Raise
The raise statement is used to raise an exception. There are two types of exception: soft and fatal.
A fatal exception will immediately halt execution (unless caught, see here) and invoke the standard, CaseMaster error routine.
A soft exception will follow the exception handling of the script.
The (optional) second parameter is an error message.
The following example shows data validation (see here):
// Check DOB to be in the past
if and(
eq( [timing], eventActionTiming.Pre ),
in( [type], eventActionType.Update, eventActionType.Insert ),
ge( bo.attr( [_me], 'dob' ), today() )
)
raise exceptionType.soft, 'DOB needs to be in the past'
end-if
Return
The return statement is used to return a value from a function (a function will return null without a return statement).
The following example returns the surface by multiplying the parameters length and width.
function surface( length, width )
return mul( [length], [width] )
end-function
Switch - case - else - end-switch
The switch - case - else - end-switch construct will execute a statement block based on the value of specific test expression.
In the following example we generate a random, whole number between 1 and 10 and use the switch statement to print what bracket the value falls into:
set( 'i', random( 1, 10 ) )
response.writeLine( [i] )
switch [i]
case 1
response.writeLine( '1' )
case 2, 3, 4
response.writeLine( '2, 3 or 4' )
case 5
response.writeLine( '5' )
else
response.writeLine( '> 5' )
end-switch
The test expression can represents a string. By default, the switch statement is case insensitive. You can however use the caseSensitive option to force that case is sensitive.
The following example uses the caseSensitive option:
function trueLike( value )
switch value: [value], caseSensitive:false()
case 'Y', 'yes', '1', 'on', 't', 'true'
return true()
else
return false()
end-switch
end-function
Trace - end-trace
The trace - end-trace construct can use used to activate tracing for a block of statements. Tracing is explained in more details here.
Trace - end-trace constructs can be nested; this way you can activate tracing on a larger statement block but de-activate it again for a sub-set of statement.
Tracing is activated in the following example.
function trueLike( value )
trace true()
switch value: [i], caseSensitive:false()
case 'Y', 'yes', '1', 'on', 't', 'true'
return true()
else
return false()
end-switch
end-trace
end-function
Try - catch - finally - end-try
The try - catch - finally - end-try construct is designed for catching errors.
Error handling is explained in detail here.
The following example shows the try - catch in action. The statement block inbetween the try and catch is the code we try to execute. If al is well, the catch is ignored. In case of an error, the control is immediately transferred to the catch statement block (and the remainder of the catch block is skipped).
In either scenario the logic in the finally block is executed. The finally block is optional.
try
// Load the query
set('query', bo.quickLoad('application/adhocQuery', qs.get('q')))
// For security we prevent access to this page when not allowed
if not(page.call('./isAllowed', [query]))
page.call('error/403')
exit-function
end-if
// Get the descriptor
set('descriptor', boDesc.parse(bo.attr([query], 'bo')))
// Start a new transaction
tx.begin()
// Write the generated page manually to allow handling any errors
response.write(page.generate(page.get('./run')))
catch
// Something has gone wrong, show the error
script.call('web/render', page.get('./error') )
finally
// Roll back the transaction whether error or not
tx.rollback()
end-try
While - loop
The while - loop construct is very similar to the do - until construct but the test is done upfront so it is possible that the code block enclosed by the while and loop is not executed.
The following shows the while - loop in action with the optional maxIterations option.
set( 'i', sum( [page], 1 ) )
while le( [i], [totalPages] ), maxIterations: 2
qualifier.call( './item', [pagination], [sb], [i] )
set( 'i', add( [i], 1) )
loop
<End of document>