Practical use of XECUTE (InterSystems ObjectScript)
If you start with InterSystems ObjectScript, you will meet the XECUTE command.
And beginners may ask: Where and Why may I need to use this ?
The official documentation has a rich collection of code snippets. No practical case.
Just recently, I met a use case that I'd like to share with you.
The scenario:
When you build an IRIS container with Docker, then, in most cases,
you run the initialization script
iris session iris < iris.script
This means you open a terminal session and feed your input line-by-line from the script.
And that's fine and easy if you call methods, or functions, or commands.
But looping over several lines is not possible.
You may argue that running a FOR loop in a line is not a masterpiece.
Right, but the lines are not endless and the code should remain maintainable.
A different goal was to leave no code traces behind after setup.
So iris.script was the location to apply it.
The solution
XECUTE allowed me to cascade my multi-line code.
To avoid conflicts with variable scoping, I just used %Variables
BTW: The goal was to populate some demo LookupTables.
Just for comfort, I used method names from %PopulateUtils as table names
;; generate some demo lookup tables
; inner loop by table
set %1="for j=1:1:5+$random(10) XECUTE %2,%3,%4"
; populate with random values
set %2="set %key=##class(%PopulateUtils).LastName()"
set %3="set %val=$ClassMethod(""%PopulateUtils"",%tab)"
; write the table
set %4="set ^Ens.LookupTable(%tab,%key)=%val"
set %5="set ^Ens.LookupTable(%tab)=$lb($h) write !,""LookupTable "",%tab"
; main loop
XECUTE "for %tab=""FirstName"",""City"",""Company"",""Street"",""SSN"" XECUTE %1,%5"
;; just in DockerThe result satisfied the requirements without leaving permanent traces behind
And it did not interfere with the code deposited in IPM.
So it was only used once by Docker container build.
Comments
Many years ago I used Xecute in some very specific situations that, I confess, I don't even remember very well anymore; however, I remember very well that debugging legacy code is always made more difficult with the use of Xecute, so I have always been an advocate against using it, except in situations of extreme necessity.
But I recognize that in your example it really seemed quite useful.
Howdy, Robert!
I'm another Rob(ert) and I've been a longtime reader (and fan) of your posts here in the developer community. Thank you for elucidating this use case of XECUTE, I have not considered using this command in a here-document before and I admire your approach! I would also like to share some of the workarounds that I've used in the past to approach similar problems:
Escape newline characters in your here-document:
var="InterSystems"
irissession MYIRIS -U%SYS<<EOF
FOR i=1:1:10 {\
W i,!\
}
W !,"Howdy, $var!",!
H
EOFSplit your string and join it on a space (easiest to use in perl and python IMHO):
import subprocess
# Let's interpolate a variable
var = "InterSystems"
# Build iris_cmd
iris_cmd = f"""
FOR i=1:1:10 {{
W i,!
}}
W !,"Howdy, {var}!",!
H
"""
# write one-big line
iris_cmd = ' '.join(iris_cmd.split()) +"\n"
# Run commands in an irissession
proc = subprocess.run(
['irissession', 'IRISHEALTH', '-U', '%SYS'],
input=iris_cmd,
check=True,
capture_output=True,
text=True
)
for line in proc.stdout.splitlines():
print(line)
Have your here-document write a routine and execute it:
cls="Config.Databases"
mthd="ListFunc"
irissession MYIRIS -U%SYS<<EOF
ZI "zExecute(cls,mthd);"
ZI " S rset=\$CLASSMETHOD(cls,mthd)"
ZI " WHILE (rset.%Next()) {"
ZI " W rset.Name,?20,rset.Directory,!"
ZI " }"
D zExecute("$cls","$mthd")
HThank you, Rob for your example. (Somewhat late as I was offline this week)!
I like the idea to use a series of ZI with no ZSAVE is great.
Similar to run some external code using $CLASSMETHOD()
a perfect solution