Article
Sylvain Guilbaud · Oct 4, 2016 6m read

How to add a parameter to a class programmatically

This sample class will add parameter DSINTERVAL to each class containing the parameter DSTIME

To execute it from a Terminal :


SAMPLES>d ##class(adm.param).add(,,1)
[SAMPLES] parameter DSINTERVAL=5 added to class DeepSee.Study.CityRainfall (via adm.param)
[SAMPLES] parameter DSINTERVAL=5 added to class HoleFoods.Transaction (via adm.param)
[SAMPLES] parameter DSINTERVAL=5 added to class News.DeepSee.NewsArticle (via adm.param)
DSINTERVAL parameter was added to 3 classes

Class adm.param [ Abstract ]
{

/// this class will add DSINTERVAL parameter to each class containing DSTIME
/// in order to synchronize a cube that is based on a source table on a mirror system
/// see: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=D2IMP_ch_current#D2IMP_current_dstime_enabling
ClassMethod add(name As %String = "DSINTERVAL", value As %String = "5", verbose As %Boolean = 0) As %Status
{
    set sc = $$$OK
    try {
        set statement = ##class(%SQL.Statement).%New()
        // %Dictionary classes are useful to select classes based on their content
        // here we use %Dictionary.ParameterDefinition class which contains all the parameters defined in all classes
        // then we will open the parent class to add the missing parameter
        set query="SELECT a.parent "
                 _"FROM %Dictionary.ParameterDefinition a "
                 _"WHERE a.name='DSTIME' "
                 _"AND substr(a.parent,1,1)<>'%' "
                 _"AND NOT EXISTS (SELECT null "
                 _"FROM %Dictionary.ParameterDefinition b "
                 _"WHERE b.name='"_name_"' and b.parent=a.parent)"
        set sc = statement.%Prepare(query)
        quit:$$$ISERR(sc)
        set classes="",nb=0
        set rs = statement.%Execute()
        while (rs.%Next(.sc)) {
            quit:$$$ISERR(sc)
            if (rs.parent '="") {
                // opening of the parent class 
                set class = ##class(%Dictionary.ClassDefinition).%OpenId(rs.parent)
                if $IsObject(class) {
                    // creating the new parameter
                    set param=##class(%Dictionary.ParameterDefinition).%New()
                    set param.Name=name
                    set param.Default=value
                    // adding the parameter to the parent class
                    do class.Parameters.Insert(param)
                    set sc=class.%Save()
                    // if parent class is successfully saved set the following message
                    set:sc msg="["_$namespace_"] parameter "_name_"="_value_" added to class "_rs.parent_" (via "_..%ClassName(1)_")",severity=0
                    // if parent class is not saved set the following message
                    set:'sc msg="["_$namespace_"] ERROR while saving "_rs.parent_" after having added the parameter "_name_"="_value_" (via "_..%ClassName(1)_") :"_$system.Status.GetErrorText(sc),severity=1
                    // if verbose=1 write the message to the terminal
                    write:verbose msg,!
                    // write the message to cconsole.log
                    do ##class(%SYS.System).WriteToConsoleLog(msg,verbose,severity)
                    // add the class name to compile 
                    set classes(rs.parent)="",nb=$increment(nb)
                }
            }
        }
        set:nb=0 msg=name_" parameter was not added to any class"
        set:nb=1 msg=name_" parameter was added to "_nb_" class"
        set:nb>1 msg=name_" parameter was added to "_nb_" classes"
        write:verbose msg,!
        // compile all modified classes 
        if nb>0 { 
            set sc=$system.OBJ.Compile(.classes,"ck"_$select(verbose:"d",1:"-d"))
            set:sc msg="["_$namespace_"] modified "_$select(nb=1:"class",1:"classes")_" successfully compiled",severity=0
            set:'sc msg="["_$namespace_"] ERROR while compiling classes : "_$system.Status.GetErrorText(sc),severity=1
            do ##class(%SYS.System).WriteToConsoleLog(msg,verbose,severity)
            write:verbose msg,!
        }    
    }
    catch(ex) {
        set sc = ex.AsStatus()
        set msg="["_$namespace_"] error while executing "_..%ClassName(1)_":add("_name_","_value_") : "_ex.DisplayString(),severity=1
        write:verbose msg,!
        do ##class(%SYS.System).WriteToConsoleLog(msg,1,severity)
    }
    return sc
}

}

You can also check the results in the cconsole.log file:


10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class DeepSee.Study.CityRainfall (via adm.param)
10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class HoleFoods.Transaction (via adm.param)
10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class News.DeepSee.NewsArticle (via adm.param)
10/05/16-19:39:11:515 (5744) 0 [SAMPLES] modified classes successfully compiled
20
2 1 6 674
Log in or sign up to continue

Replies

I think compile should be called once after the loop. While in the loop you can build a local array of affected classes and pass it to the compiler after the loop.

Done. Thanks for this comment Eduard !

Could you add a little bit of documentation to the code? While I think it's a useful example, documenting the code will make it easier to understand. Not everyone is knows about the %Dictionary classes. And it's generally a good idea to add explaining comments. Thanks!

I've added some comments in the code ; thank you Fabian.

Hi, Sylvian! Thanks for the sippet! What is the reason to add DSINTERVAL to all the classes? 

Hi Evgeny, 

this code was written while upgrading to an async mirrored a DeepSee remote instance (originally based on a shadow server configuration + ECP access to ^OBJ.DSTIME global from DeepSee instance to production. It was before DSINTERVAL was created). 

Of course this sample can be modified to add/remove/modify any other parameter by modifying the query on %Dictionary.ParameterDefinition to filter any other parameter you are trying to add/remove/modify.