Source Control
Cache Studio can communicate with source control systems via special class with Studio hooks.
Here is an example class which is used in Dimas ltd. to integrate with Subversion. It is based on Intersystems ClearCase integration class. This is definetely not an example of good Cache code, but it just works.
This hook just export new version of class and routine into filesystem after each Save and check for a new version before each Open. All checkouts, commits and other work with Subversion is done via external tools (we use console svn and GUI SmartSVN Foundation).
There is also "Build version" routine which just delete all classes and routines from namespace and then load them from disk.
Subversion path is hard-coded into "c:\VSSDB\"_$ZN
All code here have no license, use it in any way.
There is no upload function on this wiki, so get it in plain text.
p5.SourceControl.xml
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="Cache" version="9" zv="Cache for Windows NT (Intel/P4) 5.0.15 (Build 5802)" ts="2005-09-30 15:21:55">
<Class name="p5.SourceControl">
<Description>
Организация работы с MS VSSDB
Dimas 2003, logist, shu 031018 - cache 5.04 - добавлены параметры в методы</Description>
<ProcedureBlock>1</ProcedureBlock>
<Super>%Studio.SourceControl.Base</Super>
<TimeChanged>60173,55314.083424</TimeChanged>
<TimeCreated>59078,62120</TimeCreated>
<ClassDefinitionError>0</ClassDefinitionError>
<Method name="Login">
<Description><![CDATA[
The username of this source control user.
This just calls the <method>Login</method> to perform an initialisation.
Note that you must call <method>SourceControlCreate</method> to create an instance
of the source control class, do not call %New directly.
Perform any login step here.]]></Description>
<FormalSpec>Name:%String,Password:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$OK
]]></Implementation>
</Method>
<Method name="OnBeforeLoad">
<Description>
Import the file from the external format into Cache.
This is called before the actual load of data to give the chance
to load the item from an external format.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
Set filename=..ExternalName(InternalName)
If filename="" Quit $$$OK
#; If no file then skip the import
If ($zu(140,1,filename)<0) {
Write:'$get(^ClearCase("nodisplay")) !,"File ",filename," not found, skipping import"
Quit $$$OK
}
If ($zu(140,2,filename)=$get(^ClearCase(0,"FileTimeStamp",InternalName))) {
Write:'$get(^ClearCase("nodisplay")) !,"File ",filename," is up to date, skipping import"
Quit $$$OK
}
Set name=$Piece(InternalName,".",1,$Length(InternalName,".")-1)
Set ext=$ZConvert($Piece(InternalName,".",$Length(InternalName,".")),"U")
If ( (ext="CLS") || (ext="MAC") || (ext="PRJ") ) {
Set sc=$$Import^%occSAX(filename,"-d-l")
} Else {
Set sc=$$Import^%apiRTN(filename,"all,replace,nocompile,lock=0",,,,"-d")
}
If ($$$ISOK(sc)) {
Write:'$get(^ClearCase("nodisplay")) !,"Imported '",InternalName,"' from file '",filename,"'"
Set ^ClearCase(0,"FileTimeStamp",InternalName)=$zu(140,2,filename)
} Else {
do DecomposeStatus^%apiOBJ(sc,.errors)
Do:'$get(^ClearCase("nodisplay"))&($g(errors(1,"code"))'=6301) DecomposeStatus^%apiOBJ(sc,.errors,"d")
}
Quit sc
]]></Implementation>
</Method>
<Method name="OnAfterSave">
<Description>
Export the routine/class/csp file to the external format.
This is called after the item has been saved to the database.</Description>
<FormalSpec>InternalName:%String,Object:%RegisteredObject=$$$NULLOREF</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
Set filename=..ExternalName(InternalName)
If filename="" Quit $$$OK
Set name=$Piece(InternalName,".",1,$Length(InternalName,".")-1)
Set ext=$ZConvert($Piece(InternalName,".",$Length(InternalName,".")),"U")
If ( $zu(140,1,filename)<0 ) { Do ..MakeDir(filename) }
If ( (ext="CLS") ) {
Set sc=$$Export^%occSAX(filename,"-d",InternalName)
}
If $$$ISOK(sc) {
Write:'$get(^ClearCase("nodisplay")) !,"Exported '",InternalName,"' to file '",filename,"'"
Set ^ClearCase(0,"FileTimeStamp",InternalName)=$zu(140,2,filename)
} Else {
do DecomposeStatus^%apiOBJ(sc,.errors)
Do:'$get(^ClearCase("nodisplay"))&($g(errors(1,"code"))'=6301) DecomposeStatus^%apiOBJ(sc,.errors,"d")
}
Quit $$$OK
]]></Implementation>
</Method>
<Method name="OnBeforeSave">
<Description>
Called before the item is saved to the Cache database it is passed
a reference to the current temporary storage of this item so that it
can be modified before the save completes. If you quit with an error
value then it will abort the save.</Description>
<FormalSpec>InternalName:%String,Location:%String,Object:%RegisteredObject=$$$NULLOREF</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$OK
]]></Implementation>
</Method>
<Method name="OnBeforeCompile">
<Description>
Called before the compile of the item is done.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$OK
]]></Implementation>
</Method>
<Method name="OnAfterCompile">
<Description>
Called after the compile of the item is done.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
Set filename=..ExternalName(InternalName)
If filename="" Quit $$$OK
Set name=$Piece(InternalName,".",1,$Length(InternalName,".")-1)
Set ext=$ZConvert($Piece(InternalName,".",$Length(InternalName,".")),"U")
If ( (ext'="CLS") ) { Quit $$$OK } //#
// don't export ...admin.Build...
If ( $zu(140,1,filename)<0 ) { Do ..MakeDir(filename) }
If ( (ext="CLS") ) {
Set sc=$$Export^%occSAX(filename,"-d",InternalName)
}
If $$$ISOK(sc) {
Write:'$get(^ClearCase("nodisplay")) !,"Exported '",InternalName,"' to file '",filename,"'"
Set ^ClearCase(0,"FileTimeStamp",InternalName)=$zu(140,2,filename)
} Else {
do DecomposeStatus^%apiOBJ(sc,.errors)
Do:'$get(^ClearCase("nodisplay"))&($g(errors(1,"code"))'=6301) DecomposeStatus^%apiOBJ(sc,.errors,"d")
}
Quit $$$OK
]]></Implementation>
</Method>
<Method name="CheckIn">
<Description>
Check this routine/class/csp file into source control.</Description>
<FormalSpec>InternalName:%String,Description:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$ERROR(10000,"Работать с контролем версий можно только из VSSDB")
]]></Implementation>
</Method>
<Method name="CheckOut">
<Description>
Check this routine/class/csp file out of source control.</Description>
<FormalSpec>InternalName:%String,Description:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$ERROR(10000,"Работать с контролем версий можно только из VSSDB")
]]></Implementation>
</Method>
<Method name="GetLatest">
<Description>
Get the latest version of this file from source control.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$ERROR(10000,"Работать с контролем версий можно только из VSSDB")
]]></Implementation>
</Method>
<Method name="GetStatus">
<Description>
Return information about this entity.</Description>
<FormalSpec><![CDATA[InternalName:%String,&IsInSourceControl:%Boolean,&Editable:%Boolean,&IsCheckedOut:%Boolean,&UserCheckedOut:%String]]></FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[
Set Editable=1,IsCheckedOut=0,UserCheckedOut=""
Set filename=..ExternalName(InternalName)
Set IsInSourceControl=(filename'="")
If ( filename="" ) { Quit $$$OK } //#
If ($zu(140,1,filename)<0) { Quit $$$OK } //#
Open filename:"W":0 Set Editable=$T Close filename
If ('Editable) {
Write:'$get(^ClearCase("nodisplay")) !,InternalName," locked in SourceControl"
}
Quit $$$OK
]]></Implementation>
</Method>
<Method name="AddToSourceControl">
<Description>
Add this routine/class/csp file to source control.</Description>
<FormalSpec>InternalName:%String,Description:%String</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$OK
]]></Implementation>
</Method>
<Method name="RemoveFromSourceControl">
<Description>
Remove this routine/class/csp file from source control.</Description>
<FormalSpec>InternalName:%String,Description:%String=""</FormalSpec>
<ReturnType>%Status</ReturnType>
<Implementation><![CDATA[ Quit $$$OK
]]></Implementation>
</Method>
<Method name="IsInSourceControl">
<Description>
Returns true if this item is in source control and false otherwise.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%Boolean</ReturnType>
<Implementation><![CDATA[ Quit ..ExternalName(InternalName)'=""
]]></Implementation>
</Method>
<Method name="ExternalName">
<Description>
Convert the internal name, e.g. TEST.MAC to an external name that is used to export
the routine/class/csp item. This is often a filename to write the file out to.</Description>
<FormalSpec>InternalName:%String</FormalSpec>
<ReturnType>%String</ReturnType>
<Implementation><![CDATA[
Set nam=$p(InternalName,".",1,$l(InternalName,".")-1),
ext=$p(InternalName,".",$l(InternalName,".")),
ext=$zcvt(ext,"u")
If ( nam="" ) { Quit "" } //#
If ( ext="CLS" ) {
Quit "c:\VSSDB\"_$znspace_"\Classes\"_$translate(nam,".","\")_".xml"
}
Quit ""
]]></Implementation>
</Method>
<Method name="MakeDir">
<FormalSpec>filename:%String</FormalSpec>
<Private>1</Private>
<Implementation><![CDATA[
Set filename=$piece(filename,"\",1,$length(filename,"\")-1)
If ( $zu(12,filename,2)="" ) {
For i=1:1:$length(filename,"\") Do $ZU(140,9,$piece(filename,"\",1,i))
}
]]></Implementation>
</Method>
</Class>
<Checksum value="3580265751"/>
</Export>
BuildVersion.mac
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="Cache" version="20" zv="Cache for Windows (Intel) 5.2.3 (Build 710)" ts="2007-06-28 18:26:20">
<Routine name="BuildVersion" type="MAC" languagemode="0" timestamp="60809,66369"><![CDATA[
BuildVersion(ver="") //загрузка и компиляция классов для ДИМАС
// do ^BuildVersion()
new DirMac, DirClasses, DirCsr, DirCsp, time, build
set DirMac="c:\vssdb\dimas\mac" //path to .MAC
set DirClasses="c:\vssdb\dimas\classes" //path to classes
set DirCsr="\csp\dimas" //path to csr
set DirCsp="\csp\dimas" //path to CSP
set time = $piece($horolog,",",2)
set File=##class(%FileCharacterStream).%New()
set File.Filename=DirClasses_"\.svn\entries"
while 'File.AtEnd {
set line=File.ReadLine()
if $find(line,"revision")'=0 set build=$piece(line,"""",2)
}
write !,"delete classes:",!
do $system.OBJ.DeletePackage("*")
write !,"delete routine:",!
set rset = ##class(%ResultSet).%New()
set rset.ClassName = "%Library.Routine"
set rset.QueryName = "RoutineList"
for rext = "*.MAC","*.INT","*.OBJ" {
do rset.Execute(rext,,1)
while (rset.Next(.sc)) {
if ($SYSTEM.Status.IsOK(sc)) {
set rname = rset.Data("Name")
if ($t(+0)'=$extract(rname,1,$length(rname)-4)) {
write rset.Data("Name"),!
do ##class(%Library.Routine).Delete(rname)
}
}
}
}
write !,"Load MAC "_DirMac_":",!
set sc = $system.OBJ.LoadDir(DirMac,"c","",1)
write !,"compile routines:",!
for rext = "*.INT","*.MAC" {
do rset.Execute(rext)
while (rset.Next(.sc)) {
if ($SYSTEM.Status.IsOK(sc)) {
set rname = rset.Data("Name")
if ($t(+0)'=$extract(rname,1,$length(rname)-4)) {
write rset.Data("Name"),!
do $system.OBJ.CompileList(rname,"c-d")
}
}
}
}
kill rset
write !,"Load class "_DirClasses_":",!
write $system.OBJ.LoadDir(DirClasses,"","",1),!
write !,"Compile all:",!
write $system.OBJ.CompileAll(),!
write !,"Load rules: ",!
do $SYSTEM.CSP.DeleteAllRules()
do $SYSTEM.CSP.LoadRuleDir(DirCsr)
write !,"Clear cached queries via $system.SQL.Purge(0)",!
Do $system.SQL.Purge(0) ; Purge all Cached Queries
kill ^mcq,^mqh // for Cache 5.2
set time=$piece($horolog,",",2) - time
write !,"Time: ",time\60," min. ",time#60," sec.",!
quit
//#
]]></Routine>
</Export>