In the previous parts one, two, three and four I talked about various aspects of LotusScript / VoltScript classes. But there’s a LotusScript function which has become very relevant for me recently, in the context of classes.
Almost all Domino developers are used to writing LotusScript, compiling it and running it. But for those who have dug into the mail template or maybe other advanced coding, there is a less well-known approach to running LotusScript -
Execute. Execute takes a string of LotusScript.
This is actually nothing new for Domino developers. There are at least two direct comparisons I can think of. The first is also in LotusScript -
To give a simple example, imagine the following simple piece of LotusScript:
Print "Hello World"
The same code can be achieved with Execute:
Execute "Print \"Hello World\""
Of course LotusScript allows different delimiters for strings, which makes things a lot easier. So we can have:
Execute |Print "Hello World"|
On the surface, this may seem of little benefit. There is not much point is switching a
Execute statement that prints. There may be use cases for running dynamic LotusScript - maybe LotusScript that’s stored in a config document. But the real benefit, as with
Evaluate and EL / SSJS, comes from running dynamic LotusScript with context.
To explore those a little further, there are two main use cases for
Evaluate in LotusScript. The first one is to use
@Round, to perform half-up rounding as opposed to bankers rounding, which is what the LotusScript
Round function does. The second is to run contextual formula language, passing a string and a NotesDocument as the context in which to run the Formula Language. Imagine the following LotusScript:
Evaluate(|@Round(unit_price x qty)|, doc)
This is taking the
doc variable and using it as a context in which to perform the
@Round. So the variables
qty map to fields of the contextual Notes Document,
SSJS and EL do the same thing.
database variable for the current context. Of course there are other variables that can be used, like
view or the variable defined in the
var property of a repeat control. Take it to a more advanced coding level, and you can contribute additional variables as we did at various times with OpenNTF Domino API. And with the
binding property of a component, you can access that component from outside of its current context.
The same can be done with
Execute. It’s just that the variables need to be scoped appropriately. For
Execute that means declaring the variables as Globals. Then you can just reference those variables within the Execute statement. So imagine the following code:
Private doc as NotesDocument Sub calcTotal(idex as Integer) Execute(|doc.total_| & idex & | = doc.qty_| & idex & |(0) * doc.price_| & idex & |(0)|) End Sub
calcTotal() sub can be used to set a relevant total field to the product of its quantity and price. If the parameter passed in is
1, the string that is executed is calculated as
doc.total_1 = doc.qty_1 * doc.price_1. If
2 is passed in, the string that is executed is
doc.total_2 = doc.qty_2 * doc.price_2. Because
doc is a global variable, defined in the Declarations of an Agent or Script Library, the Execute statement has access to it.
Of course with the NotesDocument class, there are other ways of doing this. But with custom classes, especially if the class has not been written yet, that can’t be guaranteed. Imagine you want generic code to set variables in a class. Now we can do this:
Private obj as Variant Sub setVariable(varName as String, var as Variant) Execute(|obj.| & varName & | = | & var) End Sub
There are a wide variety of use cases here for dynamic processing of an object. But there are three points to bear in mind:
varhere can only be a scalar or array. If it’s an object, you need to pass it to a global variable and pass that to the
Executestatement, as is being done with
varis a String, then the
Executeline isn’t actually setting the variable as a string - it’s setting it to a variable with the string name. You’ll realise if you transpose the variables into the statement, because if
varNameif “foo” and
varis “bar”, what we get us
obj.foo = bar. What we need is
obj.foo = "bar". So the execute statement needs to be
Execute(|obj.| & varName & | = "| & var & |"|).
varis an array, for example a String array, the variable cannot be declared as
Public foo() as String. That’s because you cannot use assignment to load an array declared like that, you would need to redim the variable (
foo) to the same bounds as the source array (
var), iterate the source array, and assign each element in turn. The cleaner way is for the variable to be declared as a Variant -
Public foo as Variant.
The final point to bear in mind is returning values. As with normal code, if you’re returning an object you need to use
Set, if you’re returning a scalar or array, you need to not use
Set. This can create some more complex code if you’re creating something generic. Once you have the return value,
IsArray() will tell you if it’s an array of something.
IsObject() to know whether or not something you’re dealing with is an object or, against an element in an array, whether it’s an array of objects - and so you need to use
IsScalar() can be used to do the opposite.
There are a number of scenarios where you might want to run custom code, and a variety of places those custom code strings may reside. But
Execute provides a great deal of power.