Julius Kavay · Feb 4, 2025 go to post

With the help of a standard editor only - that will be a difficult and cumbersome task, for example, in the below snippet try to find the "^Test" global (not the routine ^Test):

 set value = ^Test  // take a value from the global ^Test
 do ^Test           // call the routine ^Test
 
 // a more complex case
 //
 set ref = "^Test"      // in this case you are lucky
 set ref = "^" _ "Test" // this could be a stumbling stone
 // few lines later
 set value = @ref  // get the value of ^Test global
 do @ref           // call the routine ^Test

You will need some kind of an "intelligent" editor which can make a difference between a call (do) like operation and a "get-value" like operation. The one I know of (but I have never used it, so I don't know how good it works) is the RE/parser of GJS. Asking Google is a good starting point.

Julius Kavay · Jan 19, 2025 go to post

Some comments (and please excuse the rude comments) before my answer
1) in general, it's never a good idea, to enforce a "new coding standard" for an old code. This is especially valid for old MUMPS and old ObjectScript code - except, you redesign or at least rework, the entire codebase.
2) use the Codesnippet button to write a more readable code (it's not necessary, but it helps us)
3) include only relevant parts/lines of code, in the above code, all that ^OLIVERs are completely irrelevant and makes the reading and understanding of an foreigen code more difficult, with other words, help us to help you!

And now to your problem. The old code is written in a mode, which we call "non procedural" code, i.e. all variables are public except the NEWed (which are invisible). The original code changes or kills at least the OLDIO543 variable, somewhere in the called part (do @CALL), and this fact is considered with the

 N TMPFILE,OLDIO
 S OLDIO=$IO
 I $G(^TMP("RMPV","SILENT"),1) S IOP="NULL",%ZIS=0 D ^%ZIS I '$G(POP,1) U IO
 D INIT^@DRVNAME
 D  ; scope variables
 .N (DUZ,CALL) ; Protect %response  // <-- save all except DUZ and call
 .S IOF="""""",IOM=80,U="^"
 .D @CALL
 U OLDIO

To move all commands from argumentless DO into a classmethod is OK, but you can't change a "non procedural" flow to a "procedural"!
So the solution is, you leave the old NEW as is and define your class method as:

ClassMethod doCall() [ ProcedureBlock=0 ]
{
 ...
}

If you want to keep your procedural code then you have to rework the code, which is called by "do @CALL" too and probably the code which is called from inside of "DO @CALL". Good luck!

Julius Kavay · Jan 17, 2025 go to post

This is the rare case where the statement “RTFM” leads you down the wrong path or, better said, reading the documentation confuses you more than informs.

So you have to change your viewpoint. And that depends on, what do you do.
If you work with ObjectScript (and of course, with objects) then your first citation "Case-sensitive: variable names..." holds.

But if you are more that SQL guy, who day in, day out busy with Updates, Inserts and Selects, then you will say, "No, no, my table and field names aren't case sensitive".

And now we sit in the middle of those two worlds, where a case sensitive Objectscript meets a case insensitive SQL world.

So what is the status quo?

All the (persistent) classes can be seen (and used) as SQL tables, but this freedom has its price (or, better, its consequence): you must be aware, that the two worlds (ObjectScript and SQL) do not use the same case-rule.

An ObjecScript class Test.Person can be named in SQL as Test.Person, as TEST.Person, as TEST.PERSON or even as TeSt.PeRsOn.

To de-escalate the resulting confusion, the following rule was added:

It's forbidden to have two ObjectScript classes, which have the same spelling but (somewhere) a different case (the Studio wizard checks this). I hope, this helps a bit...

Julius Kavay · Jan 15, 2025 go to post

and, by the way, it's chainable

USER>zw [1,2,3].addAll([4,5,6]).addAll([7,8,9])
[1,2,3,4,5,6,7,8,9]  ; <DYNAMIC ARRAY>
Julius Kavay · Dec 6, 2024 go to post

instead of "*.int" you could try to compile in parts, i.e."a*.int", "b*.int", ... "z*.int"

Julius Kavay · Dec 4, 2024 go to post
set sts = ##class(%Routine).CompileList("*.int")
write $system.OBJ.DisplayError(sts)

should do the trick

Julius Kavay · Dec 3, 2024 go to post
Class DC.Bundle Extends (%RegisteredObject, %XML.Adaptor)
{
Property Entries As list Of Entry(XMLNAME = "Entry", XMLPROJECTION = "ELEMENT");

ClassMethod Test()
{
	s bnd=..%New()
	s ent=##class(Entry).%New(),ent.Name="John",ent.Age=40 d bnd.Entries.Insert(ent) k ent
	s ent=##class(Entry).%New(),ent.Name="Paul",ent.Age=45 d bnd.Entries.Insert(ent) k ent
	
	d bnd.XMLExportToString(.xx) q xx
}
}

Class DC.Entry Extends (%RegisteredObject, %XML.Adaptor)
{
Property Name As %String;
Property Age As %Integer;
}

The test output is:


USER>set xml=##class(DC.Bundle).Test()

USER>write xml
<Bundle><Entry><Name>John</Name><Age>40</Age></Entry><Entry><Name>Paul</Name><Age>45</Age></Entry></Bundle>
USER>

USER>zzxs xml
<Bundle>
    <Entry>
        <Name>John</Name>
        <Age>40</Age>
    </Entry>
    <Entry>
        <Name>Paul</Name>
        <Age>45</Age>
    </Entry>
</Bundle>

USER>
Julius Kavay · Nov 6, 2024 go to post

First, my experience with tail recursion is very limited,
second, after adding the TailFact() method and a little change to Times() method in the above class, I must say, I don't see any benefit of the TailFact() method. Quite the contrary, now the function have to push two values on the stack instead of one...

/// Factorial using tail_recursion
/// 
ClassMethod TailFact(n)
{
	quit ..tfact(n,1)
}

ClassMethod tfact(n, f)
{
	quit $select(n>1:..tfact(n-1,f*n),1:f)
}

/// There is a noticeable time difference between MulFact() and RecFact().
/// 
/// Recursion helps to keep an algorithm clear an simple but needs more
/// resources - for a few loops it's OK, but for many loops it's a no-go
/// (beside a stack space, a stack frame must be prepared for each loop cycle)
ClassMethod Times(n)
{
	while $zh#1 {} s t1=$zh f i=1:1:1E6 { d ..MulFact(n) } s t1=$zh-t1
	while $zh#1 {} s t2=$zh f i=1:1:1E6 { d ..RecFact(n) } s t2=$zh-t2
	while $zh#1 {} s t3=$zh f i=1:1:1E6 { d ..TailFact(n) } s t3=$zh-t3
	write " Fact:",$j(t1,10,4),!,"Rfact:",$j(t2,10,4),!,"Tfact:",$j(t3,10,4),!
	write "RDiff:",$j(t2-t1/t1*100,8,2),"%",!,"TDiff:",$j(t3-t1/t1*100,8,2),!
}

and here some time values

USER>d ##class(DC.Math).Times(5)
 Fact:    0.3306
Rfact:    1.2199
Tfact:    1.4219
RDiff:  268.97%
TDiff:  330.07

USER>d ##class(DC.Math).Times(10)
 Fact:    0.4433
Rfact:    2.3913
Tfact:    2.5549
RDiff:  439.40%
TDiff:  476.28

USER>d ##class(DC.Math).Times(20)
 Fact:    0.7034
Rfact:    4.8017
Tfact:    5.2183
RDiff:  582.63%
TDiff:  641.86
Julius Kavay · Nov 5, 2024 go to post

If I understand you correctly, you want for a given number of objects all the possible combinations? That is, if we have 3 objects (3 numbers, 3 words or whatever) then you want:
1 of 3 (3 possibilities): (1) (2) (3)
2 of 3 (3 possibilities): (1,2) (1,3) (2,3)
3 of 3 (1 possibility)   : (1,2,3)
For 3 objects there are in sum 7 possible combinations (see above). Your ^x(1,1) string contains 137 items. I'm pretty sure, you will never see that mutch combination...

Here some numbers for all possible combinations for:
  1 item :                                                       1
 10 items:                                                   1,023
 20 items:                                               1,048,575
 50 items:                                   1,125,899,906,842,623
 75 items:                          37,778,931,862,957,161,730,000
100 items:               1,267,650,600,228,229,401,000,000,000,000
137 items: 174,224,571,863,520,493,300,000,000,000,000,000,000,000

A year has 365.25*86400, that are 31,557,600 seconds.  With another words, you would need a computer system, which is able to genarate 5.5 * 10^33 combinations each second! I don't have such a beast, and I'm sure, you too.

Anyway, maybe the below class gives you some clue, how to compute and how to generate combinations. One more note, recursion is the tool to make (some) solutions clear and simple but recursion in loops, especially in loops with thousands or millions of passes, isn't a good idea. Beside of resources (stack, memory) for each new call a stackframe must be prepared and after the call removed again. This costs time, much time. Try that Times() method in the below class.

/// Some math functions
Class DC.Math Extends %RegisteredObject
{

/// Factorial by multiplication (the more practical way)
/// Return n!
/// 
ClassMethod MulFact(n)
{
	for i=2:1:n-1 set n=n*i
	quit n+'n
}

/// Factorial by recursion (the way, people learn recursion in the school)
/// Return n!
/// 
ClassMethod RecFact(n)
{
	quit $select(n>1:n*..RecFact(n-1),1:1)
}

/// There is a noticeable time difference between MulFact() and RecFact().
/// 
/// Recursion helps to keep an algorithm clear an simple but needs more
/// resources - for a few loops it's OK, but for many loops it's a no-go
/// (beside a stack space, a stack frame must be prepared for each loop cycle)
ClassMethod Times(n)
{
	while $zh#1 {} s t1=$zh f i=1:1:1E6 { d ..MulFact(n) } s t1=$zh-t1
	while $zh#1 {} s t2=$zh f i=1:1:1E6 { d ..RecFact(n) } s t2=$zh-t2
	write " Fact:",$j(t1,10,4),!,"Rfact:",$j(t2,10,4),!," Diff:",$j(t2-t1/t1*100,8,2),"%",!
}

/// The Holy Trinity of the statistic functions: permutations, combinations and variations
/// 
/// Here we go with the combinations.
/// 
/// Return the number of combinations (without repetition) of K elements from a set of N objects
/// 
/// The number of possible combinations is: N over K
/// which is the same as:  N! / (K! * (N-K)!)
/// 
/// Note:
/// one can't make a direct use of the (above) Factorial function
/// because the MAXNUM limit will be hit very soon
ClassMethod Comb(k, n)
{
	set c=1 for k=1:1:k { set c=c/k*n, n=n-1 } quit c
}

/// Return the sum of all possible combinations of N objects
/// i.e. (1-of-n) + (2-of-n) + (3-of-n) + ... + (n-of-n)
/// 
ClassMethod AllComb(n)
{
	set s=0 for i=1:1:n { set s=s+..Comb(i,n) } quit s
}

/// Generate a list of combinations for K elements of N objects
/// 
ClassMethod GenComb(k, n)
{
	for i=1:1:k set e(i)=n-k+i
	set s=0, c(0)=0
	
10	set s=s+1, c(s)=c(s-1)
14	for c(s)=c(s)+1:1:e(s) goto 10:s<k do ..work1(.c,s)
16	set s=s-1 if s goto 14:c(s)<e(s),16
}

/// This method is the workhorse
/// either, print the combinations (one k-tuple at time)
/// or do your own task.
ClassMethod work1(c, s) [ Internal ]
{
	write "(" for i=1:1:s write:i>1 "," write c(i)
	write ")",!
}

/// For example, you could use that ^x(1,1) string
/// 
ClassMethod work2(c, s)
{
	set t=^x(1,1)
	write "(" f i=1:1:s write:i>1 "," write $p(t,",",c(i))
	write ")",!
}

/// save each combination (OK, c(0) should be killed)
/// 
ClassMethod work3(c, s)
{
	m ^mtemp("comb",$i(^mtemp("comb")))=c
}

}

write ##class(...).Comb(3,4) to compute the combinations 3-of-4 (3 items of 4 objects)
write ##class(...).AllComb(3) to compute all combinations of 3 objects (1-of-3 plus  2-of-3 plus 3-of-3)
do ##class(...).GenComb(3,4) to show all 3-of-4 combinations
For all possible combinations of N objects you must do a loop:
for i=1:1:N do ##class(...).GenComb(i,N)

Julius Kavay · Oct 11, 2024 go to post

Just as an info, I can read cyrillic, I can talk serbian, consequently I understand a little bit (the emphasis is on little) Russian, and Ukrainian but I can't make the difference between the two. By the way, we try to solve problems and not language differences... 😉

Julius Kavay · Oct 11, 2024 go to post

Your screenshot says (last line,  "ДОСТУП ..." ), access denied.  So please check your connection details (hostname, port) and check the Windows firewall too. I assume, you want to connect to port 1972 or 5x77x, those ports are not open by default. For the next time, please translate that error messages, not everybody is fluent in russian.

Julius Kavay · Oct 7, 2024 go to post

I don't see any difference between SolutinA and SolutionB.

First, $$$OK is converted at COMPILE time into 1 and second, your test code  neither has an execute nor an indirection - or do I miss something? But you can try the below code for timing difference

ClassMethod XTime()
{
	f i=1:1:3 d ..exc()
	w !
	f i=1:1:3 d ..ind()
}

ClassMethod exc()
{
	s arg="s x=1234_(1/2)"
	while $zh["." {} s t=$zh f i=1:1:1E6 { x arg } w $zh-t,!
}

ClassMethod ind()
{
	s arg="x=1234_(1/2)"
	while $zh["." {} s t=$zh f i=1:1:1E6 { s @arg } w $zh-t,!
}
Julius Kavay · Oct 6, 2024 go to post

Indirection has its rule:  Indirection works with PUBLIC variables,
i.e. the variable, you address, mustbe a public variable, in your case the <arg> variable.
This is due to compatibility with old applications,
developed before the introduction of the block structure.
  
You have two options

instead of using argument indirection (what you currently do),
use name indirection for label and routinname, see method Test1()

@lab and @(rou)  because label- and routine-names are not variables.

If you want to keep argument indirection, just tell your class, that certain variables were PUBLIC
see method Test2()

In your example, you got a wrong result because, by chance the variable <arg> was defined in the terminal session with the same value as in methode code, see method Test3()


ClassMethod Test1()
{
    s arg="argument-1"
    s lab="say", rou="hello"
    d @lab^@(rou)(arg)
}

ClassMethod Test2() [ PublicList = arg ]
{
	s arg = "argument-2"
	s routine = "say^hello(arg)"
	d @routine
}

ClassMethod Test3()
{
	s routine = "say^hello(arg)"
	d @routine
}

Now some tests in a terminal

kill   // we kill all local variables
do ##class(your.class).Test1() ---> argument-1

kill
do ##class(your.class).Test2() ---> argument-2

kill
do ##class(your.class).Test3() ---> <UNDEF> *arg ==> missing arg

kill
set arg="surprise"
do ##class(your.class).Test3() ---> surprise

// you can prove things the other way too

set arg="my-value"  // variables, defined in a terminal session are public
do ##class(your.class).Test1() ---> argument-1
write arg --> my-value  // arg wasn't overwritten!

do ##class(your.class).Test2() ---> argument-2
write arg --> argument-2  // arg IS OVERWRITTEN

Method Test3() shows, indirection works with public variables

Julius Kavay · Oct 1, 2024 go to post

Just in case anyone is upset about "command indirection". Yes, I know the correct term is "argument indirection". It's one of those old habits that never dies...

Julius Kavay · Oct 1, 2024 go to post

I assume, you have a routine 'hello' like this

hello ; this is my hello-test
	 quit
	 
say(arg)
     write arg,!
     quit
     
add(x,y) Public
{
  quit x + y
}

and some variable, set as follows

set rou="hello"
set say="say", add="add"
set a1=5,a2=10, arg="Hello World"
set sayall="say^hello(arg)"
set addall="$$add^hello(a1,a2)"

then you can do things like

do @sayall            ---> Hello World   // command indirection
do @say^@(rou)(arg)   ---> Hello World   // name-indirection
do @say^hello(arg)    ---> Hello World   // name-indirection
do say^@(rou)(arg)    ---> Hello World   // name-indirection
do say^hello(arg)     ---> Hello World

write @addall               ---> 15      // command indirection
write $$@add^@(rou)(a1,a2)  ---> 15      // name-indirection
write $$@add^hello(a1,a2)   ---> 15      // name-indirection
write $$add^@(rou)(a1,a2)   ---> 15      // name-indirection
write $$add^hello(a1,a2)    ---> 15

// Caution, you can't do
write @addall + 3  // with "+ 3", the write command is turned into an
                   // expression, and in turn, the indirection is now
                   // a name-indirection. That gives you a SYNTAX error
                   
// but you can do
write $$@add^@(rou)(a1,a2) + 3 --> 18

Julius Kavay · Sep 23, 2024 go to post

See the example class below 

Class DC.Encoding Extends %RegisteredObject
{

/// Take an raw stream (i.e. unencoded) and
/// output a new, Base64 encoded stream.
/// 
ClassMethod ToBase64(str)
{
	// Base64 encoding means:
	// you take 3*N characters from the source
	// and put  4*N characters into the destination.
	// If the size of the source is not a multiple of 3 then
	// the last one or two bytes will be padded.
	// 
	// If you take an N such that 4*N less or equal 32767
	// (the max size of a short string) then Cache or IRIS
	// can work with short strings, which perform (usually)
	// better than long strings
	// 
	// N is integer.
	// 
	// A good value for N is 8190,
	// so you read 24570 bytes from the source and write 32760 to the destination
	// 
	// Of course, you can take whatever number up to  910286
	// (3 * 910286 = 2730858,  4 * 910286 = 3641144)
	// 
	set len=8190*3
	set flg=1 // this flag instructs $system.Encryption.Base64Encode
			// not to insert linebreaks at every 76 characters
	set new=##class(%Stream.GlobalCharacter).%New()
	do str.Rewind()
	while 'str.AtEnd {
		do new.Write($system.Encryption.Base64Encode(str.Read(len),flg))
	}
	quit new
}

/// Take a Base64 encoded stream
/// and decode it to a new stream
/// 
/// The method itself has no information about the decoded data
/// hence it assumens binary data, but you, the caller (hopefully)
/// knows more about your data and can provide the correct stream
/// type for the decoder.
/// For exaple a character stream instead of binary.
ClassMethod FromBase64(str, new = 0)
{
	// Base64 decoding means:
	// you take 4*N characters from the source
	// and put  3*N characters into the destination
	// 
	// If you take an N such that 4*N less or equal 32767
	// (the max size of a short string) then Cache or IRIS
	// can work with short strings, which perform (usually)
	// better than long strings
	// 
	// N is integer.
	// 
	// A good value for N is 8190,
	// so you read 24570 bytes from the source and write 32760 to the destination
	// 
	// Of course, you can take whatever number up to  910286
	// (3 * 910286 = 2730858,  4 * 910286 = 3641144)
	// 
	
	set len=8190*4
	set:'new new=##class(%Stream.GlobalBinary).%New()
	do str.Rewind()
	while 'str.AtEnd {
		do new.Write($system.Encryption.Base64Decode(str.Read(len)))
	}
	quit new
}

ClassMethod Test(file)
{
	set str=##class(%Stream.FileBinary).%New()
	do str.LinkToFile(file)
	write str.Size,!
	
	set enc=..ToBase64(str)
	write enc.Size,!
	
	set dec=..FromBase64(enc)
	write dec.Size,!
}

}
Julius Kavay · Sep 22, 2024 go to post

In case, you talk about Cache/IRIS-Classes:

Class Example.Test Extends %Persistent
{
Property BodyText As list Of MyList;
}


Class Example.MyList Extends %SerialObject
{
Property Text As list Of %String;
}

The steps to add data:

set test=##class(Example.Test).%New()

set list1=##class(Example.MyList).%New()
do list1.Text.Insert("red")
do list1.Text.Insert("green")
do list1.Text.Insert("blue")
do test.BodyText.Insert(list1)

set list2=##class(Example.MyList).%New()
do list2.Text.Insert("Joe")
do list2.Text.Insert("Paul")
do list2.Text.Insert("Bob")
do test.BodyText.Insert(list2)

write test.%Save() --> 1

zw ^Example.TestD
^Example.TestD=1
^Example.TestD(1)=$lb("",$lb($lb($lb($lb("red","green","blue"))),$lb($lb($lb("Joe","Paul","Bob")))))


zso test
BodyText(1).Text(1).: red
BodyText(1).Text(2).: green
BodyText(1).Text(3).: blue
BodyText(2).Text(1).: Joe
BodyText(2).Text(2).: Paul
BodyText(2).Text(3).: Bob

Julius Kavay · Sep 16, 2024 go to post

Assuming, your input value is an integer, you have , along with the other solutions, one more:

// this works as long as len  < 145
//
set len =  120
set inp = 12345
write $e(1E120_inp,*-len+1,*)

// of course, if the len is shorter than, say 10,
// then you can use smaller constans like
//
set len=10
set inp=9
write $e(1E10_inp,*-len+1,*)

A good (or even a bad) side effect of the above solution is, if you get an input value which is LONGER than the length, it will be truncated to the given length

Julius Kavay · Sep 3, 2024 go to post

By the way, if you need again and again a local timestamp with decimals, just create a user defined system variable. Make a one line entry into the %ZLANGV00.mac routine:

%ZLANGV00 ; User defined (system) variables

	// Local timestamp with decimals
ZLTS()	quit $now($ztz-$s($SYSTEM.Util.IsDST():60,1:0))

You can use whatever name you want as long as it starts with Z, contains uppercase chars only and do not conflict with existing names.  Use it as a standard $-variable

write $zlts, $zdt($zlts,3,1,3)
67086,85681.092746
2024-09-03 23:48:01.092
Julius Kavay · Sep 3, 2024 go to post

Together with $now() and timezone adjustment you can have the desired result

for time=$h, $now($ztz-$s($SYSTEM.Util.IsDST():60,1:0)) write time,?20,$zdt(time,3,1,3),!
// assuming 60 min summertime offset
//
// you should get an output like thisL
67086,83334         2024-09-03 23:08:54.000
67086,83334.1341026 2024-09-03 23:08:54.134

There is an (old) undocumented function which gives the $h value with several  decimal places, unfortunately the recommended replacement is more or less the above solution instead of a simple $zlts (z-local-timestamp).

Julius Kavay · Aug 28, 2024 go to post

This is possible, but you have to use it the correct way. $SYSTEM.OBJ is just a shorthand for ##class(%SYSTEM.OBJECT), so the correct syntax for your desire is:

job ##class(%SYSTEM.OBJ).Export("yourRoutine.mac","yourPathAndFilename.xml")
Julius Kavay · Aug 22, 2024 go to post

in a routine or class, the line

ClassMethod ALine()
{
    quit $st($st,"MCODE")
}

gives you the line quit $st($st,"MCODE")

The systemvariable $zname gives you the name of the routine, where you reference $zname and in a classmethod $this gives you the name of the class

Julius Kavay · Aug 13, 2024 go to post

We need the source code, so the compiler flag for keeping the source must be on. The 38 char version does the job

ClassMethod ascii() [ ProcedureBlock = 0 ]
{
x	n i f i=32:1:126 w:$t(x)'[$c(i) *i
}

The 34-character version does the job too  has a side effect (leaving the variable i with the last value)

ClassMethod ascii() [ ProcedureBlock = 0 ]
{
y	f i=32:1:126 w:$t(y)'[$c(i) *i
}
Julius Kavay · Aug 9, 2024 go to post

It shouldn't be invalid because there are no corresponding constraints.

At the beginning, I thought not to participate, because of the problematic specification and example, but now, as I see, I'm not the only one with questions without answers, hence I offer an 38 char solution too (including the hint to compiler flags) and a shorter version with 34 chars, a correkt result but with "a little bit" of side effect.

Julius Kavay · Aug 9, 2024 go to post

The problem is, the specification for this task is simple unprecise, and according to my opinion, gives a faulty example. Your exmple code has just a Set command but the task talks about "print out" - hence, I expected to see either a Write "whatever" or at last a Quit "whatever" comand.
Also, if we talk about a method signature, I take in account the number of arguments (maybe their types) only and the return type but never their method keywords, as in the solution from Eduard, hence his solution is not only creative but valid too.
I think, a fair way to mesure the size of a solution is, if you take the size of the routine which will be executed, and that is the INT routine, which is directly (we neglect the possible compiler optimizations, that's the compilers and not the users credit) compiled into a executable. How I got that code (some generator or via a macro or whatever other method) is actually irrelevant.
A very good example for using or not using a method keyword is the "codemode=expression":

/// you save that "Quit " but have to write "codemode=expression"
/// which is not taken in account by the Implementation.Size
ClassMethod Test() [ codemode = expression]
{
 123
}

/// you have to write "Quit " and save the writing of "codemode..."
/// The Implementation.Size counts that "quit "
ClassMethod Test2()
{
 quit 123
}

Whatever you choose, the corresponding INT code is always "quit ..."

So the bottom line is, either you should define all constraints and conditions or accept each end every clever solution.

Julius Kavay · Aug 7, 2024 go to post
ClassMethod TimeDiff(inpTime = "2024-08-07 17:58:51.563")
{
	set current=$h // or $now(tz-offset)
	set inpTime=$zdth(inpTime,3)
	quit current-inpTime*86400+$p(current,",",2)-$p(inpTime,",",2)
}
Julius Kavay · Aug 5, 2024 go to post

First convert the dynamic array to a Cache List and then the Cache List to Python List - voila the job is done

/// Convert a dynamic array to a Cache List
/// 
ClassMethod ArrayToList(arr)
{
	q:'arr.%Size() $lb()
	s list="", it=arr.%GetIterator()
	while it,it.%GetNext(.key,.val) {
		s typ=arr.%GetTypeOf(key)
		s list=list_$case(typ,"object":$lb(val.%ToJSON()),"array":$lb(..ArrayToList(val)),"null":$lb(),:$lb(val))
	}
	q list
}
Julius Kavay · Aug 2, 2024 go to post

First, I presume, the Studio lacks such a functionality because usually each nsp contains independent data. As an example, for each of my customers (applications) I have an dedicated namespace  (of course, you may say, one can allways have an exeption),

and second, if there is no readymade functionality, then make your own. Sometimes it takes longer asking questions or searching the internet then writing a quick-and-dirty "one liner", something like this:

// classdefinitions are stored in ^oddDEF, mac-routines in ^rMAC
// as said above, quick-and-dirty:
// if the SEARCHTERM occurs in %-items, then you will get multiple hits
//
// the one-liner version
k n i ##class(%SYS.Namespace).ListAll(.n) s n="" f  s n=$o(n(n)) q:n=""  f s="^|n|rMAC","^|n|oddDEF" f  s s=$q(@s) q:s=""  w:@s_s["SEARCHTERM" s," ",@s,!

// for folks with less experience
//
ClassMethod SearchAllNSP(searchterm)
{
   i ##class(%SYS.Namespace).ListAll(.n) {
      s n=""
      f  {s n=$o(n(n)) q:n=""
          f s="^|n|rMAC","^|n|oddDEF" {
             f  s s=$q(@s) q:s=""  w:@s_s[searchterm s," ",@s,!
          }
      }
   }
}

It's up to you to left out all those multiple %-items and to add some formatting...

So the bottom line of my answer is: yes, there is a way to search (whatever you want) in one go