Expressions and Dynamic Attributes

Expressions

In the previous chapter you have had some minor exposure to CaseMaster Scripting as the client.cms descriptor is written in CMS.

CMS is a rich, yet easy to learn language. One important concept of that language are expressions.

The following is an example of an expression:

random()

Expressions are build-up using functions. Functions may or may not have parameters but a function will always have the open- and close parenthesis even if there are no parameters. The random function in our example, was called without parameters.

You can also use the random function with two parameters, for the random function the parameters are optional.

random( 1, 6 )

will generate random number between 1 and 6 and can thus be used as a dice.

Function names are also not case sesntive; random() and Random() are both valid.

Examples of other simple functions are:

Function Use Example
today() Date of today 2024-12-28
now() Timestamp for now 2024-12-28T20:46:12
user() Primary key for current user 2

Some developers may struggle with the fact that CaseMaster uses functions for everyting. It does not understand 8 + 4 or 12 \\ 3. Instead, you would have to use the function-equivalent:

Traditional CaseMaster
8 + 4 add( 8, 4 )
12 \ 2 div( 12, 2 )
6 * 3 mul( 6, 3 )
6 * 3 + 2 add( mul( 6, 3 ), 2 )

The final example shows how functions are nested.

Note: it is beyond the scope of this document to list all the functions. The training application has a menu option to list all the functions.

Logical Functions

I want to pay special attention to the logical functions. These are:

  • and()
  • or()
  • not()
  • xor()
  • xand()
  • _and()
  • _or()
  • true()
  • false()

The functions and() and or() do short-cutting; the and() function will return false() as soon as one parameter evaluates to false() and the remaining parameters are not evaluated.

The same is true for the or() function but this will return true() as soon as a parameter is found that evaluates to true().

The functions _and() and _or() are special versions of and() and or() where all parameters are always evaluated (i.e. no short-cutting takes place).

The functions and(), or(), _and(), _or(), xand() and xor() can all take 2 or more parameters.

Datatypes

CaseMaster supports the following datatypes:

  • Integer
  • Double
  • Decimal
  • String
  • Date
  • Time
  • Timestamp
  • Boolean
  • Business Object
  • Property bag
  • Qualifier
  • Lambda function

The latter 4 are for advanced use only.

Other Function Groups

  • Math functions
  • String functions
  • Comparison functions
  • Date functions
  • CaseMaster functions
  • Datatype conversion functions
  • Datatype test functions

Tokens in Expressions

Token Use Examples
String Can start with ', " or `; must end with same quote as start-quote. Use backslash (\) to escape next character 'Hello world'
"Hello World"
"That's all"
'That\s all'
Integer Digits 1
12
06
-8
Decimal Digits, '.' as decimal separator 18.8876
18.8876
-6.3
Date Starts and ends with '#' #1/1/2025#
#2024/12/6#
#6jan2024#
Time String with hh:mm and optionally :ss '12:10'
'18:06:45'
Boolean true() or false(), 1 or 0, 't' or 'f' true
't'
'F'

Automatic Conversion

CaseMaster is lenient and will convert parameters from one datatype to another where possible or required. For example, the expression add( 1, '6' ) will return 7. CaseMaster will simply convert the string '6' to the integer 6.

Other examples of automatic conversion are:

Expression Result Explanation
concat( 'Hello', ' ', 1 ) 'Hello 1' The integer 1 will be converted to the string '1'
addDay( '1 jan 2024', 1 ) #2/1/2024# The string '1 jan 2024' will be converted to a date
gt( '12', 2 ) false() The datatype of the first parameter is leading, the integer 2 will be converted into the string '2' and '12' is less than '2' (gt means greater than)

The latter example shows that automatic conversion does not always give the desired results.

Dynamic Values

Dynamic values are a special type of attributes. The majority of attributes are linked to a column; the data is stored in the database on insert / update and retrieved from the database when read.

The following is a snippet taken from a typical BO .cms file:

    firstName: <@bo/attribute
        label: 'First name'
        column: 'firstName'
        dataType: datatype.String
        length: 200
        optional: false()
    >

A dynamic attribute does not have a column; instead, the value is calculated on the fly based on an expression.

Imagine we have 4 attributes; title, firstName, middleName and surName and we would like to have an attribute fullName that takes the 4 attributes to create a full name for display purposes; as per following examples:

Title First name Middle name Sur name Full name
Mr John - Smith Mr John Smith
Mrs Josie - Brown Mrs Josie Brown
Mr Kees van der Broek Mr Kees van der Broek

An expression would have to implement something along the following lines:

  • Concatenate title, first name, middle name and surname and seperate by a spaces
  • As middle name is optional, make sure we do not end up with 2 consecutive spaces between first- and surname when there is no middle name

The definition of the attribute could look as follows:

    _fullname: <@bo/attribute/dynamic
        label: 'Name'
        ensureLoadGroup: 'title,forename,middleName,surname'
        sortAttr: 'surName'
        dynamicValue: $buildstring(
            ' ',
            bo.attrFormatted( [_me], 'title' ),
            bo.attr( [_me], 'firstName' ),
            bo.attr( [_me], 'middleName' ),
            bo.attr( [_me], 'surname' )
        )
    >
  • As a convention, we start the names of dynamic attributes with an underscore. This is not a CaseMaster requirements but simply a convention in our team to make dynamic attributes stand out
  • The optional ensureLoadGroup tag can be used to list any number of attributes (from this BO and with exception of dynamic attributes) that must be loaded from the database to successfully evaluate the expression
  • By default, CaseMaster does not allow you to sort on (or search by) dynamic attributes; simply because sorting and searching is done through the database. You can however use the optional sortAttr tag to list an attribute (again, from the same BO and not a dynamic attribute) that the user can sort by so it feels the result list is sorted by the dynamic attribute
  • The \$-sign in front of the expression that makes up the dynamic value is essential. The \$-sign will let CaseMaster know to not evaluate and cache the expression when the file is first loaded and parsed but to re-evaluate the expression every time it is required.
  • The buildString() function will concatenate any number of strings using the first parameter as the separator and only inserting the separator when needed
  • The bo.attr() and bo.attrFormatted() functions will be covered later in this chapter

Function Handlers

In the last paragraph you have seen a reference to the functions bo.attr() and bo.attrFormatted(). You see their name differs from, say, random() and now() in that they are prefixed with bo..

The prefix bo refers to the function handler. All functions we have looked at so far are part of, what is known as, the default function handler and do not require any prefix.

The CaseMaster runtime comes with over 50 function handlers for different purposes and it is possible to add bespoke function handlers (developed in a .Net language and following the function handler guidelines). Find more information here.