Source Control 2

From CacheWiki
Jump to: navigation, search

Source Control 2

 
 Back

This version of source control for Caché inherits of the default class and add functionalities:

  • can control either class code or routine code
  • contains a method to build the whole project from the file sources
  • source control can shut off (in production mode)
  • source files can be modified outside Caché without calculating a checksum (case of xml export)

We use it with CVS version 2.2.

Include macros

/// Contenu de ^["USER"]MPLSources
/// ^["USER"]MySources(0,InternalName)= save date time in Horolog format
/// ^["USER"]MySources(1,InternalName)= save date time in ODBC format
/// ^["USER"]MySources(2,InternalName)= $LB( user , ? )
Class Roche.SourceControl Extends %Studio.SourceControl.Base [ ProcedureBlock ]
{

/// Check this routine/class/csp file into source control.
Method CheckIn(InternalName As %String, Description As %String) As %Status
{
	q ..ExportSource(InternalName)
}

/// Check this routine/class/csp file out of source control.
Method CheckOut(InternalName As %String, Description As %String) As %Status
{
	q ..ImportSource(InternalName)
}

/// Compare two date times in Horolog format
/// returns -1 if dh1<dh2, 0 if dh1=dh2 and 1 if dh1>dh2
ClassMethod DTCompare(dh1, dh2) As %Integer
{
	s d1=+dh1
	s d2=+dh2
	q:d1<d2 -1
	q:d1>d2 1
	s d1=$P(dh1,",",2)
	s d2=$P(dh2,",",2)
	q:d1<d2 -1
	q:d1>d2 1
	q 0
}

/// Export the whole project to file. Use it in terminal window
ClassMethod ExportProject(projectName)
{
	s class=""
	f  {
		s class=$O(^oddPROJECT(projectName,"Items",class))
		q:class=""
		s ext=$O(^oddPROJECT(projectName,"Items",class,""))
		i ext'="MAC" s class=class_"."_ext
		w class," -> ",..ExportSource(class,1),!
	}
}

///  Export a routine in RSA format to a file
ClassMethod ExportRoutine(routName, extension, fileName) As %Status
{
	s path=$P(fileName,"\",1,$L(fileName,"\")-1)
	d ##class(%File).CreateDirectoryChain(path)
	i '##class(%File).DirectoryExists(path) w !,"Path ",path," not created, skipping import" q $$$OK
	k ^%utility($J)
	s ^%utility($J,1)=routName
	s ^%utility($J,2)=""
	q $$rsave^%Wr("."_extension,fileName_":(""RWN"")","Source Control Export","Cache",0,0,1)
}

/// Export a source code
ClassMethod ExportSource(InternalName As %String, force = 0) As %Status
{
	// verify that source control is allowed on this server
	s sc=$G(^["USER"]MySources("sourcecontrol"),0)
	q:sc=0 $$$OK

	//InternalName: [package] . [classe] . CLS
	s filename=..GetClassFileName(InternalName)
	q:filename="" $$$OK
	i 'force
	{
		//quit if the file time is the same as the time we backed up for this source
		q:##class(%RoutineMgr).TS(InternalName)=$G(^["USER"]MPLSources(1,InternalName)) $$$OK
	}
	s className=$P(InternalName,".",1,$L(InternalName,".")-1), ext=$P(InternalName,".",$L(InternalName,"."))
	i ext="CLS"
	{	//export in CDL for classes
		s sc=$SYSTEM.OBJ.ExportCDL(className,filename,"-d")
	}
	else
	{
		i ext="PRJ"
		{
			//export project in XML
			s sc=$SYSTEM.OBJ.Export(InternalName,filename)
		}
		else
		{
			//export routines in RSA
			d ..ExportRoutine(InternalName,ext,filename)
			s sc=$$$OK
		}
	}
	i $$$ISOK(sc)
	{
		w !,"Exported '",InternalName,"' to file '",filename,"'"
		s ^["USER"]MPLSources(0,InternalName)=##class(%File).GetFileDateModified(filename)
		s ^["USER"]MPLSources(1,InternalName)=##class(%RoutineMgr).TS(InternalName)
	}
	else
	{
		w "Error ExportSource: "_sc,!
		d DecomposeStatus^%apiOBJ(sc,,"d")
	}
	q $$$OK
}

Method ExternalName(InternalName As %String) As %String
{
	q ..GetClassFileName(InternalName)
}

/// Returns the path and filename to export a source
/// <u>Argument:</u> InternalName: name of the class as [package] . [classe] . [extension]
/// <u>Retour</u> Path and filename as:<br>
/// Root path: from de ^["USER"]MySources("base")
/// / [Directory from extension] cls, mac, int
/// / [Directory from package]
/// / [Class Name].txt
ClassMethod GetClassFileName(InternalName As %String) As %String
{
	s name=$P(InternalName,".",1,$L(InternalName,".")-1),ext=$ZCONVERT($P(InternalName,".",$L(InternalName,".")),"l")
	q:name="" ""
	s fileExt=".txt"
	i ext="cls" s fileExt=".cdl"
	i ext="prj" s fileExt=".xml"
	s filename=ext_"\"_$TRANSLATE(name,".","\")_fileExt
	q $G(^["USER"]MySources("base"),"D:\")_filename
}

Method GetStatus(InternalName As %String, ByRef IsInSourceControl As %Boolean, ByRef Editable As %Boolean, ByRef IsCheckedOut As %Boolean, ByRef UserCheckedOut As %String) As %Status
{
	s Editable=0,IsCheckedOut=0,UserCheckedOut=""
	s filename=..ExternalName(InternalName)
	s IsInSourceControl=(filename'=""&&(##class(%File).Exists(filename)))
	i 'IsInSourceControl s Editable=1 q $$$OK
	
	s UserCheckedOut=..Username
	s Editable=1
	q ..ImportSource(InternalName)
}

/// Import all classes and sources related to a project from files. Run it from terminal
ClassMethod ImportProject(projectName)
{
	s class=""
	f  {
		s class=$O(^oddPROJECT(projectName,"Items",class))
		q:class=""
		s ext=$O(^oddPROJECT(projectName,"Items",class,""))
		i ext'="MAC" s class=class_"."_ext
		w class," -> ",..ImportSource(class,1),!
	}
}

/// Import a routine from a RSA formatted file
ClassMethod ImportRoutine(fileName) As %Status
{
	i '##class(%File).Exists(fileName) w !,"File ",fileName," not found, skipping import" q $$$OK
	//args: file,mode "Cache", selection: 0=all, overwrite: 2:always, compile: 1=yes, syntax, backup, showstat, langmode, pasting: 0:=no message
	q $$rload^%Wr(fileName,"Cache",0,2,1,1,0,0)
}

ClassMethod ImportSource(InternalName As %String, force = 0) As %Status
{
	i 'force
	{
		//verify source control is enabled on this server
		s sc=$G(^["USER"]MPLSources("sourcecontrol"),0)
		q:sc=0 $$$OK
	}
	
	//System file, continue
	i $E(InternalName,1)="%" q $$$OK

	s filename=..GetClassFileName(InternalName)
	q:filename="" $$$OK

	//file doen't exist, quit
	i '##class(%File).Exists(filename) w !,"File ",filename," not found, skipping import" q $$$OK
	i 'force
	{
		s comp=..DTCompare(##class(%File).GetFileDateModified(filename),$G(^["USER"]MySources(0,InternalName)))
		i comp=0
		{
			w !,InternalName,"same version, skipping import"
			q $$$OK
		}
		i comp<0
		{
			w !,InternalName,"file older, skipping import"
			q $$$OK
		}
	}
	//loading
	s ext=$P(InternalName,".",$L(InternalName,"."))
	i (ext="CLS")||(ext="PRJ")
	{	//import a class
		s sc=$SYSTEM.OBJ.Load(filename,"k-l-d") //-l-d
	}
	else
	{
		//import a routine
		s sc=..ImportRoutine(filename)
	}
	i $$$ISOK(sc)
	{
		w !,"Imported '",InternalName,"' from file '",filename,"'"
		s ^["USER"]MySources(0,InternalName)=##class(%File).GetFileDateModified(filename)
		s ^["USER"]MySources(1,InternalName)=##class(%RoutineMgr).TS(InternalName)
	}
	else
	{
		w "ImportSource Error: "_sc,!
		d DecomposeStatus^%apiOBJ(sc,.errors,"d")
	}
	q sc
}

Method IsInSourceControl(InternalName As %String) As %Boolean
{
	q 1
}

/// Export the file if modified after compilation
Method OnAfterCompile(InternalName As %String) As %Status
{
	q ..ExportSource(InternalName)
}

Method OnAfterSave(InternalName As %String, Object As %RegisteredObject) As %Status
{
	q ..ExportSource(InternalName)
}

Method OnBeforeLoad(InternalName As %String) As %Status
{
	q ..ImportSource(InternalName)
}

}
Personal tools