Home  /  Application Development: Additional Options  /  Using Studio  /  Using Studio Source Control Hooks

Using Studio
Using Studio Source Control Hooks
[Back]  [Next] 
InterSystems: The power behind what matters   

To place InterSystems IRIS code under source control, you need to connect Studio to a third-party source control system. This appendix describes how to do this. It discusses the following topics:
To place an InterSystems IRIS development project under source control, do the following:
InterSystems IRIS Documents
An InterSystems IRIS document is a class definition, a routine, an include file, or a CSP file. InterSystems IRIS records information about each InterSystems IRIS document, such as whether it has changed since the last compilation. Your source control system treats each InterSystems IRIS document as a separate unit. The state of a document is shown by an icon in the document window.
In InterSystems IRIS, you work within one namespace at a time. The same is true for your source control system.
Tools for Managing Documents and Files
InterSystems IRIS provides the following tools for managing InterSystems IRIS documents and external files:
Deciding How to Map Internal and External Names
Each document has two names:
You will set up a bidirectional mapping between the internal names and the external names. In practice, deciding how to do this may be one of the most challenging parts of creating a source control interface. This mapping is customer-specific and should be considered carefully.
You want the source control tool to group similar items. For example, the sample uses the following directory structure:
For example, the external name for the class MyApp.Addresses.HomeAddress is C:\sources\cls\MyApp\Addresses\HomeAddress.xml.
This approach might be problematic if you had large numbers of routines. In such a case, you might prefer to group routines into subdirectories in some manner, perhaps by function.
Creating and Activating a Source Control Class
This section describes the basic requirements for creating and activating a source control class.
Extending Studio
InterSystems IRIS provides classes that you can use to add menu items to Studio. To add a source control menu to Studio, you would use either %Studio.Extension.Base or %Studio.SourceControl.Base.
Limit on how many menus you can add to Studio: You can add up two menus with 19 menu items each.
The %Studio.Extension.Base class provides the following methods, which all use the internal name of the InterSystems IRIS document:
Studio compiles processes in separate threads. If you set properties in %Studio.Extension.Base, they may not be accessible in subsequent calls, as they may be running in different object instances. Do not use a properties to pass information from MenuItem to OnBeforeCompile. Instead, use a temporary global.
The %Studio.SourceControl.Base class is a subclass of the preceding class. %Studio.SourceControl.Base provides the following additional elements:
To extend Studio, you define a new class that extends one of these classes. As you see in Activating a Source Control Class,” the Management Portal provides a way to indicate which extension class is currently active in a given namespace. If an extension class is active in a given namespace, and if that class defines an XDATA menu block, those menu items are added to Studio.
Creating a Source Control Class
To create a source control class, do the following:
  1. If you started with %Studio.Extension.Base, create an XDATA block named Menu in your subclass. (Copy and paste from %Studio.SourceControl.Base to start this.)
  2. Implement the methods of this class as needed: AddToSourceControl, CheckIn, CheckOut, and so on. These methods would typically do the following, at a minimum:
    The details depend upon the source control system. The sample demonstrates some useful techniques. See the section Sample Source Control Class in this book.
  3. Implement the GetStatus method of your source control class. This is required. You might also need to implement the IsInSourceControl method, if the default implementation is not suitable.
Activating a Source Control Class
To activate a source control class for a given namespace, do the following:
  1. Use the Management Portal to specify which extension class, if any, Studio should use for a given namespace. To specify the class to use:
    1. Navigate to the [System] > [Configuration] > [Source Control Settings] page of the Management Portal. (Select System Administration > Configuration > Additional Settings > Source Control.)
    2. On the left, select the namespace to which this setting should apply.
    3. Select the name of the extension class to use (or select NONE) and select OK.
      This list includes all compiled subclasses of %Studio.Extension.Base.
  2. If Studio is currently open, close it and reopen it, or switch to another namespace and then switch back.
Accessing Your Source Control System
The API for your source control system provides methods or functions to perform source control activities such as checking files out. Your source control class will need to make the appropriate calls to this API, and the InterSystems IRIS server will need to be able to locate the shared library or other file that defines the API itself.
If the source control system provides a COM interface, you can generate a set of InterSystems IRIS wrapper classes that you can use to call methods in that interface. To do so, you use the InterSystems IRIS Activate Wizard in Studio. Given an interface and the name of the package to contain the classes, the wizard generates the classes. For information, see Using the InterSystems IRIS ActiveX Gateway.
Also, it is important to remember that InterSystems IRIS will execute the source control commands on the InterSystems IRIS server. This means that your XML files will be on the InterSystems IRIS server, and your file mapping must work on the operating system used on that server.
Example 1
For the following fragment, we have used the InterSystems IRIS Activate Wizard to generate wrapper methods for the API for VSS. Then we can include code like the following within your source control methods:
 do ..VSSFile.CheckIn(..VSSFile.LocalSpec,Description)
The details depend on the source control software, its API, and your needs.
Example 2
The following fragment uses a Windows command-line interface to check out a file. In this example, the source control system is Perforce:
/// Check this routine/class/csp file out of source control.
Method CheckOut(IntName As %String, Description As %String) As %Status
  Set file=..ExternalName(IntName)
  If file="" Quit $$$OK
 Set cmd="p4 edit """_file_""""

  #; execute the actual command
  Set sc=..RunCmd(cmd)
  If $$$ISERR(sc) Quit sc

  #; If the file still does not exist or
  #; if it is not writable then checkout failed
  If '##class(%File).Exists(file)||(##class(%File).ReadOnly(file)) {
    Quit $$$ERROR($$$GeneralError,
                  "Failure: '"_IntName_"' not writeable in file sys")

  #; make sure we have latest version
  Set sc=..OnBeforeLoad(IntName)
  If $$$ISERR(sc) Quit sc

  Quit sc
In this example, RunCmd is another method, which executes the given command and does some generic error checking. (RunCmd issues the OS command via the $ZF(-1) interface.)
Also, this CheckOut method calls the OnBeforeLoad method, which ensures that the InterSystems IRIS document and the external XML file are synchronized.
Sample Source Control Class
The SAMPLES namespace provides a sample source control class, Studio.SourceControl.Example. This section shows how this sample works. The following topics are discussed:
The class in your SAMPLES namespace could be slightly different from the examples shown here. In particular, some of the line breaks have been adjusted for readability in this document.
Studio.SourceControl.Example is a partial example that does not make any calls to a source control system. It simply maintains external XML files that such a system would use. Despite this simplification, however, the sample demonstrates all the following:
Note that the sample does not modify the read-write state of the external files; the source control system would be responsible for that. Also, the sample implements only the Check In and Check Out methods.
To try this example in the SAMPLES namespace, do the following:
  1. Use the Management Portal to enable this source control class (Studio.SourceControl.Example), as described earlier in Activating a Source Control Class.”
  2. In the Studio Workspace window, double-click an InterSystems IRIS document. Notice a message like the following in the Output window:
    File C:\sources\cls\User\LotteryUser.xml not found, skipping import
  3. Edit the document (for example by adding a comment).
  4. Select File —> Save. You will see a message like the following in the Output window:
    Exported 'User.LotteryActivity.CLS' to file
    At this step, you have implicitly added the InterSystems IRIS document to the source control system.
  5. Try to make another edit. The Studio displays a dialog box that asks if you want to check the file out. Select No. Notice that the InterSystems IRIS document remains read-only.
  6. Select Source Control —> Check Out and then select Yes. You can now edit the InterSystems IRIS document.
  7. Select Source Control —> Check In and then select Yes. The InterSystems IRIS document is now read-only again.
Other menu items on the Source Control menu do nothing, because the sample implements only the Check In and Check Out methods.
The Studio.SourceControl.Example sample uses a global to record any needed persistent information. Methods in this class maintain and use the ^MySourceControl global, which has the following structure:
Node Contents
^MySourceControl("base") The absolute path of the directory that will store the XML files. The default is C:\sources\
^MySourceControl(0,IntName), where IntName is the internal name of an InterSystems IRIS file The date and time when the corresponding external file was last modified
^MySourceControl(1,IntName) The date and time when this InterSystems IRIS document was last modified
^MySourceControl(2,IntName) The name of the user who has this InterSystems IRIS document checked out, if any
This global is purely a sample and is used only by this class.
Determining the External Names
If you enable the Studio.SourceControl.Example class, it maintains external XML files that correspond to any InterSystems IRIS document that you load or create. It writes these files to the directory C:\sources\ by default, as described in Deciding How to Map Internal and External Names.” For example, the external name for the class MyApp.Addresses.HomeAddress is C:\sources\cls\MyApp\Addresses\HomeAddress.xml.
Within the sample, the ExternalName method determines the external file name for any InterSystems IRIS document. This method is as follows:
Method ExternalName(IntName As %String) As %String
 Set name=$piece(IntName,".",1,$length(IntName,".")-1)
 Set ext=$zconvert($piece(IntName,".",$length(IntName,".")),"l")
 If name="" Quit ""
 Set filename=ext_"\"_$translate(name,".","\")_".xml"
 Quit $get(^MySourceControl("base"),"C:\sources\")_filename
The sample is suitable only for Windows, of course. The implementation of this method would need to be different on UNIX® or OpenVMS.
Synchronizing the InterSystems IRIS Document and the External File
Two methods are responsible for ensuring that the InterSystems IRIS document and the corresponding XML file are kept synchronized with each other:
In the sample, the OnBeforeLoad method is as follows:
Method OnBeforeLoad(IntName As %String) As %Status
 Set filename=..ExternalName(IntName)
 If filename="" Quit $$$OK

 #; If no file then skip the import
 If '##class(%File).Exists(filename) {
     Write !,"File ",filename," not found, skipping import"
     Quit $$$OK

 #; If the timestamp on the file is the same as the last time
 #; it was imported, then do nothing
 If ##class(%File).GetFileDateModified(filename)=
                   $get(^MySourceControl(0,IntName)) {
     Quit $$$OK

 #; Call the function to do the load
 Set sc=$system.OBJ.Load(filename,"-l-d")
 If $$$ISOK(sc) {
 Write !,"Imported '",IntName,"' from file '",filename,"'"
 Set ^MySourceControl(0,IntName)=##class(%File).GetFileDateModified(filename)
 Set ^MySourceControl(1,IntName)=##class(%RoutineMgr).TS(IntName)
 } Else {
 Do $SYSTEM.Status.DecomposeStatus(sc,.errors,"d")
 Quit sc
The OnAfterSave method is analogous, as you can see in the sample itself.
Not only does Studio call these methods automatically as noted above, we will call these methods whenever we need to ensure that the InterSystems IRIS document and the external document are synchronized.
Controlling the Status of the InterSystems IRIS Document
The GetStatus method of your source control class is responsible for returning information about the status of the given InterSystems IRIS document. This method has the following signature:
Method GetStatus(IntName As %String,
                 ByRef IsInSourceControl As %Boolean,
                 ByRef Editable As %Boolean,
                 ByRef IsCheckedOut As %Boolean,
                 ByRef UserCheckedOut As %String) As %Status
Studio calls this method at various times when you work with an InterSystems IRIS document. It uses this method to determine if an InterSystems IRIS document is read-only, for example. When you implement a source control class, you must implement this method appropriately.
In the sample, this method is implemented as follows:
Method GetStatus(IntName As %String,
                 ByRef IsInSourceControl As %Boolean,
                 ByRef Editable As %Boolean,
                 ByRef IsCheckedOut As %Boolean,
                 ByRef UserCheckedOut As %String) As %Status
 Set Editable=0,IsCheckedOut=0,UserCheckedOut=""
 Set filename=..ExternalName(IntName)
 Set IsInSourceControl=(filename'=""&&(##class(%document).Exists(filename)))
 If 'IsInSourceControl Set Editable=1 Quit $$$OK

 If $data(^MySourceControl(2,IntName))
 {Set IsCheckedOut=1
 Set UserCheckedOut=$listget(^MySourceControl(2,IntName))}

 If IsCheckedOut,UserCheckedOut=..Username Set Editable=1
 Quit ..OnBeforeLoad(IntName)
Here is how this method works:
  1. It first initializes all the arguments that it returns by reference.
  2. The method then checks to see whether the external document exists yet; if it does not, the InterSystems IRIS document should be editable.
  3. The method then checks the ^MySourceControl global to see if anyone has checked this document out. If so, and if that user is the current user, the document is editable. If the document is checked out to a different user, it is uneditable to the current user.
  4. Finally, the method calls the OnBeforeLoad method, which was described earlier in this document. This step ensures that the InterSystems IRIS document and the external XML file are synchronized and that the relevant nodes of the ^MySourceControl global get set.
Source Control Actions
The sample implements methods for the two most basic source actions: check in and check out.
The CheckIn method is as follows:
Method CheckIn(IntName As %String, Description As %String) As %Status
 #; See if we have it checked out
 If '$data(^MySourceControl(2,IntName)) {
   Quit $$$ERROR($$$GeneralError,"You cannot check in an item
                                  you have not checked out")
 If $listget(^MySourceControl(2,IntName))'=..Username {
   Quit $$$ERROR($$$GeneralError,"User '"_
         $listget(^MySourceControl(2,IntName))_"'has this item checked out")

 #; Write out the latest version
 Set sc=..OnAfterSave(IntName)
 If $$$ISERR(sc) Quit sc

 #; Remove the global to show that we have checked it in
 Kill ^MySourceControl(2,IntName)
 Quit $$$OK
The CheckOut method is analogous.
These methods could be extended to include the appropriate calls to a third-party source control system.
Other Details
By default, the method IsInSourceControl calls the GetStatus method and gets the needed information from there.
In the sample, the method IsInSourceControl returns true for all internal names; recall that all documents are assumed to be under source control.
A class definition can be changed when you compile it, because compilation can update the storage information. Accordingly, the sample implements the OnAfterCompile method. This method just calls the OnAfterSave method, because it needs the same logic as that method provides; specifically, it needs to check whether the InterSystems IRIS document has changed and if so, save the XML file again.
We do not recommend using process private globals in source control hooks because processes may not run in the same thread.