Nikita Savchenko · Dec 18, 2015 go to post

Alex, the current WebTerminal version (3.1.4) supports only VT100/Caché TERM applications. The VT440 support was not in the close future todo list, but may become true anytime.

We had tried to run some "dinosaurs" in WebTerminal and some of them worked really well, but moslty it was all about the issue with the Caché command "USE": it breaks WebSocket connection and should be used properly considering that WebTerminal is an "over-TCP device". I believe one day we will find a way to make no difference between the common Terminal and WebTerminal behavior at this point, but today these application maybe should include a little fix to any usage of "USE" commands inside terminal applications.

Nikita Savchenko · Apr 7, 2016 go to post

Thanks!

The labels in italics are hoverable. It means that user can hover over a label and get any additional information specified for the element like comment, keywords, etc. If there is nothing specified (all the keywords are default ones), then no italic font will appear.

Nikita Savchenko · Apr 11, 2016 go to post

Thank you Wolf for the good suggestions! Especially about UML associations.

As Timur mentioned, you can fill the issue or feature request on GitHub's Class Explorer issue tracker, but don't expect it to be implemented fast if only it is not critical for you. Just left the suggestions there to keep things organized, if you have some time.

Thanks for your interest on the project!

Nikita Savchenko · Apr 17, 2016 go to post

Thanks for the great article! It made a good introduction to the new JSON capabilities in Caché for me.

But it seems like there is one super strange issue with $toJSON method.

Check this simple REST application:

Class JSON.Mystery Extends %CSP.REST
{
XData UrlMap
{
<Routes>
   <Route Url="/test" Method="GET" Call="Test"/>
</Routes>
}

ClassMethod Test() As %Status
{
set = "test"1 }
do a.$toJSON()
quit $$$OK
}

}

Guess what you get by querying "http://hostname/webAppName/test"? You get nothing. And now, by adding a pointless change to the Test method:

ClassMethod Test() As %Status
{
set = "test":1 }
write ""  // Right. This one.
do a.$toJSON()
quit $$$OK
}

Everything works as expected, string '{"test":1}' is outputted. Tested on Caché 2016.2.0.590.0.

Any ideas on why this happens? Doesn't $toJSON function initialize the output aswrite does?

Nikita Savchenko · Apr 18, 2016 go to post

write a.$toJSON() is always vulnerable for <MAXSTRING> error, which was unacceptable for my case. For some cases while cycle can solve this problem, but even with this known bug an empty write command followed by do statement looks shorter and more reliable I think.

Nikita Savchenko · May 9, 2016 go to post

After some additional editing the article is finally published. Moving to developer community feedback page to leave an issue about wrong publishing date (the date of draft creation is displayed instead of the date of actual article publishing), which causes wrong sorting order on site pages.

Nikita Savchenko · May 9, 2016 go to post

Oops! Thanks for catching this up. Somehow I missed to copy the correct version of the code.

UPD fixed.

Thank you!

Nikita Savchenko · May 9, 2016 go to post

Daniel, 

Also, in many cases, when you compile a class, you only want to perform changes only once, not with every compilation.

This can be easily held in the case of Projections. For example, by testing whether the compiled installer exists in the system, or, as I do in my project, by checking if the project's global exists, whatever. The only thing you need to write all of this pure COS code to perform the checks and build the whole installation logic.

Not sure if %Installer gives more control, except of the moment when you trigger the installation process. It looks for me as a kind of framework with a lot of useful things.

The ideal variant here would be to trigger %Installer's setup from CreateProjection method I think.

Nikita Savchenko · May 10, 2016 go to post

Thank you, Evgeny!

Showing the logs has a lot of use cases, but the most common of the top of my head are:

  1. To notify user that no errors occurred during installation and to help track any errors if any happened;
  2. To show the user what happened and what was configured;
  3. And even to collect installations statistics and catch any unpredictable error logs during setup, as I do in my VisualEditor project.
Nikita Savchenko · May 10, 2016 go to post

The general approach here is to use COS' write command to output any user-readable result. This result will be visible during and after class import for any of three cases described in the article (importing with Caché Studio, Management Portal and terminal).

Nikita Savchenko · May 10, 2016 go to post

John, you are right, in general any temporary namespace change should be new'd, as Timothy mentioned.

But both inside CreateProjection and RemoveProjection methods not changing the namespace back to the original one at the end of the methods doesn't have any side effects as I discovered.

Nikita Savchenko · Jun 2, 2016 go to post

Don't understand what are $lts and $lfs. I need to convert this string back to the structure, so I can read it's elements. The delimiter needs to be escaped somehow.

Nikita Savchenko · Jun 3, 2016 go to post

[UPD fixed] Got you. Incredible! It seems that this is what I needed:

USER>set string = $lb($C(1),$c(2),$c(3),",",$c(0))
USER>w $a($LISTGET(a,1))
1
USER>w $a($LISTGET(a,2))
2
USER>w $a($LISTGET(a,3))
3
USER>w $LISTGET(a,4)
,
USER>w $a($LISTGET(a,5))
0

Until the list values may contain any set of characters here this is the best solution, thank you, Jon!

Nikita Savchenko · Jun 3, 2016 go to post

Thanks John, the above terminal transcript is not correct showing what I wanted to show. I've fixed this.

Why can't you understand the purpose of this?

I can only transfer strings by some channel (f.e. interprocess communication). Now I need to send the array of strings (list, object, etc) from sender to receiver. To send this array, I must convert it to the string and then, after receiving, convert it back to array to read it's elements. How to do this in Caché 2013.*? The strings in array can contain any characters, so it is not possible to specify any delimiter character.

Finally I got why the question is pretty confusing for you. Just imagine, I didn't know that $lb() returns the simple string. I didn't know that I can write this string to file, read it from file, and then apply $LISTGET function to it. I didn't know $LISTGET function takes string type. I thought that $LIST is something more than just a string.

So the solution:

set string = $LB(1,2,3,",",5)
/* send this string anywhere */
set element = $LISTGET(string, 4) // = ","
Nikita Savchenko · Jun 3, 2016 go to post

Thanks for the tips! My use case is only to get the string, it doesn't matter printable or not.

I also tested that this works with nested lists, so everything is OK now:

USER>set l = $lb(1,2,$lb(3,4,5),6)
 
USER>w $LISTGET($LISTGET(l, 3), 2)
4
USER>w $LISTGET($LISTGET(l, 3), 1)
3
USER>w $LISTGET($LISTGET(l, 3), 3)
5
USER>w $LISTGET(l, 4)
Nikita Savchenko · Jun 6, 2016 go to post
Jobbed process runs independently of parent process.
Killing a  parent does not kill its children.

Oops. Thank you, it does not kill children process really.

In my application two processes already use interprocess communication. Child process rely on $ZPARENT variable. Is it possible to change it? Or the only way is to use custom variable to hold the parent's process ID?

Nikita Savchenko · Jun 6, 2016 go to post

Thanks.

Apparently I reached another question now. How to terminate child process if the parent process is gone? WaitMsg method in $system.Event class does not return when the parent process terminates.

Nikita Savchenko · Jun 7, 2016 go to post

Thanks, Dmitry, this is very close to what can be true. But to use this I need to interrupt WaitMsg method and check if the global exists each... Second? The one idea I come up with is to set timeout for that: set st = $system.Event.WaitMsg(, 1) if $LG(st,1)=0 {/*check if the process is gone and wait again*/} else {/*handle message*/} but is this the best way to do it? Are there any sort of registering trigger or system "interrupt" for this?

Nikita Savchenko · Jun 14, 2016 go to post

Thank you Fabio! The problem with all of the methods in %ZEN.Auxiliary.jsonProvider is that it writes JSON string directly to the current device, not allowing to get the string itself. But nevertheless I did it by rewriting this method and creating a custom method, which returns a string instead of printing JSON to the device.

Nikita Savchenko · Jun 14, 2016 go to post

This is awesome. It works! Wondering how many things COS has.

But it also outputs variable x. I wrapped your code into the method, and that's what I got:

ClassMethod GetVariables() As %List{  set UndefinedSpecialList = $LB()  set UndefinedSpecialInt = 0  set Undefined = ""  set Undefined = $ORDER(@Undefined)  while (Undefined '= "") {    quit:($ORDER(@Undefined) = "UndefinedSpecialList")    set $LIST(UndefinedSpecialList, $INCREMENT(UndefinedSpecialInt)) = Undefined    set Undefined = $ORDER(@Undefined)  }  return UndefinedSpecialList}

It works awesome until user specify variables named UndefinedSpecialList, UndefinedSpecialInt and Undefined. I suppose user won't ever name variables this way, of course, but the method which will consider this is the winner.

Nikita Savchenko · Jun 14, 2016 go to post

Thank you, Timothy! This is good, but what if user specify ^||VariableNameList process-private variable before calling the method? The value will be lost:

USER>set ^||VariableNameList = "TempVal" zwrite ##class(Test.Class).GetVariablesByTimothy()
$lb("Einstein","Mozart")
USER>write ^||VariableNameList
...TempVal is now gone!...

Maybe there is no absolutely save method to get the variables list without touching/rewriting other variables or globals. I hope it is enough just to name temporary variables as no one suggests to name.

Nikita Savchenko · Jun 14, 2016 go to post

OK :) But how to use this variable name generator? :)

set myNewVarName = ..GetNewVarName() // oops, no
 set @..GetNewVarName() = ""// now it's OK, but I need to call this function once more
 set @..GetNewVarName() = $ORDER(@@..GetNewVarName()) // oops, another variable names

Things get complicated here. I think we can stop on the solutions Michael and Timothy proposed.

Thanks!

Nikita Savchenko · Jun 15, 2016 go to post

Nice! Will wonder if you have some sort of more-liner, which also saves and restores objects (orefs). But I believe it is not too useful.

Nikita Savchenko · Jun 30, 2016 go to post

Thank you,  Stuart!

I think this is a complete solution. I wrapped it to a class method and it works just as expected:

ClassMethod VarList() As %String
{
    IF $DATA(%) ; This places the existence of variable % into $TEST.
    NEW SET %=$SELECT($TEST:$LISTBUILD("%"),1:"")
    SET:$DATA(%0) %=%_$LISTBUILD("%0")
    NEW %0 SET %0="%0"
    FOR {
        SET %0=$ORDER(@%0)
        QUIT:%0=""
        SET %=%_$LISTBUILD(%0)
    }
    QUIT %
}

Results with

USER > write
%=11
a=<OBJECT REFERENCE>[1@DevProject.Robot]
i=5
USER > zw ##class(DevProject.Robot).VarList()
$lb("%","a","i")
USER > kill %
USER > zw ##class(DevProject.Robot).VarList()
$lb("a","i")