Calling Functions and Returning Values
reviewed: 21 February 2025
Calling Functions, Invoking Methods and Namespaces
We have already seen calling functions in action.
There way of calling a function varies per namespace (bo, page, script, qualifier, service or document).
| Namespace | Calling a function |
|---|---|
| bo | bo.invoke( [bo], 'functionName' ) |
| page | page.call( './functionName' ) |
| script | script.call( './functionName' ) |
| qualifier | qualifier.call( './functionName' ) |
| service | service.call( './functionName' ) |
| document | document.call( './functionName' ) |
The reason why we have opted for bo.invoke rather than bo.call is twofold:
- To emphasize the important difference with calling functions in other namespaces that you need to pass a bo as the first parameter
- To adopt the terminology from traditional OO languages
Calling Functions in Other Scripts
The call script.call( './functionName' ) assumes there is a function with the name functionName in the same script as from where you try to call this function.
The prefix ./ indicates that the function is local.
script.call( 'functionName' ) (i.e. leaving the prefix ./ out) Has a completely different meaning. In this example, functionName will not be seen as the name of a function but of the name of a .cms file in the script folder. As no function name is specific, CaseMaster will run the function main in that script (or fail when that script does not exists).
If you want to call a function in another file, you use the file:function syntax.
| Namespace | Calling a local function | Calling a function in different script |
|---|---|---|
| bo | bo.invoke( [_me], 'functionName' ) |
bo.invoke( [any_BO_but_me], 'functionName' ) |
| page | page.call( './functionName' ) |
page.call( 'page_script:functionName' ) |
| script | script.call( './functionName' ) |
script.call( 'script_script:functionName' ) |
| qualifier | qualifier.call( './functionName' ) |
qualifier.call( 'qualifier_script:functionName' ) |
| service | service.call( './functionName' ) |
service.call( 'service_script:functionName' ) |
| document | document.call( './functionName' ) |
document.call( 'document_script:functionName' ) |
Again, the fundamental difference in calling functions in a BO script is highlighted here.
Note that the external function should not be marked as private or protected as the call / invoke will fail.
Consider the following code snippet (taken from page/test.cms):
function doTest()
page.call( './test' ) // Calling function test as local function
page.call( 'test:test' ) // Calling function test as remote function
end-function
private function test()
response.write( 'Hello world<p>' )
end-function
You could argue that the 2nd call should fail as the access modifier private should clash with calling a function in an external script. However, CaseMaster will recognise that test is actually this page.
Passing Parameters
Passing by position or by name
The following example shows how parameters can be passed to a function:
function doTest()
page.call( './testWithParameters', '1 Hello', 'world' )
page.call( './testWithParameters', a:'2 Hello', b:'world' )
page.call( './testWithParameters', b:'world', a:'3 Hello' )
page.call( './testWithParameters', '4 Hello', b:'world' )
end-function
function testWithParameters( a, b )
response.write(
concat(
[a],
' ',
[b],
'<br>'
)
)
// Results in
// 1 Hello world
// 2 Hello world
// 3 Hello world
// 4 Hello world
end-function
| Scenario 1 | Usage |
|---|---|
| 1 | Parameters are passed by position |
| 2 | Parameters are passed by name |
| 3 | Parameters are passed by name, position no longer matters |
| 4 | A mixture of by position and by name |
Important not on scenario 4: be careful when mixing by position and by name. Take for example the following example:
function doTest()
page.call( './testWithParameters', 'Hello', b:'beatiful', c:'world' )
// Ok
page.call( './testWithParameters', 'Hello', b:'beatiful', 'world' )
// Ok
page.call( './testWithParameters', 'Hello', 'world', b:'beatiful' )
// Fatal error: No value found for mandatory function parameter in testWithParameters; c
end-function
function testWithParameters( a, b, c )
response.write(
concat(
[a],
' ',
[b],
' ',
[c],
'<br>'
)
)
end-function
Default values (optional parameters)
A parameter is by default mandatory (i.e. you must pass a value when calling that function). You can make a parameter optional by providing a default value:
function doTest()
page.call( './testWithParameters', 'Hello' )
page.call( './testWithParameters', 'Hello', 'world' )
page.call( './testWithParameters', 'Hello', 'moon' )
end-function
function testWithParameters( a, b: 'world' )
response.write(
concat(
[a],
' ',
[b],
'<br>'
)
)
// Output
// Hello world
// Hello world
// Hello moon
end-function
Take some care when mixing optional with mandatory parameters:
function doTest()
page.call( './testWithParameters', 'Hello', c:'!' )
page.call( './testWithParameters', 'Hello', 'world', c:'!' )
page.call( './testWithParameters', 'Hello', 'moon', c:' and beyond' )
end-function
function testWithParameters( a, b: 'world', c )
response.write(
concat(
[a],
' ',
[b],
' ',
[c],
'<br>'
)
)
// Output
// Hello world !
// Hello world !
// Hello moon and beyond
end-function
A common default value for optional parameters is null():
function testWithParameters( a, b:null() )
if isNotNull( [b] )
response.write(
concat(
[a],
' ',
[b],
'<br>'
)
)
else
response.write(
concat(
[a],
'<br>'
)
)
end-if
end-function
Ending Function Execution and Returning Values
There are a number of ways to end execution of a function:
- A fatal error occurs (that is not handled)
- A
exit-functionstatement is encountered - A
returnstatement is encountered
In the first two scenarios no value is returned from the function. You can return a value using the return statement. See the following example:
function doTest()
response.write( page.call( './testWithParameters', 'Hello', 'world' ) )
end-function
private function testWithParameters( a, b )
return concat(
[a],
' ',
[b],
'<br>'
)
end-function
Calling Parent Functions
We will cover the concept of inheritance here but ahead of this, a script can inherit from another script of the same type (i.e. bo from bo, page from page, etc).
Take the following snippet from a page script:
inherits 'base' // Inherits form page/base.cms
function doTest()
response.write( concat( page.call( './helloWorld' ), '<br>' ) )
// Call local version
response.write( concat( page.call( '../helloWorld' ), '<br>' ) )
// Call parent version
end-function
function helloWorld()
return 'LOCAL: Hello world'
end-function
The file base.cms has the following function:
function helloWorld()
return 'PARENT: Hello world'
end-function
The output of the function doTest is:
LOCAL: Hello world
PARENT: Hello world
Recursion
CaseMaster supports recursion. Obviously, there is a limit to the stack size so make sure your algorithm is implemented correctly!
function doTest()
response.write( concat( '0: ', page.call( './fibonacci', 0 ), '<br>' ))
response.write( concat( '1: ', page.call( './fibonacci', 1 ), '<br>' ))
response.write( concat( '2: ', page.call( './fibonacci', 2 ), '<br>' ))
response.write( concat( '13: ', page.call( './fibonacci', 13 ), '<br>' ))
end-function
function fibonacci( number )
if lt( [number], 2 )
return [number]
else
return add(
page.call( './fibonacci', sub( [number], 1 ) ),
page.call( './fibonacci', sub( [number], 2 ) )
)
end-if
// Output
// 0: 0
// 1: 1
// 2: 1
// 13: 233
end-function
<End of document>