Vitaliy Serdtsev · Mar 14, 2019 go to post
Let's try to guess the return value of method t1().
Is that supposed to be hard? I immediately visually determined the result 20.

To be honest I don't really understand the point which the author wanted to convey. Maybe it's the lack of English.

In this case, the t3 method code is equivalent to the following code:
ClassMethod t3(ByRef pAByRef pB) [ ProcedureBlock = 1 ]
{
  set pA=4, pB=5
  set x=$increment(pA)*$increment(pB)
}
PS: by the way, absolutely nothing will change fundamentally if you replace "return" with "quit" (and "ByRef" with "Output").
Vitaliy Serdtsev · Mar 12, 2019 go to post

Hi Ponnumani.

I would like to make a number of clarifications:

  • VALUELIST "A A- B B- O" -> VALUELIST " A A- B B- O"
  • it is necessary to consider the order of the fields in the global on the basis of Storage. For example, I have following Storage:
    <Data name="samplexlsconversionDefaultData">
    <Value name="1">
    <Value>%%CLASSNAME</Value>
    </Value>
    <Value name="2">
    <Value>name</Value>
    </Value>
    <Value name="3">
    <Value>DOB</Value>
    </Value>
    <Value name="4">
    <Value>address</Value>
    </Value>
    <Value name="5">
    <Value>PhoneNumber</Value>
    </Value>
    <Value name="6">
    <Value>BloodGroup</Value>
    </Value>
    </Data>
  • need to add check for the presence of double quotes in strings, e.g. bla"bla
  • the date format for CSV should not be in the internal Caché format, because the date 37893 to users of other system/DBMS nothing says unlike e.g. 1944-09-30.

I still prefer to use built-in tools when they are there, rather than reinventing the wheel.

Class User.samplexlsconversion Extends (%Persistent%Populate)
{

Property name As %String;

Property DOB As %Date;

Property address As %String;

Property PhoneNumber As %Numeric;

Property BloodGroup As %String(VALUELIST " A A- B B- O");

ClassMethod TestExport2CSV()
{
  
  ..%KillExtent()
  ..Populate(1000)
  
  tmp=..%OpenId(1)
  tmp.name=tmp.name_$c(34)
  tmp.%Save()
  
  tmp=..%OpenId(2)
  tmp.name=$c(34)_tmp.name_$c(34)
  tmp.%Save()

  mgr ##class(%SQL.Export.Mgr).%New()
  mgr.FileName "c:\Temp\test.csv"
  mgr.ClassName = ..%ClassName(1)
  mgr.TableName $$$CLASSsqltablename($$$gWRK,mgr.ClassName)
  mgr.Delimiter ","
  mgr.StringQuote $c(34)
  mgr.DateFormat = 3
  mgr.TimeFormat = 1
  mgr.TimeStampFormat = 1
  mgr.NoCheck $$$YES
  s mgr.HasHeaders $$$YES
  
  s cols="Name:S;Address:S;PhoneNumber:N;BloodGroup:S;DOB:D"
  i=1:1:$l(cols,";"{
    c=$p(cols,";",i)
    mgr.ColumnNames.Insert($p(c,":",1))
    mgr.ColumnTypes.Insert($p(c,":",2))
  }
  
  mgr.Export()
}
}
Vitaliy Serdtsev · Mar 11, 2019 go to post

Hi Robert.

Exactly.

Programmatically Managing Roles:

This command can only be invoked either from a routine that is part of the CACHESYS database or if the current privileges held include Write permission for the CACHESYS database (%DB_CACHESYS:W).

Note that setting $ROLES only alters a process’s added roles, not its login roles.

There is a third way, but I specifically did not publish it.

Vitaliy Serdtsev · Mar 11, 2019 go to post

Below are two ways without additional permissions on %DB_CACHESYS:

  #include %systemInclude
  n
  try{

    sslName="Test"

    "1) GetSSLConfigList^%SYS.SECURITY1(.SSLConfigs)",!!
    w $$Test1(sslName)
    
    !!,"2) $$ListSSLConfigs^%SYS.SECURITY(Client/Server)",!!
    w $$Test2(sslName)
    
  }catch(ex){
    "Error "ex.DisplayString(),!
  }

Test1(ssl) {
  GetSSLConfigList^%SYS.SECURITY1(.SSLConfigs)
  i=1:1:SSLConfigs return:SSLConfigs(i)=ssl $$$YES
  q $$$NO
}

Test2(ssl) {
  ''$lf($lfs($$ListSSLConfigs^%SYS.SECURITY("Client")_","_
               $$ListSSLConfigs^%SYS.SECURITY("Server"))
          ,ssl)
}
Vitaliy Serdtsev · Mar 6, 2019 go to post

And if so?

CREATE TABLE test (
  identifier   VARCHAR(200NOT NULL,
  value        INTEGER COMPUTEONCHANGE("%%UPDATE"COMPUTECODE {&sql(select max(value)+1 into :{*} from test where identifier=:{identifier})},
  PRIMARY KEY (identifier)
)
Vitaliy Serdtsev · Feb 28, 2019 go to post

Working example:

Class dc.test Abstract ]
{

/// d ##class(dc.test).Test1()
ClassMethod Test1() [ ProcedureBlock = 0 ]
{
  new active,reactive,info,i
  
  kill info

  set active = 1
  set reactive = 2

  for i="active","reactive" {
    set info(i)= @i
  }

  zw info
}

/// d ##class(dc.test).Test2()
ClassMethod Test2() [ PublicList = (active, reactive) ]
{
  new activereactive
  kill info

  set active = 1
  set reactive = 2

  for i="active","reactive" {
    set info(i)= @i
  }

  zw info
}

}
Vitaliy Serdtsev · Feb 22, 2019 go to post

What will the following code output?

resptext="{""access_token"":""4SDFDSFDSF-aSDASDASD"",""expires_in"":""3300"",""refresh_token"":"""",""scope"":""sms_send"",""token_type"":""Bearer""}"

sc ##class(%ZEN.Auxiliary.jsonProvider).%ParseJSON(resptext,,.pObject,1)
i $$$ISOK(sc{
  pObject,!!,
    pObject."access_token",!,
    pObject."expires_in",!
  arr arr="" pObject.%CopyToArray(.arrzw arr
}else{
  d $system.OBJ.DisplayError(sc)
}

My result (2018.1):

USER>^test
1@%ZEN.proxyObject
 
4SDFDSFDSF-aSDASDASD
3300
arr("access_token")="4SDFDSFDSF-aSDASDASD"
arr("expires_in")=3300
arr("refresh_token")=""
arr("scope")="sms_send"
arr("token_type")="Bearer"
Vitaliy Serdtsev · Feb 22, 2019 go to post

What will the following code output?

USER>arr pObject.%CopyToArray(.arrzw arr

PS: "in" ≠ "expires_in"

Vitaliy Serdtsev · Feb 18, 2019 go to post

Hi Adrian.

Made a small example:

#dim rs As %ResultSet
   
pid=4652
   
rs=##class(%ResultSet).%New("%SYS.ProcessQuery:VariableByPid")
   
filter="","a*" {
 !,"Filter = ",$$$quote(filter),!
 rs.Execute(pid,,,,filter)
 while rs.Next() {
   "Name = ",rs.Get("Name"),", Value = ",rs.Get("Value"),!
 }
}

Result:

USER:4652>a="11",b="22",a1="33" w $job
4652

USER:1392>w $job,! ^dc
1392
 
Filter = ""
Name = a, Value = 11
Name = a1, Value = 33
Name = b, Value = 22
 
Filter = "a*"
Name = a, Value = 11
Name = a1, Value = 33
Vitaliy Serdtsev · Feb 16, 2019 go to post

The presence of a potential opportunity to read the documentation via "Class Reference". For macros from *.inc this is not possible.

Vitaliy Serdtsev · Feb 14, 2019 go to post

Still another option:

Class dc.test Extends %Persistent
{

Property JSONData As list Of %String SqlListType = SUBNODE ];

ClassMethod Test()
{
  ..%KillExtent()
  
  l=$lb("apple","pear","plum"),$lb({"Name":"Kyle"}.%ToJSON(),{"Name":"Evgeny"}.%ToJSON()) {
    t=..%New()
    t.JSONData.InsertList(l)
    t.%Save()
  }

  zw @$system.Dictionary.comMemberKeyGet($this,$$$cCLASSstorage,$$$nameDefaultStorageNameGet,$$$cSDEFdatalocation)
  
  rs=##class(%SQL.Statement).%ExecDirect(,"select distinct json_arrayagg(JSONData %foreach(test)) from dc.test_JSONData")
  while rs.%Next() {
    jsonStr=rs.%GetData(1),
      json=[].%FromJSON(jsonStr)

    !,jsonStr,", %Size()=",json.%Size(),!
  }
}

}

Result:

USER>##class(dc.test).Test()
^dc.testD=2
^dc.testD(1)=$lb("")
^dc.testD(1,"JSONData")=3
^dc.testD(1,"JSONData",1)="apple"
^dc.testD(1,"JSONData",2)="pear"
^dc.testD(1,"JSONData",3)="plum"
^dc.testD(2)=$lb("")
^dc.testD(2,"JSONData")=2
^dc.testD(2,"JSONData",1)="{""Name"":""Kyle""}"
^dc.testD(2,"JSONData",2)="{""Name"":""Evgeny""}"
 
["apple","pear","plum"], %Size()=3
 
[{"Name":"Kyle"},{"Name":"Evgeny"}], %Size()=2

Doc:

Vitaliy Serdtsev · Jan 16, 2019 go to post
if I call giveMeAFalseMe from Cache when it's returning "true" or "false", Cache will translate "true" into a truthy %Boolean return value, right?

No, it does zenBool, which is on the client side.

The important thing is that #server()# always returns a string, for example:

<script language="cache" method="giveMeAFalse" arguments="" procedureblock='1' returntype="%Integer">return 4</script>

var bool #server(..giveMeAFalse())#;
console.log("bool = " bool);
console.log("typeof bool = "typeof bool);

Result:
bool = 4
typeof bool =  string

PS: you can further explore the sources of the #server()# -> cspHttpServerMethod() -> cspProcessResponse(), which are in the file cspxmlhttp.js.
Vitaliy Serdtsev · Jan 15, 2019 go to post

See zenConvertType() [///Converts a typed value returned from the server to an appropriate JS type] from zenutils.js.

Try this:

<script src="zenutils.js"></script>

<script language="javascript">
function isItActuallyFalse() {

var bool zenConvertType('BOOLEAN',#server(..giveMeAFalse())#);

...
}

</script>

<script language="cache" method="giveMeAFalse" arguments="" returntype="%Boolean" procedureblock='1'>return $$$NO</script>

or

var bool zenBool(#server(..giveMeAFalse())#);
...
<script language="cache" method="giveMeAFalse" arguments="" returntype="%Boolean" procedureblock='1'>return "false"</script>
Vitaliy Serdtsev · Dec 31, 2018 go to post
tags=$lb("a","b","c"),
  str="c,a1,d,b,f"
   
l=$lfs(str),ptr=0
while $listnext(tags,ptr,tag{
  w:$lf(l,tagtag,!
}
Result

b c

Vitaliy Serdtsev · Nov 2, 2018 go to post

No.
If you beware to use %occLibrary, you may look into side %Regex.Matcher

Here is another documented solution:

#include %systemInclude
  n
  try{

    #define TAB $c(9)
    
    s csv="""ABC,"_$c(10)_" Company,"",""123 Main St, Ste 102"",""Anytown, DC"",10001,234-567-8901,""Anytown"_$$$TAB_"DC"""

    ##class(%DeepSee.TermList).%ParseCSVRecord(csv,.arr)

    csv,!!,$$WriteCSVRecord(.arr,$$$TAB)

  }catch(ex){
    "Error "ex.DisplayString(),!
  }


WriteCSVRecord(&pRecord, pDelim=",", pEncode=$$$YES{
  // made on the basis of the method %WriteCSVRecord
  Set ""
  Set $O(pRecord(""))
  While (k'=""{
    Set tValue $G(pRecord(k))
    Set:$G(pEncodetValue $ZCVT(tValue,"O","UTF8") 

    Set $S((tValue["""")||(tValue[",")||(tValue[pDelim):$$$quote(tValue),1:tValue)
    Set $O(pRecord(k))
    Set:k'="" pDelim
  }
  Quit r
}

Result:

USER>^dc
"ABC,
      Company,","123 Main St, Ste 102","Anytown, DC",10001,234-567-8901,"Anytown        DC"
 
"ABC,
      Company," "123 Main St, Ste 102"  "Anytown, DC"   10001   234-567-8901    "Anytown        DC"
Vitaliy Serdtsev · Nov 1, 2018 go to post
#include %systemInclude

    csv="ABC Company,""123 Main St, Ste 102"",""Anytown, DC"",10001,234-567-8901"
    list=$$CSVtoList^%occLibrary(csv)

    i=1:1:$ll(lists:$li(list,i)["," $li(list,i)=$$$quote($li(list,i))
        
    csv,!,$lts(list,$c(9))
Result:
USER>^test

ABC Company,"123 Main St, Ste 102","Anytown, DC",10001,234-567-8901 ABC Company "123 Main St, Ste 102" "Anytown, DC" 10001 234-567-8901

Vitaliy Serdtsev · Nov 1, 2018 go to post

Maybe the name of the topic to specify a CSV-string instead of string ?

After clarification, the question became more clear.

Vitaliy Serdtsev · Nov 1, 2018 go to post

I was looking at the generated INT code for &sql(select lpad('1',4,'0'n)

Unfortunately, for COS at the moment I have not found an analogue LPADSQL.
Can you offer a documented LPADCOS ?

Vitaliy Serdtsev · Oct 30, 2018 go to post

Once upon a time promised to make Server-Side JavaScript alongside with Caché ObjectScript and Caché Basic:

I found a JS Runtime Shell that uses Node.JS & esprima:

SAMPLES>##class(%CPT.JS.Runtime.Shell).Run()
 
JRS> ?
 
.L CLASS   - load javascript methods from CLASS into JS global namespace
.LV var,.. - load the given COS public vars into JS global namespace
 
.S FUNC  - list the source of the method which implements function FUNC
           - FUNC must be in the global namespace
           - the whole implementing method is shown, even for an inner function
           - this will also work for non-javascript methods
.M       - list MAC code from latest compilation
.I       - list INT code from latest compilation
.T       - show latest tree
 
.G       - list global symbols
 
.N CODE  - execute JS on node.js
.N       - execute the last-entered line of JS on node.js
 
.CONFIG ... - passed to %CPT.CalloutShell 'CONFIG' command
 
.SET ... - passed to %CPT.CalloutShell 'SET' command
.CLEAR ... - passed to %CPT.CalloutShell 'CLEAR' command
.SHOW ... - passed to %CPT.CalloutShell 'SHOW' command
 
!COS     - execute a line of COS
 
Anything else is interpreted as JS. :-
* if preceded by "=" it must be a single function definition
* otherwise it can be either a single expression or a sequence
  of statements separated by ";"s
 
Either way it will be compiled into a function body and :-
* if preceded by "==" it is stored as a JS global with the new function's name
* otherwise it will just be executed
 
\n can be used to represent a newline.
 
You can also take input from a file :-
 <FILE    - read JS from FILE and compile+execute it
 <=FILE   - read JS from FILE and compile+store it
 
The result of the execution is shown and also stored in global variable '_'.
 
For a global variable, set it without using 'var' (this will be fixed)


JRS> .N var obj { name: "John", age: 30, city: "New York" }; JSON.stringify(obj);
{result: "{""name"":""John"",""age"":30,""city"":""New York""}"}

JRS> .N Math.random()
{result: .5670654247514904}

JRS> .N var fruits ["Banana""Orange""Apple""Mango"]; fruits.sort();
{result: ["Apple", "Banana", "Mango", "Orange", 1: "Apple", 2: "Banana", 3: "Mango", 4: "Orange"]}

JRS> q
 
SAMPLES>
Vitaliy Serdtsev · Oct 30, 2018 go to post

If you do universal LPAD, it is best $$lpad^%qarfunc, because for certain arguments may produce an incorrect result, for example:

#define lpad(%s,%len,%pad) $tr($j(%s,%len)," ",%pad)
   
s s="a b",len=8,pad="0"
   
w $$$lpad(s,len,pad),!
 ,$$lpad^%qarfunc(s,len,pad),!
 ,$$$lpad(s,len,s),!
 ,$$lpad^%qarfunc(s,len,s),!

Result:

USER>^test
00000a0b
00000a b
aaaaaaab
a ba a b