Sure, thats needs because this variable should be available in any place for this process, just because this code executes in context where works any output commands like write
- Log in to post comments
Sure, thats needs because this variable should be available in any place for this process, just because this code executes in context where works any output commands like write
I would recommend regexr.com and regex101.com as a tools for testing regular expressions, this tools a bit simpler, but works online and cost nothing.
You have two cases, you change storage definition or you can use calculated value. But in case with calculated value, you have two options. It's an object's access and sql access, and such getter it is just only for object's access. For SQL you should define SqlComputeCode. Something like this.
Property Invalid As %Library.Boolean [Calculated, SqlComputed, SqlComputeCode = { s {*}={+$G(^GLOBAL({Code},"INVALID"))} }];
more details in documentation
There are no particular recommended SourceControl systems. Everything depends on your choice.
Some time ago, with versions cache less then 2016.2 and before Atelier was appeared, we could use nothing except Studio. And in this case to work with particular SourceControl we should write own addon for Studio as a wrapper for this SourceControl. You can find one as a good example here on github, which works with GIT
Now, when we have Atelier, we could forget about this part, and use lots of available plugins for different SourceControl systems.
BTW, I prefer git, but most of time used SVN at work, and git for my own projects.
Yes, because $system.OBJ.Load() started support UDL, only in 2016.2 version
In case when you try to load this file via %Compiler.UDL.TextServices you should know this classname, and you can parse original file. But if it possible, better to use $system.OBJ.Load(), in this case you should not pass classname, only lodaded filename.
It's a right way, but don't you think, that you forgot to save your changes ? something like this
w rtn.%Save()
or you can just use $system.OBJ.Load("les.mac") or $system.OBJ.Load("some.class.cls") in versions 2016.2+
if you have less version but not less then 2014.1, you can load class with %Compiler.UDL.TextServices as I've already offered to use it for export on stackoverflow
Every routines stores directly in database, so, you can't just open it as any file on your filesystem as well as you do it with csp files.
but you can open it with %RoutineMgr class, something like this
USER>zn "samples"
SAMPLES>set rtn=##class(%RoutineMgr).%OpenId("hello.mac")
SAMPLES>while 'rtn.Code.AtEnd { write !,rtn.Code.ReadLine()}
hello ; hello world routine
write !, "hello world"
write !, "bye"
end quit ; end
if you also need to get list of such files, you can use query StudioOpenDialog in %RoutineMgr class
SAMPLES>set st=##class(%SQL.Statement).%New()
SAMPLES>set sc=st.%PrepareClassQuery("%RoutineMgr","StudioOpenDialog")
SAMPLES>set rs=st.%Execute("*.mac")
SAMPLES>do rs.%Display()
Dumping result #1
Name IsDirectory Type Size Date Description IconType
DocBook . 9 0
badroutine.mac 0 62 2004-09-28 13:05:45 0
CinemaData.mac 0 10258 2016-05-10 22:32:04 0
compareloop.mac 0 1201 2004-12-02 18:23:57 0
datent.mac 0 3089 2002-01-03 12:05:01 0
datentobj.mac 0 2627 2002-09-06 00:15:23 0
dbconvert.mac 0 1532 2002-01-03 12:05:52 0
fibonacci.mac 0 365 2002-01-03 12:06:03 0
forexample.mac 0 502 2004-11-30 16:00:08 0
funcexample.mac 0 333 2002-01-03 12:06:30 0
hello.mac 0 251 2016-07-13 12:07:04.520803 0
LDAP.mac 0 68720 2013-12-16 11:48:09.962335 0
lookup.mac 0 8484 2008-08-21 19:17:48 0
lookup1.mac 0 1465 2002-01-03 12:07:15 0
lookup2.mac 0 5984 2002-01-07 18:08:46 0
lookupobj.mac 0 7857 2008-08-21 21:58:25 0
loopend.mac 0 242 2002-01-03 12:08:12 0
loopstart.mac 0 217 2002-01-03 12:08:21 0
nameloop.mac 0 552 2002-01-03 12:08:33 0
passbyref.mac 0 604 2002-01-07 12:01:47 0
postcond.mac 0 341 2002-01-03 12:08:51 0
procexample.mac 0 423 2002-01-03 12:08:59 0
publicvarsexample.mac 0 357 2002-01-03 12:09:09 0
RightTriangle.mac 0 1836 2011-02-24 18:56:29 0
root.mac 0 149 2004-11-30 15:57:29 0
simpleloop.mac 0 161 2002-01-03 12:09:59 0
SQLGatewayTest.mac 0 2480 2016-05-10 22:32:04 0
survivor.mac 0 179 2002-01-03 12:10:32 0
ZAUTHENTICATE.mac 0 37302 2015-03-10 10:48:43.589807 0
ZAUTHORIZE.mac 0 12120 2016-05-10 22:32:04 0
30 Rows(s) AffectedAs I'm not sure what you mean by 'dead' locks, I'll just show simple code for looking all locks in current namespace
set lockName=""
for {
set lockName=$order(^$LOCK(lockName))
quit:lockName=""
set mode=^$LOCK(lockName,"MODE")
set owner=^$LOCK(lockName,"OWNER")
write !,mode,?4,owner,?12,lockName
}
What do you mean by dead locks ?
Studio reformats some class parts, such as Methods defenitions when you save it.
But as I know feature Atelier, stores class definition as is, so you do not need more such option, just format class as you need, and it should keep so.
As I've already had some experience in such systems in production, and one of our projects, has similar architecture exclude docker, just on windows, some physical servers with ECP-Client + CSPGateway + Apache, and one HAPorxy server for all of this server. And in this case all this scheme is quite static, and adding new ECP-client means some manual works, on all levels. But with docker I expect, just call something like this command
docker-compose scale ecp=10
and just get some new working instances, which just after gets their new web-clients
To work it as microservices and split CSPgateway and Cache instance as different containers, I need to have simple package just only with CSPGateway, but FieldTest versions does not contain it. But yes sure, I think it is good way too, and in this case I can have more WEB-containers then Cache does, if it would be needed.
Timur, you can look at my example at github which I wanted to use in to article about using docker but have not managed yet. In this example I have Dockerfile for ECP-client, which can be build for particular ECP-server. And with %Installer manifest possible to make backward data channel too, while we have already known about where our server placed, we can connect to him via %Net.RemoteConnect or something else and make new backward connection, the problem is in this case how to remove old ones. I played only on one machine. But in anyway, Ansible still could be useful, but in case to prepare servers to work in docker cluster, which should be prepared before we could use docker-compose and so on. And My example also contains web-server (apache), to have an access to this new instance. What I wanted to do then is to use some load balancer, HAProxy or traefik (as recommended Luca), to get one point access to my application, and dynamically expandable, without any manual operations, except scaling.
Yes, such language extension is very useful, but there is one more feature less known, and unfortunately completely undocumented. Its structured system variables, some of them could me familiar it is: ^$JOB, ^$LOCK, ^$GLOBAL and ^$ROUTINE. More information in documentation here. But not all knows that it is possible to make your own such global. You should create routine with name SSVN"global_name" in %SYS namespace, for example SSVNZA for ^$ZA global
and for example with this code below, you SET, GET, KILL any values in this global, and this global will contain individual data for every session, and it is possible to use $order or even $query for such global
ROUTINE SSVNZA
#define global $na(^CacheTemp.ZA($select($isobject($get(%session)):%session.SessionId,1:$job)))
fullName(glob) {
set global=$$$global
for i=4:1:glob set global=$na(@global@(glob(i)))
quit global
}
set(glob, value) public {
set global=$$fullName(.glob)
set @global=value
}
get(glob) public {
set global=$$fullName(.glob)
quit @global
}
kill(glob, zkill=0) public {
set global=$$fullName(.glob)
if zkill {
zkill @global
} else {
kill @global
}
}
order(glob, dir) public {
set global=$$fullName(.glob)
quit $order(@global, dir)
}
query(glob, dir) public {
set global=$$fullName(.glob)
set result=$query(@global, dir)
quit:result="" ""
set newGlobal=$name(^$ZA)
for i=$ql($$$global)+1:1:$ql(result) {
set newGlobal=$name(@newGlobal@($qs(result,i)))
}
quit newGlobal
}But as I said before, as it is completely undocumented feature a bit difficult to get complete compatibility with ordinary globals. But it still could be useful, in other cases, like it used by InterSystems.
but WaitMsg it is on parent's side, my control code ususaly like like this
set resName="someresource"
job ..someJob(jobsData,resName)
set child=$zchild
for {
set $lb(sc,data)=$system.Event.WaitMsg(resName, 1)
if sc<=0 {
quit:'$data(^$Job(child))
}
continue:data=""
// do some staff with incomming data
}
But if you want to interrupt your child process, of course will be possible only from such process, if your process have some loops you may just check if parent still exists, if not, just stop working.
if $data(^$JOB($zparent))=0 write "Parent is gone"
ToPhone(s = "")
{
set r=""
set c=96
for p=2:1:9 {
set k=""
for j=1:1:3+(p=7)+(p=9) {
set k=k_p
set c($c($i(c)))=k
}
}
for i=1:1:$l(s) {
set n=$g(c($e(s,i)),0)
if $e(n)=$e(r,*) set r=r_" "
set r=r_n
}
quit r
}and 149, with a little bit changes
ToPhone(s = "")
{
s r="",c=96 f p=2:1:9{s k="" f j=1:1:3+(p=7)+(p=9) s k=k_p,c($c($i(c)))=k} f i=1:1:$l(s){s n=$g(c($e(s,i)),0) s:$e(n)=$e(r,*) r=r_" " s r=r_n} q r
}Well, my code below
toPhone(s = "") {
s r="",c=96,p=1 f i=3,3,3,3,3,4,3,4{s:$i(p) k="" f j=1:1:i s k=k_p,c($c($i(c)))=k} f i=1:1:$l(s){s n=$g(c($e(s,i)),0) s:$e(n)=$e(r,*) r=r_" " s r=r_n} q r
}In first I'm generate a plane array with all variants, and then just get all numbers from it, include space between identical numbers, and zero for unknown symbols such as space
too close to my variant, but without listbuilds :)
I think I let other's to share their size, and tomorrow or sometime later I can share it.
My result is 157 :)
Actually, XData content it is a just XML, in it is stored exactly as you wrote it in IDE. But it is possible to compile any XData to any other format as you need, at this portal you can find two articles about how to do it, first and second. In your case it could be much easier, like some method which in compile mode gets all XData content and place it to &html<>, which then compile again as usual, and what you need after it is just call this generated method, to show this html.
Steve,
I would recommend to distant webservers from ECP Application servers. In your case I would install webserver exactly on the server where ECP Application Server have already installed, so in this case your connect via superport will be secured, and in this case only web port should be opened in the firewall. Outside of this servers after firewall you need to use some load balancer, in this case I would recommend to use HAProxy. Connection between extarnal HAProxy and internal webservers could be secured with ssl, but I'm not sure that is really needed.
And in this case it is possible to store all static content such as JS, CSS and images on server where load balancer placed, to avoid redundant request to the data servers. It is not a big problem for the production systems.
So finally in simple view it my looks like below.

It's is not good idea, not all time and not everywhere I have an access to google, but I have an access to my installation, and I have to search on my local machine. And such search should be support browser's search engines. And then I could do something like this.
with settings

But Ensemble documentation, should provide their own search engine to install in my browser without any manual operations. As it possible to do with AddSearchProvider
Many times I need to get some link from documentation, to show particular part to someone. And some time it is quite difficult to get such link, and the worst place in Class References, where I can't get good link for a particular part of class.
So, what I need is, an active icon with link, which then I can use, and for Class reference too.
As an example github.
As for me, when I have to investigate some strange behavior in my application, I prefer to read all journal files or limited by time, and then all records, and then I have some some to find exactly what I need, like only kill records or only sets, and only for some concrete global references. So, my usual code looks like this:
set exit=0
set docId="14105401"
set jrnFile=##class(%SYS.Journal.System).GetCurrentFileName()
do {
w !!,jrnFile,!
set jrn=##class(%SYS.Journal.File).%OpenId(jrnFile)
quit:'$isobject(jrn)
#dim rec As %SYS.Journal.Record = jrn.FirstRecord
set last=jrn.End
set pr=0,opr=0
do {
set pr=$j(rec.Address/last*100,0,2)
if pr'=opr write $c(13),pr_"%" set opr=pr
if rec.%IsA("%SYS.Journal.SetKillRecord") {
set glb=rec.GlobalNode
if $qs(glb, 0),"^My.Global",$qs(glb, 1)=docId {
w !!,rec.Address,!,glb
w !
}
}
set rec=rec.Next
read q:0 set exit=q="q"
quit:exit
} while $isobject(rec)
quit:exit
set sc=jrn.GetPrev(jrnFile, .jrnFile)
} while $$$ISOK(sc)
quitThis code looks all journal files, file by file from latest to oldest, and show progress for current journal file, and shows offset if it found something interesting
In my opinion, it's more looks like by mistake, not sure why it could be done in other ways, it still working, with some limitations but any way.
We can wait what would say InterSystems, may be they will fix it in future.