Julius Kavay · May 15, 2023 go to post
Class DC.Samples Extends %RegisteredObject
{

/// partition an array into two subarrays
/// return [[even], [odd]]
ClassMethod Task1a(x As %DynamicArray) As %DynamicArray
{
	set t(0)=[],t(1)=[]
	for i=0:1:x.%Size()-1 do t(x.%Get(i)#2).%Push(x.%Get(i))
	quit [(t(0)),(t(1))]
}

/// partition an array into two subarrays
/// return [[even], [odd]]
ClassMethod Task1b(x As %DynamicArray) As %DynamicArray
{
	set t(0)=[], t(1)=[], i=x.%GetIterator()
	while i.%GetNext(,.v) {  do t(v#2).%Push(v) }
	quit [(t(0)),(t(1))]
}

/// hamming distance of two strings
ClassMethod Task2(x As %String, y As %String) As %Integer
{
	if $l(x)-$l(y) quit "<Error>"	// strings have to be the same length
	set r=0
	for i=1:1:$l(x) set r=$e(x,i)'=$e(y,i)+r
	quit r
}

/// encrypt an string
ClassMethod Task3(x As %String) As %String
{
	quit $tr($re(x),"aeiou","01223")_"aca"
}

/// check a string for identical chars
ClassMethod Task4(x As %String) As %Boolean
{
	quit $tr(x,$e(x))=""
}

/// double chars
ClassMethod Task5(x As %String) As %String
{
	f i=$l(x):-1:1 s $e(x,i)=$e(x,i)_$e(x,i)
	q x
}

}
Julius Kavay · May 4, 2023 go to post

Suppose, you have a class my.test with two methods: check1 and check2 then after running the above methode:

do ##class(your.class).Transfer("my.test", "check1,check2")

creates two new classes

my.test.parts.check1 and

my.test.parts.check.2

so what do you mean with "give the destination package a different name, maybe the original class name" ?

Julius Kavay · May 3, 2023 go to post

For a handful of methods the manual method is likely the fastest... in case, you have a real big bunch of methods to copy put this "short" method into a class and let it run...

/// Transfer all or selected methods from a class into
/// individual classes
/// cls : the donor class
/// list: list of methods to be transfered: "method1,method2,..."
///       or empty to transfer all methods
ClassMethod Transfer(cls, list = "")
{
    s old=##class(%Dictionary.ClassDefinition).%OpenId(cls)
    s:list]"" list=$lfs(list)
    
    i old {
        f i=old.Methods.Count():-1:1 {
            s met=old.Methods.GetAt(i)				// grab the next method
            i list]"",'$lf(list,met.Name) continue	// skip if not to copy
            
            s new=old.%ConstructClone()				// duplicate old class
            s tmp=old.Name							// grab the old classname
            s $p(tmp,".",*)="Parts."_met.Name		// create a new classname
            s new.Name=tmp
            s new.Abstract=1						// make the class abstract
            s new.Super=""							// remove all superclasses
            
            s dup=met.%ConstructClone()				// duplicate the old method
            s dup.Name=met.Name						// but keep the old name
            d new.Properties.Clear()					// remove all properties
            d new.Parameters.Clear()				// remove all parameters			
            d new.Methods.Clear()					// remove all methods
            d new.Methods.Insert(dup)				// insert the copied method only
            d old.Methods.RemoveAt(i)				// Remove this method from old class
            i old.Super="" { s d="" } else { s d="," }
            s old.Super=old.Super_d_new.Name		// add the new class to extends-list
            s st=new.%Save()						// save the new class
            
            w new.Name,"-->",$s(st:"OK",1:$system.Status.GetOneErrorText(st)),!
            // Possibly compile the class: do $system.OBJ.Compile(...)
            
        }
        s st=old.%Save()							// save the old class
        w old.Name,"-->",$s(st:"OK",1:$system.Status.GetOneErrorText(st)),!
        // Possibly compile...
    }
Julius Kavay · Apr 27, 2023 go to post

COS does not have a pointer operator so instead of "unsigned *rx and *tx" (those are char arrays) in COS you can use string variables. By the way, C counts from 0 to N-1, COS counts from 1 to N, where N ist the size of the character array respectively the length of the string.

Class DC.CtoCOS [ Abstract ]
{

/// Encode a whole string (and return a string)
/// Use the below $ziswide(str) function to check for allowed characters
ClassMethod Encode(str As %String) As %String
{
	f i=$l(str):-1:1 s $e(str,i)=$c($a(str,i)#16+32, $a(str,i)\16+32)
	q str
}

/// Decode a whole string (and return a string)
/// Use the below $match(str,...) function to check for allowed characters
ClassMethod Decode(str As %String) As %String
{
	f i=$l(str):-2:1 s $e(str,i-1,i)=$c($a(str,i)-32*16+$a(str,i-1)-32)
	q str
}

/// Encode a characyter into a string (variable)
/// Use: do ##class(...).Encode1("A", .res)
///      write res --> !$
ClassMethod Encode1(chr As %String, chrs As %String)
{
	i $ziswide(chr) throw ##class(%Exception.General).%New("WIDE",,,chr)
	s chrs=$c($a(chr)#16+32, $a(chr)\16+32)
}

/// Encode an integer (byte value) and return a string
/// Use: write ##class(...).Encode2("A") --> !$
ClassMethod Encode2(val As %Integer) As %String
{
	i val>255 throw ##class(%Exception.General).%New("WIDE",,,val)
	q $c($a(val)#16+32, $a(val)\16+32)
}

/// Decode two characters and return one character
/// Use: write ##class(...).Decode1("!$") --> A
ClassMethod Decode1(chrs As %String) As %String
{
	i $match(chrs,"[ -/]{2}") q $c($a(chrs,2)-32*16+$a(chrs)-32)
	throw ##class(%Exception.General).%New("RANGE",,,chrs)
}

/// Decode two characters and return an integer
/// Use: write ##class(...).Decode2("!$") --> 65
ClassMethod Decode2(chrs As %String) As %Integer
{
	i $match(chrs,"[ -/]{2}") q $a(chrs,2)-32*16+$a(chrs)-32
	throw ##class(%Exception.General).%New("RANGE",,,chrs)
}

}
Julius Kavay · Feb 9, 2023 go to post

What does a nervous twitching guitarist has to do with this developer community? I thought, this is a community of professionals and not of script kiddies. It's sorry to say, but under such circumstances I do not want to participate here. Just to make it clear, I have nothing against a video which demonstrates a Cache or IRIS feature, or which is a recording of a webinar or something similar but it should have something in common with development, with the database or at least with the lanuages or tools we use.

Julius Kavay · Feb 4, 2023 go to post

Just open the class and see, what it does.
Because the class extends %Projection.AbstractProjection, you could use it in one of your own classes:

Class your.class Extends %Persistent
{
 Projection Test As %Projection.Monitor(MONENABLED=1);
}

But don't ask me, what the class does or should do...

Julius Kavay · Feb 3, 2023 go to post

Create an abstract class and add it to all your classes, where a list of properties (for whatever reason) is needed 

Class DC.ClassInfo [ Abstract ]
{

/// Return property info: %PropNames(all)
/// 
/// all: 1 = Return a list of all properties<br>
///      0 = Return a list of storable properties only
ClassMethod PropNames(all = 0) As %String [ CodeMode = objectgenerator ]
{
    s (prop(0),prop(1))=""
    f i=1:1:%compiledclass.Properties.Count() {
        s p=%compiledclass.Properties.GetAt(i), s=p.Storable
        s prop(s)=prop(s)_$e(",",prop(s)]"")_p.Name
    }
    d %code.WriteLine($c(9)_"s stor="""_prop(1)_"""")
    d %code.WriteLine($c(9)_"q $s(all:"""_prop(0)_$e(",",prop(0)]"")_"""_stor,1:stor)")
    q $$$OK
}

}

For example

Class My.Person Extends (%Persistent, DC.ClassInfo)
{
Property Name As %String;
Property Age As %Integer;
}

Putting all together

write ##class(My.Person).PropNames() --> Age,Name
write ##class(My.Person).PropNames(1) --> %%OID,%Concurrency,Age,Name

// That way, in your application, you can easily check,
// if a property exists or not
if $length(##class(My.Person).PropNames(), ",", "TestProp") -->0
if $length(##class(My.Person).PropNames(), ",", "Age") -->1
Julius Kavay · Jan 27, 2023 go to post

my short test now

USER>
USER>k ^kav
USER>s ^kav(1)="some", ^(2)="data..."
USER>ts  k ^kav(1) f  h 1
TS  K ^kav(1) F  H 1
                 ^
<INTERRUPT>
TL1:USER>tc

USER>zw ^kav
^kav(2)="data..."
USER>

shows something else... the deleted node is gone

Julius Kavay · Jan 27, 2023 go to post

If I interpret the situation correctly, you started a (direct mode) job in a terminal session and want to stop it without loosing (i.e. avoiding a rollback) the already deleted records. I think (but not tested) a simple Ctrl + C should be do the trick. After typing "Ctrl+C" you should see

<INTERRUPT>
TLn:yournamespace>

Now type TC+<Enter>. This commits the already deleted item and you can exit your terminam session with the usual Halt command.  As above said, not tested but maybe some of the Gurus here can agree (or disagree) with this procedure.

Julius Kavay · Jan 23, 2023 go to post

Maybe the next time we get a better description which includes all details (unhidden, if possible).
Bye the way, what's the very difference between the two example pyramides ("Output" and "also a valid output")? I don't see any visible difference - but who knows, maybe I need new glasses...

Julius Kavay · Jan 23, 2023 go to post
do res.HttpResponse.Data.Rewind()
set dynObj = {}.%FromJSON(res.HttpResponse.Data)
// now, you can use the JSON-Data
write dynObj.propName  // if it's a dynObject
write dynObj.%Get(0)   // if it's a dynArray
Julius Kavay · Jan 23, 2023 go to post

I'm sorry to say, but this kind of information is an essential part of the task, and as such should be written before the recurring "As usual, the shortest ..."

Also, it would be nice, if the output example would show this trivia (the invisible spaces)!

I just saw "input = 3" and the output showed no trailing blanks!

And I saw the Notice with two links
- how to compute the size
- test examples
I wasn't interested in opening them, first, I know how to compute code size and second, I make the examples myself.
Hiding information in a side notice is a questionable practice.

ClassMethod Pyramide(n)
{
	s a="#" f i=1:1:n w ?n-i,a,?2*n-1,! s a=a_"##"
}

For a test use

w $c(27)_"[7m" d ##class(your.class).Pyramide(5) w $c(27)_"[0m"

 and you should get   

Julius Kavay · Jan 20, 2023 go to post
ClassMethod Pyramide(n)
{
    s a="#" f i=1:1:n w ?n-i,a,! s a=a_"##"
}

Maybe there is a shorter solution, I don't know...

Julius Kavay · Jan 10, 2023 go to post

The bad news is,
you have to be careful when you adapt delimited list to $list(). A stubborn change from a delimited list to $list() can become dangerous. The reason:

set $piece(var,del,1) = value // piece 1 of var is ALWAYS a string
set $list(var,1)      = value // listitem 1 is either a number or a string

kill x set $piece(x,",",1) = 100+1 write $zhex($p(x,",")) --> 257
kill x set $list(x,1)      = 100+1 write $zhex($li(x,1))  --> 65

kill x set $piece(x,",",1) = 100 write $zhex($p(x,",")) --> 256
kill x set $list(x,1)      = 100 write $zhex($li(x,1))  --> 64

kill x set $piece(x,",",1) = "100" write $zhex($p(x,",")) --> 256
kill x set $list(x,1)      = "100" write $zhex($li(x,1))  --> 256

In other words, $list() retains the type of the expression whereas $piece() always converts it to a string.

The good news is,
there are just a few situations, where this could cause a problem, to tell the truth, in a nutshell, I can only think of two possibilities: $zhex() and $zboolean(). But who knows, what a mad programmer accomplish...

Julius Kavay · Dec 22, 2022 go to post

If you want to reorder JSON properties (alphabetically or just put some of them at the beginning) then use a utility method, like this, especially if you have several object(types) to reorder

Class DC.Utility Extends %RegisteredObject
{
/// Reorder a JSON Object or Array
/// 
/// obj:  JSON-Object
/// 	  ord: prop1, prop2, ...	Desired order for (some) properties
/// 	       (Properties not listed are copied in the order in which they were created)
/// 	       If ord not present, properties will be reordered in aplphabetical order
/// 	       
/// obj: JSON-Array
/// 	 ord: pos1, pos2, ...	Desired order for (some) array items
/// 	      (Items not listed are copied in ascending order)
/// 	      
ClassMethod ReOrder(obj As %DynamicAbstractObject, ord... As %String)
{
	i obj.%Extends("%DynamicObject") {
		s new={}, itr=obj.%GetIterator()
		i '$g(ord) {
			while itr.%GetNext(.k) { s done(k)=0 }
			s k="" f  s k=$o(done(k)) q:k=""  d new.%Set(k,obj.%Get(k))
		} else {
			f i=1:1:$g(ord) { s k=ord(i),done(k)=1 d:$e(obj.%GetTypeOf(k),1,2)'="un" new.%Set(k,obj.%Get(k)) }
			while itr.%GetNext(.k,.v) { d:'$d(done(k)) new.%Set(k,v) }
		}
		
	} elseif obj.%Extends("%DynamicArray") {
		s new=[], itr=obj.%GetIterator(), max=obj.%Size(), done=""
		f i=1:1:$g(ord) { s k=ord(i) i k,k<=max d new.%Push(obj.%Get(k-1)) s $bit(done,k)=1 }
		while itr.%GetNext(.k,.v) { d:'$bit(done,k+1) new.%Push(v) }
		
	} else { s new=obj }
	
	q new
}
}

Some examples

s car={"color":"red", "fuel":"diesel", "maxspeed":150, "maker":"Audi", "model":"Quattro Q5", "power":300, "available":true, "rating":8, "allWheel":true }
s car1=##class(DC.Utility).ReOrder(car) // order all props alphabetically
s car2=##class(DC.Utility).ReOrder(car,"maker","model","available") // start with maker, model, etc.

w car.%ToJSON(),!,car1.%ToJSON(),!,car2.%ToJSON() --->
{"color":"red","fuel":"diesel","maxspeed":150,"maker":"Audi","model":"Quattro Q5","power":300,"available":true,"rating":8,"allWheel":true}
{"allWheel":"1","available":"1","color":"red","fuel":"diesel","maker":"Audi","maxspeed":150,"model":"Quattro Q5","power":300,"rating":8}
{"maker":"Audi","model":"Quattro Q5","available":"1","color":"red","fuel":"diesel","maxspeed":150,"power":300,"rating":8,"allWheel":"1"}
Julius Kavay · Dec 16, 2022 go to post

Is NOT the same. You can it prove by adding a label to the line with the read command and a new line at the end

    // DOT VERSION
    // Use fic
old Read *R:20 Else  Do  Quit    ;;;;  comando else aplicado a read.
    . Use 0 Write !!!,"Expired time."
    If $c(R)="a" d
    . Use 0 Write !!!,"A letter a has been read."
    . Quit
    write !,"If there are more lines, they will be executed",!
    quit

    // LITTLE BIT MODERN VERSION
    // Use fic
new Read *R:20
    If $Test {
    Use 0 Write !!!,"One character read"
    Quit
    }
    Else {
    Use 0 Write !!!,"Expired time."
    }
    write !,"If there are more lines, they will be executed",!
    quit
 

now let run both of them...

do old // let the timeout occur
do old // now with some input
do new // let the timeout occur
do new // now with some input

Do you see the difference? If there are more lines (at end) they will be executed in opposite cases (timeout/notimeout)

Julius Kavay · Dec 15, 2022 go to post

Things are not so easy as they seem, you have to consider scopes too. Take the above class (DC.LineNumber) and add three more methods:

ClassMethod CaseA(x)
{
    if x goto zTest
    quit "A0"
    
zTest quit "A1"
}

ClassMethod CaseB(x)
{
    if x goto Test
    quit "B0"
    
Test quit "B1"
}

ClassMethod Test()
{
    write ..CaseA(0),..CaseA(1) set linenumber=..SrcLineNumberFromStack(.routine,.label,.offset,.src) do prt
    write ..CaseB(1),..CaseB(0) set linenumber=..SrcLineNumberFromStack(.routine,.label,.offset,.src) do prt
    quit
    
    // debug
prt	write !,"routine: ",routine
    write !,"label: ",label
    write !,"offset: ",offset
    write !,"linenumber: ",linenumber
    write !,"src:",src,!!
}

and now do the test:

do ##class(DC.LineNumber).Test()

and check the output... 

OK, I know, this is a (very) constructed case and shouldn't coincide with an everyday development style, but who knows, what a mad programer sometimes produces...

Julius Kavay · Dec 13, 2022 go to post

It seems, there is a problem or (at least) a change: the above works (tested right now) in Cache-2018.1.1, IRIS 2019.1 and IRIS-2021.2

If it doesn't work in 2022.1 then the question is, why?

Julius Kavay · Dec 13, 2022 go to post

It seems, we have to wait for someone with the same version installed as you, or you ask WRC.

By the way, does the release notes for 2022.1 mentioned  changes in dynamic classes?

Julius Kavay · Dec 13, 2022 go to post

If you have just a few breaks, one of the possible solutions has been given by Robert CemperIn case, you have tons of those breaks, you could execute the unit test in background:

job ^performUnitTest

assumed, your unittest can work in background and all the output goes, for example, in a file.

The solution for the case you let run the unittest in a terminal session, could be something like this

do turnOffBreaks, performUnitTest, turnOnBreaks

The only problem is, to turn breaks off (or on), you need one view command, one $view() function and one $zutil() function (to get the right address for the view-s). Unfortunatelly, the use of the above tools is discouraged, so your best bet is, to ask WRC for a help.

Julius Kavay · Dec 13, 2022 go to post

Are you sure,  ..ObjOrigem contains a VALID object reference? Your output shows just a string "%Library.DynamicObject". A valid OREF looks like "nn@%Library.DynamicObject". What is the output of $isobject(..ObjOrigem)?

Anyway, the $method(oref, "Propname"_"Get") works in IRIS, at least, in my IRIS 2021.2

USER>set obj={"Id":"myId"}
USER>write $property(obj,"Id")  --> myId
USER>write $method(obj,"IdGet") --> myId
USER>write $zv  -->  IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
USER>write obj  -->  5@%Library.DynamicObject
Julius Kavay · Dec 11, 2022 go to post

Oh, this is a misunderstanding. I thought, the screenshot is coming from a (not showed) link, and anticipated the link points to some ISC site.
Anyway, ChatGPT and other ChatBots (nowadays, they often pop up on various sites, mainly on those of big companies) tries to mimic a human and often end up only with an reference to FAQs or with a wrong (inappropriate) answer.
They all base upon AI (some says: AI=artificial intelligence, others say: AI=absent intelligence). My bottom line is, AI is for some areas already "usable" and for others, it still "will require some more time".

Julius Kavay · Dec 10, 2022 go to post

You can't access the class name alone

write $system.Version          // is just a class name, something like
write ##class(SYSTEM.Version)  // (hence both lines gives you a syntax error)

You have to specify a property or a method. Because $System.Version is an abstract class, you can specify a method only

write $system.Version.GetVersion()

Hope this clarifies things

Julius Kavay · Dec 10, 2022 go to post

To be honest, I was also impressed, but not about ChatGPT rather about the suggested solution!  I have no idea, who at ISC wrote this recommendation, but the five clicks on my IRIS-2021.1 end up in NOTHING. On the right-side panel I see 'System Information' but this can't be clicked, below is 'View System Dashboard', here you see everithing, except a version info. So dear ChatGPT, if someone asks you again, the (better) answer is:
- login to management portal
- click on 'About' (here you see the version and much much more)
This works for all IRIS versions (until now) and for all Cache versions which have a Management Portal. For the older (over 20 years) Cache versions right-click on the cube and select 'Config Manager' (on Windows, of course. In that times, I didn't worked on other platforms, hence no advices).

Julius Kavay · Nov 17, 2022 go to post

First, the suggestion of Victor Gordillo  is correct.

do ##class(Some.Class).%BuildIndices()

solves most index related problems.

Second, there are a couple of things, we do not know, but you
- did you made an in-place-upgrade or a clean install + data transfer
- the class in question is the same on IRIS and Cache (i.e. no change was made)
- is the class re-compiled (on IRIS)
- what do you exactly mean when you say "Not opening an instance...": (1) you get an NULL-OREF or (2) you get an error
- what do you provide as a NULL value: (1) "", (2) $c(0), (3) " " or something else as open parameter

A shortened class definition (we don't need the whole definition) would be also helpfull, something like this

Class Problem.Class extends %Persistent
{
Property Field1 as %String;
Property Filed2 As %String;

Index composite on (Field1, Field2) [ Unique];
}
Julius Kavay · Nov 16, 2022 go to post

If your serial class is named Data.Serial (as in your example code) then you should use the same name for the serial property too

Class Data.Persistent Extends %Persistent
{
Property MPID as %Integer;
Property Name as Data.Serial;   <--- !!!!!
}

The correct way to set the values

Set Obj=##Class(Data.Persistent).%New()
Set Obj.MPID=MPID
Set Obj.Name.FirstName=FirstName   ; <----
Set Obj.Name.LastName=LastName     ; <----
Set tSC=Obj.%Save()