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-function statement is encountered
  • A return statement 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>